Spring Web Service

Spring Web Service

I will assume that you are aware with Spring and basics of web service. I will show you how to develop web service using Spring.

Spring uses contract-first approach for developing web service. Contract-first means first you develop XMl and then java.

Advantages of Contract First approach:

a) Object/XML impedance mismatch (E.g. Date of XMl and Java. XSD date type represents a year, month, and day. If we call this service from Java, we will probably use either a

java.util.Date

or

java.util.Calendar

. Actually both describe times, rather than dates. So, we will actually end up sending data that represents the 20th of July 2009 at midnight (

2009-07-20T00:00:00

), which is not the same as

2009-07-20

.)

b) Performance : An object might reference another object, which refers to another, etc. In the end, half of the objects on the heap in your virtual machine might be converted into XML, which will result in slow response times. When using contract-first, you explicitly describe what XML is sent where, thus making sure that it is exactly what you want.

c) Reusability : You can use one xsd element or elements in other xsd.

Now first we will start with XML/XSD followed by Java implementation.

I am taking simple example of Train booking request. Step by step I will explain how to develop spring web service. First we will create sample XML.

1) TrainRequest.xml

<TrainRequest>

<TrainDetails>

<TrainNumber>1556<TrainNumber> <JourneyDate>2009-07-25<JourneyDate>

<From>Pune<From>

<To>Dadar<To>

</TrainDetails>

<PassengerDetails >

<NumberOfPassengers></NumberOfPassengers>

<MasterPassenger>

<Name>Chirag shah</Name>

<Age>25</Age>

<Sex></Sex>

</MasterPassenger>

</PassengerDetails>

</TrainRequest>

2) Now we will generate XSD from xml using tool. By far the easiest way to create an XSD is to infer it from sample documents. Any good XML editor or Java IDE offers this functionality. Basically, these tools use some sample XML documents, and generate a schema from it that validates them all. The end result certainly needs to be polished up, but it's a great starting point.

This is our polished and final XSD which we will use further.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:train="http://sample.com/tr/schemas"
elementFormDefault="qualified"
targetNamespace="http://sample.com/tr/schemas">
<xs:element name="TrainRequest">
<xs:complexType>
<xs:all>
<xs:element name="TrainDetails" type="train:TrainDetails"/> 
<xs:element name="PassengerDetails" type="train:PassengerDetails"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="TrainDetails">
<xs:sequence>
<xs:element name="TrainNumber" type="xs:integer"/>
<xs:element name="JourneyDate" type="xs:date"/>
<xs:element name="To" type="xs:string"/> 
<xs:element name="From" type="xs:string"/> 
</xs:sequence> 
</xs:complexType>
<xs:complexType name="PassengerDetails">
<xs:sequence>
<xs:element name="NumberOfPassengers" type="xs:integer"/>
<xs:element name="MasterPassenger" type="train:masterPassenger" minOccurs="1" maxOccurs="1"/> 
</xs:sequence> 
</xs:complexType>
<xs:complexType name="masterPassenger">
<xs:sequence>
<xs:element name="Name" type="xs:string" minOccurs="1"/>
<xs:element name="Age" type="xs:integer" minOccurs="1"/>
<xs:element name="Sex" type="xs:string" minOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:element name="TrainResponse">
<xs:complexType>
<xs:all>
<xs:element name="Code" type="xs:string" minOccurs="1"/> 
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>

3) Now third is WSDL. We will not write wsdl with own because spring internally generates WSDL for us using configuration specified in context file. We will talk about context file in next section.

4) I have used maven for project build tool. I have generated one project called “railwayService” using maven and added following dependency in pom.xml which we will need to develop spring web service.

<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm</artifactId>
<version>1.5.7</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>1.5.7</version>
</dependency>
<dependency>
<groupId>jdom</groupId>
<artifactId>jdom</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.xml.soap</groupId>
<artifactId>saaj-api</artifactId>
<version>1.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.3</version>
<scope>runtime</scope>
</dependency>

5) Now Its time to develop Endpoint. I am using XPATH for navigation in xml and Jdom for xml parsing.

package com.test;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;
import org.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint;

public class RailwayEndpoint extends AbstractJDomPayloadEndpoint {

	private XPath trainNumberExpression;
	
	private XPath journeyDateExpression;

	private XPath toExpression;
	
    private XPath fromExpression;

    private XPath numberOfPassengersExpression;
    
    private XPath nameExpression;

    private XPath ageExpression;
    
    private XPath sexExpression;

    private final RailwayService railwayService;

    public RailwayEndpoint(RailwayService railwayService) throws JDOMException {
    	System.out.println("Entered in constructor");
        this.railwayService = railwayService;
        Namespace namespace = Namespace.getNamespace("tr", "http://sample.com/tr/schemas");
        
        trainNumberExpression = XPath.newInstance("//tr:TrainNumber");
        trainNumberExpression.addNamespace(namespace);
        
        journeyDateExpression = XPath.newInstance("//tr:JourneyDate");
        journeyDateExpression.addNamespace(namespace);
        
        toExpression = XPath.newInstance("//tr:To");
        toExpression.addNamespace(namespace);
        
        fromExpression = XPath.newInstance("//tr:From");
        fromExpression.addNamespace(namespace);
        
        numberOfPassengersExpression = XPath.newInstance("//tr:NumberOfPassengers");
        numberOfPassengersExpression.addNamespace(namespace);
        
        nameExpression = XPath.newInstance("//tr:Name");
        nameExpression.addNamespace(namespace);
        
        ageExpression = XPath.newInstance("//tr:Age");
        ageExpression.addNamespace(namespace);
        
        sexExpression = XPath.newInstance("//tr:Sex");
        sexExpression.addNamespace(namespace);
    }

    protected Element invokeInternal(Element railwayRequest) throws Exception {    
    	System.out.println("Entered in invokeInternal");
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        
        BookingDetail bookingDetail = new BookingDetail();
        
        Integer trainNumber = Integer.parseInt(trainNumberExpression.valueOf(railwayRequest));
        bookingDetail.setTrainNumber(trainNumber);
        Date journeyDate = dateFormat.parse(journeyDateExpression.valueOf(railwayRequest));
        bookingDetail.setJourneyDate(journeyDate);
        String destination = toExpression.valueOf(railwayRequest);
        bookingDetail.setDestination(destination);
        String source = fromExpression.valueOf(railwayRequest);
        bookingDetail.setSource(source);
        Integer noOfPass = Integer.parseInt(numberOfPassengersExpression.valueOf(railwayRequest));
        bookingDetail.setNoOfPass(noOfPass);
        String name = nameExpression.valueOf(railwayRequest);
        bookingDetail.setName(name);
        Integer age = Integer.parseInt(ageExpression.valueOf(railwayRequest));
        bookingDetail.setAge(age);
        String sex = sexExpression.valueOf(railwayRequest);
        bookingDetail.setSex(sex);

        String responseMessage = railwayService.bookHoliday(bookingDetail);
        return generateResponse(responseMessage);
    }
    
	private Element generateResponse(String message) 
	{
		System.out.println("Entered in generateResponse");
		Element response = null;
		Namespace respNamespace = Namespace.getNamespace("tr", "http://sample.com/tr/schemas");
		try {
			Element msgResponseRoot = new Element("TrainResponse",
					respNamespace);

			Element messageElement = new Element("Code", respNamespace);
			messageElement.addContent(message);
			msgResponseRoot.addContent(messageElement);

			response = new Document(msgResponseRoot).getRootElement();
		} catch (Exception e) {
			System.out.println("Error rsponse writing");
		}
		return response;
	}
}

- In above endpoint example I am using AbstractJDomPayloadEndpoint because I don’t need or bother about soap header. There are two types of endpoints 1) Message Endpoints 2) payload endpoints.

The RailwayEndpoint requires the RailwayService business service to operate, so we inject the dependency via the constructor. Next, we have used XPath expressions using the JDOM API. There are eight expressions which will be used to extracting the required parameters.

- The invokeInternal(...) method is a template method, which gets passed with the

<RailwayRequest/> element from the incoming XML message. We use the XPath expressions to extract the string values from the XML messages. With these values, we invoke a method on the business service. Typically, this will result in a finding seat availability, allotment of seats and so on. Finally, we return Success or Failure response according to user’s inputs.

We have returned a JDOM Element that represents the payload of the response message.

6) Now we will create context file. First we will register our endpoint class in railway-ws-servlet.xml like:

<bean id="railwayEndpoint" class="com.test.RailwayEndpoint">

<constructor-arg ref="railwayService"/>

</bean>

<bean id="railwayService" class="com.test.RailwayService"/>

Now we will add configuration for routing messages to the endpoint.

<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">

<property name="mappings">

<props>

<prop key="{http://sample.com/tr/schemas}TrainRequest">railwayEndpoint</prop>

</props>

</property>

<property name="interceptors">

<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>

</property>

</bean>

This means that whenever any XML message is received with the namespace http://sample.com/tr/schemas and the TrainRequest local name, it will be routed to the railwayEndpoint. We have used PayloadLoggingInterceptor interceptor which will dumps incoming and outgoing messages to the log.

At last we will add configuration for publishing WSDL.

<bean id="railway" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">

<property name="schema" ref="schema"/>

<property name="portTypeName" value="RailwayResource"/>

<property name="locationUri" value="/railwayService/"/> <property name="targetNamespace" value="http://sample.com/tr/definitions"/>

</bean>

<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">

<property name="xsd" value="/WEB-INF/trainRequest.xsd"/>

</bean>

bean id: determines the URL where the WSDL can be retrieved. http://localhost:8080/railwayService/railway.wsdl.

schema : this property refers schema that we have created.

portTypeName: WSDL port type which is RailwayResource

locationUri: We set the location where the service can be reached: /railwayService/. We use a a relative URI and we instruct the framework to transform it dynamically to an absolute URI. Hence, if the service is deployed to different contexts we don't have to change the URI manually.

targetNamespace: Finally, we define the target namespace for the WSDL definition itself. Setting these is not required. If not set, we give the WSDL the same namespace as the schema.

7) Create a war file and deploy application. I am using tomcat server. Point your browser to http://localhost:8080/railwayRequest/railway.wsdl. This WSDL is ready to be used by clients, such as soapUI, or other SOAP frameworks.

How to access it using SOAP UI.

Steps for using this WSDL in SOAP UI.

- Start SOAP UI and create new Project. Go through first image .
- Click Ok and your wsdl is ready for use. Now add entries to request parameters like below second image .
- Now click on top center text box and make it http://localhost:/railwayService.
Then click on left top blue arrow button. You will receive a response something like below third image.

Have u ever used ibotta app for rebate? Try it..Use aydtcuy and you will be added to my team, we can earn fast by making team and generating bonus..Worth it..

first
first
second
second
third
third

© 2009 chirag272003

More by this Author


Comments 13 comments

Brian 7 years ago

thanks for this webservice , I am also learning these web services but some classes are missing, like the class of RailwayService and BookingDetails, can you post them as well.

secondly I have tried to creat my sample classes but still i cannot access the wsdl file after i have deployed the war file and i dont know where the problem is, please i need your help.

thanks

regards

Brian


chirag272003 profile image

chirag272003 7 years ago Author

Hi Brian,

Sorry for delay in response.

For source code of project go through below url:

http://www.mediafire.com/download.php?gqmdn04mtgt

Let me know still if you find any difficulty in deploying.

Thanks,

Chirag Shah


Brian 7 years ago

Hi chirag

when I run the request in the soapUI i get this error in the errorlog, how can i go about it?. otherwise i was able to get the WSDL

thanks

===============ERROR===========

Fri Aug 28 10:57:49 EAT 2009:ERROR:com.eviware.soapui.model.iface.Request$SubmitException: com.eviware.soapui.impl.wsdl.submit.RequestTransportRegistry$MissingTransportException: Missing protocol in endpoint [/railwayService/]

com.eviware.soapui.model.iface.Request$SubmitException: com.eviware.soapui.impl.wsdl.submit.RequestTransportRegistry$MissingTransportException: Missing protocol in endpoint [/railwayService/]

at com.eviware.soapui.impl.wsdl.WsdlRequest.submit(WsdlRequest.java:227)

at com.eviware.soapui.impl.wsdl.panels.request.AbstractWsdlRequestDesktopPanel.doSubmit(AbstractWsdlRequestDesktopPanel.java:146)

at com.eviware.soapui.impl.support.panels.AbstractHttpRequestDesktopPanel.onSubmit(AbstractHttpRequestDesktopPanel.java:755)

at com.eviware.soapui.impl.support.panels.AbstractHttpRequestDesktopPanel$SubmitAction.actionPerformed(AbstractHttpRequestDesktopPanel.java:486)

at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)

at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)

at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)

at javax.swing.DefaultButtonModel.setPressed(Unknown Source)

at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)

at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)

at java.awt.Component.processMouseEvent(Unknown Source)

at javax.swing.JComponent.processMouseEvent(Unknown Source)

