10 November 2008

We’re using Maven 2 at work to build our projects now. One of the projects uses JAXB to manage some XML documents. Well, the JAXB standard doesn’t say anything about how namespaces are handled when marshalling to XML, which means I can’t write XSLT against our output (FAIL). Sun’s JAXB implementation has a way to control namespace output, but it’s non-standard. Even worse, it changed between Java 5.0 and 6.0 (package rename), so code written against 5.0 will fail when running in a newer JVM.

I’m <a target=“_blank” href=“http://pragmaticintegration.blogspot.com/2007/11/moving-jaxb-20-applications-built-by.html

not the only one who has had this problem, but I think my solution is different than any I’ve seen. It took me a long time to get Maven to cooperate, but I finally got it working. I believe I found a bug in Maven, but I haven’t looked at the source yet to verify it. Maven documentation is extremely lacking when it comes to explaining what’s really going on. Sadly, it’s the best thing out there; the only good thing I can say is Maven 2 is better than the original.

Here is the Java code and Maven POM excerpts necessary to explain my solution:

import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
class PrefixMapper_JDK5 extends NamespacePrefixMapper {
    @Override
    public String getPreferredPrefix(String namespaceUri, String suggestion,
            boolean requirePrefix) {
        return suggestion;
    }
}

//JDK 6 added "internal" to the package naming
import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;
class PrefixMapper_JDK6 extends NamespacePrefixMapper {
    @Override
    public String getPreferredPrefix(String namespaceUri, String suggestion,
            boolean requirePrefix) {
        return suggestion;
    }
}

public Object getNamespacePrefixMapper() {
    Object r = null;
//the mapper classes should be compiled separately before this method
//is compiled so they match the proper JDK (-source and -target flags)
    Class[] mapperList = {
        //first one in list gets priority...
        PrefixMapper_JDK6.class, PrefixMapper_JDK5.class
    };
    try { //there is a better way to implement this... you can figure it out
        r = mapperList[0].getClass().newInstance();
    } catch (Exception e) {
        try {
            r = mapperList[1].getClass().newInstance();
        } catch (Exception ex) {}
    }
    return r; //null if neither class could be loaded
}
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
        <excludes>
            <!-- Maven is buggy here and will not remove the first
                 exclude line given, so the first one needs to be
                 bogus. -->
            <exclude>==MavenBugWorkaround==</exclude>
            <exclude>**/PrefixMapper_JDK5.java</exclude>
        </excludes>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
            <phase>compile</phase>
            <configuration>
                <source>1.5</source>
                <target>1.5</target>
                <!-- clears inherited exclude list
                     (except for 1st element) -->
                <excludes/>
                <!-- only uncompiled files will be looked at -->
                <includes>
                    <include>**/*.java</include>
                </includes>
            </configuration>
        </execution>
    </executions>
</plugin>