Java architect with UST Global Biju has posted 5 posts at DZone. View Full User Profile

How to Use Spring Web Services

08.28.2009
| 102984 views |
  • submit to reddit

Spring Webservices encourages a contract first, message oriented approach to creating Webservices. The underlying details are completely under developer control starting from the contract to the marshalling/unmarshalling details to the endpoint handling the request.

Let us start by an example of a simple service to expose as a webservice - call it the MemberService. MemberService exposes one operation “Get Member Details” which returns the details of a member/person, given an identifier.

 

Creating the contract:

The Contract/WSDL for this service is fairly simple. Let us start by defining the messages and type :

		<xsd:schema targetNamespace="http://bk.org/memberservice/" elementFormDefault="qualified">
<xsd:complexType name="MemberDetailType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="phone" type="xsd:string" />
<xsd:element name="city" type="xsd:string" />
<xsd:element name="state" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="MemberDetailsRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="MemberDetailsResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="memberdetail" type="ms:MemberDetailType" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>

MemberRequest message comprises of an id to represent the requested members identifier

MemberResponse message comprises of MemberDetail type to represent the details of a member.

A sample payload over the wire would look like this:

<?xml version="1.0"?>
<ms:MemberDetailsRequest xmlns:ms="http://bk.org/memberservice/">
<ms:id>SAMPLE</ms:id>
</ms:MemberDetailsRequest>

and a sample response:

<ms:MemberDetailsResponse xmlns:ms="http://bk.org/memberservice/">
<ms:memberdetail>
<ms:name>testname</ms:name>
<ms:city>testcity</ms:city>
<ms:phone>testphone</ms:phone>
<ms:state>teststate</ms:state>
</ms:memberdetail>
</ms:MemberDetailsResponse>

The sample request and response can be easily generated by using a tool like SOAP UI –

SOAP UI

Creating an Endpoint:

A Spring-WS endpoint processes the XML message and produces the XML response. Spring provides different Endpoints based on how the XML is to be handled. If you wish to handle raw xml you have the option of implementing AbstractDom4jPayloadEndpoint, AbstractDomPayloadEndpoint, AbstractJDomPayloadEndpoint etc, based on how the raw xml needs to be handled. If you wish to handle the XML message as an object representation then you can implement the AbstractMarshallingPayloadEndpoint, which is what I will be using for this example along with JIBX as the XML binding framework for its ease of use and performance.

package org.bk.memberservice.endpoint;

import org.bk.memberservice.message.MemberDetailsRequest;
import org.bk.memberservice.message.MemberDetailsResponse;
import org.bk.memberservice.service.MemberManager;
import org.bk.memberservice.types.MemberDetail;
import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint;

public class GetMemberDetailsEndpoint extends
AbstractMarshallingPayloadEndpoint {

private MemberManager memberManager;

protected Object invokeInternal(Object requestObject) throws Exception {
MemberDetailsRequest request = (MemberDetailsRequest) requestObject;
MemberDetail memberDetail = memberManager.getMemberDetails(request
.getId());
MemberDetailsResponse response = new MemberDetailsResponse(memberDetail);
return response;

}

public void setMemberManager(MemberManager memberManager) {
this.memberManager = memberManager;
}
}
The structure of the Endpoint is very simple, it takes an Object which can be cast to MemberDetailsRequest a holder for the request, and the response is returned as an instance of MemberDetailsResponse, a holder for the response details.

Writing the Object/XML binding:

So we now have the endpoint to process the take in the request Message, process it and respond. The only missing piece is how to transform the raw request xml over the wire to the MemberDetailsRequest object, and on the way back to transform the MemberDetailsResponse object back to XML. This is where Springs Object/XML mapping support comes in. Spring Object/XML mapper provides a simple abstraction over the popular Java XML binding stacks like JAXB, Castor, JiBX, Xstream. In the current example, we will start by defining the binding file for JiBX:

<?xml version="1.0" encoding="UTF-8"?>
<binding>
<mapping name="MemberDetailsRequest"
class="org.bk.memberservice.message.MemberDetailsRequest">
<namespace prefix="ms" uri="http://bk.org/memberservice/" default="all"/>
<value name="id" field="id" />
</mapping>

<mapping name="MemberDetailsResponse"
class="org.bk.memberservice.message.MemberDetailsResponse">
<namespace prefix="ms" uri="http://bk.org/memberservice/" default="all"/>
<structure name="memberdetail" field="memberDetail"
class="org.bk.memberservice.types.MemberDetail">
<value name="name" field="name" />
<value name="city" field="city" />
<value name="phone" field="phone" />
<value name="state" field="state" />
</structure>
</mapping>
</binding>

JiBX requires a compile step with the above binding file, this is very easily wired using Maven as the build tool.

Putting it together:

So now all the pieces are in place -

To direct the request to the appropriate endpoint, Spring-WS requires a custom servlet to be set up:

    <servlet>
<servlet-name>memberservice</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext-memberservice.xml</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>memberservice</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>

