Spring-WS

Took a look at Spring-WS and came up with a quick example service to describe its use. I decided to build an 'echo' service. Send in a text and it will echo that back with a date and time appended to the text.

After building the application I saw that Spring-WS comes with a sample echo service application. Oh well. Since I put in the effort here is the article on it.

Spring-WS encourages document based web services. As you know there are mainly two types of web services:
  • RPC based. 
  • Document based.
In RPC you think in terms of traditional functional programming. You decide what operations you want and then use the WSDL to describe the operations and then implement them. If you look at any RPC based WSDL you will see in the binding section the various operations.

In the document based approach you no longer think of operations (their parameters and return types). You decide on what XML document you want to send in as input and what XML document you want to return from your web service as a response.

Spring-WS encourages a more practical approach to designing document based web services. Rather than think WSDL, it pushes you to think XSD (or the document schema) and then Spring-WS can auto-generate the WSDL from the schema.

Lets break it up into simpler steps:
  1. Create your XML schema (.xsd file). Inside the schema you will create your request messages and response messages. Bring up your favourite schema edit or to create the schema or write sample request and response XML and then reverse-engineer the schema (check if your tool supports it).
  2. You have shifted the focus onto the document (or the XML). Now use Spring-WS to point to the XSD and set up a few Spring managed beans and soon you have the web service ready. No WSDL was ever written.
Spring-WS calls this the contract-first approach to building web services.

Lets see the echo service in action. You will notice that I do not create any WSDL document throughout this article.

BusinessCase:
Echo service takes in an XML request document and returns an XML document with a response. The response contains the text that was sent in, appended with a timestamp.


RequestXML Sample:
<ec:EchoRequest>
        <ec::Echo>
           <ec:Name>Mathew</ec:Name>
        </ec:Echo>
</ec:EchoRequest>

The schema XSD file for this can be found in the WEB-INF folder of the application (echo.xsd).


ResponseXML Sample:
<ec:EchoResponse>
    <ec:EchoResponse>
         <ec:Message>echoback: name Mathew received on 05-06-2007 06:42:08PM</ec:Message>
    </ec:EchoResponse>
</ec:EchoResponse>


The schema XSD file for this can be found in the WEB-INF folder of the application (echo.xsd).

If you inspect the SOAP request and response you will see that this XML is whats inside the SOAP body. This is precisely what is document based web services.


EchoService Implementation:
Here is the echo service Java interface and its related implementation. As you can see this is a simple POJO.
package echo.service;

public interface EchoService {
    public String echo(java.lang.Stringname);
}


package echo.service;

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class EchoServiceImpl implements EchoService {

    public String echo(String name) {
        if (name== null || name.trim().length() == 0) {
           return "echo back: -please provide aname-";
        }
       SimpleDateFormat dtfmt = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss a");
        return"echo back: name " + name + " received on "
               +dtfmt.format(Calendar.getInstance().getTime());
    }
}


Nowthe Spring-WS stuff:

Here is the web.xml for the sake of clarity.
<?xml version="1.0"encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
">java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
        version="2.4">

    <display-name>Echo WebService Application</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>

</web-app>

Only thing to note in the web.xml is the Spring-WS servlet.

Next is the all important Spring bean configuration 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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
">www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean id="echoEndpoint"class="echo.endpoint.EchoEndpoint">
       <property name="echoService"><refbean="echoService"/></property>
    </bean>

    <bean id="echoService"class="echo.service.EchoServiceImpl"/>

    <beanclass="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
       <property name="mappings">
           <props>
              <propkey="{http://www.averconsulting.com/echo/schemas}EchoRequest"
              >echoEndpoint</prop>
           </props>
       </property>
       <property name="interceptors">
           <bean
              class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"
           />
       </property>
    </bean>

    <bean id="echo"class="org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition">
       <property name="builder">
           <bean
              class="org.springframework.ws.wsdl.wsdl11.builder.XsdBasedSoap11Wsdl4jDefinitionBuilder">
              <property name="schema" value="/WEB-INF/echo.xsd"/>
              <property name="portTypeName" value="Echo"/>
              <property name="locationUri"value="http://localhost:9090/echoservice/"/>
           </bean>
       </property>
    </bean>
