About     Search     Feed

Nilesh D Kapadia


Music     Twitter     Github

Java/.Net 2.0 web service interop with XFire

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 past entries 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:

So lets take a look at XFire. After working with it (version 1.1) a bit, I found that:

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:

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):

<import resource="<strong>classpath:org/codehaus/xfire/spring/</strong>customEditors.xml"/>

Now add this to xfire.xml to:

<bean id="aegisTypeConfiguration" class="org.codehaus.xfire.aegis.type.Configuration">
     <property name="defaultMinOccurs" value="1"/>
</bean>

Note that there are other options in the Configuration class, so you may want to look at its Javadoc.

Now modify xfire.typeMappingRegistry and inject aegisTypeConfiguration into the configuration property:

<bean id="xfire.typeMappingRegistry"
   class="org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry"
   init-method="createDefaultMappings" singleton="true">
     <strong><property name="configuration" ref="aegisTypeConfiguration"/></strong>
</bean>

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:

<mappings>
  <mapping>
    <property name="datePropertyName" nillable="true"/>
    <property name="anotherDatePropertyName" nillable="true"/>
  </mapping>
</mappings>    

If you are using Java 5, you have the option of using Aegis Java 5 Annotations. For example:

import java.util.Date;
import org.codehaus.xfire.aegis.type.java5.XmlElement;

public class ClassThatHasDate {

	private Date someDate;

	<b>@XmlElement(minOccurs="1", nillable=true)</b>
	public Date getSomeDate() { return someDate; }

	public void setSomeDate(Date someDate) { this.someDate = someDate; }

	// ...

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:

import javax.servlet.http.HttpSession;
import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.handler.AbstractHandler;
import org.codehaus.xfire.transport.Session;
import org.codehaus.xfire.transport.http.XFireHttpSession;

public class SessionHandler extends AbstractHandler {
	public void invoke(MessageContext context) throws Exception {
		Session session = context.getSession();
		if(session instanceof XFireHttpSession) {
			XFireHttpSession xFireHttpSession = (XFireHttpSession) session;
			HttpSession httpSession = xFireHttpSession.getSession();
			// Do something with httpSession ...
		}
	}
}

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!

© 2013 Nilesh D Kapadia