Thursday, June 05, 2008

JAXB schemagen - Finally got it working

For those not in the know, schemagen is a program that comes as part of the Java Web Services Developer Pack and it's used to generated XML schemas from JAXB annotations on Java beans. It so happens that the creators of the JAXB reference implementation also made a Maven 2 plugin for this program. I had previously attempted to use it to generate schemas in my initial attempts with JAXB, but had been presented with a slough of unfriendly exceptions being thrown at me whenever I ran the plugin. Not having the time back then to really play around with it and being considerably less experienced with JAXB, I had to shelve it and find other, less satisfying solutions to my problems with generating documentation.


Recently, I've had to come back to using JAXB because I'm making a RESTful Web Services API that leverages the power of both JAXB and Hibernate to do all my heavy lifting for me. This time around, I wasn't willing to tolerate a lack of schema to give to our users of the API, because it would mean a lot more work for them, and a lot more work for me. This time, I decided to be persistent and dig around the jaxb-schemagen plugin to make sure I could get it working. I'm proud to say that my perseverance paid off, and I now have my RESTful API schema being automatically generated for my application. Here are the problems and the solutions I ran into when I was trying to get going on this:



NullPointerException

I came across persistent NullPointerExceptions while trying to run the plugin at first. The stack trace indicated that the problem was with apt, the Java annotation processing tool. I later discovered that in order to properly map your classes into XSD schema types, you must specify an @XmlType annotation on *all* of the classes you wish to map (at least with JAXB 2.0, which is the version I'm currently using)

ClassCastException

After I resolved the first problem, I came across another pesky exception that likewise did not provide any useful debugging information. The plugin output indicated that it was finding annotations that it didn't know how to deal with, specifically the Java Persistence API annotations that I was using for Hibernate. After much digging around on Google and some detective work, I discovered that any annotations encountered by the jaxb-schemagen plugin had to be on the Maven runtime classpath. I double checked my POM to find that I had specified the scope of the JPA / Hibernate annotations as 'provided', which instructs Maven not to load them into either the compile or runtime classpaths. The initial reason I had for specifying the dependency scope as 'provided' was so that they would not needlessly get included with my project WAR files. I changed their scope to 'compile' (which also includes 'runtime') and voila, apt could now properly detect the unknown annotations, and get past them to deal with the JAXB annotations it needs to create the XML schema.



If anybody else encounters similar errors with JAXB-schemagen, I hope they find this page and find it useful.

4 comments:

Mike said...

Hello

Thanks a billion for this blog entry!
Although changing the scope of the package in Maven2 didn't quite do the trick for me. I finally understood the problem. All I did was: I referenced the 2 jars I needed in the schemagen command using the -cp switch.

Jigar said...

can you post the pom here..

Tommer Wizansky said...

I encountered a different ClassCastException. This was my own fault but, still, this might help someone.

I had an abstract class, AbstractA which was inherited by ConcreteA. The @XmlSeeAlso annotation was incorrect:

@XmlSeeAlso({
ConcreteB.class
})

Should have been, of course,

@XmlSeeAlso({
ConcreteA.class
})

Rags said...

I was (presumably) getting a NullPointerException too. I used mvn jaxb2-maven-plugin and schemagen execution and all my maven output showed (even with debug option etc) was

[INFO] --- jaxb2-maven-plugin:1.6:schemagen (schemagen) @ myproject ---
null

After a lot of time I figured a few of my java classes had a parameterized constructor without a default parameterless constructor. I found this out by including each java file one by one to know which java file had the issue... Hope this helps someone. I fixed this by ensuring all types having parameterized constructors also have a default constructor.