Spring Web Service

In this section lets discuss about how to deploy a web service and access the same service with a client using spring framework.

Web Service – Introduction:

Let me give a brief introduction about web services. Web service is a piece of software application available in one system and accessible from another system using internet-based protocol(HTTP) and XML based messages(SOAP protocol). It is platform independent i.e the language in which a web service is exposed and the language using which the web service is accessed can be different. Lets see some of the terminologies used in Web Service world.

SOAP is a lightweight protocol intended for exchanging structured information in a decentralized, distributed environment. Defined by W3 Consortium current version is SOAP 2.0

SOAP uses XML technologies to define an extensible messaging framework providing a message construct that can be exchanged over a variety of underlying protocols

WSDL - XML language for describing web services, defined by W3 Consortium. Current version is WSDL 2.0. It is like an interface which defines the set of services defined and the input/output parameter types, messaging style and the service endpoint URL to access the web serivces from client.

UDDI - Programmatic registration and discovery of Business entities and their Web services.

Take a look at a sample SOAP message below. It basically consist of an Envelope, SOAP header and body. The envelope mostly consist of the request/response message. It may also contain fault tag if there is any error.

SOAP Envelope

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<env:Header>
</env:Header>

<env:Body env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <m:sayHello xmlns:m="http://www.bea.com/servers/wls70/samples/examples/webservices/basic/java...">
        <intVal xsi:type="xsd:int">100</intVal>
        <string xsi:type="xsd:string">sample string</string>
        <intVal0 xsi:type="xsd:int">100</intVal0>
    </m:sayHello>
</env:Body>
</env:Envelope>

There are two ways of WSDL binding for a web service.

RPC style – exposing web service method as a remote procedure call. The client sends a request to call a method in the server object and a corresponding response is returned to the client. It is as simple as creating a java class with web service methods and using a Java-to-Wsdl conversion tool to create a WSDL and expose the same,(Bottom up Approach). It is tightly coupled because the sending parameters and return values are as described in WSDL document.

Document Style - In Document style, the request and response are in the form of XML documents. The Web Service receives the XML document from client, processes the document, executes the operation and constructs and sends the response to the client as an XML document. There is no direct mapping between the server objects(parameters, method calls etc) and the values in XML documents. For this the user requires good knowledge about XML schema creation for creating XML simple and complex types for defining the i/o parameters,(Top down Approach).
Have look at a sample WSDL document.

It basically consist of following elements

Types – a set XML data types representing the parameters used for request/response.

Message – defines the data being sent to received from the web service including fault message.

Operation – defines an abstract operation consisting of input/output/fault messages as above.

Port Type - a collection of operations

Binding – binding element specifies for each method the concrete protocols(HTTP/soap) to be used and the messaging style(rpc or document).

Service – it defines the internet address for a set of bindings. Usually provides an Endpoint URL.

WSDL Definitions

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl"
targetNamespace="your namespace here"
xmlns:tns="your namespace here"
xmlns:soapbind="http://schemas.xmlsoap.org/wsdl/soap">
<wsdl:types>

<xs:schema targetNamespace="your namespace here (could be another) "
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
<!-- Define types and possibly elements here -->
</schema>
</wsdl:types>

<wsdl:message name="some operation input">
<!-- part(s) here -->
</wsdl:message>

<wsdl:message name="some operation output">
<!-- part(s) here -->
</wsdl:message>

<wsdl:portType name="your type name">
<!-- define operations here in terms of their messages -->
</wsdl:portType>

<wsdl:binding name="your binding name" type="tns:port type name above">
<!-- define style and transport in general and use per operation -->
</wsdl:binding>

<wsdl:service>
<!-- define a port using the above binding and a URL -->
</wsdl:service>

</wsdl:definitions>

Ok! I think we had enough of Web service details. Now its time to take a deep dive in to Spring Web services.

Spring framework supports the top down approach(document style ), i.e creating a WSDL document and then creating the web service implementation classes. In between, we need to convert the XML schema types defined in the web service in to Java types using XML Marshaller.

