Generating JAXB2 with Maven2

I have blogged before about how to generate JAXB binding classes based on your WSDL file, this time I wanted to generate JAXB classes based on just XSD files. Although I expected this to be simple, it took me quite some time to get it right, so I decided to give this item its own post 🙂
I have got the following schema ‘customer.xsd’ that I want to bind to Java classes:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.redstream.nl/schema/customer"
    xmlns:tns="http://www.redstream.nl/schema/customer"
    elementFormDefault="qualified">

    <xsd:element name="customer" >
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="id" type="tns:idType" />
                <xsd:element name="name"  type="tns:nameType" />
                <xsd:element name="address" type="tns:addressType" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:simpleType name="idType">
        <xsd:restriction base="xsd:positiveInteger">
            <xsd:minInclusive value="1"/>
            <xsd:maxInclusive value="9999999999"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="nameType">
        <xsd:restriction base="xsd:string">
            <xsd:minLength value="1"/>
            <xsd:maxLength value="128"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="addressType">
        <xsd:restriction base="xsd:string">
            <xsd:minLength value="1"/>
            <xsd:maxLength value="255"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>

I have added this schema to my /main/resources/ directory in my Maven project. The generated JAXB objects will be used in an EJB class called ‘CustomerLogger’. The class looks like:

package net.pascalalma.services;

import javax.ejb.Stateless;
import net.pascalalma.schema.customer.Customer;

/**
 *
 * @author pascal
 */
@Stateless
public class CustomerLogger implements CustomerLoggerRemote {

    public void log(Customer cust)
    {
        System.out.println("################################");

        System.out.println("# Name:" + cust.getName());
        System.out.println("# Address + " +cust.getAddress());

        System.out.println("################################");
    }
}

and the corresponding interface:

package net.pascalalma.services;

import javax.ejb.Remote;
import net.pascalalma.schema.customer.Customer;

/**
 *
 * @author pascal
 */
@Remote
public interface CustomerLoggerRemote {

    void log(Customer cust);
}

I also created a test class that tests the JAXB marshalling and unmarshallig (only for this case, I don’t think you normally want to test that) and the CustommerLogger EJB class. It looks like this:

package net.pascalalma.services;

import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import net.pascalalma.schema.customer.Customer;

import net.pascalalma.schema.customer.ObjectFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 *
 * @author pascal
 */
public class CustomerLoggerTest extends EJBTestBase {

    private CustomerLoggerRemote cl = null;

    @Before
    public void setUpTest() throws Exception {
        cl = (CustomerLoggerRemote) context.lookup("CustomerLoggerRemote");
    }

    @After
    public void tearDownTest()
            throws Exception {
        cl = null;
    }

    @Test
    public void logTest() throws Exception {

        cl.log(getTestCustomerObject());

    }

    @Test
    public void unmarshalTest() throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

        Source ss = new StreamSource(new StringReader(getTestCustomerXml()));

        JAXBElement<customer> root = unmarshaller.unmarshal(ss, Customer.class);

        Customer c1 = root.getValue();

        assert c1 != null;
        assert c1.getId() == getTestCustomerObject().getId();
        assert c1.getName().equals(getTestCustomerObject().getName());
        assert c1.getAddress().equals(getTestCustomerObject().getAddress());
    }

    @Test
    public void marshallTest() throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
        Marshaller m = jaxbContext.createMarshaller();


        Writer sw = new StringWriter();

        m.marshal(getTestCustomerObject(), sw);

        String result = sw.toString();

        assert result.length() > 0;
        assert result.indexOf("Palma IT") > 0;
        assert result.indexOf("Papendrecht") > 0;
    }
    private String getTestCustomerXml() {
        return "<customer xmlns=\"http://www.redstream.nl/schema/customer\">\n" +
                "\t<id>1</id>\n" +
                "\t<name>Palma IT</name>\n" +
                "\t<address>Papendrecht, Holland</address>\n" +
                "</customer>\n";
    }

    private Customer getTestCustomerObject() {
        ObjectFactory of = new ObjectFactory();

        Customer ct = of.createCustomer();
        ct.setId(1);
        ct.setAddress("Papendrecht, Holland");
        ct.setName("Palma IT");

        return ct;
    }
}

Next is setting up the correct plugin in your pom like this:

<plugin>
  <groupId>org.jvnet.jaxb2.maven2</groupId>
  <artifactId>maven-jaxb2-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <schemaDirectory>src/main/resources/schemas</schemaDirectory>
    <readOnly>true</readOnly>
    <removeOldOutput>true</removeOldOutput>
    <verbose>false</verbose>
    <extension>true</extension>
    <bindingIncludes>
       <bindingInclude>jaxb-bindings.xml</bindingInclude>
    </bindingIncludes>
  </configuration>
</plugin>

As you can see I am reffering to a specific binding file to be used: ‘jaxb-bindings.xml’. This file is used to make the generated objects ‘serializable’ (I described similar situation here). The complete pom can be found here.

If all this is in place you can test your setup by performing
mvn clean install.
Here is my complete project structure:

While putting up this project I ran into several issues:

  • First make sure you have the right plugin
  • You should be using this one for JAXB2 and not this one which is for JAXB.

  • Second please note the differences in including JAXB binding file
  • As I set before I did the same thing before using JAXWS plugin, but the syntax of including the binding file and the content of the binding file is a little different in this case, so make sure you are aware of that. I wasn’t and it took some time to figure it all out.

  • Last issue had to do with my xsd syntax
  • Originally I had the following syntax in my XSD file:

    ...
     <xsd:element name="customer" type="tns:customerType" />
        <xsd:complexType name="customerType">
            <xsd:sequence>
                <xsd:element name="id" type="tns:idType" />
                <xsd:element name="name"  type="tns:nameType" />
                <xsd:element name="address" type="tns:addressType" />
            </xsd:sequence>
        </xsd:complexType>
    ...
    

    But that caused the following exception:

    com.sun.istack.SAXException2: unable to marshal type “net.pascalalma.schema.customer.CustomerType” as an element because it is missing an @XmlRootElement annotation

    The cause for this issue is described here and it made me to redefine my xsd in the one I showed at top of this post.

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in Maven, XML/ XSD/ XSLT and tagged , , , . Bookmark the permalink.