Codehaus XFire

Documentation

Quicklinks

Developers

Sponsors

This tutorial offers a 7-step approach to creating XFire web services that utilize JSR 181 annotations and Apache XMLBeans.

Step 1: Dependencies

Sample IVY Config File

One of the first steps in getting XFire working is simply setting up the initial JAR dependencies. For those of you already familiar with maven or ivy, this dependency list below should look familiar. While there may be a few extra JAR files listed below that are not required to use XFire, the overall list and JAR revisions should help you get started.

Check the XFire dependency guide for more information.

Be Careful

Note the use of the nightly XFire build below (xfire-all-1.0-SNAPSHOT.jar). You may wish to use a specific release for your project. I pull the nightly XFire snapshot down for my development environment only.

<dependencies>
		<!--  WEBAPP Jars -->
		<dependency org="log4j" name="log4j" rev="1.2.9" conf="bundle,runtime->default" />
		<dependency org="springframework" name="springframework" rev="1.2.6" conf="bundle,runtime->default">
			<artifact name="spring" type="jar"/>
		</dependency>
		<dependency org="commons-codec" name="commons-codec" rev="1.3" conf="client,bundle,runtime->default" />
		<dependency org="commons-beanutils" name="commons-beanutils" rev="1.7.0" conf="bundle,runtime->default" />
		<dependency org="commons-collections" name="commons-collections" rev="2.1" conf="bundle,runtime->default" />
		<dependency org="commons-digester" name="commons-digester" rev="1.5" conf="bundle,runtime->default" />
		<dependency org="commons-discovery" name="commons-discovery" rev="0.2" conf="bundle,runtime->default" />
		<dependency org="commons-httpclient" name="commons-httpclient" rev="3.0" conf="client,bundle,runtime->default" />
		<dependency org="commons-el" name="commons-el" rev="1.0" conf="bundle,runtime->default" />
		<dependency org="commons-fileupload" name="commons-fileupload" rev="1.0" conf="bundle,runtime->default" />
		<dependency org="commons-logging" name="commons-logging" rev="1.0.4" conf="client,bundle,runtime->default" />
                  <dependency org="commons-validator" name="commons-validator" rev="1.1.4" conf="bundle,runtime->default" />
                  <dependency org="oro" name="oro" rev="2.0.8" conf="bundle,runtime->default" />
		<dependency org="jstl" name="jstl" rev="1.1.2" conf="bundle,runtime->default" />
		<dependency org="javamail" name="mailapi" rev="1.3.2" conf="client,bundle,runtime->default" />
                   <dependency org="xbean" name="xbean-spring" rev="2.0" conf="bundle,runtime->default" />
		<dependency org="jaxen" name="jaxen" rev="1.1-beta-8" conf="bundle,runtime->default" />
		<dependency org="jdom" name="jdom" rev="1.0" conf="client,bundle,runtime->default" />
                   <dependency org="wsdl4j" name="wsdl4j" rev="1.4" conf="client,bundle,runtime->default" />
		<!-- 
		Stax is the XML api that powers XFire and allows it to process XML efficiently. 
		There are many different Atax implementations out there - the reference
		implementation (RI), Woodstox, FastInfoset, and there is one included in the JWSDP. 
		Regardless of which you one choose you must have the stax-api jar on your classpath. 
		In addition we highly recommend that you use Woodstox as your Stax implementation. 
		It is about 30-40% faster then the RI and works more reliably. To ensure you are 
		using Woodstox and not the RI make sure the stax-1.1.x-dev jar is off your 
		classpath and the wstx-2.0.3 jar is on it.
		<dependency org="wstx" name="wstx" rev="2.0.3" conf="bundle,runtime->default" />
		 -->
		<dependency org="woodstox" name="wstx-lgpl" rev="2.8" conf="client,bundle,runtime->default" />
		<dependency org="stax" name="stax" rev="api-1.0" conf="client,bundle,runtime->default" />
		<dependency org="xfire" name="xfire-all" rev="1.0-SNAPSHOT" conf="client,bundle,runtime->default" />
		<dependency org="acegisecurity" name="acegisecurity" rev="0.9.0" conf="bundle,runtime->default">
			<artifact name="acegi-security" type="jar" />
		</dependency>
		<dependency org="xfire" name="xfire-jsr181-api" rev="1.0-M1" conf="bundle,runtime->default" />
		<dependency org="xfire" name="jaxb-api" rev="20051106" conf="bundle,runtime->default" />
		<dependency org="xfire" name="jaxb-impl" rev="20051106" conf="bundle,runtime->default" />
		<dependency org="xfire" name="jaxb-xjc" rev="20051106" conf="bundle,runtime->default" />
		<dependency org="xfire" name="XmlSchema" rev="20051110" conf="bundle,runtime->default" />
		<dependency org="activation" name="activation" rev="1.0.2" conf="client,bundle,runtime->default" />
                  <dependency org="xmlbeans" name="xmlpublic" rev="2.1.0" conf="client,bundle,runtime->default" />
		<dependency org="xmlbeans" name="xbean" rev="2.1.0" conf="client,bundle,runtime->default" />
		<dependency org="xmlbeans" name="xbean_xpath" rev="2.1.0" conf="bundle,runtime->default" />
