Implementing of JAK

Abstract

This chapter is not essential in understanding the usage of the Java API for KML.

JAK is automatically generated by OGC's KML schema specification file ogckml22.xsd [KML22] and Google's Gx extensions file kml22gx.xsd [KML22GX]. Every time the KML schema evolves, an build cycle is initiated and JAK reflects all changes automatically. This chapter is about the implementation details, why things were done as they are, and how a JAXB plugin does all the automatic API creation.

The build cycle of JAK

To initiate the JAK's automatic build process, an Ant build target is defined. This build target configures JAXB's build options and invokes the XJC schema compiler.

Strictly speaking this Ant build target is embedded into a Maven pom.xml and invoked by Maven's maven-antrun-plugin.

Part of Maven's pom.xml

Part of Maven's pom.xml script that invokes JAXB's schema compiler and runs a Perl script via Maven's ant-run-plugin. The Perl script cleans all occurrences of the JAXBElement.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <id>xjc-invocation</id>
      <phase>generate-sources</phase>
      <configuration>
        <tasks>
          <property name="src.dir" location="src/main" />
          <property name="src.dir.gen" location="${src.dir}/java" />
          <property name="schema.dir" value="${src.dir}/resources/schema" />
          <property name="schema.dir.kml" location="${schema.dir}/ogckml/ogckml22.xsd" />
          <property name="schema.dir.kml.binding" value="${schema.dir}/ogckml/JAK_binding.xjb" />
          <taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask" classpathref="maven.compile.classpath" />
          <description>generate JAK from OGC's KML schema</description>
          <mkdir dir="${src.dir.gen}/de/micromata/opengis/kml/" />
          <delete>
            <fileset dir="${src.dir.gen}/de/micromata/opengis/kml/" includes="**" />
          </delete>
          <xjc extension="true" binding="${schema.dir.kml.binding}" destdir="${src.dir.gen}" removeOldOutput="yes"
            schema="${schema.dir}/ogckml/kml22gx.xsd">
            <arg value="-XJavaForKmlApi" /> <!-- the XJC plugins that creates JAK -->
          </xjc>
          <exec executable="perl" dir="${basedir}"> <!-- remove the damn JAXBElements -->
            <arg file="scripts/CleanUpGeneratedJAXBSourceFolder.pl" /> 
            <arg file="${src.dir.gen}/de/micromata/opengis/kml" />
          </exec>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
</plugin>

With the invocation of the build target shown, Listing 1 in the build cycle is started (a graphical presentation of the build cycle can be found in Figure 2). It can be called with the command mvn install. This invokes JAXB's schema compiler (XJC) and generates the Java API for KML. JAXB binds Google's Gx extensions schema kml22gx.xsd to automatically generated classes, which are annotated with an implicit schema definition by the schema compiler. In Listing 1 only the kml22gx.xsdis mentioned. Due to an internal link defined in Google's Gx extensions schema OGC's KML schema is implicitly considered by the build process.

As to be expected, JAXB's schema compiler is not able to determine the desired result of the generated Java code from the XML schema by itself. The creation process of Java's data-model has to be tweaked with JAXB's binding customizations, as many name clash errors occur. The binding customizations needed for compiling both KML schemas can be found in the file JAK_binding.xjb. The XJC plugin used is explained below at JAK's XJC Plugins.

A Perl script using regular expression is invoked (seen in Figure 2 and in Listing 1) after the POJOs are generated as Java source files. It removes all JAXBElement occurrences in the generated files. XJC creates 132 Java source files from the KML schema with 632 fields encapsulated into JAXBElement. A complete reference of which elements are affected can be found at Figure 1. It is similar to the one shown as KML in the Java world#Figure 1. It shows the KML reference of all types defined in the KML schema which need to be mapped to a Java class. The difference is that the types that could not be resolved correctly are marked in amber. Thus, each time they are referred to in a POJO as a field, they are encapsulated into a JAXBElement. XJC creates 132 classes from the KML schema. With the default binding mode as defined in the JAXB's specification, 632 fields are encapsulated into JAXBElements; even with the simpler and better binding mode, XJC is not able to resolve all types correctly and encapsulates 635 fields into JAXBElements (three more!).

