Codehaus XFire

Documentation

Quicklinks

Developers

Sponsors

JiBX Bindings

JiBX is a binding framework which allows you to define a fairly complex mapping between an XML schema and your Java classes. This enables you to create a service that marshalls data between your pre-existing POJOs and documents that conform to an arbitrary XML schema. It's worth noting that you can also use the JiBX Generator tools to generate an XML schema and you can also do code generation from an XML schema to Java code using Xsd2Jibx. JiBX also has excellent performance compared to other binding frameworks; see the java.net bindmark project for more details. For more information on JiBX, try the official JiBX documentation.

JiBX and XFire

In this explanation of using the JiBX bindings with XFire, we'll start with an XML schema and some Java classes, and then describe how to define a mapping between them. Let's say you have some existing Java code which converts temperatures between Farenheit and Celsius, and you want to provide a service which converts weather reports given in one or other scale. You might decide that you want to utilise this pre-existing XML schema. The interesting part of this schema for us is the definition of the WeatherData type:

<xsd:complexType name="WeatherData">
   <xsd:sequence>
    <xsd:element minOccurs="0" maxOccurs="1" name="Day" type="xsd:string"/>
    <xsd:element minOccurs="0" maxOccurs="1" name="WeatherImage" type="xsd:string"/>
    <xsd:element minOccurs="0" maxOccurs="1" name="MaxTemperatureF" type="xsd:string"/>
    <xsd:element minOccurs="0" maxOccurs="1" name="MinTemperatureF" type="xsd:string"/>
    <xsd:element minOccurs="0" maxOccurs="1" name="MaxTemperatureC" type="xsd:string"/>
    <xsd:element minOccurs="0" maxOccurs="1" name="MinTemperatureC" type="xsd:string"/>
   </xsd:sequence>
  </xsd:complexType>

We also have our existing TemperatureConverter class

public class TemperatureConverter {

  public static float convertToCelsius(float fahrenheit) {
    return (fahrenheit - 32) / 1.8f;
  }

  public static float convertToFahrenheit(float celsius) {
    return (celsius * 1.8f) + 32;
  }
}

We also have an interface for our service and an implementation of it utilises the conversion code:

public interface TemperatureConversionService {

    Weather convertFahrenheitToCelsius(Weather fahrenheitWeather);

    Weather convertCelsiusToFahrenheit(Weather celsiusWeather);
}


public class TemperatureConversionServiceImpl implements TemperatureConversionService {

  public TemperatureConversionServiceImpl() {}

  public Weather convertFahrenheitToCelsius(Weather fahrenheitWeather) {
    fahrenheitWeather.setMaxTempC(
       TemperatureConverter.convertToCelsius(fahrenheitWeather.getMaxTempF()));
    fahrenheitWeather.setMinTempC(
       TemperatureConverter.convertToCelsius(fahrenheitWeather.getMinTempF()));
    return fahrenheitWeather;
  }

  public Weather convertCelsiusToFahrenheit(Weather celsiusWeather) {
    celsiusWeather.setMaxTempF(
       TemperatureConverter.convertToFahrenheit(celsiusWeather.getMaxTempC()));
    celsiusWeather.setMinTempF(
       TemperatureConverter.convertToFahrenheit(celsiusWeather.getMinTempC()));
    return celsiusWeather;
  }
}

We also have a Weather class which our service interface uses to accept data and return the results:

public class Weather {

  private String day;
  private String image;
  private float maxTempF;
  private float minTempF;
  private float maxTempC;
  private float minTempC;

  public Weather() {}

  public String getDay() {
    return day;
  }

  public void setDay(String day) {
    this.day = day;
  }

  public String getImage() {
    return image;
  }

  public void setImage(String image) {
    this.image = image;
  }

  public float getMaxTempC() {
    return maxTempC;
  }

  public void setMaxTempC(float maxTempC) {
    this.maxTempC = maxTempC;
  }

  public float getMaxTempF() {
    return maxTempF;
  }

