Thursday, March 27, 2008

SOAP faults with spring ws and JAXB 2.0

When migrating existing web services from Axis2 to spring web services (1.0.3) and JAXB (2.0), I ran into three problems related to SOAP faults. I found some hints towards a solution on the net, but none of them were really exhaustive. In this blog post, I give a step by step overview of how we solved these three issues.

Let's start by sketching the context in which we encountered the problems. We already had a contract first approach (actually, our WSDL was generated using an AndroMDA cartridge), so we could start from an existing WSDL. We only had to move the types into a separate XSD file (for JAXB, see further). Our WSDL defines a lot of operations with input, output and fault messages. With axis2, these faults were represented as java exceptions (inheriting from java.lang.Exception). When serializing these java classes back to XML, they were represented as SOAP faults with as detail element of the soap fault a serialized version of the java exception (as defined in the XML schema). This is an example of a SOAP fault message sent to the client:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <soapenv:Fault>
        <faultcode>soapenv:Client</faultcode>
        <faultstring>FooException: null</faultstring>
        <detail>
           <ns:FooException xsi:type="ns:FooException" xmlns:ns="http://our.namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
              <ns:customField>0</ns:customField>
           </ns:FooException>
        </detail>
     </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>
The first problem when migrating to spring web services and JAXB, is that JAXB generates java types from the XSD rather then from the WSDL. The types of the fault messages are off course known as an XSD element, but the XSD has no information to distinguish them from other types (input and output messages). Typically all input messages end with "Request", all output messages with "Response" and all exceptions with "Exception", but JAXB doesn't do anything special with these naming conventions. Apart from these names, there is thus nothing special about the java types that JAXB generates for them. For exceptions, this leads to the confusing situation of having something named FooException that doesn't inherit from java.lang.Exception (I'll call them "fake" exceptions from now on).

This means that in our AbstractMarshallingPayloadEndpoint implementation (which is what you use if you want to implement web services with spring and a marshalling technology like JAXB), we can't throw these exceptions (as they are not throwable).

The second problem is that these fake exceptions don't have the JAXB @XmlRootElement annotation on them. This means that they can not directly be serialized: you first have to create a valid JAXBElement for them using the ObjectFactory that is generated by JAXB, something like
final JAXBElement element = new ObjectFactory().createFooException((FooException) exception);
The third problem is that spring's built in support for dealing with exceptions doesn't serialize the exception in the detail part of a SOAP fault. By default, spring only renders a fault code and error message in the SOAP fault.

Serializing exceptions as SOAP fault detail messages

Let's forget about the first and second problems for now and assume all exceptions properly inherit from java.lang.Exception and have the JAXB @XmlRootElement annotation on them. By writing a custom SoapFaultMappingExceptionResolver (or directly implementing EndpointExceptionResolver if you want to), you can customize the way the SOAP fault is generated. This is how the customizeFault method could be implemented:
@Override
protected void customizeFault(
 final Object endpoint, 
 final Exception exception, 
 final SoapFault fault)
{
 super.customizeFault(endpoint, exception, fault);

 // get the marshaller
 AbstractMarshallingPayloadEndpoint marshallingendEndpoint = (AbstractMarshallingPayloadEndpoint) endpoint;

 // get the result inside the fault detail to marshal to
 Result result = fault.addFaultDetail().getResult();

 // marshal
 try
 {
   marshallingendEndpoint.getMarshaller().marshal(exception, result);
 } catch (IOException e)
 {
   throw new RuntimeException(e);
 }
}
Note how we use the marshaller from the given endpoint to marshal the exception into the detail of the SOAP fault (represented using the Result interface).

Using JAXB's ObjectFactory to solve the second problem

As explained already, the second of the three problems forces us to use JAXB's ObjectFactory for the exceptions. This means that, in the code example above, we can't directly pass the exception to the marshaller. We first have to create a JAXBElement from the exception. This forces us to known the exact type of the exception, and thus requires ugly casting, for example:
@Override
protected void customizeFault(
 final Object endpoint,
 final Exception exception,
 final SoapFault fault)
{
 super.customizeFault(endpoint, exception, fault);

 // get the marshaller
 AbstractMarshallingPayloadEndpoint marshallingendEndpoint = (AbstractMarshallingPayloadEndpoint) endpoint;

 // get the result inside the fault detail to marshal to
 Result result = fault.addFaultDetail().getResult();

 // create the corresponding jaxb element
 final JAXBElement element;
 if (exception instanceof FooException)
 {
  element = new ObjectFactory().createFooException((FooException) exception);
 } else if (...)
 {
  // else if required for all possible exceptions
 }

 // marshal
 try
 {
   marshallingendEndpoint.getMarshaller().marshal(exception, result);
 } catch (IOException e)
 {
  throw new RuntimeException(e);
 }
}
As you can see, this code is no longer scalable. For every new exception, you have to add code.

Wrapping fake exceptions in a real exception

The last problem to solve is that the fake exceptions are not really java exceptions. We've looked into possibilities to customize the JAXB generation of java types in such a way that everything that ends with "Exception" is actually a real exception, but that didn't seem to be possible.

We ended up writing a solution that works, although I must say it is not very elegant. We've created a custom exception to wrap the "fake" exceptions generated by JAXB (called it UnmarshalledExceptionWrapperException). The wrapper wraps instances of type Object and verifies that their class names end with "Exception" (that really seems to be the best thing we can do). The custom SoapFaultMappingExceptionResolver now has to unwrap these fake exceptions and build a valid JAXB element from them:
@Override
protected void customizeFault(
 final Object endpoint,
 final Exception exception,
 final SoapFault fault)
{
 super.customizeFault(endpoint, exception, fault);

 // get the wrapper
 UnmarshalledExceptionWrapperException exception = (UnmarshalledExceptionWrapperException) ex;

 // unwrap the exception
 Object unwrappedException = exception.getWrappedException();

 // get the marshaller
 AbstractMarshallingPayloadEndpoint 
  marshallingendEndpoint = (AbstractMarshallingPayloadEndpoint) endpoint;

 // get the result inside the fault detail to marshal to
 Result result = fault.addFaultDetail().getResult();

 // create the corresponding jaxb element
 final JAXBElement element;
 if (unwrappedException instanceof FooException)
 {
  element = new ObjectFactory().createFooException((FooException) unwrappedException);
 } else if (...)
 {
  // else if required for all possible exceptions
 }

 // marshal
 try
 {
   marshallingendEndpoint.getMarshaller().marshal(exception, result);
 } catch (IOException e)
 {
  throw new RuntimeException(e);
 }
}

Conclusion

We ended up with a working spring web services implementation to mimic the behavior of Axis2 in respect to SOAP faults, although it is not the kind of java code one can be very proud of. The major problem seems to be that code modification is required when adding, removing or changing the available fault messages in the WSDL. We would have a far more elegant solution if we could get JAXB 2.0 to generate real exceptions with an @XmlRootElement annotation on them.

Wednesday, March 5, 2008

building the latest scala eclipse plugin from source

Rumors have been around for some time now that someone in the scala team is working on a new version of the eclipse plugin for scala. From the scala website you can download a version of the eclipse plugin that is up to date with the latest development on scala itself, but that plugin doesn't seem to get any new features.

The new rewritten plugin can not be downloaded yet (or at least I didn't find it anywhere), so this is what I did to build it from source. You'll need java and ant (including the optional tasks) installed to get this working. The examples are for building on linux, so there might be some differences for other platforms.

First you'll need to checkout the scala and plugin sources from subversion:
svn co http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk scala
svn co http://lampsvn.epfl.ch/svn-repos/scala/plugin plugin
You can build scala, or if you already have an up to date scala distribution on your system somewhere, you can also point the plugin build to that existing scala distribution (see build.properties.SAMPLE in the plugin directory).

Building scala is as simply as running ant with enough memory (in the scala directory):
export ANT_OPTS='-Xms512M -Xmx1024M'; ant dist
The same is true for building the plugin (in the plugin directory):
export ANT_OPTS='-Xms512M -Xmx1024M'; ant dist
Now start eclipse and uninstall the existing scala plugin if you happen to have that one installed. To install the new plugin, you can create a new local update site and point it to the dist/scala.update directory in the plugin directory you've just built.

To get existing scala projects to work with the new plugin, you have to modify the .project files a bit. This is an example:
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
 <name>myproject</name>
 <comment></comment>
 <projects>
 </projects>
 <buildSpec>
  <buildCommand>
   <name>scala.plugin.scalabuilder</name>
   <arguments>
   </arguments>
  </buildCommand>
 </buildSpec>
 <natures>
  <nature>scala.plugin.scalanature</nature>
  <nature>org.eclipse.jdt.core.javanature</nature>
 </natures>
</projectDescription>
That's all. Now you've got code completion in scala ;-)