Search code examples
mavenjaxbmaven-jaxb2-plugin

Can't extends base schema in maven-jaxb2-plugin in a secondary project


I'm not able to extends an xsd schema from another project.

I've a basemodel.xsd in a jar project (ModelBase:jar), which also contains generated classes via maven-jaxb2-plugin. I'm trying to extends that model in another project (test), using basemodel.xsd as dependency

Here the setup:

basemodel.xsd in ModelBase:jar

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:tnsBaseModel="h-spamfilter-p://it.test.base.model" xmlns:xs="h-spamfilter-p://www.w3.org/2001/XMLSchema" xmlns:jaxb="h-spamfilter-p://java.sun.com/xml/ns/jaxb" targetNamespace="h-spamfilter-p://it.test.base.model" version="1.0" jaxb:version="2.1">
    <xs:complexType name="requestBase">
                  <xs:sequence>
            <xs:element name="errorMSG" type="xs:string" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

model2.xsd in test:jar

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:tns="h-spamfilter-p://it.test.newmodel" xmlns:tnsBaseModel="h-spamfilter-p://it.test.base.model" xmlns:xs="h-spamfilter-p://www.w3.org/2001/XMLSchema" xmlns:jaxb="h-spamfilter-p://java.sun.com/xml/ns/jaxb" 
xmlns:annox="h-spamfilter-p://annox.dev.java.net" targetNamespace="h-spamfilter-p://it.test.newmodel" version="1.0" jaxb:version="2.1"
 jaxb:extensionBindingPrefixes="annox">
    <xs:import namespace="h-spamfilter-p://it.test.base.model"/>
        <xs:complexType name="testInfo">
        <xs:complexContent>
            <xs:extension base="tnsBaseModel:requestBase">
                <xs:sequence>
                    <xs:element name="body" type="tns:other"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

Please note the import without explicit schemaLocation.

test:jar pom:

<plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.14.0</version>
            <executions>
                <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <phase>generate-sources</phase>
                </execution>
            </executions>
            <configuration>
                 <generateDirectory>${project.build.directory}/generated-sources/annotations/</generateDirectory>
                <cleanPackageDirectories>true</cleanPackageDirectories>
                <forceRegenerate>true</forceRegenerate>
                <schemas>
                  <schema>       
                    <fileset>
                        <directory>${project.basedir}/src/main/resources</directory>
                        <includes>
                            <include>model2.xsd</include>
                        </includes>
                    </fileset>
                   </schema>
                    <schema>
                       <dependencyResource>
                            <groupId>it.test</groupId>
                             <artifactId>ModelBase</artifactId>
                            <version>${modelbase.version}</version>
                            <resource>basemodel.xsd</resource>
                        </dependencyResource>
                    </schema>
                </schemas>
            </configuration>
        </plugin>
   

mvn -X output:

...

[INFO] dependsURIs (resolved):[file:/Users/computer/Documents/eclipse-workspace/test/src/main/resource/catalog.cat, file:/Users/computer/Documents/eclipse-workspace/test/src/main/resources/model2.xsd, file:/Users/computer/Documents/eclipse-workspace/test/src/main/resources/model2.xsd, jar:file:/Users/computer/.m2/repository/it/test/ModelBase/trunk-SNAPSHOT/ModelBase-trunk-SNAPSHOT.jar!/basemodel.xsd, file:/Users/computer/Documents/eclipse-workspace/test/pom.xml]
[INFO] optionsConfiguration:OptionsConfiguration [specVersion=2.2
 generateDirectory=/Users/computer/Documents/eclipse-workspace/test/target/generated-sources/annotations
 generatePackage=null
 schemaLanguage=null
 grammars.systemIds=[file:/Users/computer/Documents/eclipse-workspace/test/src/main/resources/model2.xsd, file:/Users/computer/Documents/eclipse-workspace/test/src/main/resources/model2.xsd, maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd]
 bindFiles.systemIds=[]
 plugins=[]
 readOnly=false
 packageLevelAnnotations=true
 noFileHeader=false
 enableIntrospection=false
 disableXmlSecurity=true
 accessExternalSchema=all
 accessExternalDTD=all
 contentForWildcard=false
 extension=true
 strict=true
 verbose=true
 debugMode=false
 arguments=[-episode, /Users/computer/Documents/eclipse-workspace/test/target/generated-sources/annotations/META-INF/sun-jaxb.episode]]