</dependencies>

Step 2: Start with XSD

If you subscribe to XML-driven development or MDA or AMDD, then I recommend starting your application with (3) XSD files:

  • Common.xsd
    • Common.xsd simply stores types used in both request and response
      <complexType name="UserToken">
        <sequence>
          <element name="username" type="string" maxOccurs="1" minOccurs="1"/>
          <element name="password" type="string" maxOccurs="1" minOccurs="1"/>
        </sequence>
      </complexType>
  • Request.xsd
  • Response.xsd
Handy Hint

Don't forget that Eclipse has a great XSD editor (which created the XSD visuals you see below).

Below are some instructions outlining the intent behind creating these (3) separate XSD files, which are used to then generate XMLBeans in Step 3 of this process.

  Request.xsd Response.xsd
Description
  • 1 request element:
    <element name="request" type="tns:Request">
    </element>
  • 1 Request complex type:
    <complexType name="Request" abstract="true">
    </complexType>
  • All SOAP requests extend Request
    <complexType name="GetContactTypes">
    <complexContent>
    <extension base="tns:Request">....</extension>
    </complexContent>
    </complexType>
  • 1 response element:
    <element name="response" type="tns:Response">
    </element>
  • 1 Response complex type:
    <complexType name="Response" abstract="true">
    ...
    </complexType>
  • All SOAP responses extend Response
    <complexType name="GetContactTypesResponse">
    <complexContent>
    <extension base="tns:Response">...</extension>
    </complexContent>
    </complexType>
Image
Notes preview :: this will create the following XMLBean class structure
RequestDocument.java
// Some comments here
public abstract interface 
request.RequestDocument implements XMLObject {
    request.Request getRequest();
}
RequestDocumentImpl.java
public class RequestDocumentImpl 
implements request.RequestDocument
GetContactTypes.java
public abstract interface 
request.GetContactTypes implements Request
 
Warning

Because I was unable to deploy the service as document/literal wrapped service, you should create a separate XSD element per exposed method on your webservice implementation class

Step 3: Generate XMLBeans

If your XSDs are placed in META-INF/xfire/, then the following Ant task will generate XMLBeans from the XSD files:

  Ant Task  
Task XML
<target name=">step3.gen.xmlbeans.from.schema" description="Generate XMLBeans">
<taskdef name="xmlbean" classname="org.apache.xmlbeans.impl.tool.XMLBean" 
classpathref="all.app.path" />
 <echo message="About to generate XMLBeans from XSDs"/>
 <xmlbean srcgendir="src" destfile="build\appXMLBeans-1.0.jar" 
classpath="classes" debug="true" verbose="true">
<fileset *dir="/META-INF/xfire/"* includes="*.xsd"/>
 </xmlbean> </target>

Step 4: Setup Eclipse to Support Annotations

  • Install JDK 1.5 and Add in JDT-APT to your Eclipse install JDT-APT
Set JDK Compliance to 5.0 Allow Annotations
Useful Information

JDT-APT currently runs on top of Eclipse 3.1.1. It is not currently part of Eclipse itself, but rather is a separate plugin along with a modified version of the JDT plugin. We are in the process of porting to Eclipse 3.2, which is currently in development.

Step 5: Create your annotated service

Web Service Interface
ServiceInterface.java
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.WebResult;
import org.codehaus.xfire.fault.XFireFault;
import org.company.service.request.RequestDocument;
import org.company.service.response.ResponseDocument;

@WebService(name="ServiceInterface", targetNamespace="http://www.company.org/service/server")
public interface ServiceInterface  {
    
    @WebMethod(operationName = "GetXYZ")
    @WebResult(name = "response", targetNamespace = "http://www.company.org/service/response")
	public ResponseDocument getXYZ(RequestDocument request) throws XFireFault;
}
Web Service Implementation
ServiceImpl.java
package org.company.service.server;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;
import javax.xml.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.xfire.fault.XFireFault;
import org.company.service.request.GetXYZ;
import org.company.service.request.RequestDocument;    // XMLBeans generated
import org.company.service.response.GetXYZResponse;    // XMLBeans generated
import org.company.service.response.ResponseDocument;  // XMLBeans generated 
import org.company.service.service.XYZService;         // XMLBeans generated