Maven automatically compiles the generated POJOS and tests them using predefined use-cases. The use-cases are composed of JUnit and XMLUnit defined in the ATC and the KML reference. The ATC defines 77 use-cases considered to make an OGC conform KML implementation [OGC07D], whereas 73 examples were taken from the KML reference and implemented as use-case [KML09]. These points were modeled as JUnit tests. They serve to check that the API fulfills all requirements.

The next three listings show such a use-case. It is an example taken from [KML09] and shows the creation of a Placemark element that contains a Point element:

Example taken from KML09. It shows the creation of a Placemark element containing a Point element.

<Placemark>
  <name>Google Earth - New Placemark</name>
  <description>Some Descriptive text.</description>
  <LookAt>
    <longitude>-90.86879847669974</longitude><latitude>48.25330383601299</latitude>
    <range>440.8</range><tilt>8.3</tilt><heading>2.7</heading>
  </LookAt>
  <Point>
    <coordinates>-90.86948943473118,48.25450093195546,0</coordinates>
  </Point>
</Placemark>

All examples taken from the KML reference were defined as shown in Listing 2. The use-cases, labeled as ATC (Abstract Test Case) defined in the ATS are in a more abstract form:

ATC 67 Placemark
IDENTIFIER http://www.opengis.net/kml/2.2/atc/level-2/Placemark
TEST PURPOSE Check if a kml:Placemark element is not a descendant of kml:Update, it includes a geometry element (any element that substitutes for kml:AbstractGeometryGroup
TEST METHOD Pass if the assertion is satisfied; fail otherwise
REFERENCE OGC-07-147r2: cl. 9.11.2
TEST TYPE Basic

These use-cases are now transformed into Java programs as shown next:

The KML document shown in Listing 2 is done programmatically with JAK. The Check.placemarkExample() method is called twice.

 The Check.placemarkExample() method is called twice.

Placemark placemark = new Placemark()
    .withName("Google Earth - New Placemark")
    .withDescription("Some Descriptive text.");

placemark.createAndSetLookAt()
    .withLongitude(-90.86879847669974).withLatitude(48.25330383601299)
    .withRange(440.8).withTilt(8.3).withHeading(2.7);

placemark.createAndSetPoint()
    .addToCoordinates("-90.86948943473118,48.25450093195546,0");

Check.placemarkExample(placemark);
Placemark marshalledAndBackAgain = TestUtil.marshalAndUnmarshall(placemark);
Check.placemarkExample(marshalledAndBackAgain);

The code of Listing 3 should be self-explanatory as its structure is similar to the listings in Usage and KML in the Java world. As before, 1) it creates a Java object model (line 01 to 10), which 2) is marshalled and unmarshalled again with the marshalAndUnmarshall method (line 13). This method is a help-method which does the same as KML in the Java world#Listing 6.

Before the data structure is marshalled into a file, a method with several JUnit asserts is invoked. They ensure that the actual result matches the expected results as defined in the use-case.

This method defines JUnit asserts for every element in Listing 2.

public static void placemarkExample(final Placemark placemark) {
  Assert.assertEquals("Google Earth - New Placemark", placemark.getName());
  Assert.assertEquals("Some Descriptive text.", placemark.getDescription());

  LookAt lookat = (LookAt) placemark.getAbstractView();
  Assert.assertEquals(-90.86879847669974, lookat.getLongitude(), 0.0001);
  Assert.assertEquals(48.25330383601299, lookat.getLatitude(), 0.0001);
  Assert.assertEquals(440.8, lookat.getRange(), 0.0001);
  Assert.assertEquals(8.3, lookat.getTilt(), 0.0001);
  Assert.assertEquals(2.7, lookat.getHeading(), 0.0001);

  Assert.assertEquals(new Coordinate(-90.86948943473118, 48.25450093195546, 0.0), 
                      ((Point) placemark.getGeometry()).getCoordinates().get(0));
}

If Listing 4 results in a green-colored bar, the data structure is finally marshalled into a file. This file is then marshalled back into an object graph and the object returned is checked again by theCheck.placemarkExample() method.

