14 May 2010

Java XML Binding with Property Change Support

In an earlier post, I was talking about generating Java classes with xjc with property change support which is not enabled by default. This post explains the details, an in fact the Maven build explained below is a lot a easier than my original approch with Ant which required some downloads to set things up.

The general idea is same as described by Kohsuke Kawaguchi in a post of 2007, but his POM is broken with the current stage of the Maven repositories. In addition, there is a tweak with the PropertyListener customization.

So here is a little example schema:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.org/person/" targetNamespace="http://www.example.org/person/">
    <complexType name="Person">
      <sequence>
        <element name="firstName" type="string"></element>
        <element name="lastName" type="string"></element>
        <element name="address" type="tns:Address"></element>
      </sequence>
    </complexType>

    <complexType name="Address">
      <sequence>
        <element name="street" type="string"></element>
        <element name="houseNumber" type="string"></element>
        <element name="city" type="string"></element>
        <element name="postalCode" type="string"></element>
        <element name="country" type="string"></element>
      </sequence>
    </complexType>
</schema>

We use the following file bindings.xjb to customize the Java classes generated by xjc. We make all classes serializable and include support for PropertyChangeListeners.

<?xml version="1.0" encoding ="UTF-8"?>
<jaxb:bindings
    schemaLocation="person.xsd"
    version="2.1"
    xmlns:li="http://jaxb.dev.java.net/plugin/listener-injector"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <jaxb:bindings node="/xs:schema">
    <jaxb:globalBindings>
      <jaxb:serializable uid="1" />
    </jaxb:globalBindings>
    <li:listener>java.beans.PropertyChangeListener</li:listener>    
  </jaxb:bindings>
</jaxb:bindings>


The following POM takes care of the code generation:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jaxb-properties</groupId>
  <artifactId>jaxb-properties</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>JAXB PropertyListener Demo</name>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.jvnet.jaxb2.maven2</groupId>
        <artifactId>maven-jaxb2-plugin</artifactId>
        <version>0.7.3</version>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <extension>true</extension>
          <generatePackage>com.example.jaxb</generatePackage>
          <schemaIncludes>
            <schemaInclude>person.xsd</schemaInclude>
          </schemaIncludes>
          <bindingIncludes>
            <bindingInclude>bindings.xjb</bindingInclude>
          </bindingIncludes>
          <args>
            <arg>-Xinject-listener-code</arg>
          </args>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-xjc</artifactId>
            <version>2.1.13</version>
          </dependency>
          <dependency>
            <groupId>org.jvnet.jaxb2-commons</groupId>
            <artifactId>property-listener-injector</artifactId>
            <version>1.1-SNAPSHOT</version>
            <exclusions>
              <exclusion>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-xjc</artifactId>
              </exclusion>
            </exclusions>
          </dependency>
        </dependencies>
      </plugin>

    </plugins>
  </build>
</project>


You do need to use the 1.1-SNAPSHOT version of the property-listener-injector, as the 1.0 release only supports VetoableChangeListeners and not the PropertyChangeListeners we want to use. The <li:listener> element in the bindings file is used to define the listener class.

It is important to exclude the jaxb-xjc dependency from the property-listener-injector, or else it will try to download an non-existing snapshot version. Looks like something is broken in the java.net repository, but then again, working with snapshot releases is always dangerous....

3 comments:

Biasutti said...

Hi, could you help me with an update to this article and the new repository and plugins addresses? That would be nice, thanks!

Harald Wellmann said...

What exactly are you missing?

The maven-jaxb2-plugin (lastest is 0.7.5) is available from Maven Central, and the property-listener-injector is still at http://download.java.net/maven/2/

HTH,
Harald

Biasutti said...

thanks for the reply! Got it working! A tiny problem though, the generated code is firing the changes before the change actually happens:

firePropertyChange(...)
this.name = name;

This is not the correct behavior for property changes to be fired, this is actually for vetoablepropertychange, where the new value is validated and if it passed, then the value is assigned. In my case, property changes and event is fired.

Right? Solutions? Thanks again!