There are a lot of applications which are written in Cobol or C or C++ or Assembler. Sometimes, it is necessary to communicate with these applications in Java application. Legacy applications send and receive messages in binary mode, so it is difficult for the legacy applications to understand XML or WebService or SOAP messages, and it is also difficult to construct a binary message that the legacy application can understand. As a result, formatters are useful in converting data.
The following section introduces the terms:
▪ Binary Message:
Binary Message is an array of bytes. You can regard it as an array of characters. Many legacy applications can only consume and produce Binary Message rather than XML or SOAP messages.
For example, following is an example of Binary Message: 06C785969987850400000012.
It is an array of bytes. The 06 is a length indicator indicating that the following 6 bytes C78596998785 should be recognized as a field. The legacy application parses it into a String, and the encoding is cp937. C78596998785 is the binary representation of the String "George". Byte 04 is also a length indicator, indicating that the following 4 bytes 00000012 should be recognized as a field. In this case, the legacy application parses it into an integer, and 00000012 is the binary representation of integer "18".
▪ Data Object:
Data Object is a structured data in Java. There are many kinds of Data Objects, such as BTTContext, DataElement, Java Bean Service, and HashMap that holds keys and values.
▪ Format:
Format is to translate a Data Object into a Binary Message.
▪ Unformat:
Unformat is to translate a Binary Message into a Data Object.
▪ Formatter:
Formatter component is used to handle legacy binary messages. It can format a data object into binary message, and unformat a binary message into data object. To do so, you only need several lines of Java code and a rule defined in an XML file, for example, FormatElement. There are many predefined FormatElements that can be used directly. You can add your own FormatElements.
Formatter translates data object through a DataAdapter interface. The toolkit provides JavaDataAdapter (adapter for java data object) and BTTContextDataAdapter (UDTT Context data object). You can extend your own adapter to support other types data objects, for example SDODataAdapter (adapter for Service Data Object), HashMapDataAdapter (adapter for HashMap), and so on.
Take the binary message 06C785969987850400000012 for example.
It is an array of bytes:
▪ The 06 is a length indicator indicating that the following 6 bytes C78596998785 should be recognized as a field. The legacy application parses it into a String, and the encoding is cp937.
▪ C78596998785 is the binary representation of the String "George".
▪ Byte 04 is also a length indicator, indicating that the following 4 bytes 00000012 should be recognized as a field. In this case, the legacy application parses it into an integer.
▪ 00000012 is the binary representation of integer "18".
The binary message is a data object with two fields: the first one is String and the second field is an integer.
Example
This example defines a JavaBean to hold data in Java world:
public class Person { private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
▪ The tag <format id="PersonFormat"> is the root element of the formatter. The Java code finds this definition by the value of id, that is PersonFormat.
▪ Tag <record> is a representation of Person in the formatter definition. Data that can hold a set of fields is defined as <record>.
▪ Tag <fString dataName="name" encoding="cp937"/> knows how to parse an array of bytes into a Java String. fString uses cp937 and puts the parsed data into field name of type Person.
▪ Tag <selfLength/> knows how to handle the length indicator in the binary message.
▪ Tag <fInteger dataName="age" byteOrdering="host"/> knows how to parse an array of bytes into an integer. fInteger parses the bytes in big-endian byte ordering, and puts the parsed data into the field age of type Person.
Following is the sample code for how to unformatting binary message into the JavaBean Person.
public static void main(String[] args) throws Exception { //Read btt.xml from the default package in class-path InitManager.reset("jar:///btt.xml"); //Create a instance of Person, left the field data blank Person person = new Person(); //Get the defined format element by id "PersonFormat" FormatElement format = FormatFactory.getFormatElement("PersonFormat"); //Create a WriteAdapter for person WriteAdapter write = new JavaWriteAdapter(person); //Prepare binary message byte[] bytes = HexCodecUtil.decodeHex("06C785969987850400000012".toCharArray()); Message message = new Message(bytes); //Perform unformat procedure format.unformat(message, write);
This section describes how to convert an instance of Person into binary message. Following is the sample code:
public static void main(String[] args) throws Exception { InitManager.reset("jar:///format/config/abc.xml"); //Prepare person populate data into its fields Person person = new Person(); person.setName("George. Wilhelm. T"); person.setAge(57); //Get the defined format element by id "PersonFormat" FormatElement format = FormatFactory.getFormatElement("PersonFormat"); //Create a ReadAdapter for person ReadAdapter read = new JavaReadAdapter(person); //Perform unformat action Message msg = format.format(read);
1 You can create a facade class in your application. With this facade class, you can call the formatJavaBean and unformatJavaBean methods. Following is the code sample:
public class FormatFacade { public static byte[] formatJavaBean(String formatID, Object javaBean) throws FormatterException { ReadAdapter adapter = new JavaReadAdapter(javaBean); FormatElement format = FormatFactory.getFormatElement(formatID); Message message = format.format(adapter); return message.toBytes(); } public static void unformatJavaBean(String formatID, byte[] data, Object javaBean) throws FormatterException { WriteAdapter adapter = new JavaWriteAdapter(javaBean); FormatElement format = FormatFactory.getFormatElement(formatID); format.unformat(new Message(data), adapter); } }
2 For each formatID, there is only one instance of format element.
You can call method public static FormatElement FormatFactory.getFormatElement(String formatID) to get the instance of format element for the formatID defined in the xml file. FormatFactory caches the format element instance of each formatID for further usage.
3 There are two ways to instantiate FormatElement.
▪ Creating format element from xml definition.
To create a format element from xml definition, you need to define the format element in the xml file first. Following is a sample defintion:
After the definition is ready, you can create the format element instance using the following code:
FormatElement format = FormatFactory.getFormatElement(formatID);
▪ Creating format element by hard code. Following is the sample code:
FormatDefine format = new FormatDefine(); format.setId("PersonFormat"); RecordFormat record = new RecordFormat(); StringFormat strF = new StringFormat(); strF.setDataName("name"); strF.setEncoding("cp937"); SelfLength selfLength1 = new SelfLength(); IntegerFormat intF = new IntegerFormat(); intF.setDataName("age"); intF.setByteOrdering("host"); SelfLength selfLength2 = new SelfLength(); record.addChild(strF); record.addChild(selfLength1); record.addChild(intF); record.addChild(selfLength2); format.addChild(record);
The UDTT formatter manipulates data object through an interface DataAdapter. The toolkit provides JavaDataAdapter (adapter for Java data object) and BTTContextDataAdapter (adapter for BTTContext data object). You can extend your own adapter to support other kinds of data object, for example, SDODataAdapter (adapter for Service Data Object), HashMapDataAdapter (adapter for HashMap), and so on.
The Formatting a JavaBean into binary messagedescribes how to handle Java data object with JavaDataAdapter. This section describes how to handle BTTContext and BTTDataElement with BTTContextDataAdapter and BTTDataElementDataAdapter, and the instructions on how to support more kinds of data object by implementing your own DataAdapter are also provided.
In the unformatting process, it is not required to define the data element. You can use a dynamic data element instead of predefined data element. Following is the sample code to unformat the dynamic data element:
public static void main(String[] args) throws Exception { KeyedCollection dataElement = new KeyedCollection(); dataElement.setDynamic(true);
FormatElement format = FormatFactory.getFormatElement("PersonFormat"); WriteAdapter write = new DataElementWriteAdapter(dataElement); byte[] bytes = HexCodecUtil.decodeHex("06C785969987850400000012".toCharArray()); Message message = new Message(bytes); format.unformat(message, write); System.out.println("====Unformat Result===="); System.out.println(dataElement); }
If you run this main method, you can get the following result in the console:
You can implement your own DataAdapter to support more kinds of data object. You need to implement com.ibm.btt.format.ReadAdapter and com.ibm.btt.format.WriteAdapter.
2 Create a Java class com.mycompany.format.MyFormatElement.
This class needs to be a subclass of com.ibm.btt.format.CompositeFormat, com.ibm.btt.format.FieldFormat or com.ibm.btt.format.BaseDecorator. Any class in package com.ibm.btt.format.impl is not intended to be subclassed.
3 Create default constructor method for class com.mycompany.format.MyFormatElement.
Every format element can be instantiated by default constructor method.
4 Create get and set methods for the attributes.
Format element can be customized in definition. For example, you can specify encoding attribute for com.ibm.btt.format.impl.StringFormat in the format definition xml tag. If your format element has some attributes to be customized, you need to create get and set methods for them. The methods need to comply with Java Bean specification. When you create the instance of FormatElement, FormatFactory populates your attributes from definition automatically. The attribute can be in simple types such as int, Integer, float, Float, long, Long, double, Double, byte, Byte, char, Character and String, and so on.
5 Override or implement methods from base class in class com.mycompany.format.MyFormatElement. The following table tells which method you should override or implement.
Methods that can be overridden
Base ClassDescription
Methods
Override
CompositeFormatSplit the input message into two messages, and return them as an array in length of 2. The first message in the array is the message that should be processed by this format element, and the second message is the remaining message.
Take Message 1FCD2639FE for example: the returned array of Message may be {1FCD, 2639FE}. The first element 1FCD is the Message required by this FormatElement, while the second element 2639FE is the remaining Message.
Translate the data into binary message.
Translate the binary message into data.
Return the children format element of the composite format element. You can call it in extract, format and unformat methods.
public Message[] extract(Message message) throws ExtractException;
YES
public Message format(ReadAdapter dataAdapter) throws FormatException;
YES
public void unformat(Message message, WriteAdapter dataAdapter) throws UnformatException
YES
public List<FormatElement>getChildren ();
NO
FieldFormatSplit the input message into two messages, and returned them as an array in length of 2.
Translate the data into binary message.
Translate the binary message into data.
public Message[] extract(Message message) throws ExtractException;
YES
public Message format(ReadAdapter dataAdapter) throws FormatException;
YES
public void unformat(Message message, WriteAdapter dataAdapter) throws UnformatException
YES
BaseDecoratorSplit the input message into two messages, and returned them as an array in length of 2.
public Message[] extract(Message message) throws ExtractException;
YES
Modify the binary message after execute format method of the decorated format element.
You can override this method. It returns a Map with both key and value in type of String. The key represents the attribute name and the value represents the attribute value. This method is only used in toString() method of the format element.
FormatFacory leverages ElementFactory to create FormatElement. FormatFacory a subclass of ElementFactory, but it can only be accessed in static way. Because you cannot get the instance of FormatFactory, it is difficult to customize BasicElementFactory as usual. And because classTable is configured in btt.xml, you can also configure your FormatFactory to use your own ElementProcessor.
To implement your own ElementProcessor, you need to implement the interface com.ibm.btt.element.ElementProcessor, besides this interface, you need also implement com.ibm.btt.config.Initializer, which is used to pass configuration into your own ElementProcessor.
Sample code for ElementProcessor
public class DefaultValueProcessor implements ElementProcessor, Initializer { private Map<String, Map<String, String>> values = new HashMap<String, Map<String, String>>(); public Object afterInitializeElement(Tag tag, Object element) throws ElementException { return element; } public Tag beforeCreateElement(Tag tag) throws ElementException { Map<String, String> defaultAttrs = values.get(tag.getname()); if (defaultAttrs != null) { Set set = defaultAttrs.keySet(); Iterator<String> iter = set.iterator(); while(iter.hasNext()) { String attrName = iter.next(); if (tag.getAttributeValue(attrName) == null){ TagAttribute attr = new AttributeImp(); attr.setName(attrName); attr.setValue(defaultAttrs.get(attrName)); tag.addAttribute(attr); } } } return tag; } public Object beforeInitializeElement(Tag tag, Object element) throws ElementException { return element; } public void cleanup(KeyedCollection config) throws BTTCleanUpException { } public void initialize(KeyedCollection config) throws BTTInitException { try { IndexedCollection icoll = (IndexedCollection) config.getElementAt("defaultValues"); for (int i = 0; i < icoll.size(); i ++) { KeyedCollection kc = (KeyedCollection) icoll.getElementAt(i); String tagName = (String) kc.getValueAt("tagName"); String attributeName = (String) kc.getValueAt("attributeName"); String defaultValue = (String) kc.getValueAt("defaultValue"); if (values.get(tagName) == null) { values.put(tagName, new HashMap<String, String>()); } values.get(tagName).put(attributeName, defaultValue); } } catch (DSEObjectNotFoundException e) { throw new BTTInitException( "Configuration error to DefaultValueProcessor"); } } }
Configuring format component to use your own ElementProcessor
You need to configure format component so that it uses your own ElementProcessor. You need to add a kColl of ElementProcessor, and in this kColl, you need to specify a field name class. Then you need to add the parameter of your own ElementProcessor in the kColl.
With this ElementProcessor, you can get the following definition:<fInteger dataName="dByte" />, which equals the following definition: <fInteger dataName="dByte" size="4" byteOrdering="host">.
All UDTT components recognize the class com.ibm.btt.base.FormatElement, but not the class com.ibm.btt.format.FormatElement.
The com.ibm.btt.base.FormatElement is the class of the Formatter in versions of UNICOM® Multichannel Bank Transformation Toolkit prior to version 6.1, and the com.ibm.btt.format.FormatElement is the class of the Formatter in UNICOM® Digital Transformation Toolkit version 10.0 and UNICOM® Multichannel Bank Transformation Toolkit version 6.1 and later.
Differences between formatters in releases prior to UNICOM® Multichannel Bank Transformation Toolkit V6.1
Features
Formatter in UNICOM® Multichannel Bank Transformation Toolkit V6.1 and later
Formatter in previous releases
Supported data objects
BTTContext
BTTDataElement
JavaBean
BTTContext
BTTDataElement
Extensibility in data object
Support more data objects by implementing DataAdapter interface
Not supported
Built-in FormatElements
14 FormatElements
14 FormatElements
Extensibility in adding more FormatElements
Formatter in UNICOM® Multichannel Bank Transformation Toolkit V6.1 and later has simplified API.
Supported
Xml definition
Compatible with Formatter in previous releases
Can only read definition from a single xml file
Customize TagProvider
Support your own TagProvider
Not supported
ElementProcessor
Can preprocess FormatElement when you create it by FormatFactory. This feature is provided by ElementFactory.
Not supported
Formatter Simulator support (Formatter Simulator is a tool that can simulate the execution of unformat process to help verify the formatter definition)
Integrating new formatter with other UDTT components
Generally speaking, it is suggested that you use the new formatter in your application. The new formatter is an independent component, you can use it anywhere whenever you need it. There are two ways to integrate new formatter with other toolkit components.
Using FormatFacade
The following example demonstrates how to use FormatFacade in other toolkit components:
public class FormatFacade { public static byte[] formatContext(String formatID, Context ctx) throws FormatterException { ReadAdapter adapter = new ContextReadAdapter(ctx); FormatElement format = FormatFactory.getFormatElement(formatID); Message message = format.format(adapter); return message.toBytes(); } public static void unformatContext(String formatID, byte[] data, Context ctx) throws FormatterException { WriteAdapter adapter = new ContextWriteAdapter(ctx); FormatElement format = FormatFactory.getFormatElement(formatID); format.unformat(new Message(data), adapter); } }
Using FormatAdapter
Sometimes, you cannot use the new formatter directly. In this case, you can use FormatAdapter to integrate the new formatter with other toolkit components.
▪ Configure old formatter component to create FormatAdapter:
You need to configure the old formatter component and new formatter to use the same definition file, and configure the old formatter to create FormatAdapter when necessary. Following is an example:
In this example, externalizer in the old formatter component creates the a FormatAdapter when the root tag is format.
▪ Define new FormatElement:
Then you need to define FormatElement in the way of new formatter component. The root tag is different with the old formatter. In the old formatter component, the root tag can only be fmtDef. But in the new formatter component, you can configure the root tag in classTable.
In this way, the format instance is created from FormatElement. FormatElement.readObject("sampleFmt") is an instance of com.ibm.btt.format.impl.FormatAdapter. FormatAdapter extends com.ibm.btt.base.FormatElement and it delegates all the logic to the new formatter component.