Codehaus XFire

Documentation

Quicklinks

Developers

Sponsors

Schema First Development

With XFire and JAXB 2.0 it is very easy to do Schema First Development. This page will lead you through what it takes to develop a service. This example is contained in the distribution under "examples/jaxb2". The build files are available there and have been left out of this guide for clarity.

Since, this is schema first development the first thing we're going to do is develop our schema. In this case we're going to borrow our schema from http://www.webservicex.net/WS/default.aspx and their Weather Service.

<s:schema elementFormDefault="qualified" targetNamespace="http://www.webservicex.net"
          xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.webservicex.net">
    <s:element name="GetWeatherByZipCode">
        <s:complexType>
            <s:sequence>
                <s:element minOccurs="0" maxOccurs="1" name="ZipCode" type="s:string"/>
            </s:sequence>
        </s:complexType>
    </s:element>
    <s:element name="GetWeatherByZipCodeResponse">
        <s:complexType>
            <s:sequence>
                <s:element minOccurs="1" maxOccurs="1" name="GetWeatherByZipCodeResult"
                           type="tns:WeatherForecastsType"/>
            </s:sequence>
        </s:complexType>
    </s:element>
    <s:complexType name="WeatherForecastsType">
        <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="Latitude" type="s:float"/>
            <s:element minOccurs="1" maxOccurs="1" name="Longitude" type="s:float"/>
            <s:element minOccurs="1" maxOccurs="1" name="AllocationFactor" type="s:float"/>
            <s:element minOccurs="0" maxOccurs="1" name="FipsCode" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="PlaceName" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="StateCode" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="Status" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="Details" type="tns:ArrayOfWeatherData"/>
        </s:sequence>
    </s:complexType>
    <s:complexType name="ArrayOfWeatherData">
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="unbounded" name="WeatherData" type="tns:WeatherData"/>
        </s:sequence>
    </s:complexType>
    <s:complexType name="WeatherData">
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="Day" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="WeatherImage" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="MaxTemperatureF" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="MinTemperatureF" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="MaxTemperatureC" type="s:string"/>
            <s:element minOccurs="0" maxOccurs="1" name="MinTemperatureC" type="s:string"/>
        </s:sequence>
    </s:complexType>
</s:schema>

We'll need a service to go along with this though. Instead of writing a WSDL, we're going to first set up our build to generate POJOs via XJC and then use them to write our service interface.

An error occurred: http://svn.codehaus.org/xfire/trunk/xfire/examples/jaxb2/maven.xml. The system administrator has been notified.

Once thats done we simply write our service interface. In this case we just want to do one operation, "GetWeatherByZipCode":

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

import net.webservicex.GetWeatherByZipCode;
import net.webservicex.GetWeatherByZipCodeResponse;

@WebService(name="WeatherServiceIntf", targetNamespace="http://www.webservicex.net")
public interface WeatherService
{
    @WebMethod
    GetWeatherByZipCodeResponse GetWeatherByZipCode(@WebParam(name="GetWeatherByZipCode") GetWeatherByZipCode body);
}

You'll see we've written up some JSR 181 annotations which should be pretty self explanatory. We'll also need to write our service implementation:

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

import net.webservicex.GetWeatherByZipCode;
import net.webservicex.GetWeatherByZipCodeResponse;
import net.webservicex.WeatherForecastsType;

/**
 * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
 */
@WebService(endpointInterface="org.codehaus.xfire.jaxb.WeatherService", serviceName="WeatherService")
@SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.BARE)
public class WeatherServiceImpl implements WeatherService
{

    public GetWeatherByZipCodeResponse GetWeatherByZipCode(GetWeatherByZipCode body)
    {
        GetWeatherByZipCodeResponse res = new GetWeatherByZipCodeResponse();
        String zipCode = body.getZipCode();
        
        WeatherForecastsType weather = new WeatherForecastsType();

        weather.setLatitude(1);
        weather.setLongitude(1);
        weather.setPlaceName("Vienna, AT");
        weather.setAllocationFactor(1);

        res.setGetWeatherByZipCodeResult(weather);

        return res;
    }
}

