Setting up for build

Before you can generate a data model for the OTA schemas you first need to download and install both JiBX and a version of the OTA schema distribution. Once you've done this, edit the build.properties file found in the root directory of JiBX/OTA distribution and set the paths to these installations. If you're running on Windows, you'll need to double each '\' directory separator character in all paths, since Java uses '\' as an escape character in properties files (this also applies to all the other .properties files used in the project) - or you can just use Unix/Linux-style paths, with '/' directory separator characters, even on Windows. The ota-schemas path needs to be the actual directory containing the standard (not "flattened") OTA schema definitions (.xsd files). The ota-samples path needs to be the actual directory containing the sample documents (which may be the same directory as that containing the schemas, but still needs to be entered separately).

Full build

The first example data model is generated from the full set of OTA schemas in a single step. It uses a CodeGen customization file to change some of the CodeGen default handling, including creating a number of separate packages for the data model classes. Here's a partial listing of that customization file, included in the download as custom-full.xml:

<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema"
    generate-all="true" delete-annotations="true" any-handling="dom"
    type-substitutions="xs:integer xs:int xs:decimal xs:float">
  <name-converter strip-suffixes="_Type Type"/>
  <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
  <schema-set package="org.ota.air" names="OTA_Air*.xsd">
    <name-converter strip-prefixes="OTA_Air"/>
    <schema-set generate-all="false" prefer-inline="true" names="OTA_AirCommonTypes.xsd OTA_AirPreferences.xsd"/>
  </schema-set>
  <schema-set package="org.ota.shared" generate-all="false" prefer-inline="true"
      names="OTA_Common*.xsd OTA_SimpleTypes.xsd"/>
  <schema-set package="org.ota.cruise" names="OTA_Cruise*.xsd">
    <name-converter strip-prefixes="OTA_Cruise"/>
    <schema-set generate-all="false" prefer-inline="true" names="OTA_CruiseCommonTypes.xsd"/>
  </schema-set>
  ...
  <schema-set package="org.ota.vehicle" names="OTA_Veh*.xsd">
    <name-converter strip-prefixes="OTA_Veh"/>
  </schema-set>
</schema-set>

Without going into all the details, you can see from looking at this customization document how the various OTA schema definitions are grouped into sets based on file names and separate packages are configured for each set of schemas. The customization also configures some type substitutions (using xs:int and xs:float representations for values defined as awkward xs:integer and xs:decimal types in the schemas) and name simplifications (via the <name-converter strip-suffixes="..." and <name-converter strip-prefixes="..." elements) intended to make the generated Java data model easier to use. For full details of CodeGen customizations, see the documentation included in the JiBX distribution (which includes some detailed examples based on a subset of OTA schemas).

The Ant build.xml script runs CodeGen as shown below:

  <!-- generate full set of classes and bindings all-at-once -->
  <target name="generate-full" depends="check-runtime,clean">
    
    <echo message="Running code generation from schema"/>
    <delete quiet="true" dir="${basedir}/gen/src"/>
    <java classname="org.jibx.schema.codegen.CodeGen" fork="yes"
        classpathref="classpath" failonerror="true">
      <jvmarg value="-Xms512M"/>
      <jvmarg value="-Xmx512M"/>
      <arg value="-c"/>
      <arg value="${basedir}/custom-full.xml"/>
      <arg value="-s"/>
      <arg value="${ota-schemas}"/>
      <arg value="-t"/>
      <arg value="${basedir}/gen/src"/>
      <arg value="*RQ.xsd"/>
      <arg value="*RS.xsd"/>
    </java>
    
  </target>

The <jvmarg> lines configure added memory for use by CodeGen, which makes the code generation for large sets of schemas (such as the OTA schemas) much faster than would be the case with the default memory settings. The regular arguments set the customizations file (-c and following value), the base directory for the schema definitions (-s and following value), the target directory for the generated code and JiBX binding definition (-t and following value), and the actual schemas to be processed (the remaining arguments, in this case using patterns to select all the request and response message schemas).

