Advanced java code dealing with real world problems.

Friday, May 4, 2012

SOAP Web Service using Spring-WS 2.0 - Part 1

Spring Web Service framework has come a long way, and it has made implementing web services in java a trivial task with release 2.0. Let's walk-through a creation of a simple "Project Search" web service project, step by step.

Step 1) create a web project with your favorite IDE, I use Eclipse or IBM RAD.

Step 2) create or modify your web.xml with following entries:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
            <!-- classpath*:spring-ws-test.xml, -->
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 
<servlet>
    <description>Spring Message Dispatcher</description>
    <display-name>ws-test</display-name>
    <servlet-name>ws-test</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>
</servlet>

 

<servlet-mapping>
    <servlet-name>ws-test</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

Step 3) create an xml schema that defines service request and response message format, and save it as ws-project.xsd under WEB-INF folder:

<schema xmlns="http://www.w3.org/2001/XMLSchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://javaclue.blogger.com/ws-project"
    targetNamespace="http://javaclue.blogger.com/ws-project"
    elementFormDefault="qualified">

    <xs:element name="ProjectRequest" type="tns:ProjectRequestType"/>
    <xs:element name="ProjectResponse" type="tns:ProjectResponseType"/>

    <xs:complexType name="ProjectRequestType">
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="ProjectResponseType">
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="Description" type="xs:string"/>
            <xs:element name="Url" type="xs:string"/>
            <xs:element name="StartDate" type="xs:date"/>
            <xs:element name="EndDate" type="xs:date"/>
        </xs:sequence>
    </xs:complexType>
</schema>

Step 4) create a Spring Web Service configuration file called "ws-test-servlet.xml" and save it under WEB-INF folder. Notice that the "servlet-name" in web.xml is used to name this Spring web service configuration xml file, this is required by Spring-WS framework.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:sws="http://www.springframework.org/schema/web-services"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="javaclue.ws"/>
<sws:annotation-driven/>

<sws:dynamic-wsdl id="projectSearch"
    portTypeName="ProjectSearch"
    locationUri="/SpringWSTest/" requestSuffix="Request" responseSuffix="Response"
    targetNamespace="http://javaclue.blogger.com/ws-project">
  <sws:xsd location="/WEB-INF/ws-project.xsd"/>
</sws:dynamic-wsdl>
</beans>

In this config file, we tell Spring where our web service schema is located, and Spring will generate WSDL based on the xsd file. Please also notice that we have enabled "component-scan" and "annotation-driven" in the config file, this is required for our project.

Once our project is deployed to a servlet container, for example a local tomcat server, we should be able to access the wsdl file from browser with address: http://localhost:8080/SpringWSTest/projectSearch.wsdl

Step 5) create an Endpoint class that will be used to serve the request. For simplicity, we display the request xml to the console and load a static xml from a file and return it as response. The class is annotated with @Endpoint, and service method is annotated with @PayloadRoot:

package javaclue.ws.endpoint;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.jdom2.input.DOMBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Spring-WS End-Points are scoped as singleton by default and have to be thread safe.
 */
@Endpoint
public class ProjectSearchEndpoint {
    static Logger logger = Logger.getLogger(ProjectSearchEndpoint.class);
   
    @PayloadRoot(namespace = "http://javaclue.blogger.com/ws-project", localPart = "ProjectRequest")
    @ResponsePayload
    public Element searchProjects(@RequestPayload Element request) {
        // print out xml payload using jdom
        XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
        DOMBuilder builder = new DOMBuilder();
        org.jdom2.Element doc = builder.build(request);
        try {
            xout.output(doc, System.out);
        }
        catch (IOException e) {
            logger.error("Exception", e); // put your error handling logic here
        }
       
        // load response from xml file and return it
        try {
            Document resp = loadDocumentFromFilePath("/TestXmls/TestResponse.xml");
            return resp.getDocumentElement();
        }
        catch (Exception e) {
            logger.error("Exception", e); // put your error handling logic here
        }
        return null;
    }

    private Document loadDocumentFromFilePath(String doc_path)
            throws ParserConfigurationException, SAXException, IOException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        InputStream doc_is = loader.getResourceAsStream(doc_path);
        if (doc_is == null) {
            throw new IllegalArgumentException("Could not find xml file: " + doc_path);
        }
        // use jaxp to initialize a DOM parser
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(false);
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new InputSource(doc_is));
        return document;
    }
}

Step 6) to make above Endpoint work, create a folder called "TestXmls" in your project's src (source) folder, and save following sample xml as "TestResponse.xml" to the folder. In real world you will need to construct a real response based on the request and return it.

<tns:ProjectResponse
    xsi:schemaLocation="http://javaclue.blogger.com/ws-project ws-project.xsd"
    xmlns:tns="http://javaclue.blogger.com/ws-project"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <tns:Name>SpringWSTest</tns:Name>
    <tns:Description>Test project using Spring-WS 2.0</tns:Description>
    <tns:Url>http://localhost:8080/SpringWSTest/projectSearch.wsdl</tns:Url>
    <tns:StartDate>2011-01-01</tns:StartDate>
    <tns:EndDate>2012-01-30</tns:EndDate>
</tns:ProjectResponse>

Step 7) deploy the project to your favorite container. And it's ready to serve.

No comments:

Post a Comment

Followers

About Me

An IT professional with more than 20 years of experience in enterprise computing. An Audio enthusiast designed and built DIY audio gears and speakers.