MemeStorm

Exploring the Spring Framework and Application Development

Basic Spring Web services with XFire and JSR 181

Posted in All by Jon on the May 30th, 2006

I hadn’t intended to expose any of our functionality with Web services so soon, but that’s the great thing about Spring—many of the services are orthogonal to each other. Let’s look at how we can expose some Spring beans as Web services. We’ll do it using JSR 181 too, which means that the Web service implementation is so easy that even a Ruby hacker could write it…;-)

Web Services and JSR 181

Look here for an introduction to JSR 181 and where it fits in to the world of Web services. You need not though, as it’s pretty simple. First, let’s define the interface to our simple Web service:

package com.memestorm.ws;

import javax.jws.WebService;

@WebService( targetNamespace = “http://memestorm.com/skeleton1/AuthorService” )
public interface AuthorService {

	public String sayHello();

}

That’s it. Note the nice @WebService annotation. It’s all that’s really necessary. I threw in the targetNamespace attribute to show how you can modify things. The system will automatically generate a WSDL file - and using these attributes (and others) you can modify the generated WSDL. Now that we have the interface, let’s implement it!

package com.memestorm.ws;

import javax.jws.WebService;

@WebService(
  serviceName = "MyAuthorService",
  endpointInterface = "com.memestorm.ws.AuthorService"
)
public class AuthorServiceImpl implements AuthorService {

  public String sayHello() {
    return "Hello World Services!";
  }
}

Oh my, that was easy….There’s nothing to say, except:

  • Do you see how there aren’t any external dependencies in this code? The only thing I import is the annotation…this is cool…
  • This is just a POJO - which we’ll be defining in a Spring context file. So we can easy wire it up with other beans using IoC.
  • This is an example of CoC again—you don’t have to do much other than write @WebService.

Okay, now that we’ve written our Web service - and you can write as many as you like of course, let’s look at how to wire it all together.

Hooking XFire into your Spring Application

I used XFire as the Web services stack here. It implements some nice things that I don’t find in Apache Axis, such as the JSR 181 stuff, multiple XML binding mechanisms, multiple transports (including Jabber & JMS) etc. You only have to hook it into your web application once. After that, simply create your POJOs and expose them. What you need to do is:

  1. Create the XFire Spring context configuration file. It’s here that you expose your POJOs, and where you can wire in other Spring beans etc.
  2. Modify the Web application web.xml to deploy the XFire servlet
  3. Modify your build process to include all the right JARs

Let’s look at these in order.

1. XFire Configuration File

Now XFire’s pretty powerful. You can configure just about anything, and much of this you can do from the configuration file. It’s also where you expose your POJOs. The following file, xfire-servlet.xml, contains a little of both:

<beans>

 <!--  add your beans here -->
 <bean id=”annotatedAuthorService”
     class=”com.memestorm.ws.AuthorServiceImpl”/>
 <!–  fin  –>

 <bean id=”webAnnotations”
     class=”org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations”/>
 <bean id=”handlerMapping”
     class=”org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping”>
        <property name=”typeMappingRegistry”>
            <ref bean=”xfire.typeMappingRegistry”/>
        </property>
        <property name=”xfire”>
            <ref bean=”xfire”/>
        </property>
        <property name=”webAnnotations”>
            <ref bean=”webAnnotations”/>
        </property>
 </bean>

 <bean class=”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping”>
        <property name=”urlMap”>
            <map>
                <entry key=”/”>
                    <ref bean=”handlerMapping”/>
                </entry>
            </map>
        </property>
    </bean>

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

</beans>

The lines in bold are the only ones that matter to us - it exposes our bean implemented by com.memestorm.ws.AuthorServiceImpl. The rest configures XFire to use annotations in the first place and sets up some defaults. You will want to come back here when you do more advanced stuff such as type mappings.

2. Configuring the XFire Servlet

As you’ve seen, we’ve got this xfire-servlet.xml context configuration file. You’ll want to tell Spring about it, and as we’ve shown before, you can do this by modifying the contextConfigLocation context parameter in web.xml:

 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
     /WEB-INF/applicationContext.xml /WEB-INF/jdbcContext.xml
     /WEB-INF/xfire-servlet.xml
   </param-value>
 </context-param>

You also need to add the following lines:

   <servlet>
     <servlet-name>XFireServlet</servlet-name>
     <servlet-class>
       org.codehaus.xfire.spring.XFireSpringServlet
     </servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>XFireServlet</servlet-name>
      <url-pattern>/servlet/XFireServlet/*</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
     <servlet-name>XFireServlet</servlet-name>
     <url-pattern>/services/*</url-pattern>
   </servlet-mapping>

That simply ensures that XFire is deployed as a servlet.

3. Build

XFire has an astounding array of dependencies - and you only need some under various circumstances. Here’s how I had to modify the build file to account for this:

  • Modify the master-classpath so that I had the right JARs in my path to compile everything:
     <fileset dir="${xfire.root}">
       <include name="xfire-all-1.1.jar" />
     </fileset>
     <fileset dir="${xfire.root}/lib">
       <include name="xfire-jsr181-api-1.0-M1.jar" />
     </fileset>
  • Modify the libraries target to ensure all run-time JARs are available:
    <fileset dir="${xfire.root}">
      <include name="xfire-all-1.1.jar" />
    </fileset>
    <fileset dir="${xfire.root}/lib">
      <include name="stax-api-1.0.1.jar" />
      <include name="xfire-jsr181-api-1.0-M1.jar" />
      <include name="activation-1.1.jar" />
      <include name="mail-1.4.jar" />
      <include name="jaxen-1.1-beta-8.jar" />
      <include name="jdom-1.0.jar" />
      <include name="wsdl4j-1.5.2.jar" />
      <include name="wstx-asl-2.9.3.jar" />
    </fileset>

Okay, I admit there is a little pain here. But you only need to do this once of course, and you are deploying an awesome Web service stack that will bring you hours of pleasure…

4. Running it

That’s it though - grab the source below, run build, deploy, and you’ll have exposed the web service. The XFire servlet provides a nice way to view the deployed Web services too. If you go to http://localhost:8080/skeleton2/services/ you’ll see:

 Services:
  o MyAuthorService [wsdl]

with a link to the WSDL file. Viewing the WSDL file you’ll get the expected output:

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns=”http://memestorm.com/skeleton1/AuthorService” xmlns:wsdlsoap=”http://schemas.xmlsoap.org/wsdl/soap/” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” targetNamespace=”http://memestorm.com/skeleton1/AuthorService”>
  <wsdl:types>
    <xsd:schema targetNamespace=”http://memestorm.com/skeleton1/AuthorService” elementFormDefault=”qualified” attributeFormDefault=”qualified”>
      <xsd:element name=”sayHello”>
        <xsd:complexType />
      </xsd:element>
      <xsd:element name=”sayHelloResponse”>
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name=”out” type=”xsd:string” nillable=”true” minOccurs=”1″ maxOccurs=”1″ />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>

……

  <wsdl:service name=”MyAuthorService“>
    <wsdl:port binding=”tns:MyAuthorServiceHttpBinding” name=”MyAuthorServiceHttpPort”>
      <wsdlsoap:address location=”http://localhost:8080/skeleton1/services/MyAuthorService” />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Nice huh? No hassle Web services…

A Client

Okay, here’s a client that you can run against the Web service. Read the XFire documentation to learn more about it if you like:

package com.memestorm.ws.client;

import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.annotations.AnnotationServiceFactory;
import com.memestorm.ws.*;

import java.net.MalformedURLException;

public class Test {
	public static void main(String[] args) {
		Service serviceModel = new AnnotationServiceFactory()
				.create(AuthorServiceImpl.class);
		try {
			AuthorService service = (AuthorService) new XFireProxyFactory().create(
					serviceModel,
					"http://localhost:8080/skeleton1/services/MyAuthorService");
			String s = service.sayHello();
			System.out.println(s);

		} catch (MalformedURLException e) {
			e.printStackTrace();
		}

	}
}

Summary

What we’ve done here is set up a scenario where you can easily expose POJOs as Web services using JSR 181 standard annotations. These are standard annotations that you’ll find in Java EE 5 - so it’s pretty nice to be able to use them here in the XFire Web services stack.

Moreover, this scenario is hooked into our Spring web application. We can trivially add additional POJOs, and use the full Spring toolbox to manipulate them.

Download

Here’s a JAR of all the source, with instructions on how to build and deploy:
skeleton2-1.zip

Acknowledgements

I’d like to reference the Logemann Blog that showed me how to do most of this stuff.


Print This Post Print This Post

23 Responses to 'Basic Spring Web services with XFire and JSR 181'

Subscribe to comments with RSS or TrackBack to 'Basic Spring Web services with XFire and JSR 181'.

  1. laszlo.fogas said,

    on June 9th, 2006 at 6:25 am

    Hi,

    can we do the same functionality with java 1.5?

    because “import javax.jws.WebService; ” seems to be java 1.6.

    L

  2. des09 said,

    on June 16th, 2006 at 9:48 am

    changes needed to support spring 2.0-m5:
    change dispatch-servlet.xml :
    org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping

    add spring-webmvc.jar

  3. Matt Dowell said,

    on June 22nd, 2006 at 12:56 pm

    When I click on my [wsdl] link, I get this error:

    org.jdom.IllegalNameException: The name “null” is not legal for JDOM/XML DocTypes: XML names cannot be null or empty.
    at org.jdom.DocType.setElementName(DocType.java:171)

    Anyone have any ideas?

  4. Matt Dowell said,

    on June 23rd, 2006 at 12:22 pm

    Found out the answer, it was a bug in Resin. Downloading the latest version fixed the problem.

  5. vikas said,

    on June 30th, 2006 at 3:22 am

    i m getting this error
    any idea?
    org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: ParseError at [row,col]:[9,8]
    Message: end tag name ‘body’ must match start tag name ‘hr’
    org.codehaus.xfire.fault.XFireFault: ParseError at [row,col]:[9,8]
    Message: end tag name ‘body’ must match start tag name ‘hr’

  6. Matt P said,

    on August 22nd, 2006 at 5:55 am

    My service returns Abstract classes and interfaces. How do I indicate to Xfire, through Spring, that these subclasses/implementations need to be in the WSDL?

  7. Ray said,

    on September 25th, 2006 at 8:32 am

    Just a reply to vikas’ problem.

    I think those errors may have arisen if you tried to implement just those 5 files alone in a web project, as I was getting the same when I tried it.

    I would suggest using skeleton2-1.zip file and start from there instead, the only pain is that you have to get all the required jars, but you have that problem nonetheless.

    If you cant see the link for the skeleton2-1.zip file, just search for it on the page.


  8. on November 13th, 2006 at 11:27 am

    […] Basic Spring Web services with XFire and JSR 181 […]


  9. on November 22nd, 2006 at 2:16 am

    XFireGuide2…

    XFire????(?) 1. JSR181      JSR181???annotated POJO ?????????Web????BEA????JavaEE5??????? XFire???????? 1.1 ???? Spring, Hibernate and XFire…

  10. Mittal said,

    on January 4th, 2007 at 8:17 pm

    Service serviceModel = new AnnotationServiceFactory()
    .create(AuthorServiceImpl.class);

    Does client code require classes from web service ?

  11. olonga said,

    on January 11th, 2007 at 3:20 pm

    I was able to run your example in a webapp (with tomcat) with a context in tomcat such as this: http://localhost:8080/skeleton, however, when I created a virtual host for it such as: http://ske.acme.com/, I was not able to run the example….

  12. olonga said,

    on January 12th, 2007 at 11:40 am

    It turns out I was trying to test it with secure http, i.e. https://ske.acme.com/, so when I tried something like this: http://ske.acme.com:8080/services/AuthorService?wsdl
    it worked.
    Now, I would like to know what does it take to run the web services in https?


  13. on January 22nd, 2007 at 6:41 am

    […] XFire is indeed very easy to configure (if you follow a complete example, like this one that I found later), but lacks many “advanced” SOAP features. […]


  14. on February 21st, 2007 at 10:00 am

    […] the web services created by XFire module integrated into the Spring container. The article provided MemeStorm blog highlights the Spring integration with XFire more in detail. One handy tool in testing Web Services […]

  15. supriya said,

    on April 19th, 2007 at 5:34 pm

    Hi,

    I tried to running an example based on your article examples. The wsdl is created and the test runs fine. But I am struggling with seeing meaningful method parameters and return types in the wsdl inspite of using the annotations as specified in your example. Tried document/literal and rpc/literal style/use pair settings but in vain.
    I am using spring 2.3.0, xfire 1.2.5, weblogic 9.2.

    Any help is highly appreciated.

  16. John Hunsley said,

    on August 16th, 2007 at 12:56 am

    Hi,

    using Spring-2.0, xfire-all-1.0-M5, jsr181-1.0

    I get an error when i try and view the services

    java.lang.IllegalStateException: No WebApplicationContext found: No ContextLoaderListener registered?………….

    I’ve tried both ContextLoaderListener and contextConfigLocation defining my xfire-servlet.xml as a param-value, but I get the same error. The XFireSpringServlet.init isn’t getting a handle on my Spring file. What am I missing?

  17. John Hunsley said,

    on August 16th, 2007 at 4:42 am

    Me again,

    The problem is something to do with the xfire-servlet.xml. The contextLoaderListener wont come up because there is a problem with the xfire beans, defined in that Spring config file. Ive gone through eaqch bean and they are all in the class path of the web app, so there is a conflict somewhere. I am using maven to build, not the ant build script described here.

  18. John Hunsley said,

    on August 16th, 2007 at 5:40 am

    Right! sovled the problem!

    My ContextLoadListener wasnt coming up because one of the init methods of one of the beans in the xfire.xml Spring config within the xfire-all jar wasn’t working because maven had not downloaded a runtime dependancy!

    I’m not sure which one, I just canned all the jars I found in my download version 1.2.6 from the xfire site into the lib dir of my web app and it worked!

    I think I was referencing xfire-all-1.0M6 or something, there seems to be a lots of versions of xFire floating around in lots of mvn repos. I can’t 1.2.6 so I’ll have to install it manually.

    Hope this is help to anyone else trying with mvn.

    lol

    John

  19. Julien said,

    on September 18th, 2007 at 1:04 am

    Hi,

    I was wondering whether there is a way to expose in the wsdl the javadoc of a method using jsr181.

    I have seen some generated wsdl files with tag within the tag, but i could not find any information about it.

    Any hint?

  20. Av said,

    on October 1st, 2007 at 10:27 pm

    One issue that I hit after following the steps described on this website was this- I set up the service and then tried to go to http://localhost:8080/mycontext/services/TestService where TestService was my test service. I constantly got back a “Invalid SOAP Request” response. Same thing when I tried using a tool- freeware tool called soapui (try soapui.org). Then, after a bit of research it turned out that I had to use http://localhost:8080/mycontext/services/TestService?wsdl. With that Internet Explorer displayed the wsdl. The tool soapui allowed me to invoke and test out the service (without having to write the client).

    Hope this helps someone.

  21. tech said,

    on October 2nd, 2007 at 6:07 am

    Thanks Av!

  22. Tew Reonal said,

    on January 17th, 2008 at 12:16 am

    I came up with Apache CXF while looking for ways to integrate XFire with my Spring app.

    http://cwiki.apache.org/CXF20DOC/writing-a-service-with-spring.html

  23. Dexterz said,

    on January 17th, 2008 at 12:21 am

    I am getting the following error when trying to use this example…
    Failed to import bean definitions from URL location [classpath:org/codehaus/xfire/spring/xfire.xml]
    Any idea what i might be missing..?

Leave a Reply