at java.awt.Component.processEvent(Unknown Source)

at java.awt.Container.processEvent(Unknown Source)

at java.awt.Component.dispatchEventImpl(Unknown Source)

at java.awt.Container.dispatchEventImpl(Unknown Source)

at java.awt.Component.dispatchEvent(Unknown Source)

at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)

at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)

at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)

at java.awt.Container.dispatchEventImpl(Unknown Source)

at java.awt.Window.dispatchEventImpl(Unknown Source)

at java.awt.Component.dispatchEvent(Unknown Source)

at java.awt.EventQueue.dispatchEvent(Unknown Source)

at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)

at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)

at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)

at java.awt.EventDispatchThread.pumpEvents(Unknown Source)

at java.awt.EventDispatchThread.pumpEvents(Unknown Source)

at java.awt.EventDispatchThread.run(Unknown Source)


chirag272003 profile image

chirag272003 7 years ago Author

Hi Brian,

If you see second image oF SOAP UI there is a drop down list. Edit that and make it "http://localhost:8080/railwayService" instead of "/railwayService/". This will change according to your deployment setup.

Let me know still if you face any difficulties.

Thanks,

Chirag Shah


Brian 7 years ago

Hi Chirag

Your service has made wonders for me, i do really appretiate,

however, i want to run javaclient in my eclipse to communicate to the service and then get back the response how can i go about it.

I have however done some client using spring but i receive this error

=========================error=============

Sep 1, 2009 6:35:45 PM org.springframework.ws.soap.saaj.SaajSoapMessageFactory afterPropertiesSet

INFO: Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol

Exception in thread "main" org.springframework.ws.client.WebServiceTransportException: Not Found [404]

at org.springframework.ws.client.core.WebServiceTemplate.handleError(WebServiceTemplate.java:475)

at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:399)

at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:350)

at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:296)

at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:281)

at octesting.javawsclass.main(javawsclass.java:26)

======================end of error=========

SaopUI works well but now i want to make java client to access the service and get the response result in the code.

How can i go about it

this is the javaclient code i wrote.

============clientcode==================

package octesting;

import org.springframework.ws.client.*;

import org.springframework.ws.client.core.*;

import org.springframework.ws.*;

import java.io.StringReader;

import javax.xml.transform.stream.StreamResult;

import javax.xml.transform.stream.StreamSource;

import javax.xml.transform.*;

public class javawsclass {

public static void main(String[] args)

{

final WebServiceTemplate template = new WebServiceTemplate();

String url = "http://localhost:8080/railwayService/";

Source source = new StreamSource(new StringReader(" " +

"" +

"6723-8-2009" +

"masakakampala630" +

"charles34f" +

" "));

Result result = new StreamResult(System.out);

template.sendSourceAndReceiveToResult(url, source, result);

}

}

=============end of code================

looking forward to receiving your reply

thanks

regards

Brian


chirag272003 profile image

chirag272003 7 years ago Author

Hi brian,

I have written spring web service client hub. please go through it and still if you find any difficulties let me know.

http://hubpages.com/technology/Spring_Web_Service_

Thanks,

Chirag


Madhu Prasad 6 years ago

Hi Chirag,

Thanks a lot for providing such a nice example. I have gone through Ur example, unfortunate am struggling while importing wsdl in the soapUI. pls reply ASAP.

Thanks in Advance.

Regards,

Madhu Prasad.


Shahzad K. 6 years ago

Spring Web Service 75 : This is a nice tutorial and the replys to the comments solved a problem for me.

Thanks

Shahzad Khan.


Malli 6 years ago

Hi Chirag,

i have tried your example, it is very clear and useful.

while i was testing soup ui i got below result, it was not giving any errors also. Please help me.

soapenv:Server

java.lang.NullPointerException


Shameer Kunjumohamed 6 years ago

Just wanted to share another related post with title, Building a web service with Spring-WS at

http://justcompiled.blogspot.com/2010/09/building-...


Ravi 5 years ago

how about marshalling and unmarshalling in this example


venkata 5 years ago

Hi Chirag,

I gone through u r tutorial its amazing and self explanatory. In need of writing junit test cases to the spring web services. Is there any samples, if so could u pls share with us.


Roger 5 years ago

I build and deploy the WAR file in Tomcat 7.0.5.

Question regarding the interceptors config in railway-ws-servlet.xml

Where is the payload logged? I can not find it in catalina.log.

Thanks!

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working