  public void setMaxTempF(float maxTempF) {
    this.maxTempF = maxTempF;
  }

  public float getMinTempC() {
    return minTempC;
  }

  public void setMinTempC(float minTempC) {
    this.minTempC = minTempC;
  }

  public float getMinTempF() {
    return minTempF;
  }

  public void setMinTempF(float minTempF) {
    this.minTempF = minTempF;
  }
}

Now we need to define the relationship between the Weather class and the WeatherData type defined in the XML schema. In JiBX, this is done using a binding definition; the binding definition for our service is given below. Since there is a close correspondence between our Weather class and the WeatherData XML type, this binding definition is pretty straightforward. JiBX allows far more flexibility and complexity in these definitions. We've set 'usage="optional"' on the temperature properties since the client need only set these values for one of the temperature scales.

<binding>
 <namespace uri="http://www.webservicex.net"/>
 <mapping name="WeatherData" class="org.codehaus.xfire.jibx.Weather">
   <value name="Day" set-method="setDay" get-method="getDay"/>
   <value name="WeatherImage" set-method="setImage" get-method="getImage"/>
   <value name="MaxTemperatureF" usage="optional" set-method="setMaxTempF" get-method="getMaxTempF"/>
   <value name="MinTemperatureF" usage="optional" set-method="setMinTempF" get-method="getMinTempF"/>
   <value name="MaxTemperatureC" usage="optional" set-method="setMaxTempC" get-method="getMaxTempC"/>
   <value name="MinTemperatureC" usage="optional" set-method="setMinTempC" get-method="getMinTempC"/>
 </mapping>
</binding>

The service definition for a JiBX service is like any other, except that you need to specify the use of  "org.codehaus.xfire.jibx.JibxServiceFactory" as the service factory. For example, using Spring we would do the following:

<import resource="classpath:org/codehaus/xfire/spring/xfire.xml"/> <bean id="jibxServiceFactory" class="org.codehaus.xfire.jibx.JibxServiceFactory">
  <constructor-arg ref="xfire.transportManager"/>
</bean>

<bean id="serviceBean"
  class="org.codehaus.xfire.jibx.TemperatureConversionServiceImpl"
  singleton="true"/>

<bean id="temperatureConversionService"
  class="org.codehaus.xfire.spring.remoting.XFireExporter"
  singleton="true">
 <property name="serviceBean" ref="serviceBean"/>
 <property name="serviceFactory" ref="jibxServiceFactory"/>
 <property name="serviceClass" value="org.codehaus.xfire.jibx.TemperatureConversionService"/>
 <property name="namespace" value="http://jibx.xfire.codehaus.org/"/>
 <property name="xfire" ref="xfire"/>
 <property name="schemas">
  <list>
   <value>WeatherService.xsd</value>
  </list>
 </property>
</bean>

Note that the values of the schema property (here "WeatherService.xsd") can be either a filepath or the path to a resource on the classpath. We need to ensure that we have run the JiBX compiler on the class files and binding definition. You can do this on the command line or using the JiBX Ant task, like so:

<taskdef name="bind" classname="org.jibx.binding.ant.CompileTask"
  classpath="/path/to/jibx-bind.jar"/>

 <target name="jibxCompile">
  <bind verbose="true" load="true" binding="jibx_bindings.xml">
   <classpathset dir="/classes"/>
  </bind>
 </target>