[INFO] The [forceRegenerate] switch is turned on, XJC will be executed.
[INFO] Parsing input schema(s)...
[DEBUG] Resolving publicId [null], systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd].
resolveSystem(maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd)
[DEBUG] Parent resolver has resolved publicId [null], systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd] to [null].
[DEBUG] Resolving systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd] as Maven dependency resource.
[DEBUG] Resolved dependency resource [Dependency {groupId=it.test, artifactId=ModelBase, version=trunk-SNAPSHOT, type=jar, classifier=null, resource=basemodel.xsd}] to resource URL [jar:file:/Users/computer/.m2/repository/it/test/ModelBase/trunk-SNAPSHOT/ModelBase-trunk-SNAPSHOT.jar!/basemodel.xsd].
[DEBUG] Resolved systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd] to [jar:file:/Users/computer/.m2/repository/it/test/ModelBase/trunk-SNAPSHOT/ModelBase-trunk-SNAPSHOT.jar!/basemodel.xsd].
[DEBUG] Resolving publicId [h-spamfilter-p://it.test.base.model], systemId [null].
resolvePublic(h-spamfilter-p://it.test.base.model,null)
[DEBUG] Parent resolver has resolved publicId [h-spamfilter-p://it.test.base.model], systemId [null] to [null].
[ERROR] Error while parsing schema(s).Location [ file:/Users/computer/Documents/eclipse-workspace/test/src/main/resources/model2.xsd{1007,50}].
org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'tnsBaseModel:requestBase' to a(n) 'type definition' component.
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException (ErrorHandlerWrapper.java:203)
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error (ErrorHandlerWrapper.java:134)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError (XMLErrorReporter.java:396)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr (XSDHandler.java:4154)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError (XSDHandler.java:4137)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl (XSDHandler.java:1684)
    at 
[DEBUG] Resolving publicId [null], systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd].
resolveSystem(maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd)
[DEBUG] Parent resolver has resolved publicId [null], systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd] to [null].
[DEBUG] Resolving systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd] as Maven dependency resource.
[DEBUG] Resolved dependency resource [Dependency {groupId=it.test, artifactId=ModelBase, version=trunk-SNAPSHOT, type=jar, classifier=null, resource=basemodel.xsd}] to resource URL [jar:file:/Users/computer/.m2/repository/it/test/ModelBase/trunk-SNAPSHOT/ModelBase-trunk-SNAPSHOT.jar!/basemodel.xsd].
[DEBUG] Resolved systemId [maven:it.test:ModelBase:jar::trunk-SNAPSHOT!/basemodel.xsd] to [jar:file:/Users/computer/.m2/repository/it/test/ModelBase/trunk-SNAPSHOT/ModelBase-trunk-SNAPSHOT.jar!/basemodel.xsd].
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.076 s
[INFO] Finished at: 2023-03-29T10:21:00+02:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.14.0:generate (default) on project test: Unable to parse input schema(s). Error messages should have been provided. -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.14.0:generate (default) on project test: Unable to parse input schema(s). Error messages should have been provided.
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:375)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:351)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:215)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:171)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:163)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:294)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
   
[ERROR] 
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] h-spamfilter-p://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

I get Cannot resolve the name 'tnsBaseModel:requestBase' to a(n) 'type definition' component., adding schemaLocation="basemodel.xsd" cause to get [WARNING] Error while parsing schema(s).Location [ file:/Users/computer/Documents/eclipse-workspace/test/src/main/resources/model2.xsd{5,94}]. org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'basemodel.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.