@WebService(serviceName = "ServiceInterface", 
targetNamespace = "http://www.company.org/service/server", 
endpointInterface = "org.company.service.server.ServiceInterface")
@SOAPBinding(style = Style.DOCUMENT, use = Use.LITERAL, 
parameterStyle = ParameterStyle.BARE)
public class ServiceImpl implements ServiceInterface {

	private XYZService xyzService; // injected by Spring
	protected final Log log = LogFactory.getLog(getClass());
	
	public ResponseDocument getProvidersUsingVendor(RequestDocument request)
			throws XFireFault {
	         GetXYZ xyzRequest = (GetXYZ) request.getRequest();
		GetXYZResponse xyzResponse = getXYZService().getXYZ(xyzRequest);
		ResponseDocument responseDoc = ResponseDocument.Factory.newInstance();
		responseDoc.setResponse(xyzResponse);
		return responseDoc;
	}
...
}
Handy Hint

Note how the use of XMLBeans and how the RequestDocument is cast to the GetXYZ type

Step 6: Wire up Spring

If you notice in the ServiceImpl class above, I'm using Spring to do some dependency injection. If you want to do this (sharing your XFire application context with the Spring application context loaded by your web application's org.springframework.web.context.ContextLoaderListener for example, then strip out everything from META-INF/xfire/services.xml and put it in your own Spring config file loaded by the ContextLoaderListener declared in your web application.

In your WEB-INF/web.xml, load Spring like this (using the classpath: notation in order to load xfire.xml from your XFire JAR deployed in WEB-INF/lib)

<!-- context parameters -->	 
	<context-param>
    	<param-name>contextConfigLocation</param-name>
      	    <param-value>
      		/WEB-INF/MyService-acegi-base.xml
	         	/WEB-INF/MyService-authentication.xml
	        	/WEB-INF/MyService-authorization.xml
	        	/WEB-INF/MyService-dao.xml
	        	/WEB-INF/MyService-sqlmap.xml
	        	/WEB-INF/MyService-datasource.xml
		/WEB-INF/MyService-servlet.xml
		/WEB-INF/MyService-xfire.xml			
		classpath:org/codehaus/xfire/spring/xfire.xml
      	    </param-value>
   	</context-param>
	<!-- listeners -->
   	<listener>
    	    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   	</listener>

