Highlights of JAXB 2.1 Proposed Changes

By Kohsuke Kawaguchi

This document explains some of the key changes proposed for JAXB 2.1 maintenance release, in the hope of getting feedback. Everything written here is a proposal not final and is subject to change.

1. Separate Compilation Support

It is a fairly common for a schema to be developed as a 'module', to be used by other schemas. The idea is for the party X to develop a 'core' schema, then the party Y will develop additional schema on top of the core. Examples of this can be seen in many places, including W3C XML Schema itself, WSDL, WS-Addressing, SOAP, UBL, ...

When people develop corresponding Java libraries for these schemas, there's often a need to compile the core schema and the additional schema separately. That is, the party X generates (or even hand-write) Java classes for the core schema, then the party Y compiles the additional schema, in such a way that the generated classes refer to the classes generated earlier (or hand-written) by X.

A similar problem applies to schema generation. Sometimes your Java classes refer to other classes, which already have pre-generated (or hand-written) corresponding schemas somewhere. In this case, it's desirable to simply refer to that schema, as opposed to generate definitions.

Proposed Solution

On the schema compiler side, we'll expand the <jaxb:class> customization so that the references to the existing classes can be specified. When such customizations are seen, a schema compiler must not generate a class, and instead simply refer to the referenced classes.

So for example, given the following schema:


   <schema targetNamespace="foo" ...>
     <complexType name="foo">
       <annotation><appinfo>
         <jaxb:class ref="org.acme.foo.Foo"/>

a schema compiler will simply assume that there's already such a class called "org.acme.foo.Foo", and will not generate another class. All the generated classes that reference this type will refer to "org.acme.foo.Foo".

The same annotation can be applied on simple types.

We'll also define the map attribute on <schemaBindings> customization, to disallow the code generation for the entire namespace (unless otherwise overriden by <jaxb:class ref="..."/>). While we can conceptually define such attributes on smaller units (such as on <class> customization), given that a common use case of separate compilation happens at the namespace level, it's unlikely to be useful.