Here our class files are in the "classes" directory and the "jibx_bindings.xml" file contains the binding definitions. Note that the relevant classes on both the client and server side will need to be "JiBX compiled", so if you have separate compilation stages for your service and for your client, you'll probably need to run this Ant task during each of these. You can now deploy this service like any other, ensuring that the "WeatherService.xsd" schema is available to the service at runtime. The binding definitions file does not need to be deployed with your service or client. The client also has to use the JibxServiceFactory, so for example we could have the following Spring config:

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

  <bean name="jibxServiceFactory" class="org.codehaus.xfire.jibx.JibxServiceFactory">
    <constructor-arg ref="xfire.transportManager"/>
  </bean>

  <bean id="client" class="org.codehaus.xfire.spring.remoting.XFireClientFactoryBean">
     <property name="serviceInterface">
         <value>org.codehaus.xfire.jibx.TemperatureConversionService</value>
     </property>
     <property name="wsdlDocumentUrl">
        <value>http://yourhostand:port/jibxExample/TemperatureConversion?wsdl</value>
     </property>
     <property name="namespaceUri">
        <value>http://jibx.xfire.codehaus.com/</value>
     </property>
     <property name="serviceFactory" ref="jibxServiceFactory"/>
  </bean>

Since the client and the service need to use the same service factory, it's normally useful to refactor the definition of the service factory out into a separate file which is imported by both the service and the client configurations.

In our client code, we would have something along the lines of:

// get a Spring BeanFactoryReference ...
    TemperatureConversionService service = (TemperatureConversionService)ref.getFactory().getBean("client");
    Weather fahrenheitWeather = new Weather();
    fahrenheitWeather.setDay("Monday");
    fahrenheitWeather.setImage("WeatherMap.png");
    fahrenheitWeather.setMaxTempF(70f);
    fahrenheitWeather.setMinTempF(55f);
    Weather celsiusWeather = service.convertFahrenheitToCelsius(fahrenheitWeather);

And that's about it.

JiBX and JSR 181 Annotations

We can configure XFire to utilise JSR-181 annotations with JiBX bindings as follows. First, we add some annotations to our service interface and class

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService(
    name = "TemperatureConversionService",
    targetNamespace = "http://jibx.xfire.codehaus.com")
public interface TemperatureConversionService {

  @WebMethod(operationName = "convertFahrenheitToCelsius")
  Weather convertFahrenheitToCelsius(Weather fahrenheitWeather);

  @WebMethod(operationName = "convertCelsiusToFahrenheit")
  Weather convertCelsiusToFahrenheit(Weather celsiusWeather);
}


@WebService(
    endpointInterface = "org.codehaus.xfire.jibx.TemperatureConversionService")
public class TemperatureConversionServiceImpl implements TemperatureConversionService {  // as the above implementation}

We now have to tell XFire to use the Web Annotations service factory with the JiBX type registry. With a Spring config, we can do this as follows:

<!-- import the xfire beans -->
  <import resource="classpath:org/codehaus/xfire/spring/xfire.xml"/>

  <bean id="webAnnotations" class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations"/>

  <bean id="jibxTypeRegistry" class="org.codehaus.xfire.jibx.JibxTypeRegistry"/>

  <bean id="bindingProvider" class="org.codehaus.xfire.aegis.AegisBindingProvider">
    <constructor-arg ref="jibxTypeRegistry"/>
  </bean>

  <bean id="serviceFactory" class="org.codehaus.xfire.annotations.AnnotationServiceFactory">
    <constructor-arg index="0" ref="webAnnotations"/>
    <constructor-arg index="1" ref="xfire.transportManager"/>
    <constructor-arg type="org.codehaus.xfire.service.binding.BindingProvider" ref="bindingProvider"/>
  </bean>

We then need to configure the service to use this service factory:

<bean id="temperatureConversionService"
    class="org.codehaus.xfire.spring.remoting.XFireExporter">
   <property name="serviceFactory" ref="serviceFactory"/>
 ... etc
 </bean>

We also need to configure the client to use this service factory. One thing to watch out for here is that we now need to configure the client to use the service implementation class as value of the 'serviceInterface' property (you might have to add this class to the compilation of the client code, since the client may not explicitly depend on it):

<bean id="client" class="org.codehaus.xfire.spring.remoting.XFireClientFactoryBean">
     <property name="serviceFactory" ref="serviceFactory"/>

     <property name="serviceInterface">
         <value>org.codehaus.xfire.jibx.TemperatureConversionServiceImpl</value>
     </property>.. etc
  </bean>