Modify java classes to include specific annotations at compile time - java

I have many classes generated by JAXB's xsd2java. I need all these classes to be annotated with specific annotations at compile time (for example with lombok annotations). Is there any way to do this, with some code generation tool for example?

Disclaimer: I am the author of JAXB2 Annotate Plugin which allows you adding arbitrary annotations to the schema-derived classes.
Short example:
<xsd:complexType name="FooType">
<xsd:annotation>
<xsd:appinfo>
<annox:annotate>#java.lang.SuppressWarnings({"unchecked","rawtypes"})</annox:annotate>
<annox:annotate target="package">#javax.annotation.Generated({"XJC","JAXB2 Annotate Plugin"})</annox:annotate>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="bar" type="xsd:string"/>
<xsd:element name="foobar" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<annox:annotate>#java.lang.SuppressWarnings({"unchecked","rawtypes"})</annox:annotate>
<annox:annotate target="setter">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="setter-parameter">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="getter">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="field">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="class">#java.lang.Deprecated</annox:annotate>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
Works in external binding files as well.
Limitations:
Annotations are provided in Java syntax, but you have to use fully qualified names.
You have to include annotations classes (lombok, for instance) into the XJC classpath as these classes must be available during the schema compilation time.
ps. I assume that when you said xsd2java you probably meant XJC.
Update
The OP asked about in comments how to configure it with the jaxb2-maven-plugin.
You can use jaxb2-annotate-plugin with jaxb2-maven-plugin as well. I just have never tried it.
You can include additional JARs into classpath by using the dependencies/depenency in pom.xml.
Then you'll need to add arguments into the configuration.
See this answer (and question) for examples:
https://stackoverflow.com/a/12804497/303810
It is about other plugins but you'll get a clue on how to configure it with Codehaus jaxb2-maven-plugin.
Configuration with my maven-jaxb2-plugin would be as follows:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<configuration>
<extension>true</extension>
<args>
<arg>-Xannotate</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics-annotate</artifactId>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-annotate-plugin-test-annox-annotations</artifactId>
</plugin>
</plugins>
</configuration>
</plugin>
This part:
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-annotate-plugin-test-annox-annotations</artifactId>
</plugin>
refers to the artifact containing the annotation classes.
Here's a sample pom.xml for maven-jaxb2-plugin/jaxb2-annotate-plugin combo.

Related

jaxb2-maven-plugin generating package-info.java with xmlns prefixes

I want to generate java classes with the jaxb2-maven-plugin. I am using the following configuration:
pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>SomeID</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<extension>true</extension>
<clearOutputDir>true</clearOutputDir>
<sources>
<source>src/main/xsd/schema.xsd</source>
</sources>
<noGeneratedHeaderComments>true</noGeneratedHeaderComments>
</configuration>
</execution>
</executions>
</plugin>
schema.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://my.target.namespace/uri"
xmlns="http://my.target.namespace/uri"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:h="http://my.uri.for.prefix.h"
xmlns:f="http://my.target.namespace/uri">
<xsd:import namespace="http://my.uri.for.prefix.h" schemaLocation="schema2.xsd"/>
<xsd:complexType name="FooType">
<xsd:sequence>
<xsd:element ref="h:something" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="FooType" type="FooType" />
</xsd:schema>
The Jaxb2 plugin is generating me the following package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://my.target.namespace/uri", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package ...;
But, what I want to get is this:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://my.target.namespace/uri", xmlns = {
#XmlNs(prefix="f", namespaceURI="http://my.target.namespace/uri"),
#XmlNs(prefix="h", namespaceURI="http://my.uri.for.prefix.h")
}, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package ...;
import javax.xml.bind.annotation.XmlNs;
The prefixes are missing in my generated file. How to do that? I tried already to create a binding file but this didn't worked how I expected.
Please see this answer on how to solve this problem:
https://stackoverflow.com/a/10812236/1389219
The answer is very well written and easy to follow. Basically you will have to:
Drop jaxb2-maven-plugin in favour of maven-jaxb2-plugin.
Include the jaxb2-namespace-prefix dependency and provide the <arg>-Xnamespace-prefix</arg>.
Write a new bindings.xml file which is only a few lines long.
Your POM file will become more verbose, but it is worth it to have a package-info.java generated the way you require.
As a bonus, there are a heap of additional plugins and dependencies related to maven-jaxb2-plugin that provide extra features. One that I found helpful was jaxb2-rich-contract-plugin that gave the ability to generate builders and make the generated classes immutable*.
* Well, not strictly speaking immutable (as it just changes the setter methods to be package private), but enough to make them feel safer.