You can run the basic code generation from a console open to the root directory of the JiBX/OTA installation by typing ant generate-full. This just generates the data model to the target directory (gen/src, relative to the installation root directory). You can then type ant build-full to compile the classes, run the JiBX binding compiler, and assemble the classes into a jar file. Once that's done you can type ant build-test test to build the test program and actually run the sample document roundtrip test. As a simpler alternative, you can just type ant full to generate and build the data model and test program and actually run the test program.

If you try the provided test with the OTA 2009A schemas and sample documents you'll get a failure (actually six failures, in total). This is due to encoding errors in the OTA sample documents, and does not represent any problem with the test program or data model.

You can build JavaDocs for the generated data model code by typing ant javadoc-full on the console. This builds the JavaDocs in the docs/apidocs directory (which you can also view online). The CodeGen tool converts schema documentation to JavaDocs in the generated source code, so the JavaDocs generated from the source code contain much more useful information than is typically the case with other tools generating code from schemas.

Modular build

The second example data model is generated as two separate modules. The first module, referred to here as the "base" module, consists of classes corresponding to the common schema definitions shared across different types of message. The second module, referred to as the "extension" module, consists of classes corresponding to all the actual message definitions. The classes in the extension module use the classes in the base module via composition or extension.

As with the first example, each module of the second example is generated using a customization file. Here's the customization file used for the base module, included in the download as custom-modular-base.xml:

<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema"
    generate-all="true" delete-annotations="true" any-handling="dom"
    type-substitutions="xs:integer xs:int xs:decimal xs:float"
    package="org.ota.shared" binding-file-name="base-binding.xml">
  <name-converter strip-suffixes="_Type Type"/>
  <schema-type type-name="dateTime" format-name="DateTime.UTC"/>
  <schema-type type-name="date" format-name="LocalDate.default"/>
  <schema-type type-name="time" format-name="LocalTime.local"/>
  <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
</schema-set>

The first part of this customization file is very similar to that used for the earlier example. The added <schema-type> elements in this file demonstrate changing the handling of built-in schema simple types, in this case using Joda Time classes for representing schema date/time types.

The customization file used for the second module, custom-modular-extends.xml, is another variation on the customization used for the first example. It again uses <schema-type> customizations to specify Joda Time handling of the schema date/time types, and leaves out references to the schemas included by the base module.

The Ant build.xml script runs CodeGen separately for each module. The target for the base module is shown below:

  <!-- generate only base classes and binding as a separate step -->
  <target name="generate-base" depends="check-runtime,clean">
    
    <echo message="Running base code generation from schema"/>
    <delete quiet="true" dir="${basedir}/gen/base"/>
    <java classname="org.jibx.schema.codegen.CodeGen" fork="yes"
        classpathref="classpath" failonerror="true">
      <jvmarg value="-Xms512M"/>
      <jvmarg value="-Xmx512M"/>
      <arg value="-c"/>
      <arg value="${basedir}/custom-modular-base.xml"/>
      <arg value="-s"/>
      <arg value="${ota-schemas}"/>
      <arg value="-t"/>
      <arg value="${basedir}/gen/base"/>
      <arg value="-u"/>
      <arg value="http://www.opentravel.org/OTA/2003/05"/>
      <arg value="OTA_Common*.xsd"/>
      <arg value="OTA_SimpleTypes.xsd"/>
    </java>
    
  </target>

Most of this build target is the same as for the first example. The differences are that this one uses a different target directory (gen/base rather than gen/src), passes an additional argument pair, and specifies only a few schema files (those with names starting with "OTA_Common" - OTA_CommonPrefs.xsd and OTA_CommonTypes.xsd - and OTA_SimpleTypes.xsd). The added -u argument tells CodeGen to use the following argument as the namespace to be applied to schemas without a namespace, a step that's necessary because these common schemas don't specify any namespace, instead taking on the namespace of the OTA message schemas when the common schemas are referenced.