We could look at our request, but we aren't going to get all fancy here. We'll just return some data in a GetWeatherByZipCode response object. On this service we have some annotations as well. The SOAPBinding annotation is telling XFire we don't want our requests wrapped with the operation name by specifying the parameter style as BARE. The @WebService annotation is needed to tell XFire where the endpoint interface is. XFire uses this to generate our WSDL.

At this point we're almost done. Now we just need to write our services.xml descriptor. This file goes in META-INF/xfire/ on the classpath:

<beans xmlns="http://xfire.codehaus.org/config/1.0">

  <bean id="weatherService" class="org.codehaus.xfire.jaxb.WeatherServiceImpl"/>

  <service>
    <serviceBean>#weatherService</serviceBean>
    <serviceFactory>#jaxbServiceFactory</serviceFactory>
    <schemas>
      <schema>META-INF/xfire/WeatherForecast.xsd</schema>
    </schemas>
    <style>document</style>
  </service>

  <bean name="jaxbServiceFactory" class="org.codehaus.xfire.jaxb2.JaxbServiceFactory">
    <constructor-arg ref="xfire.transportManager"/>
  </bean>

</beans>

ServiceFactories are responsible for creating a Service inside of XFire. In this case, we want to use our JAXB 2.0 ServiceFactory to create the service. Since XFire uses Spring to build up its services, we can use the Spring format to define them. This is the <bean name="jaxbServiceFactory"...> portion of the services.xml. Then in our service definition we can reference this service factory by using the # sign - hence "#jaxbServiceFactory". We also are defining our service as a Spring bean and referencing it this way.

The other thing to note about the services.xml file is that we are specifying where our schema is. In this case, its on the classpath as META-INF/xfire/WeatherForecast.xsd. This parameter can also be a file if you desire.

Now, to view your service you'll need to build a war and deploy it. This is easily done by running "maven war". This will place a war in the "target" directory. Just drop this into your favorite servlet container and you should be set to go!

Access the WSDL via: http://localhost:8080/jaxb2/services/WeatherService?wsdl

Code First Development

If you don't want to write your own schema, you can use JAXB 2.0 to do code first development. This allows you to use JAXB 2.0, JSR 181 Annotations and XFire's wsdl generation together.

Taking the Quick Start example, we can change it use JAXB relatively easily. First, we may want to customize our Book object:

@XmlType(name="Book", namespace="urn:xfire:book")
public class Book
{
    private String title;
    private String isbn;
    private String author;
...
}

The XmlType annotation will control the name and namespace of the type in the wsdl.

The only difference in deploying JAXB 2.0 services is that you need to change the ServiceFactory used. If you're using services.xml it will look something like this:

<beans xmlns="http://xfire.codehaus.org/config/1.0">
  <service>
    <name>BookService</name>
    <namespace>http://xfire.codehaus.org/BookService</namespace>
    <serviceClass>org.codehaus.xfire.demo.BookService</serviceClass>
    <serviceFactory>org.codehaus.xfire.jaxb2.JaxbServiceFactory</serviceFactory>
  </service>
</beans>

JAXB 2 and Inheritance

If you have are using JAXB with polymorphic javabeans as part of your data model, you may need to let JAXB know about additional packages. For instance, say you have a service whose method returns a "Shape" class, but you sometimes respond with a "Square". You need to let JAXB know about this. This can be done via code like so:

List searchPackages = new ArrayList();
searchPackages.add("com.acme.squares");

Service service = ...;
service.setProperty(JaxbType.SEARCH_PACKAGES, searchPackages);

Or via services.xml like this:

<service>
...
<property name="jaxb.search.packages">
  <list>
   <entry>com.acme.square</entry>
  </list>
</property>
</service>

The example above does not work with 1.2.1, the syntax should look like this:

<property xmlns="" name="properties">
        <map xmlns="">
        <entry  xmlns="" key="jaxb.search.packages">
            <value  xmlns="">
                <list  xmlns="">
                  <value xmlns="">com.acme.square</value>
                </list>
            </value>
        </entry>
        </map>
    </property>