Wednesday, 23 January 2013

Spring Web Services Example Easy Steps.....

Just a glimpse........
Spring Web Services is a product from Spring community to develop the web services in easier manner. Spring Web Services is following the strategy of contract-first web services. It focus more on XML and lesser on Java implementation. In this article, we will learn how to write a simple web service application using Spring Web Services. It is expected that the reader has a fair amount of knowledge on Web services concepts before continuing with the article along with the basics of Spring Framework like Dependency Injection and Inversion of Control.

Spring Web Services Example :
In this article, we will design a simple application that'll ask for your name and accordingly greet you as "Hello Name !" using Spring Web Service in Eclipse IDE. This article will provide the content in a tutorial fashion in a step-by-step manner and parallely giving relevant details to that section. Generally speaking, developing web services involves writing artifacts in the server and the client tier. We will look into the details about those artifacts in the subsequent sections. Given below are the pre-requisite softwares to develop and run the application
 1.Java Development Kit (1.5 or later)
 2.Spring Web Service Framework (2.0.0)
 3.Eclipse IDE
 4.Web Server(Apache Tomcat 6)

And the following jar files
1.commons-logging-1.1.1.jar
2.jaxen-1.1.1.jar
3.spring.jar
4.spring-webmvc.jar
5.spring-ws-2.0.0-M1-all.jar

A. Server-side artifacts :

The following steps are provided in building up the various server-side artifacts.
 1.Contract Design
 2.Defining the service interface
 3.Implementing the service interface
 4.Defining Spring Web-Service endpoints
 5.Configuring deployment descriptor.
 6.Configuring Spring’s Application Context.
 [N.B. In this example I've merged Step - 2, 3 & 4 into a single step, i.e. I implemented the code of service interface & it's implementation inside endpoint itself as our business logic is quite simple & straight forward(accepts a string as well as returns a string).]

Remember to build the server tier as a web application(Dynamic Web Project in Eclipse IDE), so that it can be made runnable in any web server.

Application folder structure in eclipse'll look like :
      

1.Contract design :

We will design the contract for the service through xml, i.e. a .wsdl(Web Service Description Language) file. The contract defines the input and the output messages that a service expects. Since we wanted to enforce restriction on the input and output, we will provide an XSD, i.e. a.xsd(XML Schema Definition) file. In our case, the request and the response messages will be simple string objects. The following definitions define the .xsd file & .wsdl file:

Greeting.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.greeting.com/schema" xmlns:tns="http://www.greeting.com/schema"
    elementFormDefault="qualified">

    <xsd:element name="Message" type="xsd:string"/>

    <!-- Greeting Method -->
    <xsd:element name="GreetingRequest">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="tns:Message" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:element name="GreetingResponse">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="tns:Message" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

Greeting.wsdl
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://zensar.com/Greeting/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Employee"
    targetNamespace="http://zensar.com/Greeting/">
    <wsdl:types>
        <xsd:schema targetNamespace="http://zensar.com/Greeting/">
            <xsd:element name="Message" type="xsd:string"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="GreetingRequest">
        <wsdl:part element="tns:Message" name="GreetingRequest" />
    </wsdl:message>
    <wsdl:message name="GreetingResponse">
        <wsdl:part element="tns:Message" name="GreetingResponse" />
    </wsdl:message>
    <wsdl:portType name="Greeting">
        <wsdl:operation name="Greeting">
            <wsdl:input message="tns:GreetingRequest" />
            <wsdl:output message="tns:GreetingResponse" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="Greeting" type="tns:Greeting">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="Greeting">
            <soap:operation soapAction="http://zensar.com/Greeting/Greeting" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="Greeting">
        <wsdl:port binding="tns:Greeting" name="Greeting">
            <soap:address location="http://localhost:8090/Greeting/services" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

2.Defining the service interface, 3.Implementing the service interface &  4.Defining Spring Web-Service endpoint :

Defining the service interface - The service interface represents the client-facing interface for invoking the service by passing in the required inputs. Note that the interface will be a simple POJO, the interface doesn’t need to extend or implement any of the spring web-service related APIs. In this example, the input will be a simple java string object.
Implementing the service interface - Writing the implementation for the above service interface will be fairly straight-forward. It prepends "Hello " & appends " !" to the input string by using Java built-in API and returns the result to the caller.
Defining endpoints - Endpoints are components for processing the input request messages and are usually responsible for invoking the desired service by passing in the required details. Spring Web Services implementation comes with two flavors of endpoints – message endpoints and payload endpoints. Message endpoints provide access to the raw input XML message including the XML header along with the XML body. On the other hand, Payload endpoints are useful in dealing with the XML body alone. In our simple application, we will define a Payload endpoint for parsing the input XML message for invoking the service.
In this example we are writing the service method inside the Endpoint class(for simplicity, but not recommended).