Lets create an Employee web service. The request should contain an employeeId and the response from that web service should contain a XML document of his details like name,salary,age etc. Now lets define the XML types for our web service.

employee.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/employee" xmlns:tns="http://www.example.org/employee" elementFormDefault="qualified">

<complexType name="empType">

<sequence>
<element name="id" type="integer"></element>
<element name="name" type="string"></element>
<element name="age" type="integer"></element>
<element name="salary" type="double"></element>
</sequence>
</complexType>

<element name="GetEmpProfileRequest">
<complexType>
<sequence>
<element name="id" type="integer" nillable="false"/>
</sequence>
</complexType>
</element>

<element name="GetEmpProfileResponse">
<complexType>
<sequence>
<element name="employee" type="tns:empType" nillable="false"/>
</sequence>
</complexType>
</element>

</schema>

Now, we should use XML marshaller to create the corresponding java classes from the XML schema. Use the below ant build script to create the same.

Build.xml

<?xml version="1.0" ?>
<project name="EMPLOYEE_WSI" basedir="." default="usage">
<description>EMPLOYEE_WSI Ant Build file</description>
<property name="schema" value="${basedir}/employee.xsd"/>
<property name="generate.target.directory" value="${basedir}/src/"/>

<target name="genSrcCommonsXSD">
<taskdef name="xmlbean" classname="org.apache.xmlbeans.impl.tool.XMLBean" classpathref="compile.classpath" />
<xmlbean schema="${schema}" download="false" classgendir="./dist/employee/" destfile="./dist/employee.jar" classpathref="compile.classpath" >
</xmlbean>
</target>
</project>

After running the build.xml, check whether the Java classes are created in the dist/employee folder. Then copy them to either your classes folder in WEB-INF or make a jar out of it and place them in the lib folder in WEB-INF.

Now lets see what are the Spring beans required to create a Web service.

Configuration File

<!-- SAAJ-specific implementation of the WebServiceMessageFactory. -->
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

<!— bean which process the soap message received -->
<bean id="messageReceiver" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>

<!—
marshallingEndpoint is the class where web service calls are mapped with methods
-->

<bean id="marshallingEndpoint" class="in.techdive.spring.ws.MarshallingEmployeeServiceEndpoint">
<description>
This endpoint handles the Web Service messages using XML-BEANS marshalling.
</description>
<property name="empService" ref="empService" />
</bean>

<bean id="marshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller"/>

<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller"/>
</bean>

<bean id="annotationMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">

<property name="order" value="1"/>
</bean>

<bean class= "org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver">
<description>
This exception resolver maps exceptions with the @SoapFault annotation to SOAP Faults.
The business logic exceptions DeviceProfileWSException and DeviceProfileNotFoundException have these.
</description>
<property name="order" value="1"/>
</bean>

<!— java class which implements web service methods
<bean id="empService" class= "in.techdive.spring.service.impl.EmployeeServiceImpl" init-method="createEmployees">
</bean>

Now lets get into the MarshallingEmployeeServiceEndpoint class.

MarshallingEmployeeServiceEndpoint Class

