I'm using CXF maven plugin to generate Java classes out of XSD scheme definitions. I use two xsdOptions-tags to create Java files of two different schemes into two different packages. The generated Java classes and factories have the same name, e.g. "ObjectFactory.java". How can I change the name of the generated files to a custom name?
Plugin entry in my POM.xml file:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-xjc-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>xjc</id>
<phase>generate-sources</phase>
<goals>
<goal>xsdtojava</goal>
</goals>
</execution>
</executions>
<configuration>
<sourceRoot>${basedir}/src/main/java/</sourceRoot>
<xsdOptions>
<xsdOption>
<xsd>${basedir}/src/main/resources/schemes/A.xsd</xsd>
<packagename>com.exmaple.A</packagename>
</xsdOption>
<xsdOption>
<xsd>${basedir}/src/main/resources/B.xsd</xsd>
<packagename>com.example.B</packagename>
</xsdOption>
</xsdOptions>
</configuration>
</plugin>
I tried adding extensionArgs-tags but that did not run.
This download (zip) is a stand-alone Maven project to demonstrate the configuration of the Maven cxf-xjc-plugin from Apache CXF together with the XJC HiSrc BasicJAXB Plugins. Specifically,
ObjectFactory
instances from different Java packages can be conveniently used in the same code block.Note: After extracting the download to your local folder, you can run the tests and/or the demo using:
mvn -Ptest clean test
mvn -Pexec compile exec:java
Both XML schemas (A and B) define similar business models. Each models a notebook
. The A schema models a paper notebook while the B schema models an electronic notebook. Both models have the same element names:
Business Model
notebook
owner
pageSpec
By default, XJC generates a JAXB class for each of the above, in separate Java packages:
Without Customization: A
org.example.nb.A.Notebook
org.example.nb.A.Owner
org.example.nb.A.PageSpec
org.example.nb.A.ObjectFactory
Without Customization: B
org.example.nb.B.Notebook
org.example.nb.B.Owner
org.example.nb.B.PageSpec
org.example.nb.B.ObjectFactory
Although the class names are the same, each class is qualified by its own Java package. And on the XML side, each document declares it's own namespace. When unmarshalling, JAXB uses the namespace from the XML documents (nbA.xml and nbB.xml) to determine the correct business model.
The problem is that references to the two models, in the same Java code block, require use of fully qualified class names.
To solve that problem, each XML schema is customized using a JAXB binding file invoking jaxb:class
declarations to rename the generate Java classes.
The classes from the A schema are customized to include the suffix: Analog
Extract from nbA.xjb
<jaxb:bindings schemaLocation="nbA.xsd" node="/xs:schema">
<jaxb:bindings node="//xs:element[@name='notebook']/xs:complexType">
<anx:annotate>@jakarta.xml.bind.annotation.XmlRootElement(name = "notebook")</anx:annotate>
<jaxb:class name="NotebookAnalog" />
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='Owner']">
<jaxb:class name="OwnerAnalog" />
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='PageSpec']">
<jaxb:class name="PageSpecAnalog" />
</jaxb:bindings>
</jaxb:bindings>
The classes from the B schema are customized to include the suffix: Digital
Extract from nbB.xjb
<jaxb:bindings schemaLocation="nbB.xsd" node="/xs:schema">
<jaxb:bindings node="//xs:element[@name='notebook']/xs:complexType">
<anx:annotate>@jakarta.xml.bind.annotation.XmlRootElement(name = "notebook")</anx:annotate>
<jaxb:class name="NotebookDigital" />
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='Owner']">
<jaxb:class name="OwnerDigital" />
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='PageSpec']">
<jaxb:class name="PageSpecDigital" />
</jaxb:bindings>
</jaxb:bindings>
Note: Why is there an
<anx:annotate ... >
extension on thenotebook
element? Because, XJC heuristics omit the normally present@XmlRootElement
annotation whenjaxb:class
is used to change theNotebook
class name. HiSrc HyperJAXB Annox is used to add the annotation back in.
The JAXB/XJC naming convention for ObjectFactory
is less flexible. The JAXB reference implementation and the API both assume that this is a well known class name, qualified by its Java package name. For example, the JAXB RI registers the name as a reserved class name.
The good news is that, an application only requires one instance of each ObjectFactory
; thus, each can be declared with short names as static instances.
Example: AbstractNotebookTest
protected static final org.example.nb.A.ObjectFactory OFA = new org.example.nb.A.ObjectFactory();
protected static final org.example.nb.B.ObjectFactory OFB = new org.example.nb.B.ObjectFactory();
Then the OFA
and OFB
instances can be used conveniently, in the same code block, as demonstrated here...
final Object object = getUnmarshaller().unmarshal(sample);
if ( object instanceof NotebookAnalog )
{
NotebookAnalog nba1 = (NotebookAnalog) object;
NotebookAnalog nba2 = OFA.createNotebookAnalog()
.useTitle("PaperBook")
.useOwner(OFA.createOwnerAnalog()
.useFirstname("Paul")
.useLastname("McCartney"))
.usePageSpec(OFA.createPageSpecAnalog()
.useLinesPerPage(new BigInteger("66"))
.usePageCount(new BigInteger("100")));
getLogger().debug("NBA1: {}", nba1);
getLogger().debug("NBA2: {}", nba2);
assertEquals(nba1, nba2, "Unmarshaled and Fluent NBAs are equal.");
}
else if ( object instanceof NotebookDigital )
{
NotebookDigital nbb1 = (NotebookDigital) object;
NotebookDigital nbb2 = OFB.createNotebookDigital()
.useTitle("EBook")
.useOwner(OFB.createOwnerDigital()
.useFirstname("John")
.useLastname("Lennon"))
.usePageSpec(OFB.createPageSpecDigital()
.useKbPerPage(new BigInteger("512"))
.usePageCount(new BigInteger("1024")));
getLogger().debug("NBB1: {}", nbb1);
getLogger().debug("NBB2: {}", nbb2);
assertEquals(nbb1, nbb2, "Unmarshaled and Fluent NBBs are equal.");
}
POM Configuration
The POM is configured to use two blocks for each schema: A and B.
Extract from pom.xml
...
<!-- mvn cxf-xjc:help -Ddetail=true -->
<!-- mvn cxf-xjc::generate -->
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-xjc-plugin</artifactId>
<version>${cxf-xjc-plugin.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>xsdtojava</goal>
</goals>
<configuration>
<fork>false</fork>
<sourceRoot>${basedir}/target/generated-sources/xjc/</sourceRoot>
<extensions>
<extension>org.patrodyne.jvnet:hisrc-basicjaxb-plugins:2.2.1</extension>
<extension>org.patrodyne.jvnet:hisrc-hyperjaxb-annox-plugin:2.2.1</extension>
</extensions>
<xsdOptions>
<xsdOption>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/nb.xjb</bindingFile>
<bindingFile>${basedir}/src/main/resources/nbA.xjb</bindingFile>
</bindingFiles>
<packagename>org.example.nb.A</packagename>
<xsd>${basedir}/src/main/resources/nbA.xsd</xsd>
<extension>true</extension>
<extensionArgs>
...
<extensionArg>-XfluentAPI</extensionArg>
<extensionArg>-XfluentAPI-fluentMethodPrefix=use</extensionArg>
</extensionArgs>
</xsdOption>
<xsdOption>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/nb.xjb</bindingFile>
<bindingFile>${basedir}/src/main/resources/nbB.xjb</bindingFile>
</bindingFiles>
<packagename>org.example.nb.B</packagename>
<xsd>${basedir}/src/main/resources/nbB.xsd</xsd>
<extension>true</extension>
<extensionArgs>
...
<extensionArg>-XfluentAPI</extensionArg>
<extensionArg>-XfluentAPI-fluentMethodPrefix=use</extensionArg>
</extensionArgs>
</xsdOption>
</xsdOptions>
</configuration>
</execution>
</executions>
... (dependencies)
</plugin>
...
Note: The cxf-xjc-plugin provides
extensionArgs
to activate XJC plugins from an extension library like HiSrc BasicJAXB Plugins. It is list of additional arguments passed to XJC. (ex:-XfluentAPI
).
The cxf-xjc-plugin
above explicitly declares several dependencies (not shown) to resolve JAR conflicts between the CXF product and the HiSrc product. Because these products are produced by independent teams, some version conflicts are likely. The download (zip) contains a tool, named ReadJarManifest.java to identify and resolve conflicts. The conflicts have been resolved in the download
sample.
Generate build.log
mvn -Dorg.slf4j.simpleLogger.log.org.apache.cxf.maven_plugin.XSDToJavaMojo=DEBUG -Ptest clean test >build.log
ReadJarManifest.java
Read JAR Manifest from a JAR file or a LOG file.
Usage:
javac ReadJarManifest.java
java ReadJarManifest [jarfile | stdin]
Example:
java ReadJarManifest <build.log
Note: The ReadJarManifest.java tool catches most issues but can also report false conflicts. In this example, the conflict percentage is falsely high because two non-conflicting jars have the same version number but different yet similar names.
Conflict%: 89
file:/home/rick/.m2/repository/org/glassfish/jaxb/txw2/4.0.5/txw2-4.0.5.jar
file:/home/rick/.m2/repository/org/glassfish/jaxb/xsom/4.0.5/xsom-4.0.5.jar
Disclaimer: I am the maintainer for the HiSrc projects.