GreetingEndpoint.java
package com.greeting.endpoint;
import org.springframework.ws.server.endpoint.AbstractDomPayloadEndpoint;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class GreetingEndpoint extends AbstractDomPayloadEndpoint{
    @Override
    protected Element invokeInternal(Element requestElement, Document document)
            throws Exception {
        String requestContent = requestElement.getTextContent();
        Element response = document.createElementNS("http://www.greeting.com/schema", "GreetingResponse");
        response.setTextContent(sayHello(requestContent));
        System.out.println("Service metod called...\n" + ("Hello " + requestContent + " !").replaceAll("\n", ""));
        return response;
    }
    private String sayHello(String name){
        return ("Hello " + name + " !").replaceAll("\n", "");
    }
}

Here we've used AbstractDomPayloadEndpoint as we want to perform manual xml-parsing for reading xml-request & sending-xml response. If you want to use JDOM/XPATH for xml parsing, then you've to go with AbstractJDomPayloadEndpoint. If you want to use JAXB for marshalling/unmarshalling, the you have to go with AbstractMarshallingPayloadEndpoint and so forth.

The method invokeInternal will be called when an XML request is received by the framework and the whole xml content will be available in the requestElement. The rest of the code does the job of finding the request string from the input XML, invoke the greeting service and then constructs the appropriate XML response.

5.Configuring deployment descriptor :

The web application’s deployment descriptor must be configured with a servlet that is capable of dispatching web service message to the appropriate handler. This happens to be the MessageDispatcherServlet servlet.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringWS_1.5_Employee_Demo1</display-name>
  <servlet>
      <servlet-name>spring-ws</servlet-name>
      <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>spring-ws</servlet-name>
      <url-pattern>/*</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

In the above XML, we have routed all requests to the servlet spring-ws-service which in-turns maps to MessageDispatcherServlet. The above dispatcher servlet will search for a configuration file representing the spring application context for looking up the definition for various spring beans. By default, the name of the configuration file will be derived from the servlet name. If the name of the servlet is xyz, then the name of the configuration file will be xyz-servlet.xml. In our case, the configuration file will be spring-ws-servlet.xml. We can also give any name to the Spring Configuration File by just adding the following init-param to the MessageDispatcherServlet :

       <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/context/spring-context.xml</param-value>
      </init-param>

6.Configuring Spring’s Application Context :

The minimal requirement of the spring application configuration file that resides in a web server is to contain the following entries:
 i Endpoints definition
 ii Endpoint mappings definition
 iii WSDL Definition

spring-ws-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" xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd">
  
    <!-- For Hand-Written contract(.wsdl) -->
    <bean id="Greeting" class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
        <constructor-arg value="/WEB-INF/Greeting.wsdl"/>
    </bean>
  
    <!-- Endpoint Mapping... -->
    <bean id="greetingEndpoint" class="com.greeting.endpoint.GreetingEndpoint"/>
    <bean name="endPointMapping" class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
        <property name="mappings">
            <props>
                <prop key="{http://www.greeting.com/schema}GreetingRequest">greetingEndpoint</prop>
            </props>
        </property>
    </bean>
</beans>

B. Client-side artifacts :

1.Configuring client-side spring's application context
2.Client class

And the following jar files
1.commons-logging-1.1.1.jar
2.commons-httpclient-3.1.jar
3.spring.jar
4.spring-webmvc.jar
5.spring-ws-2.0.0-M1-all.jar


There is not too much work to be done in the client end as the client-side artifact represents a plain java application that is capable of contacting the web service that is residing on top of the web server. Writing the client code is fairly simple. Spring Template classes for web services simplify the process of writing the client side. Take a look at the following client code,


1.Configuring Client-Side Spring’s Application Context :

applicationContext.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"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd">
    <bean name="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <property name="defaultUri" value="http://localhost:8090/Greeting"/>
    </bean>
</beans>

2.Client class

Client.java
package com.client;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.w3c.dom.Document;
public class Client {
    public static void main(String[] args) throws Exception{
        //preparing request
        File requestFile = new File("D:/request.xml");
        OutputStream fileOutputStream = new FileOutputStream(requestFile);
        String xmlRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<GreetingRequest xmlns=\"http://www.greeting.com/schema\">Sidhartha</GreetingRequest>";
        fileOutputStream.write(xmlRequest.getBytes());
        fileOutputStream.close();
        StreamSource requestMessage = new StreamSource(requestFile);
      
        //preparing response
        File responseFile = new File("D:/response.xml");
        StreamResult responseMessage = new StreamResult(responseFile);
      
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
        WebServiceTemplate template = (WebServiceTemplate) applicationContext.getBean("webServiceTemplate");
        template.sendSourceAndReceiveToResult(requestMessage, responseMessage);
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(responseFile);
        System.out.println(document.getFirstChild().getTextContent());
    }   

No comments:

Post a Comment