This allows a library to "hide" certain definitions that are globally defined in XSD. (In schema, it's a common practice to define almost everything as global types, which restricts Java library designer's ability to design classes.

With this, one could write a schema like the following:


   <schema targetNamespace="foo" ...>
     <annotation><appinfo>
       <jaxb:schemaBindings map="false" />
     <complexType name="bar" />
     <complexType name="foo">
       <annotation><appinfo>
         <jaxb:class ref="org.acme.foo.Foo"/>
       <sequence>
         <element name="xyz" type="bar" />

The schemaBindings statement prevents any class generation from this package, reference to the complex type "foo" will become a reference to "org.acme.foo.Foo" type, and reference to the complex type "bar" will be an error.

On the schema generator side, we'll add a new 'location' annotation element to @XmlSchema annotation. When this attribute is present, it points to the URI of the existing schema document that defines the namespace. For example,


    @XmlSchema(namespace="foo")
    package foo;

    @XmlType
    class Foo {
      @XmlElement Bar zot;
    }

    @XmlSchema(namespace="bar",location="http://example.org/test.xsd")
    package bar;

    @XmlType
    class Bar {
      ...
    }

    <xs:schema targetNamespace="foo">
      <xs:import namespace="bar"
                 schemaLocation="http://example.org/test.xsd"/>
      <xs:complexType name="foo">
        <xs:sequence>
          <xs:element name="zot" type="bar:Bar" xmlns:bar="bar"/>
        </xs:sequence>
      </xs:complexType>
    </xs:schema>

2. Customization without @schemaLocation

The most common customization that people specifies is the globalBindings customization, and probably the schemaBindings customization. But the current syntax for doing this is somewhat verbose.

For example, to specify a global customization, you need to do this:


<jxb:bindings version="2.0"
              xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <jaxb:bindings schemaLocation="path/to/some.xsd" node="/xs:schema">
     <jxb:globalBindings />
  </jxb:bindings>
</jxb:bindings>

The @schemaLocation and @node is pointless given that this is a globalBindings. A similar problem applies to schemaBindings, where @node is pointless.

This change rectifies this situation by not requiring those attributes for globalBindings and schemaBindings. The user will be able to write the same customization as follows:


<jxb:bindings version="2.0"
              xmlns:jxb="http://java.sun.com/xml/ns/jaxb">
  <jxb:globalBindings />
</jxb:bindings>

This also allows those customizations to be applied to other schemas without any changes, as customization files no longer hard-code any path names.

3. @XmlElementWrapper.required()

Currently there is no way to enforce the presence of an @XmlElementWrapper, the wrapper is always generated with minOccurs=0.

So let's add @XmlElementWrapper.required(). This defaults to false to be backward compatible, and when true, it will cause the wrapper element to be generated minOccurs=1.

4. Making type substitution easier

For JAXB to operate, it needs to know a list of classes that it's going to handle up front. This is the list of classes that are passed to JAXBContext.newInstance(). While the implementation of this method does a transitive type reference analysis, this analysis is unable to find subclasses due to the way Java works.

At the runtime, the list of classes that JAX-WS knows to be bound by JAXB is primarily those types that appear in the SEI. Any types that are not transitively reachable from these classes will not be a part of JAXBContext, and as such they'll fail to marshal/unmarshal.

What's needed here is for a portable way for the WSDL compiler tool to pass a list of additional classes to the runtime. If such a mechanism exist, then wscompile can capture all the generated types (by communicating with xjc) to be used by runtime.

Since the JAX-WS implementation used at the development time and the JAX-WS implementation at runtime might differ, any mechanism that captures the list of classes need to be portable. That means this requires a spec change.

Proposed Solution

Define an annotation in JAXB that instructs JAXB runtime to bind other classes. The feature is intended so that classes that are not otherwise statically reachable will become reachable for the JAXB runtime.

    @XmlSeeAlso({FooBeanEx.class,FooBean2.class,...})
    class FooBean {
      ...
    }

The semantics is that this would extend the transitive reference closure computation. When the closure includes FooBean, this annotation will add all the referenced classes into this closure.

To make this work with JAX-WS, JAX-WS spec will allow this annotation to be placed on the web service class, and JAX-WS implementation needs to be involved in passing this information to JAXB implementation (as JAXB won't see the web service class itself as a bindable class.)

So for example, this can be used like this:

    @XmlSeeAlso({Bar.class,Zot.class})
    abstract class Foo {}
    class Bar extends Foo {}
    class Zot extends Foo {}
and JAXBContext.newInstance(Foo.class) will include all three classes. (Without @XmlSeeAlso annotation on Foo, JAXBContext.newInstance(Foo.class) will include only Foo.)

The JAX-WS 2.1 spec proposes to allow @XmlSeeAlso on the SEI or endpoint implementation, like this:

    @XmlSeeAlso(Foo.class)
    interface SEI {
      Object echo(Object o);
    }

JAX-WS implementation will be responsible for making sure that the JAXBContext it creates includes all the classes listed in @XmlSeeAlso. This annotaion is allowed but not required.

5. Skip Binding of Intermediate Classes

Java type hierarchy and XML type hierarchy may not always match one to one. One such example is where you define a common base class between various classes to share some utility code, yet such a base class should be considered as an implementation detail.

  class AbstractModelObject {
    public void toString() {
      // use commons-lang to define a toString() for all
      return ToStringBuilder.reflectionToString(this);
    }
  }

  class Person extends AbstractModelObject { ... }
  class Computer extends AbstractModelObject { ... }

The user do not wish to create a complex type that corresponds to the 'abstractModelObject', since it's pointless. It's often even actively harmful, because such a base class blocks the use of @XmlValue annotation on Person and Computer classes.

Proposed Solution

Allow @XmlTransient to be placed on a class, to indicate that it doesn't have the corresponding XML representation. So in the above example, one can do as follows to achieve the desired effect of not defining 'abstractModelObject' complex type:

  @XmlTransient
  class AbstractModelObject {
    public void toString() {
      // use commons-lang to define a toString() for all
      return ToStringBuilder.reflectionToString(this);
    }
  }

  class Person extends AbstractModelObject { }
  class Computer extends AbstractModelObject { }

  <complexType name="person" />
  <complexType name="computer" />

The following example illustrates more general case:


  class Foo {
    @XmlElement String a;
  }

  @XmlTransient
  class Bar extends Foo {
    @XmlElement String b;
  }

  @XmlType(propOrder={"c","b"})
  class Zot extends Bar {
    @XmlElement String c;
  }

  <complexType name="foo">
    <sequence>
      <element name="a" type="string"/>

  <complexType name="zot">
    <complexContent>
      <extension base="foo">
        <sequence>
          <element name="c" type="string"/>
          <element name="b" type="string"/>

A few things to note: