Home >> Spring Framework
Using Spring Framework and creating a simple to understand
web service application that can be deployed onto Tomcat web
server. And also writing a test harness class for invoking
this web service using Spring Framework.
For this example to work, environment needed are as follows:
Spring ws version 1.5.8
Java version 6.0
Tomcat version 6.0
Eclipse 3.5.1
|
|  |
|
(Please be informed that this example is tested only using
above version of software, don't know whether it will work
or not in any other version, but one can try and refer
official Spring documentation for version compatibility
related aspects.)
Initially I started with referring to the documentation
supplied with Spring WS distribution files. And after having
enough knowledge on the basics and technology stack related
aspects, started with putting some thought and coming up with
a very easy to understand (that is what I think so), case study
as follows:
Suppose there is a supplier (I will name it as SUPL) and a
warehouse company (I will name it as WAREINC). Both these entities
are going to interact with each other irrespective of operating
environment and location these two entities are having as far as
their application technology stack is concerned.
This means SUPL can have an application that is using command line
application and the WAREINC is having a web based application.
SUPL wants to query WAREINC for details on storage capacity of certain
items. The method name could be as simple as itemStatus(ItemRequest itemRequest)
and the return type could be List of following:
String itemNumber
int quantity
Date asOfDate
|
So in this example there are basically two value objects such as
ItemRequest and ItemResponse.
ItemRequest is having the itemNumber of type String as shown below:
ItemRequest.java
package com.wareinc.ws;
public class ItemRequest {
private String itemNumber;
public String getItemNumber() {
return itemNumber;
}
public void setItemNumber(String itemNumber) {
this.itemNumber = itemNumber;
}
}
|
And the response to the web sevice call will return another value
object of type ItemResponse, as shown below:
ItemResponse.java
package com.wareinc.ws;
import java.util.Date;
public class ItemResponse {
private String itemNumber;
private int quantity;
private Date asOfDate;
public String getItemNumber() {
return itemNumber;
}
public void setItemNumber(String itemNumber) {
this.itemNumber = itemNumber;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Date getAsOfDate() {
return asOfDate;
}
public void setAsOfDate(Date asOfDate) {
this.asOfDate = asOfDate;
}
}
|
The so called business service for this example, is also
known as ItemService.java is an interface with an implementation
class as ItemServiceImpl, as shown below:
ItemService.java
package com.wareinc.ws;
public interface ItemService {
public ItemResponse itemStatus(ItemRequest itemRequest);
}
|
And
ItemServiceImpl.java
package com.wareinc.ws;
import java.util.Calendar;
public class ItemServiceImpl implements ItemService {
public ItemResponse itemStatus(ItemRequest itemRequest) {
//This method can have some call
//to the business tier of this example
//and do some thing useful.
//as of now this is just returning some
//hard code values for those fields.
ItemResponse itmResp = new ItemResponse();
itmResp.setItemNumber("ITM001");
itmResp.setQuantity(20);
Calendar cd = Calendar.getInstance();
cd.set(2010, 01, 03);
itmResp.setAsOfDate(cd.getTime());
return itmResp;
}
}
|
While using Spring Framework web service, There needs to be having
an endpoint, in this case I have used a sub class of
org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint
ItemServiceEndPoint.java
package com.wareinc.ep;
import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint;
import com.wareinc.ws.ItemRequest;
import com.wareinc.ws.ItemService;
public class ItemServiceEndPoint extends AbstractMarshallingPayloadEndpoint {
public static final String NAMESPACE_URI_VALUE = "http://ws.wareinc.com";
public static final String NS_RESPONSE_NAME = "itemStatusResponse";
private ItemService itemService;
public void setItemService(ItemService itemService) {
this.itemService = itemService;
}
@Override
protected Object invokeInternal(Object arg0) throws Exception {
System.out.println("***** "+(ItemRequest)arg0);
return itemService.itemStatus((ItemRequest)arg0);
}
}
|
This namespace uri value should be same as the target Namespace valu
from the WSDL definition file as shown below:
item.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:ns="http://ws.wareinc.com"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
targetNamespace="http://ws.wareinc.com">
<wsdl:types>
<xs:schema attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://ws.wareinc.com/xsd">
<xs:complexType name="ItemRequest">
<xs:sequence>
<xs:element minOccurs="0" name="itemNumber"
nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ItemResponse">
<xs:sequence>
<xs:element minOccurs="0" name="asOfDate"
nillable="true" type="xs:date"/>
<xs:element minOccurs="0" name="itemNumber"
nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="quantity"
type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
<xs:schema xmlns:ax22="http://ws.wareinc.com/xsd"
attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://ws.wareinc.com">
<xs:import namespace="http://ws.wareinc.com/xsd"/>
<xs:element name="itemStatus">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="arg0"
nillable="true"
type="ax22:ItemRequest"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="itemStatusResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="return"
nillable="true" type="ax22:ItemResponse"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="itemStatusRequest">
<wsdl:part name="parameters" element="ns:itemStatus"/>
</wsdl:message>
<wsdl:message name="itemStatusResponse">
<wsdl:part name="parameters" element="ns:itemStatusResponse"/>
</wsdl:message>
<wsdl:portType name="ItemServicePortType">
<wsdl:operation name="itemStatus">
<wsdl:input message="ns:itemStatusRequest"
wsaw:Action="urn:itemStatus"/>
<wsdl:output message="ns:itemStatusResponse"
wsaw:Action="urn:itemStatusResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="ItemServiceSoap11Binding"
type="ns:ItemServicePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
<wsdl:operation name="itemStatus">
<soap:operation soapAction="urn:itemStatus"
style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ItemService">
<wsdl:port name="ItemServiceHttpSoap11Endpoint"
binding="ns:ItemServiceSoap11Binding">
<soap:address
location="http://localhost:8080/itemservice/item.wsdl"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
This WSDL definition file is one out of many possible ways one can write,
one can have a separate XSD file, and refer that in WSDL file, but in this
example I have added user-defined element and data type in the same WSDL
definition itself.
In this example I shall be using MessageDispatcherServlet from Spring
Framework.
So the web.xml file goes as follows:
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>Spring-WS-Servlet</servlet-name>
<servlet-class>
org.springframework.ws.transport.http.MessageDispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Spring-WS-Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
|
As the MessageDispatcherServlet is configured with a servlet name
as "Spring-WS-Servlet", so I needed to create another Spring Framework
servlet XML configuration file as follows:
Spring-WS-Servlet-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="itemService" class="com.wareinc.ws.ItemServiceImpl"/>
<bean id="castorMarshaller"
class="org.springframework.oxm.castor.CastorMarshaller" >
<property name="mappingLocation" value="classpath:mapping.xml" />
</bean>
<bean id="itemEndPoint" class="com.wareinc.ep.ItemServiceEndPoint">
<property name="itemService" ref="itemService"/>
<property name="marshaller" ref="castorMarshaller"/>
<property name="unmarshaller" ref="castorMarshaller"/>
</bean>
<bean id="itemEndPointMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="defaultEndpoint" ref="itemEndPoint"/>
</bean>
<bean id="item"
class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<constructor-arg value="/WEB-INF/wsdl/item.wsdl"/>
</bean>
</beans>
|
This file will be automatically read by Spring Framework and appropriate
endpoint and endpoint mapping can be configured.
Bean "item" is going to read item.wsdl file from the folder and
will be available for use.
org.springframework.oxm.castor.CastorMarshaller is used for providing
marshaller and unmarshaller support to the Spring Web service Framework.
For this appropriately created mapping.xml file is provided
under WEB-INF/classes folder,
mapping.xml
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC
"-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.wareinc.ws.ItemResponse">
<map-to xml="item"/>
<field name="itemNumber" type="string">
<bind-xml name="itemNumber" node="element"/>
</field>
<field name="quantity" type="integer">
<bind-xml name="quantity" node="element"/>
</field>
<field name="asOfDate" type="date">
<bind-xml name="asofdate" node="element"/>
</field>
</class>
<class name="com.wareinc.ws.ItemRequest">
<map-to xml="itemStatus"/>
<field name="itemNumber" type="string">
<bind-xml name="itemNumber" location="arg0"
node="element"/>
</field>
</class>
</mapping>
|
In order to understand the way Castor Framework Mapping works,
please visit URL as mentioned in the reference section below.
List of Jar files used in this example (under WEB-INF/lib folder):
castor-1.2.jar
commons-logging-1.1.1.jar
log4j-1.2.15.jar
org.springframework.aop-3.0.0.RELEASE.jar
org.springframework.asm-3.0.0.RELEASE.jar
org.springframework.aspects-3.0.0.RELEASE.jar
org.springframework.beans-3.0.0.RELEASE.jar
org.springframework.context-3.0.0.RELEASE.jar
org.springframework.context.support-3.0.0.RELEASE.jar
org.springframework.core-3.0.0.RELEASE.jar
org.springframework.expression-3.0.0.RELEASE.jar
org.springframework.web-3.0.0.RELEASE.jar
org.springframework.web.servlet-3.0.0.RELEASE.jar
spring-ws-1.5.8-all.jar
Of course along with all the required License files.
All these files are put in appropriate places/folders
under a web application "itemservice" (refer to the SOAP
Address location in WSDL file)
<soap:address
location="http://localhost:8080/itemservice/item.wsdl"/>
After starting the web server successfully, it is needed to
check the WSDL URL by using the complete URL from the WSDL file
on browser's addess bar. One should see the complete WSDL definitions
with XML tags on browser, to make sure everything is okay till
this point.
So far the WAREINC(so to say company in this example) has got its web
service up and running.
For the SUPL (so to say another company in this example), would be
writing a client to test whether it is able to invoke the remote
web service by using WSDL URL from the WAREINC.
For the sack of simplicity, I have written a test client as follows:
ItemServiceTestClient.java
package test.client;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.xml.transform.StringResult;
public class ItemServiceTestClient extends WebServiceGatewaySupport {
public ItemServiceTestClient() {
try {
setDefaultUri("http://localhost:8080/itemservice/item.wsdl");
Source requestSource = new StreamSource("src/itemrequest.xml");
StringResult resultStr = new StringResult();
getWebServiceTemplate().
sendSourceAndReceiveToResult(requestSource, resultStr);
System.out.println(resultStr);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String args[]){
new ItemServiceTestClient();
}
}
|
This client is using the WebServiceGatewaySupport from Spring-WS Framework
api, just to make thinks quick enough to test.
defaultUri is set as the complete URL of the WSDL definition from web
server and the XML file as the payload along with the SOAP request.
itemrequest.xml
<itemStatus>
<arg0>
<itemNumber>25</itemNumber>
</arg0>
</itemStatus>
|
After invoking this web service, the StringResult returned is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<item>
<itemNumber>ITM001</itemNumber>
<quantity>20</quantity>
<asofdate>
2010-02-03T16:30:49.406+05:30
</asofdate>
</item>
|
This is how I could able to pass XML string to (as request) and
from (as response) web service using Spring WS Framework.
References:
http://castor.org/xml-mapping.html
http://static.springsource.org/spring-ws/sites/1.5/reference/html/oxm.html
If anything missed out , please let me know at
techienjoy at yahoo . com