mojohaus / jaxb2-maven-plugin

JAXB2 Maven Plugin
https://www.mojohaus.org/jaxb2-maven-plugin/
Apache License 2.0
105 stars 76 forks source link

Schemagen will fail if package-info.java contains xmlns-attributes with the same prefix as in transformSchema #141

Open umefjord opened 5 years ago

umefjord commented 5 years ago

Re-posting the same problem as mentioned in https://github.com/mojohaus/jaxb2-maven-plugin/issues/52, with a new description and motivation:

We specify explicit xmlns namespace prefixes in our package-info.java files, in order to get well-defined namespace-prefixes in the generated XML.

Example package-info.java:

@XmlSchema(namespace = "http://schemas.acme.com/student", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, 
    xmlns = {
        @XmlNs(namespaceURI = "http://schemas.acme.com", prefix = "base"), 
        @XmlNs(namespaceURI = "http://schemas.acme.com/dap", prefix = "dap"),
        @XmlNs(namespaceURI = "http://schemas.acme.com/student", prefix = "st") })
package com.acme.student.interfaces;

We want the same well-defined namespaces in the XSD:s that are generated with the schemagen mojo, so we configure the mojo with:

<transformSchemas>
    <transformSchema>
        <uri>http://schemas.acme.com</uri>
        <toPrefix>base</toPrefix>
        <toFile>schemas.acme.com.xsd</toFile>
    </transformSchema>
    <transformSchema>
        <uri>http://schemas.acme.com/dap</uri>
        <toPrefix>dap</toPrefix>
        <toFile>schemas.acme.com-dap.xsd</toFile>
    </transformSchema>
    <transformSchema>
        <uri>http://schemas.acme.com/student</uri>
        <toPrefix>st</toPrefix>
        <toFile>schemas.acme.com-student.xsd</toFile>
    </transformSchema>
<transformSchemas>

However, the mojo fails when the namespaces are specified in the package-info.java files, with the following error:

Caused by: java.lang.IllegalStateException: Replaced prefix [st] with [tns] for URI [http://schemas.acme.com/student]
    at org.codehaus.mojo.jaxb2.schemageneration.postprocessing.schemaenhancement.SimpleNamespaceResolver$NamespaceAttributeNodeProcessor.process (SimpleNamespaceResolver.java:215)
    at org.codehaus.mojo.jaxb2.schemageneration.XsdGeneratorHelper.process (XsdGeneratorHelper.java:408)
    at org.codehaus.mojo.jaxb2.schemageneration.postprocessing.schemaenhancement.SimpleNamespaceResolver.initialize (SimpleNamespaceResolver.java:154)
    at org.codehaus.mojo.jaxb2.schemageneration.postprocessing.schemaenhancement.SimpleNamespaceResolver.<init> (SimpleNamespaceResolver.java:74)
    at org.codehaus.mojo.jaxb2.schemageneration.XsdGeneratorHelper.getFileNameToResolverMap (XsdGeneratorHelper.java:130)
    at org.codehaus.mojo.jaxb2.schemageneration.AbstractXsdGeneratorMojo.performExecution (AbstractXsdGeneratorMojo.java:462)
...

The problem seems to be related to the "tns" namespace prefix that is used by the mojo prior to the transform schema post-processing phase. As mentioned above we want to replace this with something unique and well-defined.

If we remove the xmlns-part in the package-info.java file we don't get predefined namespace prefixes in the generated XML, and if we remote the -part of the -tags we don't get the predefined prefixes in the schemagen generated XSD. Hence none of that is acceptable.

However, by a couple of small modifications to the mojo we can fix the problem:

SimpleNamespaceResolver.java#214

//If old prefix has changed, throw exception. The "tns" prefix may be overridden by a specific namespace in @XmlSchema(xmlns=...), and is therefore ignored here
            if (oldPrefixValue != null && !oldPrefixValue.equals(cacheKey) && !cacheKey.equals("tns")) {
                throw new IllegalStateException(
                        "Replaced prefix [" + oldPrefixValue + "] with [" + cacheKey + "] for URI [" + aNode.getNodeValue()
                                + "]");
            }

XsdGeneratorHelper.java#490

        if (!newPrefix.equals(oldPrefix) && currentResolver.getNamespaceURI2PrefixMap().containsValue(newPrefix)) {
            throw new MojoExecutionException(MISCONFIG + "Namespace prefix [" + newPrefix + "] is already in use."
                    + " Cannot replace namespace prefix [" + oldPrefix + "] with [" + newPrefix + "] in file ["
                    + currentResolver.getSourceFilename() + "].");
        }

This fix does the trick for us, hopefully it can make it into an upcoming official version as well.

Tested with jaxb2-maven-plugin, version 2.5.0.

slophantom commented 2 years ago

Not sure I agree with this conclusion, "user error." The plugin does not correctly handle integration with the package-info.java to set a unique prefix and namespace. You can set the namespace via package-info but not the prefix. The result is you are forced to use the dreaded NamespacePrefixMapper in your jaxb marshaller. Hopefully, umefjord's commit would fix this; if it did, it would be a nice addition to this plugin.