Work on this project started off based on my involvement in a project where inter-process communication between different components was done using OFX, an SGML based dialect which, in many respects, was a precursor to IFX. IFX offered the same features as OFX, but allowed for easier parsing since it was well-formed XML. Also, because it was backed by an industry-wide consortium, it seemed to have the momentum that would lead to its adoption in many financial institutions.
Using IFX to communicate across different applications within an organization, or even between different organizations, would lead to efficiencies because all applications would communicate with each other using the same language, and the translation of the XML to application objects and back would have to be done once, and can then reused. The IFX-Framework addresses the issues of translating from the IFX XML to JavaBean application objects and back.
Financial institutions are generally not very receptive to open-source. However, I believe the existence of an open shared framework that sits between the XML and the application layer would be beneficial to all. Developers can concentrate on the actual application logic rather than on the details of interprocess-communication, thereby reducing time-to-market for new IFX-aware applications. Developers can also contribute to the development of the IFX-Framework if they feel that some missing functionality needs to be addressed, and the benefits of their work can be gratefully enjoyed by the rest of the community.
The IFX-Framework provides an open source framework to build and parse IFX XML messages to and from JavaBeans. The JavaBeans are generated by the IFX-Framework at build time from the supplied XSD file. The JavaBeans correspond to the element definitions in the XML file, and methods are provided by the framework to convert between the IFX and the JavaBeans. An application can choose to work directly with the beans (the preferred approach, since that involves least work) or extract or populate data from the framework JavaBeans to their own classes.
The people who would benefit from using this framework are developers who are developing IFX aware applications, but who do not necessarily want to build custom classes to handle conversion of their application classes to equivalent IFX XML streams and back.
The IFX-Framework is released under a Lesser GNU Public License (also known as a Library GNU Public License) which allows an individual or company to use the package within their application without any obligation. Releases of the IFX-Framework starting with version 1.0 will be released under the LGPL.
The Code Generator parses the Schema supplied by the IFX Framework and generates the sources for a hierarchy of JavaBean objects. The need for the Code Generator grew out of the slow pace of hand coding the JavaBean classes, coupled with the fact that writing them by hand was not a very interesting exercise to do.
The Code Generator can be invoked from the supplied build.xml by invoking the generate target. This will parse the docs/IFX-1.7/IFX170.xsd file representing the 1.7 schema supplied by the IFX Framework, consisting of 4004 classes. To generate the classes for a different version of the schema, change the xsd.file variable in the build.xml file. IFX-Framework has been tested with version 1.3, 1.4, 1.5 and 1.7 of the IFX specification. The files are available under the docs/ directory of the distribution. There are minor changes in the XSD files in each version, which are listed in the "Changes to the IFX Schema" section.
The generated package hierarchy looks like this:
org.sourceforge.ifx.framework.simpletype
org.sourceforge.ifx.framework.element
org.sourceforge.ifx.framework.complextype
org.sourceforge.ifx.framework.interfaces
The simpletype package contains source for elements declared as xsd:simpleType, the element package contains source for elements declared as xsd:element, and the complextype package contains elements declared as xsd:complexType. The interfaces package contain marker interfaces for elements which are denoted as xsd:choice.
Starting with version 1.6 of the IFX specification, the specification consists of multiple XSD files, each in a different namespace. For each imported XSD file, the classes are generated in packages as listed above, with an additional package offset representing the namespace. For example, the RemitDetail_Type.xsd specifies the remit namespace, so the package structure for classes generated from this file are as follows:
org.sourceforge.ifx.framework.remit.simpletype
org.sourceforge.ifx.framework.remit.element
org.sourceforge.ifx.framework.remit.complextype
The code for the Code Generator can be found in the org.sourceforge.ifx.tools package. The main parsing code is in CodeGenerator.java. It parses the specified XSD schema file and populates the JavaSource bean, and writes it down once a particular element has been traversed.
The rules to generate the beans are as follows:
General Design Strategy. The design pattern we use primarily is the Composition pattern. A bean is composed of sub-beans, which are composed further of sub-beans, and so on, until we get the Java objects which model XSD datatypes or are an extension of these types. The classes that model XSD datatypes and their extensions implement the IBaseType interface which mandate a setString() and getString() method to convert from and to a String. Higher level beans simply define ways to get and set the sub-beans one level deep. No validation on the data is performed at any level. Data validation will occur when we attempt to build the XML out of the beans, or to parse the XML into the beans, using the XSD file as our reference.
Determining the Package name. Elements which are declared within xsd:simpleType tags end up in the simpletype package, those which are declared within xsd:element tags end up in the element package, and those which are declared within xsd:complexType tags end up in the complextype package. The classes in the interfaces package are marker interface class, and are used to group related beans defined in the schema using xsd:choice.
Determining xsd to Java mappings. Data types for base types are declared as xsd:string, xsd:hexBinary, etc. Each of these xsd base types have a corresponding class in the org.sourceforge.ifx.basetypes package. The mapping is declared in the CodeGenerator class. The basetypes provide a getString() and setString() method to convert between a String representation from the text in the XML file, to its internal Java representation. This is all transparent to the framework user.
Handling simpleTypes. IFX beans declared using the xsd:simpleType element simply have a xsd:restriction attribute which is used to determine its superclass. The superclass will usually map directly or indirectly to an XSD datatype. Additionally, the xsd:annotation element is used to provide some user documentation, which go into the class level Javadocs for the generated sources.
Handling complexTypes. IFX beans declared using the xsd:complexType element can either be declared as a xsd:restriction of a xsd:complexContent, which is translated as the bean extends the complexContent element, or it could be composed of a xsd:sequence or xsd:choice. A sequence is interpreted simply as a list of sub-objects which have getters and setters. A choice causes the code generator to build a marker interface, an empty interface without properties, useful for referring to different classes which implement that interface by the same name, by pre-pending I to the first element in the xsd:choice structure. A choice or sequence can have nested choice and sequence elements, which is handled by the CodeGenerator in a recursive manner. In cases where any of the elements have a maxOccurs attribute which is greater than 0, the element getter and setter refer to arrays of the sub-object.
Handling elements. Handling of elements is similar to handling of complexTypes described above, with one addition. Often an element is simply a specialization or restriction on a simpleType, which is translated by the CodeGenerator as making the generated element source a subclass of the simpleType.
The XML Adapter consists of two components, the IFXEncoder, which converts from IFX Framework beans to XML, and the IFXDecoder, which converts an IFX XML stream back to IFX Framework beans. Both components use reflection to invoke accessor and mutator methods on the IFX Framework beans, and use JDOM to build and parse the XML. No data validation occurs when the IFX XML document is created from the framework beans, since the generated beans contain no validation. However, validation can occur when the XML document is being parsed back into a bean, since the IFXDecoder can validate the XML using the schema.
To use the IFX-Framework beans, include the ifx-framework-${release}.jar in your CLASSPATH. IFX-Framework uses JDOM to do building and parsing of XML and the SAX parser for validating against the schema, which needs the Xerces parser, so you will also need the JDOM and Xerces JAR files. They are included in the release (jdom.jar and xercesImpl.jar). If you do not already have them in your CLASSPATH, they need to be included as well.
The code snippet below illustrates how to use the IFX Framework to build and parse IFX XML messages from and to IFX-Framework JavaBeans.
// import the packages. You will mostly need the element and interfaces // packages, but you may want the simpletype and complextype packages as // well. import org.sourceforge.ifx.framework.element.*; import org.sourceforge.ifx.framework.interfaces.*; import org.sourceforge.ifx.framework.simpletype.*; import org.sourceforge.ifx.framework.complextype.*; // import the utils class for the IFXEncoder and IFXDecoder import org.sourceforge.ifx.utils.*; // import Document object from JDOM import org.jdom.Document; // other application specific imports ... // build the bean in the application IFX ifxBean = buildIFXBeanFromApplication(); ... // encoding to a Document Document doc = IFXDocumentHandler.build(ifxBean, applicationNameSpacePrefix,// can be null, example "ifx" applicationNameSpaceURI); // can be null, example // "http://sourceforge.net/ifx-framework/ifx" ... // writing to a file or similar OutputStream object IFXDocumentHandler.write(doc, // the document object 2, "\r\n", // formatting directives new FileOutputStream("test.xml")); // the OutputStream to write to ... ... // :NOTE: this could be in some other program ... // decoding an incoming InputStream // set up the validation properties, ie use the included xsd file // to validate against the example namespaceURI Map props = new HashMap(); props.put( "http://sourceforge.net/ifx-framework/ifx", // namespaceURI key "/home/schemas/IFX150.xsd"); // path of XSD file to use Document docIncoming = IFXDocumentHandler.read( new FileInputStream("test.xml"), // the InputStream to read true, // validation needed? props); // validation properties // build the IFX bean for consumption by application IFX ifxIncoming = (IFX) IFXDocumentHandler.parse(docIncoming); ... // consume the bean doApplicationSpecificTaskWithBean(ifxIncoming); |
The ifx-framework-${version}.jar is available in the lib directory. You may also want to build the JARs from source. This may be necessary, for example, if you need to work with an earlier (or later) IFX version, since the default build is with the latest version of the schema file, currently version 1.5. To do this, change the xsd.file property in the build.xml file to the appropriate XSD file name and then run:
$ ant clean compile |
This will create a new directory src-gen under your current directory, compile the CodeGenerator, run it to generate the IFX beans in the src-gen directory, compile the basetypes, then compile the generated beans. Finally it will compile the XML adapter classes.
You may want to run the JUnit Tests built into the IFX Framework code, to make sure everything was built correctly. To do this, run:
$ ant test-compile test |
The framework is not too complicated, but it becomes hard to work with because of its size, unless you have the Javadocs installed. The Javadocs are available in the distribution under the docs/api directory, as well as on the web, linked off the project home page. However, you may want to rebuild your local copy, perhaps because of some changes you have made. To do this, run:
$ ant javadoc |
This will build the Javadocs under the docs/api directory.
A few changes were needed to the XSD files because they referred to elements in an ambiguous manner which was throwing the code generator off. Rather than implement complicated workarounds to deal with these special (and in my opinion, incorrect) cases, I decided to change the XSD file, keeping the logic intact. The changes to the XSD schema are documented below:
Replaced the URL simpleType with URL_Type simpleType because of name collision with URL element. The change is in keeping with the convention followed in the rest of the schema where xsd:simpleType elements are typically _Type elements which are extended by the xsd:element of the same name (without the _Type).
Added the xmlns and targetNamespace attributes to the schema since we want to use the schema for XML validation.
Replaced the URL simpleType with URL_Type simpleType because of name collision with URL element.
Replaced the PmtRemitRefId simpleType with PmtRemitRefId_Type simpleType because of name collision with PmtRemitRefId element.
Added the xmlns and targetNamespace attributes to the schema since we want to use the schema for XML validation.
Replaced the URL simpleType with URL_Type simpleType because of name collision with URL element.
Replaced the PmtRemitRefId simpleType with PmtRemitRefId_Type simpleType because of name collision with PmtRemitRefId element.
Added the xmlns and targetNamespace attributes to the schema since we want to use the schema for XML validation.
Replaced the URL simpleType with URL_Type and removed the URL simpleType altogether since it was causing name-collisions with the URL element. All xsd:restrictions from other URL like types that were using base="URL" have been replaced by base="URL_Type".
Suffixed the name attribute of simpleTypes with _Type. For example, simpleType CustPayeeName had name-collisions with the element CustPayeeName, so the simpleType has been renamed CustPayeeName_Type. Similar changes have been made for CustPayeeName, PayorAcctName, PmtRemitRefId, PmtTrnStatusCode, PmtBatchInfo, PmtBatchStatusRec, PmtBatchRec, PmtBatchStatus, pain.002.001.01 and pain.004.001.01. This will cause no problems with actual user code, since user code deals with elements, not with simple or complex types.
Added the xmlns and targetNamespace attributes to the schema since we want to use the schema for XML validation in our tests. You will need to do this in your user code as well.