The target for the extension module is shown below:

  <!-- generate extension classes and binding as a separate step -->
  <target name="generate-extend" depends="check-runtime,check-base,clean-partial">
    
    <echo message="Running extension code generation from schema"/>
    <delete quiet="true" dir="${basedir}/gen/src"/>
    <java classname="org.jibx.schema.codegen.CodeGen" fork="yes" failonerror="true">
      <classpath>
        <path refid="classpath"/>
        <pathelement location="${basedir}/lib/base.jar"/>
      </classpath>
      <jvmarg value="-Xms512M"/>
      <jvmarg value="-Xmx512M"/>
      <arg value="-c"/>
      <arg value="${basedir}/custom-modular-extends.xml"/>
      <arg value="-s"/>
      <arg value="${ota-schemas}"/>
      <arg value="-t"/>
      <arg value="${basedir}/gen/src"/>
      <arg value="-i"/>
      <arg value="classpath:META-INF/base-binding.xml"/>
      <arg value="*RQ.xsd"/>
      <arg value="*RS.xsd"/>
    </java>
    
  </target>

This build target has a few more differences from the prior ones. First off, it uses a classpath which includes a base.jar file. This jar file is created by the actual build steps for the base module (not shown here, but in the build.xml), and is needed as a resource for use by the extension code generation. It also includes another pair of arguments passed to CodeGen, a -i argument followed by the path to the base module binding definition (here accessed from the jar file, with a classpath reference). This tells CodeGen to include the specified binding(s) in the code generation and use the existing bindings for any matching schema components.

You can generate code for the base module from a console by typing ant generate-base. This just generates the base data model to the target directory (gen/base). You can then type ant build-base to compile the classes, run the JiBX binding compiler, and assemble the classes into a jar file. Once that's done you can generate the extension module with ant gemerate-extend and build it with ant build-extend, then build and run the test program with ant build-test test. As a shortcut, you can just type ant modular to run this entire sequence of steps in order.

As with the first example, you'll get six document roundtrip failures if you try the test with the OTA 2009A schemas and sample documents, due to encoding errors in the documents.

You can generate JavaDocs for both modules of the data model code by typing ant javadoc-modular on the console. This creates the JavaDocs for the base module in the docs/basedocs directory, and those for the extension module in the docs/apidocs directory.

Modular generation tradeoffs

Modular code generation can help make large schemas manageable, especially when different parts of the the schemas are being used by different teams within an organization. It should be easy to see how you can extend this example to build separate modules for air, cruise, and hotel documents using the OTA schemas, for example. With this type of approach the whole organization can use common code for working with basic components of the schemas, while each team maintains control over the data model used for their specific data. Of course, it's also easy to just have each team generate their own data model independently - but then it may be difficult to share any code between the different teams.

The only significant drawback to modular generation is that it forces CodeGen to generate classes for schema definitions which might otherwise be inlined within other definitions. One very simple example of this is the <Success> element used as a marker in OTA responses when no errors have occurred. If you generate the schemas all-at-once, as in the first example, CodeGen constructs a model where there's no actual data associated with the <Success> element, since it's effectively just a choice setting. But if you use modular generation, CodeGen needs to construct an empty class to represent the type used by a <Success> element (because that type is defined in the common schemas generated in the base module), and you must then set an instance of that class on every success response (all of which are in the extension module).

Going further

Hopefully these two samples have given you an idea of how easily you can customize a generated Java data model using the JiBX CodeGen tool and customizations. The CodeGen examples included in the JiBX distribution (which are based on a subset of the OTA schemas) go much further, including showing how you can simplify a generated data model by eliminating components you know you won't be using in your applications. This can be an especially valuable feature when working with the OTA schema structures, which include many different options which may only be applicable for certain types of usages.

You can also use JiBX for handling your own extension data, using the <TPA_Extensions> element included in the OTA message definitions as a placeholder for custom data. If you already have a schema definition for your extension data you can just run CodeGen to generate a data model for your schema. Modular generation is especially useful in this case, since you can generate a stable data model for the OTA schemas as a base module and then generate your extensions as a separate extension module, allowing you to easily modify your extensions without effecting the base data model. If you instead are starting from Java classes for your extension data, you can use the BindGen tool included in the JiBX distribution to generate a binding and schema definition, then distribute the schema definition while using the JiBX binding directly with your existing classes.

If you want to use extension data you'll need to change one of the settings from the example builds. This is the any-handling="dom" attribute on the root <schema-set> element of each customization, which tells CodeGen to construct a Document Object Model (DOM) representation for any document data matching an <xs:any> element in the schema definition. Change this attribute to any-handling="mapped", meaning CodeGen will look for a JiBX binding definition for any such document data and convert it to and from a data model structure.