In this entry I discuss reasons to switch from Axis to XFire, and then how to get XFire as a server working nicely with .Net as a client.
I have written some pastentries about Java/.Net web service interop, specifically how to get a Java Axis server working with a .Net 1.1 client. Now with .Net 2.0 we have nullable types built in to the language which removes the need to use the 3rd party library NullableTypes to handle nullable values.
I am aware that using a WSDL-first approach tends to lead to better interoperability, but here I will only look at generating WSDL from Java classes, and generating the .Net proxy code from WSDL using Microsoft’s WSDL tool. The simple fact of the matter is that people are using the tools this way.
So with nullable types built in to the language things work a little better, but Axis still has some of the same problems interoperating with .Net that it did before:
Problems returning null for a web method.
Passing arrays of objects had a bug which required a workaround. Although they reported it was fixed in 1.2, it still persists in my tests in 1.3 and 1.4
So lets take a look at XFire. After working with it (version 1.1) a bit, I found that:
It had no problems returning null values for any web method.
It had none of the problems with arrays that Axis did.
Spring integration is very nice and allows your web service class to be very clean. The web service class is a simple class implementing an interface with no dependencies on anything. You don’t have to extend any classes or depend on anything web service specific. The methods that are exposed are those of the interface.
Writing handlers is extremely simple and easy.
No need to define objects like you would in Axis’s server-config.wsdd. It figures everything out on its own.
It interoperates with .Net 2.0 much better than Axis does
I recommend having the source code for XFire handy when you’re working with it. Though there is documentation, there is a lot you can figure out from the source code and it is clean enough that you won’t get lost looking at it.
With XFire’s default configuration, it produces WSDL that has the attribute minOccurs=”0” in addition to nillable=”true” on properties. .Net 2.0 interprets this to mean “not only can you pass a null value for it, it can also not exist at all” which means that null is not the same thing as nonexistant. In the web service proxy, for any property that is a type such as integer, decimal, etc it creates a boolean property in the form propertyNameSpecified (where “propertyName” is the name of the property). When you are passing an object to the web service, you have to set propertyNameSpecified to true for the value of the property to be passed. It is not smart enough to know when you set a value for the property.
So you have a couple of options here:
Deal with it and set the boolean when you set the value.
Modify the proxy code to either set the boolean when you set the property or set it always equal to true. This can be automated like the usage of NullableTypes was.
Reconfigure XFire to exclude the minOccurs=”0” attribute.
I suppose the advantage of leaving minOccurs=”0” in the WSDL means less to be transmitted over the wire, especially if you have objects with a large number of properties and/or large arrays of such objects. But I chose the latter option since it simplified things and made my WSDL say exactly what I meant it to. They just added the ability to enable/disable minOccurs=”0” right before the release of 1.1. However, at the time of this writing documentation was/is non-existant for this. The JIRA issue I just linked to doesn’t give you enough information to configure it (it is also slightly inaccurate), so I will document how to do it here:
I am going to describe how to configure this if you have configured XFire using Spring like the included example that comes with XFire. First, you need to get the file “xfire-spring/src/main/org/codehaus/xfire/spring/xfire.xml” from the source code (or jar file). Copy it into your project, as you will be modifying it. Make sure to change your web.xml to reflect the new location of xfire.xml. Then change the import for customEditors.xml to point to it in your classpath (unless you want to copy that over too):
Now modify xfire.typeMappingRegistry and inject aegisTypeConfiguration into the configuration property:
However, this still does not cover date fields. For date fields one option is to using an Aegis map file. For this you will create a file called ClassName.aegis.xml where ClassName is the name of the class that is serialized which contains the date field, and the file is placed in the same package/folder as that class. The file may look something like this, where datePropertyName and anotherDatePropertyName are Date fields you wish to be a nillable:
Be sure to annotate the get method of your property, and also to import to correct XmlElement.
That should be all you need, assuming your Spring-configured XFire was working before you made the changes. You should notice a change in your WSDL (the minOccurs=”0” attributes should no longer be there).
If you were using HttpSession in Axis to hold session data, you’ll find that XFire does things a little differently. It abstracts away HttpSession and makes it completely unaccessible. Instead, it provides a Session object to store and retrieve session attributes. To access it, you can get it from the MessageContext object. To get MessageContext, you can either include it as a method parameter for your web method, or create a Handler which will receieve it. Once you have the Session object, HttpSession can be retrieved from it. For example, you may have a handler that looks like this:
Speaking of Handlers, it is very important that you NOT configure handlers in xfire.xml, it will throw a NullPointerException if you do. Instead, configure them in xfire-servlet.xml in your XFireExport configuration. In general, I would probably avoid modifying xfire.xml unless there was no other way to configure something, like in the case of minOccurs (correct me if there is a better way).
One thing I was hoping would interoperate better was serializing custom exceptions as SoapFaults. Unfortunately, I have not had any luck get the web service tool in .Net 2.0 to recognize custom exceptions that I expose. If anyone figures this out please comment here or e-mail me!