Then declare your XFire spring beans in another Spring XML config file (such as the /WEB-INF/MyService-xfire.xml referenced above).

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

	<!-- ============================================ -->
	<!--  Simple Web Service                          -->
	<!-- ============================================ -->

	<bean id="BookService" class="org.codehaus.xfire.spring.ServiceBean" >
		<property name="serviceClass" value="org.codehaus.xfire.demo.BookService"/>
		<property name="namespace" value="http://xfire.codehaus.org/BookService"/>
	</bean>

	<!-- ============================================ -->
	<!--  Illustration of simple service using        -->
	<!--  JSR 181 annotations                         -->
	<!-- ============================================ -->

	<bean id="CustomerService" class="org.codehaus.xfire.spring.ServiceBean" >
		<property name="serviceClass" value="org.codehaus.xfire.demo.CustomerService"/>
		<property name="serviceFactory" value="jsr181"/>	
	</bean>
	
	<!-- ============================================ -->
	<!--  BASIC BEAN DEFINITIONS                      -->
	<!-- ============================================ -->	

	<bean id="webService" class="org.company.service.server.ServiceImpl">  
		<property name="xyzService" ref="xyzService"/>
	</bean>

	<!-- ============================================ -->
	<!--  FACTORIES                                   -->
	<!-- ============================================ -->

	<bean name="xfire.jaxbServiceFactory" class="org.codehaus.xfire.jaxb2.JaxbServiceFactory">
	  <constructor-arg ref="xfire.transportManager"/>
	</bean>	 
 		
         <bean id="xfire.xmlbeansServiceFactory"
	       class="org.codehaus.xfire.xmlbeans.XmlBeansServiceFactory"
	       singleton="true">
                 <constructor-arg index="0" ref="xfire.transportManager"/>
         </bean>	 

         <bean id="xfire.annotationServiceFactory"
                class="org.codehaus.xfire.annotations.AnnotationServiceFactory">
            <constructor-arg index="0" ref="webAnnotations"/>
            <constructor-arg index="1" ref="xfire.transportManager"/>
            <constructor-arg index="2" ref="xfire.aegisBindingProvider"/>
         </bean>	 
    
	<bean id="xfire.xmlBeansAnnotationServiceFactory"
               class="org.codehaus.xfire.annotations.AnnotationServiceFactory">
            <constructor-arg index="0" ref="webAnnotations"/>                  <!-- WebAnnotations   -->
            <constructor-arg index="1" ref="xfire.transportManager"/>          <!-- TransportManager -->
            <constructor-arg index="2" ref="xmlbeans.aegisBindingProvider"/>   <!-- BindingProvider  -->
         </bean>    
    
	<!-- ============================================ -->
	<!--  REGISTRIES + PROVIDERS                      -->
	<!-- ============================================ -->    
    
         <bean id="xmlbeansTypeRegistry" class="org.codehaus.xfire.xmlbeans.XmlBeansTypeRegistry"/>
 	
         <bean id="xmlbeans.aegisBindingProvider" class="org.codehaus.xfire.aegis.AegisBindingProvider">
           <constructor-arg index="0" ref="xmlbeansTypeRegistry"/>
         </bean> 
 
 	<!-- ============================================ -->
	<!-- ANNOTATION FACADES                           -->
	<!-- ============================================ -->   
 
	 <bean id="webAnnotations" class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations"/>    
    
 	<!-- ============================================ -->
	<!-- WEB SERVICE TEMPLATES                        -->
	<!-- ============================================ -->     
	
	<bean id="xfireServiceTemplate" 
                 class="org.codehaus.xfire.spring.remoting.XFireExporter" abstract="true">
		<property name="serviceFactory" ref="xfire.serviceFactory" />
		<property name="xfire" ref="xfire" />
		<property name="style" value="document" />
		<property name="use" value="literal" />
	</bean>

	<bean id="xfireXMLBeansServiceTemplate" 
                 class="org.codehaus.xfire.spring.remoting.XFireExporter" abstract="true">
		<property name="serviceFactory" ref="xfire.xmlbeansServiceFactory" />
		<property name="xfire" ref="xfire" />
		<property name="style" value="document" />
		<property name="use" value="literal" />
	</bean>
	
	<bean id="xfireXMLBeansAnnotationsServiceTemplate" 
                class="org.codehaus.xfire.spring.remoting.XFireExporter" abstract="true">
		<property name="serviceFactory" ref="xfire.xmlBeansAnnotationServiceFactory" />
		<property name="xfire" ref="xfire" />
		<property name="style" value="document" />
		<property name="use" value="literal" />
	</bean>		  
	
	<!-- ============================================ -->
	<!--  EXPORTED SERVICES                           -->
	<!-- ============================================ -->

	<bean id="MyXFireService" parent="xfireXMLBeansAnnotationsServiceTemplate" >
		<property name="serviceBean" ref="webService"/>
                  <!-- 
                      Interface specification is no longer required when using 
                      xfire-jsr181-api-1.0-M1.jar instead of wsm-1.0-alpha.jar
		    
                      <property name="serviceInterface" value="org.company.service.server.ServiceInterface"/>	
                  -->
		<property name="schemas">
		   <list>
		     <value>/META-INF/xfire/Common.xsd</value>
		     <value>/META-INF/xfire/Request.xsd</value>
		     <value>/META-INF/xfire/Response.xsd</value>
		   </list>
		</property>
	</bean>

Step 7: Deploy the Web Service & Generate the Client

Ant Task
<!-- =================================================================== -->
         <!--  TARGET : download.wsdl                                             -->
         <!--         : Downloads the web service wsdl	                           -->
         <!-- =================================================================== -->
	<target name="download.wsdl">
	 <get src="http://localhost:8080/services/ServiceInterface" 
	    dest="dist/yourapp-1.0.wsdl" 
	    verbose="true"
	    usetimestamp="true"/>
	</target>
	
	<!-- =================================================================== -->
	<!--  TARGET : step7.gen.client.from.wsdl                                -->
	<!--         : Client Generation from WSDL                               -->
	<!-- =================================================================== -->
	<target name=">step7.gen.client.from.wsdl" depends="download.wsdl">
		
		<taskdef name="wsgen" classname="org.codehaus.xfire.gen.WsGenTask" classpathref="wsgen.path" />

		<!-- Run the client generator -->
		<wsgen outputDirectory="src"
			 	       wsdl="dist/yourapp-1.0.wsdl"
			 	       package="org.company.service.wsclient" 
			 	       binding="xmlbeans"  />		
	</target>

