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:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  3.     targetNamespace="http://www.pascalalma.net/schema/customer"
  4.     xmlns:tns="http://www.pascalalma.net/schema/customer"
  5.     elementFormDefault="qualified">
  6.  
  7.     <xsd:element name="customer">
  8.         <xsd:complexType>
  9.             <xsd:sequence>
  10.                 <xsd:element name="id" type="tns:idType" />
  11.                 <xsd:element name="name"  type="tns:nameType" />
  12.                 <xsd:element name="address" type="tns:addressType" />
  13.             </xsd:sequence>
  14.         </xsd:complexType>
  15.     </xsd:element>
  16.     <xsd:simpleType name="idType">
  17.         <xsd:restriction base="xsd:positiveInteger">
  18.             <xsd:minInclusive value="1"/>
  19.             <xsd:maxInclusive value="9999999999"/>
  20.         </xsd:restriction>
  21.     </xsd:simpleType>
  22.     <xsd:simpleType name="nameType">
  23.         <xsd:restriction base="xsd:string">
  24.             <xsd:minLength value="1"/>
  25.             <xsd:maxLength value="128"/>
  26.         </xsd:restriction>
  27.     </xsd:simpleType>
  28.     <xsd:simpleType name="addressType">
  29.         <xsd:restriction base="xsd:string">
  30.             <xsd:minLength value="1"/>
  31.             <xsd:maxLength value="255"/>
  32.         </xsd:restriction>
  33.     </xsd:simpleType>
  34. </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:

JAVA:
  1. package net.pascalalma.services;
  2.  
  3. import javax.ejb.Stateless;
  4. import net.pascalalma.schema.customer.Customer;
  5.  
  6. /**
  7. *
  8. * @author pascal
  9. */
  10. @Stateless
  11. public class CustomerLogger implements CustomerLoggerRemote {
  12.  
  13.     public void log(Customer cust)
  14.     {
  15.         System.out.println("################################");
  16.  
  17.         System.out.println("# Name:" + cust.getName());
  18.         System.out.println("# Address + " +cust.getAddress());
  19.  
  20.         System.out.println("################################");
  21.     }
  22. }

and the corresponding interface:

JAVA:
  1. package net.pascalalma.services;
  2.  
  3. import javax.ejb.Remote;
  4. import net.pascalalma.schema.customer.Customer;
  5.  
  6. /**
  7. *
  8. * @author pascal
  9. */
  10. public interface CustomerLoggerRemote {
  11.  
  12.     void log(Customer cust);
  13. }

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:

JAVA:
  1. package net.pascalalma.services;
  2.  
  3. import java.io.StringReader;
  4. import java.io.StringWriter;
  5. import java.io.Writer;
  6. import javax.xml.bind.JAXBContext;
  7. import javax.xml.bind.JAXBElement;
  8. import javax.xml.bind.JAXBException;
  9. import javax.xml.bind.Marshaller;
  10. import javax.xml.bind.Unmarshaller;
  11. import javax.xml.transform.Source;
  12. import javax.xml.transform.stream.StreamSource;
  13. import net.pascalalma.schema.customer.Customer;
  14.  
  15. import net.pascalalma.schema.customer.ObjectFactory;
  16. import org.junit.After;
  17. import org.junit.Before;
  18. import org.junit.Test;
  19.  
  20. /**
  21. *
  22. * @author pascal
  23. */
  24. public class CustomerLoggerTest extends EJBTestBase {
  25.  
  26.     private CustomerLoggerRemote cl = null;
  27.  
  28.     @Before
  29.     public void setUpTest() throws Exception {
  30.         cl = (CustomerLoggerRemote) context.lookup("CustomerLoggerRemote");
  31.     }
  32.  
  33.     @After
  34.     public void tearDownTest()
  35.             throws Exception {
  36.         cl = null;
  37.     }
  38.  
  39.     @Test
  40.     public void logTest() throws Exception {
  41.  
  42.         cl.log(getTestCustomerObject());
  43.  
  44.     }
  45.  
  46.     @Test
  47.     public void unmarshalTest() throws JAXBException {
  48.         JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
  49.         Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
  50.  
  51.         Source ss = new StreamSource(new StringReader(getTestCustomerXml()));
  52.  
  53.         JAXBElement<customer> root = unmarshaller.unmarshal(ss, Customer.class);
  54.  
  55.         Customer c1 = root.getValue();
  56.  
  57.         assert c1 != null;
  58.         assert c1.getId() == getTestCustomerObject().getId();
  59.         assert c1.getName().equals(getTestCustomerObject().getName());
  60.         assert c1.getAddress().equals(getTestCustomerObject().getAddress());
  61.     }
  62.  
  63.     @Test
  64.     public void marshallTest() throws JAXBException {
  65.         JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
  66.         Marshaller m = jaxbContext.createMarshaller();
  67.  
  68.  
  69.         Writer sw = new StringWriter();
  70.  
  71.         m.marshal(getTestCustomerObject(), sw);
  72.  
  73.         String result = sw.toString();
  74.  
  75.         assert result.length()> 0;
  76.         assert result.indexOf("Palma IT")> 0;
  77.         assert result.indexOf("Papendrecht")> 0;
  78.     }
  79.     private String getTestCustomerXml() {
  80.         return "<customer xmlns=\"http://www.pascalalma.net/schema/customer\">\n" +
  81.                 "\t<id>1</id>\n" +
  82.                 "\t<name>Palma IT</name>\n" +
  83.                 "\t<address>Papendrecht, Holland</address>\n" +
  84.                 "</customer>\n";
  85.     }
  86.  
  87.     private Customer getTestCustomerObject() {
  88.         ObjectFactory of = new ObjectFactory();
  89.  
  90.         Customer ct = of.createCustomer();
  91.         ct.setId(1);
  92.         ct.setAddress("Papendrecht, Holland");
  93.         ct.setName("Palma IT");
  94.  
  95.         return ct;
  96.     }
  97. }

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

XML:
  1. <plugin>
  2.   <groupId>org.jvnet.jaxb2.maven2</groupId>
  3.   <artifactId>maven-jaxb2-plugin</artifactId>
  4.   <executions>
  5.     <execution>
  6.       <goals>
  7.         <goal>generate</goal>
  8.       </goals>
  9.     </execution>
  10.   </executions>
  11.   <configuration>
  12.     <schemaDirectory>src/main/resources/schemas</schemaDirectory>
  13.     <readOnly>true</readOnly>
  14.     <removeOldOutput>true</removeOldOutput>
  15.     <verbose>false</verbose>
  16.     <extension>true</extension>
  17.     <bindingIncludes>
  18.        <bindingInclude>jaxb-bindings.xml</bindingInclude>
  19.     </bindingIncludes>
  20.   </configuration>
  21. </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:

    XML:
    1. ...
    2.  <xsd:element name="customer" type="tns:customerType" />
    3.     <xsd:complexType name="customerType">
    4.         <xsd:sequence>
    5.             <xsd:element name="id" type="tns:idType" />
    6.             <xsd:element name="name"  type="tns:nameType" />
    7.             <xsd:element name="address" type="tns:addressType" />
    8.         </xsd:sequence>
    9.     </xsd:complexType>
    10. ...

    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.