This would direct all requests starting with serivces/* to be handled by Spring-WS.

The MessageDispatcherServlet would look for a Spring bean with id of “payloadMapping” to direct the incoming XML to an appropriate endpoint, for the example the bean entry is the following:

	<bean id="payloadMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="endpointMap">
<map>
<entry key="{http://bk.org/memberservice/}MemberDetailsRequest"
value-ref="getMemberDetailsEndpoint" />
</map>
</property>
</bean>

 Essentially a request with MemberDetailsRequest as the element will be directed to getMemberDetailsEndpoint which is :

	<bean id="getMemberDetailsEndpoint" class="org.bk.memberservice.endpoint.GetMemberDetailsEndpoint">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="unmarshaller" />
<property name="memberManager" ref="memberManager" />
</bean>

The Endpoint needs the marshaller and unmarshaller to be injected to transform the request XML to request object and the response object to response XML. These are created as follows:

	<bean id="marshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
<property name="targetClass"
value="org.bk.memberservice.message.MemberDetailsResponse" />
</bean>

<bean id="unmarshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
<property name="targetClass"
value="org.bk.memberservice.message.MemberDetailsRequest" />
</bean>

 

Exposing the Webservice WSDL:

Since the WSDL was created from scratch, to expose this pre-canned wsdl requires a bit more configuration with Spring:

	<bean id="MemberDetailsRequest"
class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<property name="wsdl" value="classpath:/memberservice.wsdl" />
</bean>

Now, when the request comes in for the URI /memberservice/MemberDetailsRequest.wsdl, Spring-WS would serve out the static memberservice.wsdl.

 

This completes the Webservice implementation. It can be further enhanced to handle the Exception scenarios, Security etc which is an exercise for another day.

The complete example can be run using the attached maven enabled code, which will download all dependencies, and can be run using the following command:

mvn jetty:run

Conclusion:

Spring-WS provides a compelling way to create a webservice with a contract first approach. The amount of code appears extensive, however there is clean separation of the contract defined by the WSDL and the implementation, thus being able to change underlying implementation without affecting the contract.

Legacy
Published at DZone with permission of its author, Biju Kunjummen.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Arvind Saraf replied on Tue, 2009/09/01 - 4:44pm

I have hard time making the samples work: I could run(mvn jetty6:run) from within eclipse(Galileo). However, I can't connect from SOAPUI. Only this url: http://localhost:8080/memberservice/ gives 'Hello World'. Anything else is Internal server error. However, if I use console mvn jetty:run command, I get this error:

[INFO] Running JiBX binding compiler (single-module mode) on 2 binding file(s)
Running binding compiler version jibx_1_2_1
Unable to process binding ._memberservice_jibx_binding.xml
Error accessing document
java.io.IOException: Surrogate pairs not yet supported
    at org.jibx.runtime.impl.InputStreamWrapper$WrappedStreamUTF8Reader.read(InputStreamWrapper.java:490)

 Anything missing?

saraffa

Vijay Bhatt replied on Tue, 2009/09/08 - 8:26am

There is problem downloading memberservice_0.zip. Can you please look into this?

Joe Constant replied on Thu, 2009/09/17 - 12:14pm in response to: Vijay Bhatt

The file is gzip'd. Change the name to memberservice_0.zip.gz and you should be able to open it

Kelly Stevens replied on Mon, 2009/12/14 - 9:06pm

Is there a more simple method to create a web service in Java? This seems a little complicated and over the top to create something simple. That is one thing I like about .Net development is you can quickly and easily create a web service. I just haven't found a method in Java tha is as easy.

Shameer Kunjumohamed replied on Wed, 2010/09/29 - 3:42am

Just wanted to share another article that I have posted with a detailed account on Building a web service with Spring-WS (using JAXB Marshaller) in this link –

http://justcompiled.blogspot.com/2010/09/building-web-service-with-spring-ws.html

Joy Saha replied on Wed, 2011/02/23 - 9:26pm

Hello  i am new to web service ,  i have configure in the Eclipse Helios, as a typical web application, dont know how to deploy the service , and how to call the service , you mention soap ui is require , how can i get it, i donot have clear about the maven jetty, i execute from cmd , not able to figure it out what exactly its trying , is the jetty stands like a server. I want execute the app in the jboss app. 

Please help me

David James replied on Thu, 2011/03/17 - 9:37am

This is a very good artical, I'm new to spring ws and you have outlined the above perfect. The only thing is that wen I attempt to run your junit test I get the following error:- Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getMemberDetailsEndpoint' defined in class path resource [applicationContext-memberservice.xml]: Cannot resolve reference to bean 'marshaller' while setting bean property 'marshaller'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'marshaller' defined in class path resource [applicationContext-memberservice.xml]: Invocation of init method failed; nested exception is org.springframework.oxm.jibx.JibxSystemException: Binding information for class org.bk.memberservice.message.MemberDetailsResponse must be regenerated with current binding compiler; nested exception is org.jibx.runtime.JiBXException: Binding information for class org.bk.memberservice.message.MemberDetailsResponse must be regenerated with current binding compiler at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:275) Not sure what is causing this. I currently ref the following build.xml to bind my res/req objects with jibx. Any help would be appreciated. Thanks

Mustafa Asif replied on Fri, 2011/07/08 - 5:48pm

Hi This was very helpful. What controls the JIBX compilation? Is it jetty. Please note that i am new to all - Spring/Jetty/JIBX/Web Services.

Shameer Thaha replied on Thu, 2012/08/23 - 5:48am

Very good reference to those who want to explore webservices with SOAP and Jibx. Thanks for your post.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.