The generated client code will look something like this:

package org.company.service.wsclient;

import java.net.MalformedURLException;
import javax.xml.namespace.QName;
import org.codehaus.xfire.XFireRuntimeException;
import org.codehaus.xfire.aegis.AegisBindingProvider;
import org.codehaus.xfire.annotations.AnnotationServiceFactory;
import org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Endpoint;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.soap.AbstractSoapBinding;
import org.codehaus.xfire.transport.TransportManager;
import org.codehaus.xfire.xmlbeans.XmlBeansTypeRegistry;
import org.company.service.server.ServiceInterface;

public class ServiceInterface {

    private Service service;
    private static XFireProxyFactory proxyFactory = new XFireProxyFactory();

    public ServiceInterfaceClient() {
        createService();
        service.addEndpoint(new QName("http://www.company.org/service/server", 
"ServiceInterfaceLocalPort"), 
new QName("http://www.company.org/service/server", "ServiceInterfaceLocalBinding"), 
"xfire.local://ServiceInterface");
        service.addEndpoint(new QName("http://www.company.org/service/server", 
"ServiceInterfaceHttpPort"), 
new QName("http://www.company.org/service/server", 
"ServiceInterfaceHttpBinding"), 
"http://localhost:8080/MyService/services/ServiceInterface");
         
    }

    private void createService() {
        TransportManager tm = (org.codehaus.xfire.XFireFactory.newInstance().getXFire().getTransportManager());
        AnnotationServiceFactory asf = new AnnotationServiceFactory(
new Jsr181WebAnnotations(), tm, new AegisBindingProvider(new XmlBeansTypeRegistry()));
        asf.setBindingCreationEnabled(true);
        service = asf.create((org.company.service.server.ServiceImpl.class));
        {
            AbstractSoapBinding soapBinding = asf.createSoap11Binding(service, 
new QName("http://www.company.org/service/server", "ServiceInterfaceHttpBinding"), 
"http://schemas.xmlsoap.org/soap/http");
        }
        {
            AbstractSoapBinding soapBinding = asf.createSoap11Binding(service, 
new QName("http://www.company.org/service/server", 
"ServiceInterfaceLocalBinding"), 
"urn:xfire:transport:local");
        }
    }

    public void addEndpoint(QName name, QName binding, String address) {
        service.addEndpoint((name), (binding), (address));
    }

    public ServiceInterface getEndpoint(Endpoint endpoint) {	
        try {
            return ((ServiceInterface ) proxyFactory.create((endpoint).getBinding(), (endpoint).getUrl()));
        } catch (MalformedURLException e) {
            throw new XFireRuntimeException("Invalid URL", e);
        }
    }

    public ServiceInterface getEndpoint(QName name) {
        Endpoint endpoint = service.getEndpoint((name));
        if ((endpoint) == null) {
            throw new IllegalStateException("No such endpoint!");
        }
        return getEndpoint((endpoint));
    }

    public ServiceInterface getEndpoint() {
        if (service.getEndpoints().size() == 0) {
            throw new IllegalStateException("No available endpoints!");
        }
        Endpoint endpoint = ((Endpoint) service.getEndpoints().iterator().next());
        return (this).getEndpoint((endpoint));
    }

    public ServiceInterface getServiceInterfaceLocalPort() {
        return (this).getEndpoint(
new QName("http://www.company.org/service/server", "ServiceInterfaceLocalPort"));
    }

    public ServiceInterface getServiceInterfaceHttpPort() {
        return (this).getEndpoint(
new QName("http://www.company.org/service/server", "ServiceInterfaceHttpPort"));
    }

}
Handy Hint

For those of you who are concerned about the apparent requirement here to package your Service implementation class with a Java client JAR that you might need to send to a business partner using your web service, Dan Diephouse offered the following suggestion:

You need to pass off the implementation class because according to JSR 181 the soap binding annotations need to be on the implementation class, and we need those to figure out how to send the messages. You can however make a copy of your service impl class and put "throws UnsupportedOperationException" in all the bodies though.


common_xsd.bmp (image/bmp)
common_xsd.bmp (image/bmp)
request_xsd.bmp (image/bmp)
response_xsd.bmp (image/bmp)
common_xsd.JPG (image/pjpeg)
request_xsd.JPG (image/pjpeg)
response_xsd.JPG (image/pjpeg)
xmlbeans-project-logo.gif (image/gif)
xsd_pic.jpg (image/pjpeg)
annotation_setup.JPG (image/pjpeg)
annotation_setup_2.JPG (image/pjpeg)