Search This Blog

Tuesday, 18 December 2012

SOAP Webservices using Spring -1

The Spring Web Services project is used for creating document-driven Web services. I decided to create a simple web service that takes a value and returns a value (i.e. no logic - just data transfer) .
The focus of Spring is to allow for contract-first SOAP service development. Accordingly I defined a simple WSDL document detailing my web services.
<?xml version="1.0"  encoding = "UTF-8" ?>
<wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:random="http://ws.com/Service/xsd/random-schema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://ws.com/Service/samplews-ns"
    targetNamespace="http://ws.com/Service/samplews-ns">
    
    <wsdl:types>
        <xs:schema>
            <xs:import namespace="http://ws.com/Service/xsd/random-schema"
                schemaLocation="xsd/RandomService.xsd" />
        </xs:schema>
    </wsdl:types>

    <wsdl:message name="GetRandomRequest">
        <wsdl:part element="random:GetRandomRequest" name="GetRandomRequest" />
    </wsdl:message>
    <wsdl:message name="GetRandomResponse">
        <wsdl:part element="random:GetRandomResponse"
            name="GetRandomResponse" />
    </wsdl:message>
    <wsdl:message name="GetRandomFault">
        <wsdl:part element="random:GetRandomFault" name="GetRandomFault" />
    </wsdl:message>

    <wsdl:portType name="SampleServiceOperationsPortType">
        <wsdl:operation name="random">
            <wsdl:documentation>Returns a random value
            </wsdl:documentation>
            <wsdl:input message="tns:GetRandomRequest" name="GetRandomRequest" />
            <wsdl:output message="tns:GetRandomResponse" name="GetRandomResponse" />
            <wsdl:fault message="tns:GetRandomFault" name="GetRandomFault" />
        </wsdl:operation>
    </wsdl:portType>

    <wsdl:binding name="SampleServiceOperationsPortTypeSoap"
        type="tns:SampleServiceOperationsPortType">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="random">
            <soap:operation soapAction="/Service/random" />
            <wsdl:input name="GetRandomRequest">
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="GetRandomResponse">
                <soap:body use="literal" />
            </wsdl:output>
            <wsdl:fault name="GetRandomFault">
                <soap:fault use="literal" name="GetRandomFault" />
            </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="SampleServiceOperationsPortTypeSoap">
        <wsdl:port binding="tns:SampleServiceOperationsPortTypeSoap"
            name="SampleServiceOperationsPortTypeSoap">
            <soap:address location="/Service/ws-operations" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>
As can be seen the wsdl defines a single operation which takes a GetRandomRequest object as input and returns a GetRandomResponse as output. In case of failure the operation returns a fault object of type GetRandomFault. The details of the these object is in an accompanying XSD file.
RandomService.xsd:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified" 
    xmlns:random="http://ws.com/Service/xsd/random-schema"
    targetNamespace="http://ws.com/Service/xsd/random-schema">

    <xs:element name="GetRandomRequest">
        <xs:complexType>
            <xs:all>
                <xs:element name="name" type="xs:string" maxOccurs="1"
                    minOccurs="1" />
            </xs:all>
        </xs:complexType>
    </xs:element>
    
    <xs:element name="GetRandomResponse">
        <xs:complexType>
            <xs:all>
                <xs:element name="value" type="xs:int" maxOccurs="1"
                    minOccurs="1" />
            </xs:all>
        </xs:complexType>
    </xs:element>
    
    <xs:element name="GetRandomFault">
        <xs:complexType>
            <xs:all>
                <xs:element name="faultMessage" type="xs:string"
                    maxOccurs="1" minOccurs="1" />
            </xs:all>
        </xs:complexType>
    </xs:element>
</xs:schema>
Now that the contract is defined the next step is to implement the webservice via Spring:
Step 1 is to create a Server project to host our web-service. I did this in Eclipse creating a dynamic web project.
Step 2 is to configure the Server side to handle our requests. Handler in Java Enterprise is at its simplest a servlet. Just like the DispatcherServlet which acts as a front controller receiving all requests and then delegating to controllers, spring ws uses the MessageDispatcherServlet. As per the code documentation:
Servlet for simplified dispatching of Web service messages.
This servlet is a convenient alternative to the standard Spring-MVC DispatcherServlet 
with separate WebServiceMessageReceiverHandlerAdapter, MessageDispatcher, and
WsdlDefinitionHandlerAdapter instances.
This servlet automatically detects EndpointAdapters, EndpointMappings, and 
EndpointExceptionResolvers by type.
The servlet acts like a front controller for our web service, receiving all requests and then delegating them to appropriate endpoints (Controllers in the web service world).
<servlet>
    <servlet-name>spring-ws</servlet-name>
    <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    <init-param> 
        <param-name>transformWsdlLocations</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
This is from the docs: When the transformWsdlLocations init-param is set to true in this servlet's configuration in web.xml, all location attributes in the WSDL definitions will reflect the URL of the incoming request.  
Step 3 is telling our controller the details of the requests it needs to handle. In our example we would like our controller to handle all requests starting with "Service". Hence the servlet-mapping:
<servlet-mapping> <!-- For the End Points -->
    <servlet-name>spring-ws</servlet-name>
    <url-pattern>/Service/*</url-pattern>
</servlet-mapping>
Step 4 involved adding the jars necessary for the code to execute:
As can be seen most of the jars are the same that we use in a normal web application. New jars of interest are:
  1. wsdl4j-1.6.1.jar - Is the reference implementation for JSR110 'JWSDL' (jcp.org).(WSDL4J allows the creation, representation, and manipulation of WSDL documents.) In this jar, the package javax.wsdl.* includes all the interfaces while the package com.ibm.* includes all the
    implementations.
  2. spring-ws-core - This is the Spring Web Services Core package.
  3. spring-xml - The jar is a utility jar that includes various XML support classes for Spring Web Services.
  4. spring-oxm -The jar provides for Object/XML Mapping, or O/X mapping. It is the act of converting an XML document to and from an object
This completes the basic project setup. We shall continue with the example in the next post.

1 comment:

  1. Thanks for writing such a good article, I stumbled onto your blog and read a few post. I like your style of writing... Auto Detailing Irvine

    ReplyDelete