Adding #Generated annotation to JAXB generated classes

I am trying to add a #javax.annotation.Generated annotation to all my classes generated by JAXB from a XSD.
I am using maven-jaxb2-plugin and a binding.xjb file to generate the source. I saw that the JAXB plugin jaxb2-basics-annotate should do the trick. But all I can find are examples that add annotations to specific classes. Like this one:
<jaxb:bindings schemaLocation="csw/2.0.2/CSW-discovery.xsd" node="/xs:schema">
<jaxb:bindings node="xs:complexType[#name='GetRecordsType']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="GetRecordsType" />
</annox:annotate>
</jaxb:bindings>
How can I do this for each of my generated files? Is this possible at all?
It appears you are using highsource/jaxb2-annotate-plugin If you are also using his highsource/maven-jaxb2-plugin, there is builtin support for this feature. Just add this to the plugin configuration:
<configuration>
<markGenerated>true</markGenerated>
</configuration>
Also check controlling the output.
If you are the official jaxb2 plugin, that feature is also builtin
<configuration>
<addGeneratedAnnotation>true</addGeneratedAnnotation>
</configuration>

Getting a list of JAXBElement where I want a list of predefined java objects

I use Apache Camel + JAXB for Soap processing. The java glasses are generated by a maven plugin called cxf-codegen-plugin.
The Problem I am facing is that when I want to use a property which is a list. In that case I will always get a list of JAXBElement instead of objects of the correct class.
Assume this given xml snipped:
<domainObjects avqxsi:type="avqsq:AssetAllocation" id="100" name="Some Name">
<nodes>101</nodes>
<nodes>102</nodes>
</domainObjects>
Now all the "nodes" are ids of different domain objects of type AANode. So in the xsd it is defined like so:
<xsd:complexType name="AssetAllocation">
<xsd:complexContent>
<xsd:extension base="avqsq:DomainObject">
<xsd:sequence>
<xsd:element ecore:reference="avqsq:AANode" maxOccurs="unbounded" name="nodes" type="xsd:IDREF"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
And I have defined some bindings.xml:
<jaxb:bindings node="xsd:complexType[#name='AssetAllocation']//xsd:element[#name='nodes']">
<jaxb:property>
<jaxb:baseType name="my.api.xsd.AANode"/>
</jaxb:property>
</jaxb:bindings>
What I want is a POJO property like this:
#XmlElementRef(name = "nodes")
protected List<AANode> nodes;
But what I actually get at runtime is a List<JAXBElement<AANode>> which leads into a ClassCastException.
EDIT 1:
I have missed the fact that the cxf-codegen framework is generating a class where you clearly can see that the property is annotated with JAXBElement.class which i think is wrong. Interestingly changing the annotation by hand to AANode.class will fail with an IllegalAnnotationException: AANode" or any of its subclasses are not known to this context.
public class AssetAllocation
extends DomainObject
implements Serializable, Equals, HashCode, ToString
{
#XmlElementRef(name = "nodes", type = JAXBElement.class)
protected List<AANode> nodes;
apache CXF code gen plugin will always generate codes with JAXBElement until you set the generate element property flag.
Please create Jaxb binding.xml and refer that binding xml in your code gen plugin section from pom file as below
binding.xml
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
<jaxb:bindings>
<jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
</jaxb:bindings>
code gen plugin
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/META-INF/wsdl/CxfExampleService.wsdl</wsdl>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/META-INF/wsdl/binding/bindings.xml</bindingFile>
</bindingFiles>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>
This will resolve the issue
In fact the wsdl2java generates classes with wrong annotations. Instead of
#XmlElementRef(name = "nodes", type = JAXBElement.class)
protected List<AANode> nodes;
One would expect to have:
#XmlIDREF
protected List<AANode> nodes;
I was not able to manage this by bindings.xml. So my final solution is that I use a Byte-Code manipulation to fix the annotations. That way I do not have to mess around with the generated classes or with the generator itself.

Jaxb doesn't preserve case of namespace in package

I'm using the codehaus jaxb2-maven-plugin, v1.5 to compile XSDs into POJOs, but the generated package name coerces the package name to lower case (so, if I have my target namespace as http://example.com/sampleNamespace, then the generated package is com.example.samplenamespace).
I've googled around a bit and found mostly people having problems with underscores getting munged to dots, and the solution for that, but I can't seem to find something specific for preserving the case of the namespace.
NB: I don't want to have to repeat myself and override the generated package name, so the generatePackage option in the maven config isn't for me.
Before finding about the underscore munging, I had tried that, and also a regular space - both stick a dot in there.
Any ideas?
Schema:
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:t="http://example.com/sampleNamespace" targetNamespace="http://example.com/sampleNamespace"
jaxb:version="2.0">
<complexType name="MyFirstClass">
<sequence>
<element name="MyFirstElement" type="string" />
</sequence>
</complexType>
</schema>
Maven config:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
</plugin>
You will need to leverage a JAXB bindings file to specify a package name if you do not want to use the one that JAXB generates based on common Java coding conversions.
<bindings
xmlns="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<bindings schemaLocation="schema.xsd">
<schemaBindings>
<package name="com.example.sampleNamespace"/>
</schemaBindings>
</bindings>
</bindings>

Why does JAXB say "xxx is an interface, and JAXB can't handle interfaces". Even though the generated class is not an interface

I used JAXB to bind my xsd's and then tried creating the JAXBContext:
JAXBContext jaxbContext = JAXBContext.newInstance("my package name");
But JAXB gives 180 IllegalAnnotationsException.
Most of the exceptions have the following messages:
XXX is an interface, and JAXB can't handle interfaces
XXX does not have a no-arg default constructor
#XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML.
When I look at the classes generated none of them are interfaces and I can't understand why JAXB is interpreting them as interfaces.
Here's is the stack trace of one of the errors reported by JAXB :
com.sc.md.datatypes.schemas.csemessage.EnvelopeType is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at com.sc.md.datatypes.schemas.csemessage.EnvelopeType
at protected com.sc.md.datatypes.schemas.csemessage.EnvelopeType com.sc.md.datatypes.schemas.csemessage.cseMessage.envelope
at com.sc.md.datatypes.schemas.csemessage.cseMessage
com.sc.md.datatypes.schemas.csemessage.EnvelopeType does not have a no-arg default constructor.
this problem is related to the following location:
at com.sc.md.datatypes.schemas.csemessage.EnvelopeType
at protected com.sc.md.datatypes.schemas.csemessage.EnvelopeType com.sc.md.datatypes.schemas.csemessage.cseMessage.envelope
at com.sc.md.datatypes.schemas.csemessage.cseMessage
And this is how the type is defined in xsd :
<xs:complexType name="EnvelopeType">
<xs:sequence>
<xs:element name="Sent" type="DateTimeType"/>
<xs:element name="Identifier" type="String_1_14"/>
<xs:element name="AcknowledgementCode" type="AcknowledgementCodeType"/>
</xs:sequence>
<xs:simpleType name="AcknowledgementCodeType">
<xs:restriction base="xs:string">
<xs:enumeration value="m"/>
<xs:enumeration value="p"/>
</xs:restriction>
</xs:simpleType>
Here is the pom.xml I have used to generate the binding :
<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>com.cs</groupId>
<artifactId>cs-jaxb</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.4-1</version>
</dependency>
</dependencies>
<name>cs jaxb</name>
<version>1.0.0</version>
<parent>
<artifactId>hip-jaxb-parent</artifactId>
<groupId>com.cs</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<id>CS</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<schemaDirectory>src/main/resources/</schemaDirectory>
<schemaIncludes>
<include>**/*.xsd</include>
</schemaIncludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
Please be patient with me since its the first time I'm asking a question on the web :)
Thanks to all, it was as usual a big mistake on my part, but anyways I enjoyed using stackoverflow for the first time and will make a point to snoop around here.
The problem was with my classpath.I was referring to a xmlbeans binding project, which had the java source with the same package and classes as the ones generated by jaxb, which were included as a jar on my classpath.
So I had 2 classes with same name and package, but to my misery JAXB was picking the Xmlbeans class and I didn't realise that for 2 days.This is one of the oldest errors in Java and I apologise for the blunder.If anyone needs any clarification please drop a comment.
Have you annotated all root classes with the #XmlRootElement annotation?
See: Unofficial JAXB Guide - Mapping interfaces - Java.net
Second, I would recommend you not to create your JAXBContext via JAXBContext.newInstance("my package name");. It is better to specify the root classes explicitly. Therefore if you have two root classes named ClassA and ClassB use it this way:
JAXBContext.newInstance(new Class[] {ClassA.class, ClassB.class});
I suspect that you are trying to use a JAXB 1 (JSR-31) model with a JAXB 2 (JSR-222) runtime. In JAXB 1 implementations generated spec defined interfaces backed by implementation specific impl clases. In JAXB 2 this became spec defined classes with standard annotations that are compatible with all implementations. Most JAXB 2 implementations support their own JAXB 1 models, but some additional config may be necessary, if you try to use a JAXB 1 model with a difference JAXB 2 provider you may see this type of error.

Categories

Resources