/**
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package in.techdive.spring.ws;

import java.math.BigInteger;
import org.springframework.util.Assert;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.w3c.dom.Node;
import org.example.employee.*;
import org.example.employee.GetEmpProfileRequestDocument.GetEmpProfileRequest;
import org.example.employee.GetEmpProfileResponseDocument.GetEmpProfileResponse;
import org.example.employee.impl.*;
import in.techdive.spring.service.EmployeeService;

@Endpoint
public class MarshallingEmployeeServiceEndpoint
{

  final String          GET_EMPLOYEE_PROFILE  = "GetEmpProfileRequest";

  final String          MESSAGES_NAMESPACE   = "http://www.example.org/employee";

  private EmployeeService  empService           = null;

  public EmployeeService getEmpService()
  {
    return empService;
  }

  public void setEmpService(EmployeeService empService)
  {
    this.empService = empService;
  }

  @PayloadRoot(localPart = GET_EMPLOYEE_PROFILE, namespace = MESSAGES_NAMESPACE)
  public GetEmpProfileResponseDocument getEmpProfile(GetEmpProfileRequestDocument request)
  {
    System.out.println("Entered into endpoint web service");
    GetEmpProfileRequest r = request.getGetEmpProfileRequest();
    System.out.println(r.getId());

    GetEmpProfileResponseDocument responseDocument = GetEmpProfileResponseDocument.Factory.newInstance();
    GetEmpProfileResponse response = responseDocument.addNewGetEmpProfileResponse();

    EmpType eT = empService.getEmpTypeForId(r.getId().longValue());

    if (eT != null)
    {
      response.setEmployee(eT);
    }
    return responseDocument;
  }
}

Note the annotation @Endpoint added to the class. It specifies that this the webservice endpoint class.

Consider the @PayloadRoot(localPart = GET_EMPLOYEE_PROFILE, namespace = MESSAGES_NAMESPACE) annotation above the getEmpProfile(..) method. This is the endpoint method which takes the marshaled xml request, processes it and returns the marshaled XML response. In the @PayloadRoot annotation, localPart maps the the request to this method and the namespace is as provided in "employee.xsd" schema file.
The method argument for getEmpProfile() GetEmpProfileRequestDocument and the return type GetEmpProfileResponseDocument are XML marshalled java types created by XML Marshaller. The XML document from soap request is taken and converted to GetEmpProfileRequestDocument java class type. Similarly the response GetEmpProfileResponseDocument returned will be converted as XML document and sent inside the soap message.

Now we need to put entries in web.xml to expose the webservice

web.xml

<servlet>
    <servlet-name>spring-ws</servlet-name>

    <servlet-class> org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-ws.xml,/WEB-INF/ws-servlet.xml</param-value>
    </init-param>
    <servlet-class>
</servlet>

Once you deploy the above application in a web container (say Tomcat), the Web service will be exposed and you can view the same using the following url,

http://localhost:8080//services/EmployeeService.wsdl

Here is the WSDL file

WSDL File

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:schema="http://www.example.org/employee" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://www.example.org/employee">

<wsdl:types>

<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.org/employee" elementFormDefault="qualified" targetNamespace="http://www.example.org/employee">

<complexType name="empType">
<sequence>
<element name="id" type="integer" />
<element name="name" type="string" />
<element name="age" type="integer" />
<element name="salary" type="double" />
</sequence>
</complexType>

<element name="GetEmpProfileRequest">
<complexType>
<sequence>
<element name="id" nillable="false" type="integer" />
</sequence>
</complexType>
</element>

<element name="GetEmpProfileResponse">
<complexType>
<sequence>
<element name="employee" nillable="false" type="tns:empType" />
</sequence>
</complexType>
</element>

</schema>

</wsdl:types>

<wsdl:message name="GetEmpProfileResponse">
<wsdl:part element="schema:GetEmpProfileResponse" name="GetEmpProfileResponse" />
</wsdl:message>

<wsdl:message name="GetEmpProfileRequest">
<wsdl:part element="schema:GetEmpProfileRequest" name="GetEmpProfileRequest" />
</wsdl:message>

<wsdl:portType name="EmployeeProfile">

<wsdl:operation name="GetEmpProfile">
<wsdl:input message="schema:GetEmpProfileRequest" name="GetEmpProfileRequest" />
<wsdl:output message="schema:GetEmpProfileResponse" name="GetEmpProfileResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="EmployeeProfileBinding" type="schema:EmployeeProfile">

<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />

<wsdl:operation name="GetEmpProfile">
<soap:operation soapAction="" />

<wsdl:input name="GetEmpProfileRequest">
<soap:body use="literal" />
</wsdl:input>

<wsdl:output name="GetEmpProfileResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>

</wsdl:binding>
<wsdl:service name="EmployeeProfileService">
<wsdl:port binding="schema:EmployeeProfileBinding" name="EmployeeProfilePort">
<soap:address location="http://localhost:8080/employee/services" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

Its time to check whether our web-service is actually working or not. We can test it in two ways

1. Using SOAP UI tool, create the SOAP Request for the above URL and send the Request to our Web service deployed. This will result in fetching the Response SOAP message from our Service. Configuration of the SOAP UI tool is not covered as part of this article.

2. Creating a client in Spring and accessing the web service.

To create a Web Service Client in spring, We need to do the following

@ create a class WebServiceClient with (org.springframework.ws.client.core.WebServiceTemplate) and defaultUri string injected in to it.

@ call webServiceTemplate.sendSourceAndReceiveToResult (javax.xml.transform.stream.StreamSource, javax.xml.transform.stream.StreamResult) to send a web-service request and receive a response.

Take a look at the WebServiceClient.java class below.

WebServiceClient Class

/**
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package in.techdive.spring.ws;

import java.io.StringReader;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

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

public class WebServiceClient
{

  private static final String  MESSAGE  = "<emp:GetEmpProfileRequest xmlns:emp=\"http://www.example.org/employee\">"
    + "<emp:id>100</emp:id> "
    + "</emp:GetEmpProfileRequest>";

  private WebServiceTemplate  webServiceTemplate;

  public WebServiceTemplate getWebServiceTemplate()
  {
    return webServiceTemplate;
  }

  public void setWebServiceTemplate(WebServiceTemplate webServiceTemplate)
  {
    this.webServiceTemplate = webServiceTemplate;
  }

  public void setDefaultUri(String defaultUri)
  {
    webServiceTemplate.setDefaultUri(defaultUri);
  }

  // send to the configured default URI
  public void simpleSendAndReceive()
  {
    StreamSource source = new StreamSource(new StringReader(MESSAGE));
    StreamResult result = new StreamResult(System.out);
    webServiceTemplate.sendSourceAndReceiveToResult(source, result);
  }
}

Have a look at the spring-ws-client.xml bean config file

spring-ws-client.xml

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="defaultUri" value="http://localhost:8080/SprWs/employee/services/EmployeeService.wsdl"/>
</bean>

<bean id="webServiceClient" class="in.techdive.spring.ws">
<property name="webServiceTemplate" ref="webServiceTemplate" />
<property name="defaultUri" value="http://localhost:8080/SprWs/employee/services/EmployeeService.wsdl"/>
</bean>

<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

The following class has the main method to initiate the Client execution process

public static void main(String[] args)
{
  ApplicationContext ctx = new FileSystemXmlApplicationContext(
  "classpath*:spring-ws-client.xml");

  WebServiceClient e1 = (WebServiceClient)ctx.getBean("webServiceClient");
  e1.simpleSendAndReceive();
}

Once we execute the above method we will get the output as follows,

Output

2010-06-29 13:18:18,684 INFO [org.springframework.ws.soap.saaj.SaajSoapMessageFactory] - Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol
2010-06-29 13:18:18,713 DEBUG [org.springframework.ws.client.core.WebServiceTemplate] - Opening connection to [http://localhost:8080/SprWs/employee/services/EmployeeService.wsdl] using [org.springframework.ws.transport.http.HttpUrlConnectionMessageSender@10849bc]
2010-06-29 13:18:18,754 DEBUG [org.springframework.ws.client.MessageTracing] - Sent request [SaajSoapMessage {http://www.example.org/employee}GetEmpProfileRequest]
2010-06-29 13:18:19,035 DEBUG [org.springframework.ws.client.MessageTracing] - Received response [SaajSoapMessage {http://www.example.org/employee}GetEmpProfileResponse] for request [SaajSoapMessage {http://www.example.org/employee}GetEmpProfileRequest]

<?xml version="1.0" encoding="UTF-8"?>
<emp:GetEmpProfileResponse xmlns:emp="http://www.example.org/employee">
<emp:employee><emp:id>100</emp:id><emp:name>Williams</emp:name>
<emp:age>30</emp:age></emp:employee>
</emp:GetEmpProfileResponse>

Finally, our first ever Web service using Spring framework is working!

For Further Study
Axis Web Service

Technology: 

Search