</beans>

  • Registered the 'echoService' implementation bean.
  • Registered an endpoint class named 'echoEndpoint'. The endpoint is the class that receives the incoming web servicerequest. 
  • The endpoint receives the XML document. You parse the XML data and then call our echo service implementation bean.
  • The bean 'PayloadRootQNameEndpointMapping'is what maps the incoming request to the endpoint class. Here we set up one mapping. Anytime we see a 'EchoRequest' tag with the specified namespace we direct it to our endpoint class.
  • The 'XsdBasedSoap11Wsdl4jDefinitionBuilder'class is what does the magic of converting the schema XSD to a WSDL document for outside consumption. Based on simple naming conventions in the schema (like XXRequest and XXResponse) the bean can generate a WSDL. This rounds up the 'thinking in XSD for document web services' implementation approach.  Once deployed the WSDL is availableat http://localhost:9090/echoservice/echo.wsdl.
Finally here is the endpoint class. This is the class, as previously stated, that gets the request XML and can handle the request from there.
package echo.endpoint;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;
importorg.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint;

import echo.service.EchoService;

public class EchoEndpoint extends AbstractJDomPayloadEndpoint{
    private EchoService echoService;

    public void setEchoService(EchoServiceechoService) {
       this.echoService = echoService;
    }

    protected Element invokeInternal(Elementrequest) throws Exception {
        // ok nowwe have the XML document from the web service request
        // letssystem.out the XML so we can see it on the console (log4j
        // latter)
       System.out.println("XML Doc >> ");
       XMLOutputter xmlOutputter = new XMLOutputter();
       xmlOutputter.output(request, System.out);

        // I amusing JDOM for my example....feel free to process the XML in
        //whatever way you best deem right (jaxb, castor, sax, etc.)

        // somejdom stuff to read the document
        Namespacenamespace = Namespace.getNamespace("ec",
              "http://www.averconsulting.com/echo/schemas");
        XPathnameExpression = XPath.newInstance("//ec:Name");
       nameExpression.addNamespace(namespace);

        // letscall a backend service to process the contents of the XML
        //document
        Stringname = nameExpression.valueOf(request);
        String msg =echoService.echo(name);

        // buildthe response XML with JDOM
        NamespaceechoNamespace = Namespace.getNamespace("ec",
              "http://www.averconsulting.com/echo/schemas");
        Elementroot = new Element("EchoResponse", echoNamespace);
        ElementechoResponse = new Element("EchoResponse", echoNamespace);
       root.addContent(echoResponse);
        Elementmessage = new Element("Message", echoNamespace);
       echoResponse.addContent(message);
       message.setText(msg);
        Documentdoc = new Document(root);

        // returnresponse XML
       System.out.println();
       System.out.println("XML Response Doc >> ");
       xmlOutputter.output(doc, System.out);
        returndoc.getRootElement();
    }
}

This is a simple class. Important point to note is that it extends 'AbstractJDomPayloadEndpoint'. The 'AbstractJDomPayloadEndpoint' class is ahelper that gives you the XML payload as a JDom object. There are similar classes built for SAX, Stax and others. Most of the code above is reading the request XML using JDOM API and parsing the data out so that we may provide it to our echo service for consumption.

Finally I build a response XML document to return and thats it.

Download the sample Application:
Click here to download the jar file containing the application. The application is built using Maven. If you do not have Maven please install it. Once Maven is installed run the following commands:
  1. mvn package (this will generate the web service war file in the target folder).
  2. mvn jetty:run (this will bring up Jetty and you can access the wsdl at http://localhost:9090/echoservice/echo.wsdl.
  3. Finally use some web service accessing tool like the eclipse plug-in soapUI to invoke the web service.
As you can see this is relatively simple. Spring-WS supports the WS-I basic profile and WS-Security. I hope to look at the WS-Security support sometime soon. Also interesting to me is the content based routing feature. This lets you configure which object gets the document based on the request XML content. We did the QName based routing in our example but the content based parsing is of greater interest to me.

While I could not find a roadmap for Spring-WS, depending on the features it starts supporting this could become a very suitable candidate for web service integration projects. Sure folks will say where is WS-Transactions and all of that, but tell me how many others implement that. I think if Spring-WS grows to support 90% of what folks need in integration projects then it will suffice.

 del.icio.us  Stumbleupon  Technorati  Digg 

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this entry.
Comments

Leave a comment

Submitted comments will be subject to moderation before being displayed.

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.