Wednesday, August 31, 2011

Customizing Namespace Prefixes

Changing the namespace prefix on XML elements sent to a web service seemed like a simple task. I wasn't intimately familiar with the granular customization details of JAXB or JAX-WS; but how hard could it be? After spending over a week with XSL, JAX-WS filters, endorsed packages, classpath ordering, and other fun hacks; I've developed a bit of hatred for JAXB. I thought I'd document my findings and hopefully save some other poor soul from its torment.

Start by enabling logging:
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
Flipping this property on will print all of the sent and received SOAP messages to standard output.

Next, annotate the namespace's generated package with a prefix directive:
@javax.xml.bind.annotation.XmlSchema(
    namespace = "http://tempuri.org/address",
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
    xmlns = {
        @javax.xml.bind.annotation.XmlNs(
            prefix = "addr",
            namespaceURI = "http://tempuri.org/address")
    })
package org.tempuri.address;
This is the package-info.java file generated by wsimport. Ideally, a bindings file should have wsimport create the XmlNs annotation but I couldn't find any documentation on how to do that. It'll be overwritten each time but there's a hack for that. (More details below.)

Strangely, the prefix would not work for me at this point and I had to upgrade to JAX-WS 2.2.1. This, in turn, forced me to move the JAX-WS 2.2.1 libraries higher up in the order of my Eclipse project classpath. My deployment package also now required the java.endorsed.dirs JVM property to be set and pointed at the 2.2.1 jars.

With the namespace prefix now working and no documentation on generating an XmlNs annotation, the package-info changes need to be automated. For my hacked solution, I moved package-info.java into my static source tree and added an Ant task to remove the file after generation:
<wsimport ... />
<delete file="${gen-src}/package-info.java" />

This produces a new issue. The wsimport task will generate and compile the package-info. When a full build executes later, the class file is already up to date and will be skipped. There are two solutions to this:
  1. Add a delete task to remove the class file as well.
  2. Add the -npa option to the wsimport task to suppress the generation of package-info files:
<wsimport ...>
  <xjcarg value="-npa" />
</wsimport>
<delete file="${gen-src}/package-info.java" />

It's awful, it's hideous, it keeps me awake at night, but it works. If you're reading this article looking for help and now you're just shaking your head in disgust, try Harald Wellmann's blog post. He does a great job of presenting two solutions I wish I had found much earlier in my research.

Saturday, August 20, 2011

wsimport depends/produces

Working with the wsimport Ant task, you may come across this warning:

Consider using <depends>/<produces> so that wsimport won't do unnecessary compilation

For whatever reason these nested elements were left out of the documentation.  I borrowed some wording from the JAXB documentation and wrote this amendment:

depends
Files specified with this nested element will be taken into account when the task does a modification date check.  For proper syntax, see <fileset>.

produces
Files specified with this nested element will be taken into account when the task does a modification date check.  For proper syntax, see <fileset>.

Example:
<wsimport
    wsdl="${local-wsdl-path}"
    destdir="${build.dir}"
    sourcedestdir="${src.generated.dir}">
    <depends file="${local-wsdl-path}"/>
    <produces dir="${src.generated.dir}"/>
</wsimport>

This wsimport example specifies a depends/produces relationship between a local WSDL file and the generated source directory. If the WSDL file has a modification date more recent than the generated directory, the wsimport task will regenerate and recompile the source code.

Tuesday, February 1, 2011

Dynamic Java Classpath in UNIX

I've been playing "guess the dependency" with some Axis2 Java code all morning.  After encountering three or four NoClassDefFound exceptions, I'm rewriting my java command to dynamically list all of the jar libraries on the classpath.  Here's what I compiled from browsing some forums and blogs:

find /opt/axis2/lib -type f -name *.jar | \
sed ':a;N;$!ba;s/\n/:/g'