Basic Spring Web services with XFire and JSR 181
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:
- 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.
- Modify the Web application
web.xml
to deploy the XFire servlet - 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.