In each build iteration cycle three things were constantly checked:

  • That the output java code is complete and correct. Signifies, that no syntax or compile errors at the tests are present, e.g. in the Placemark test shown in Listing 3.
  • That the tests are able to write out KML code. Signifies, that the tests succeed with the display of a green-colored bar, e.g. Listing 3 in line 14.
  • That the tests are able to read in KML code. Signifies, that the tests succeed with the display of a green-colored bar, e.g. Listing 3in line 14.

If all tests pass with a green-colored bar and no syntax errors prevent Maven from compiling the generated source files, the result of a build cycle is the Java API for KML. If not, the code generating XJC plugins are modified and the build cycle is iterated again until the desired result is created and all requirements are fulfilled.

Figure 2 is a graphical representation of this build cycle described above:

XJC Plugins or the "Implementation of the Java API for KML"

JAXB's plugin mechanism allows accessing the semantic information of the XML schema during the code creation process to determine what kind of classes and properties are going to be generated. Thus, it opens up the possibility of generating the semantic application layer rather than coding it by hand. TheJava API for KML is not artificially divided into two layers anymore. They are merged together. As a result, the Generation Gap mentioned by Fowler disappears completely. This procedure has the advantage that each plugin only influences a small part of the API, and is build incrementally in small steps.

As mentioned at KML in the Java world the KML standard seems to be stable, but if the KML schema evolves and a new version of the standard is defined, only the schema defining XML files need to be exchanged and a proper semantic model of the KML API is automatically build upon, which reflects all changes. This makes it possible to adapt to changes quickly and in a fully automatic.

This chapter acts as a plugin catalog, similar to Gamma's Pattern catalog [GAM95]. All XJC plugins are described in a common format.

Each XJC plugin justifies itself why it was created, followed by a short description about its purpose, and how it influences the created code. In most cases, a short of the default code generated followed by a short example the improved code is given.

The plugins are released under a BSD license and can be found at:http://code.google.com/p/xjcpluginjavaapiforkml/

Alternatively in the official dev.java.net Maven 2 repository:

JAK at maven2-repository.dev.java.net

<dependencies>
    ...
   <!-- The XJC Plugin -->
   <!-- It is able to create the API (only needed if OGC's KML schema changes) -->
   <dependency>
      <groupId>de.micromata.jak</groupId>
      <artifactId>XJCPluginJavaApiforKml</artifactId>
      <version>1.0.1-SNAPSHOT</version>
   </dependency>
    ...
</dependencies>
<repositories>
    ...
   <repository>
      <id>maven2-repository.dev.java.net</id>
      <name>Java.net Maven 2 Repository</name>
      <url>http://download.java.net/maven/2</url>
      <layout>default</layout>
      <snapshots>
         <enabled>true</enabled>
      </snapshots>
      </repository>

Bibliography

BIEN07 Adam Bien; How to Javadoc (efficient and maintainable); http://www.adam-bien.com/roller/abien/entry/how_to_javadoc_efficient_and

BLO08 JOSHUA BLOCH; Effective Java: A Programming Language Guide; Addison-Wesley (2008)
COD07 JEFF ATWOOD; Coding Horror - If It Isn't Documented, It Doesn't Exist; http://www.codinghorror.com/blog/archives/000776.html

GAM95 ERICH GAMMA, RICHARD HELM, RALPH E. JOHNSON, JOHN VLISSIDES; Design Patterns. Elements of Reusable Object-Oriented Software. Addison-Wesley Longman, Amsterdam; 1st ed., Reprint. (March, 14th 1995)
KML09 GOOGLE CODE; KML Reference; http://code.google.com/apis/kml/documentation/kmlreference.html

KML22 OGC; OGC KML standard; http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd

KML22GX GOOGLE CODE; Google's Gx extension; http://code.google.com/intl/de/apis/kml/schema/kml22gx.xsd

KOH05C KOHSUKE KAWAGUCHI; A story of migration from JAXB 1.0 to 2.0 http://weblogs.java.net/blog/kohsuke/archive/2005/08/a_story_of_migr.html

OGC07A OGC; OGC KML 2.2 - Abstract Test Suite (1.0.0); 07-147r2_OGC_KML_2.2.pdf; http://www.opengeospatial.org/standards/kml/

OGC07D OGC; Abstract Test Suite; OGC 07-134r2.pdf; http://portal.opengeospatial.org/files/index.php?artifact_id=27811