Solution

  • The HiSrc HigherJAXB Maven Plugin is a fork of maven-jaxb2-plugin that can be used to demonstrate this solution (zip). The solution includes a catalog and an episode configuration to generate JAXB classes from basemodel.xsd and model2.xsd where the second schema imports the first schema to extend a complex type named requestBase and defines an element/type named testInfo.

    A catalog is a logical structure that contains "mapping" information. In order to make optimal use of the information about an XML external resource, there needs to be some interoperable way to map the information in an XML external identifier into a URI reference for the desired resource.

    and

    An episode file, typically *META-INF/sun-jaxb.episode*, is generated by the XJC (XML Schema to Java) compiler. It is a schema bindings file that associates schema types with existing classes. It is useful when you have one XML schema that is imported by other schemas, as it prevents the model from being regenerated. XJC will scan JARs for *.episode files, then use them as binding files automatically.

    Execution

    This solution (zip) is a stand-alone, multi-module Maven project. You can run the test(s) using:

    mvn -Pall,test clean test
    mvn -Ptest clean install
    cd extend
    mvn -Ptest clean test
    mvn -Pexec clean compile exec:java
    

    Only the first execution is necessary to demonstrate the solution and this output shows the test results. The other executions provide more insight, as follows:

    mvn -Pall,test clean test

    This solution contains a top-level POM project with two sub-modules: base and extend. The goal of this first execution is to clean, compile and test all three projects using the Maven reactor. Note: The reactor keeps all modules on the classpath; thus, the extend module is able to resolve the base module, cleverly.

    mvn -Ptest clean install

    To resolve the base artifact outside of the reactor, you need to install the artifact into your local repository. Note: A profile named test is used to add the SLF4J implementation into the build for its test phase, only.

    cd extend

    Change to the extend sub-module for testing and execution outside of the multi-module reactor.

    mvn -Ptest clean test

    Perform a clean test using the test profile for unit testing.

    mvn -Pexec clean compile exec:java

    Execute a simple Java application to demonstrate the unmarshalling of this sample TestInfo.xml. Note: The exec profile adds the simplelogger.properties and SLF4J implementation into the classpath for this execution only.

    Catalog Configuration

    Because model2.xsd imports the namespace defined in basemodel.xsd, a catalog.xml is required to resolve the physical location using MavenCatalogResolver.

    model2.xsd

    ...
    <xs:import namespace="h-spamfilter-p://it.test.base.model"/>
    ...
    

    In this case, the namespace is the public identifier for the catalog's publicId.

    catalog.xml

    ...
    <public
        publicId="h-spamfilter-p://it.test.base.model"
        uri="maven:org.patrodyne.jvnet:hisrc-higherjaxb-sample-base-extend-base!/basemodel.xsd"
    />
    ...
    

    The catalog resolves the publicId to a uri, the physical location. In this case, the physical location is inside of the base jar.

    Episode Configuration

    Because the base module generates and provides the RequestBase class, you need to configure the extend module to use the *META-INF/sun-jaxb.episode* bindings from the base module.

    extend/pom.xml

    ...
    <plugin>
        <groupId>org.patrodyne.jvnet</groupId>
        <artifactId>hisrc-higherjaxb-maven-plugin</artifactId>
        <executions>
            <execution>
                <goals>
                    <goal>generate</goal>
                </goals>
                <configuration>
                    <episodes>
                        <episode>
                            <groupId>org.patrodyne.jvnet</groupId>
                            <artifactId>hisrc-higherjaxb-sample-base-extend-base</artifactId>
                        </episode>
                    </episodes>
                    <!-- Using catalogs to resolve schema URIs in strict mode is known to be problematic and may fail. -->
                    <strict>false</strict>
                    <catalog>src/main/resources/catalog.xml</catalog>
                </configuration>
            </execution>
        </executions>
    </plugin>
    ...
    

    Important: You must configure the plugin to use your catalog.xml file!

    JAXB Class and Episode Generation

    The multi-module Maven project generates these JAXB classes, where TestInfo.java extends RequestBase.java:

    base/target/generated-sources/xjc

    ├── h_spamfilter_p
    │   └── it_test_base
    │       ├── ObjectFactory.java
    │       ├── package-info.java
    │       └── RequestBase.java
    └── META-INF
        └── sun-jaxb.episode
    

    extend/target/generated-sources/xjc

    ├── h_spamfilter_p
    │   └── it_test
    │       ├── ObjectFactory.java
    │       ├── package-info.java
    │       └── TestInfo.java
    └── META-INF
        └── sun-jaxb.episode
    

    Testing

    The JUnit test classes, RequestBaseTest and TestInfoTest, scan for the sample file(s) and invoke the method checkSample(File sample) to provide each file to the tester. For this project, a JAXBContext is created and each file in the samples path is unmarshaled to a base-extend object. When successful, the base-extend object is marshaled for logging and your review.

    Disclaimer: I am the maintainer for the forked HiSrc projects.