Spring Web Services Tutorial

I’ve recently written a more up to date version of this post which describes how to build a contract first web service using Apache CXF. Check it out here.

SOAP Services

Modern enterprise applications are rarely stand alone and often rely on data and services provided by external systems. In order for different types of systems to communicate there must be a  communication protocol of some sort, a standard way of sending and receiving messages in a format that is recognised and supported by all major platforms. SOAP (Simple Object Application Protocol) is such a protocol, and allows applications to communicate by exchanging messages in a standard XML format.
SOAP Web Services provide a platform agnostic integration mechanism that allows disparate systems to exchange data regardless of the platform they are running on. For example, SOAP web services are commonly used to integrate .NET applications with applications running on the Java platform. Almost all modern platforms and frameworks (Java, .Net, Ruby, PHP, etc) provide comprehensive libraries and tooling that allow developers to quickly and easily expose and consume SOAP services.
This post will look at Spring Web Services and take you through a step by step tutorial for building, deploying and testing a simple contract first SOAP service for retrieving simple bank account details.

Tech Stack

The technology stack used in this tutorial will include Spring 3.1 for Web Services Support, Maven for  dependency resolution & build, Tomcat for our test server and SoapUI to build sample SOAP messages for testing our service.

Creating the Project

The project structure is similar to that used in a some of my other tutorials and is typical of most modern Spring web applications. We’ll start off by creating a simple Spring web project like the one shown in figure 1.0 below.

Figure 1.0 Project Structure

Contract Last vs Contract First

There are two fundamental approaches to building web services, Contract Last and Contract First. The Contract Last approach involves taking existing code and generating a service contract directly from that code in order to expose it as a SOAP interface. There are a variety of Java frameworks out there (Axis2, XFire etc) that provide this Java2WSDL tooling, to quickly generate the server side proxies, marshallers and Servlet classes required to expose a SOAP service.
The Contract First approach involves defining the Service contract before implementing the service. This means describing the service parameters and return types using XSD’s (XML Schema Definitions), then using those XSD’s to construct a WSDL (web service definition language) to provides a public facing contract or description of the service. Only after the service contract has been clearly defined, is the service implementation actually written.
This post will describe a Contract First service, as this is the preferred approach for various reasons, some of which are explained in this article.

Service Contract Definition

Given that we’re building a service to retrieve simple bank account details we’ll start off by defining our core business entity, an Account. We’ll define our account entity in src/main/webapp/schemas/AccountDetails.xsd.

<?xml version="1.0" encoding="UTF-8"?>  
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://webservices.samples.blog.com" targetNamespace="http://webservices.samples.blog.com" elementFormDefault="qualified" attributeFormDefault="unqualified">  
      <xs:element name="Account" type="Account"/>  
      <xs:complexType name="Account">  
           <xs:sequence>  
                <xs:element name="AccountNumber" type="xs:string"/>  
                <xs:element name="AccountName" type="xs:string"/>  
                <xs:element name="AccountBalance" type="xs:double"/>  
                <xs:element name="AccountStatus" type="EnumAccountStatus"/>  
           </xs:sequence>  
      </xs:complexType>  
      <xs:simpleType name="EnumAccountStatus">  
           <xs:restriction base="xs:string">  
                <xs:enumeration value="Active"/>  
                <xs:enumeration value="Inactive"/>  
           </xs:restriction>  
      </xs:simpleType>  
 </xs:schema>

I use XMLSpy for working with XML as it provides a useful graphical representation of the XML types being defined. This can be useful when working on large applications with complex data models. A visual representation of the above XSD is shown below.

Figure 2.0 Account Entity

Next we’ll define the service request and response types in src/main/webapp/schemas/AccountDetailsServiceOperations.xsd.

<?xml version="1.0" encoding="UTF-8"?>  
 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://com/blog/samples/webservices/accountservice" xmlns:account="http://webservices.samples.blog.com" targetNamespace="http://com/blog/samples/webservices/accountservice" elementFormDefault="qualified">  
      <xsd:import namespace="http://webservices.samples.blog.com" schemaLocation="AccountDetails.xsd"/>  
      <xsd:element name="AccountDetailsRequest">  
           <xsd:complexType>  
                <xsd:sequence>  
                     <xsd:element name="accountNumber" type="xsd:string"/>  
                </xsd:sequence>  
           </xsd:complexType>  
      </xsd:element>  
      <xsd:element name="AccountDetailsResponse">  
           <xsd:complexType>  
                <xsd:sequence>  
                     <xsd:element name="AccountDetails" type="account:Account"/>  
                </xsd:sequence>  
           </xsd:complexType>  
      </xsd:element>  
 </xsd:schema>

A visual representation of these types is shown below.

Figure 3.0 AccountDetailsRequiest Entity

 

Figure 4.0 AccountDetailsResponse Entity

Object to XML Mapping

A fundamental part of web services is the conversion of SOAP messages from XML to Java objects and vice versa. This is a non trivial task if you were to set out to do it yourself so we’ll make use of the JAXB framework to take car of this for us. I’ve been working with JAXB for a few years now and find it to be a powerful and flexible framework, and a huge improvement on older OXM frameworks like Castor.
In order to use our XSD defined types in the application we need to generate Java classes from those types. We do this as part of the Maven build process by using the jaxb-maven-plugin in our POM. The plugin is configured to parse a set of XSD’s and run JAXB’s class generator to create Java classes for each of the defined types. For brevity only part of the Maven POM definition is shown below. The entire  POM definition can be found with the source code that accompanies this tutorial.

<?xml version="1.0"?>  
 <project xmlns="http://maven.apache.org/POM/4.0.0"  
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0  
                                     http://maven.apache.org/maven-v4_0_0.xsd">  
      <artifactId>spring-webservices-sample</artifactId>  
      <modelVersion>4.0.0</modelVersion>  
      <inceptionYear>2013</inceptionYear>  
      <packaging>war</packaging>  
      <groupId>com.blog.webservices</groupId>  
      <version>1.0</version>  
      <properties>  
           <spring.version>3.1.1.RELEASE</spring.version>  
           <spring.ws.version>2.0.0.RELEASE</spring.ws.version>  
           <log4j.version>1.2.16</log4j.version>  
           <context.path>spring-webservices-sample</context.path>  
      </properties>  
      <build>  
           <plugins>  
                <plugin>  
                     <groupId>org.codehaus.mojo</groupId>  
                     <artifactId>jaxb2-maven-plugin</artifactId>  
                     <version>1.4</version>  
                     <executions>  
                          <execution>  
                               <goals>  
                                    <goal>xjc</goal>  
                               </goals>  
                               <phase>generate-sources</phase>  
                          </execution>  
                     </executions>  
                     <configuration>  
                          <clearOutputDir>false</clearOutputDir>  
                          <outputDirectory>src/main/java</outputDirectory>  
                          <schemaDirectory>src/main/webapp/schemas</schemaDirectory>  
                          <includeSchema>**/*.xsd</includeSchema>                        
                          <enableIntrospection>false</enableIntrospection>  
                     </configuration>  
                </plugin>  
                <plugin>  
                     <groupId>org.apache.maven.plugins</groupId>  
                     <artifactId>maven-war-plugin</artifactId>  
                     <configuration>  
                          <warName>${context.path}</warName>  
                     </configuration>  
                </plugin>  
           </plugins>  
      </build>  
      <dependencies>  
     ...  
     ...

Running a Maven build will create Java classes for each of the defined schema types. The screenshot below shows what the generated classes should look like in your project after you run a Maven build. Note that JAXB has used the the namespaces in the XSD’s to derive package names for the generated classes.

Figure 5.0 JAXB Generated Classes

Defining the Service

The next step is to define the Service interface using the types we generated above. The Service interface is defined below and is very simple indeed.

package com.blog.samples.services;  
import com.blog.samples.webservices.Account;  
/**  
 * The Interface AccountService.  
 */  
public interface AccountService  
{  
     /**  
      * Gets the account details.  
      *  
      * @param accountNumber the account number  
      * @return the account details  
      */  
     public Account getAccountDetails(String accountNumber);  
}  

Now we’ll provide a really simple implementation of this interface. As you can see our service implementation returns some hard coded values. Obviously a real service implementation would do something more meaningful.

package com.blog.samples.services;  
 import org.springframework.stereotype.Service;  
 import com.blog.samples.webservices.Account;  
 import com.blog.samples.webservices.EnumAccountStatus;  
 /**  
  * The Class AccountService.  
  */  
 @Service  
 public class AccountServiceImpl implements AccountService  
 {  
      /**  
       * Gets the account details.  
       *  
       * @param accountNumber the account number  
       * @return the account details  
       */  
      public Account getAccountDetails(String accountNumber)  
      {  
           /* hard coded account data - in reality this data would be retrieved  
            * from a database or back end system of some sort */  
           Account account = new Account();  
           account.setAccountNumber("12345");  
           account.setAccountStatus(EnumAccountStatus.ACTIVE);  
           account.setAccountName("Joe Bloggs");  
           account.setAccountBalance(3400);  
           return account;  
      }  
 }

Creating the Service Endpoint

A service endpoint is the component that deals with processing web service requests and responses. In the background a Spring Servlet intercepts incoming SOAP requests for a defined URL and routes them to an endpoint for processing. Below we’re going to define that endpoint.

package com.blog.samples.services.endpoints;  
import org.springframework.beans.factory.annotation.Autowired;  
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 com.blog.samples.services.AccountService;  
import com.blog.samples.webservices.Account;  
import com.blog.samples.webservices.accountservice.AccountDetailsRequest;  
import com.blog.samples.webservices.accountservice.AccountDetailsResponse;  
/**  
 * The Class AccountService.  
 */  
@Endpoint  
public class AccountServiceEndpoint  
{  
  private static final String TARGET_NAMESPACE = "http://com/blog/samples/webservices/accountservice";  
  
  @Autowired  
  private AccountService accountService_i;  
  
  /**  
   * Gets the account details.  
   *  
   * @param accountNumber the account number  
   * @return the account details  
   */  
  @PayloadRoot(localPart = "AccountDetailsRequest", namespace = TARGET_NAMESPACE)  
  public @ResponsePayload AccountDetailsResponse getAccountDetails(@RequestPayload AccountDetailsRequest request)  
  {  
    AccountDetailsResponse response = new AccountDetailsResponse();  
    /* call Spring injected service implementation to retrieve account data */  
    Account account = accountService_i.getAccountDetails(request.getAccountNumber());  
    response.setAccountDetails(account);  
    return response;  
  }  
  
  public void setAccountService(AccountService accountService_p)  
  {  
    this.accountService_i = accountService_p;  
  }  
}  

Our sample application makes sue of  Springs Web Services annotation support. The above class uses a number of these annotations, each of which is explained below.

Line 14 – @Enpoint is a specialised version of the standard Spring @Component annotation and allows this class to get picked up and registered by Springs component scanning.
Lines 19 & 20 – Our simple service implementation is Spring injected so that it can be used by our web service endpoint.
Line 17 – this is the namespace we defined in our XSD type definitions earlier. We use this in the endpoint class for mapping request to specific methods for processing.
Line 28 – @PayloadRoot indicates that this method will process service requests with the XML root element matching that defined by the localPart attribute. In the example above our method will process incoming requests of type AccountDetailsRequest with namespace http://com/blog/samples/webservices/accountservice. Remember that we defined this XSD type and namespace earlier.
Line 29 – @ResponsePayload indicates the type to be returned in the SOAP response. In this example the AccountDetailsResponse object will be converted to XML and returned to the client application as a SOAP response. @RequestPayload AccountDetails tells Spring to convert incoming requests of type AccountDetails, from XML to Java and the pass that object as a parameter to this endpoint method.

Spring Configuration

Next we’ll write our Spring configuration to bring everything together. The Spring configuration is defined as follows.

<?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: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="com.blog.samples.services" />  
      <sws:annotation-driven />  
      <!--  
           Our test service bean  
      -->  
      <bean id="AccountDetailsService" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition" lazy-init="true">
      
      <property name="schemaCollection">  
       <bean class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">  
         <property name="inline" value="true" />  
         <property name="xsds">  
           <list>  
             <value>schemas/AccountDetailsServiceOperations.xsd</value>  
           </list>  
         </property>  
       </bean>  
      </property>
  
      <property name="portTypeName" value="AccountDetailsService"/>  
      <property name="serviceName" value="AccountDetailsServices" />  
      <property name="locationUri" value="/endpoints"/>  
   </bean>  
 </beans>
  • Line 13 – Component scanning scans the defined package (com.blog.sample.services) for Spring managed components to load into the bean factory.
  • Line 14 – Enables Spring Web Services annotation support so that annotations like @PayloadRoot can be used to configure the service endpoint.
  • Line 20 to 33 – Use of DefaultWsdl11Definition enables automated WSDL generation. Spring uses the schema definitions listed in the schemaCollection property, as well as the portType, serviceName and locationUri to generate a WSDL file the first time it is requested. Although this is a powerful feature it should be used with caution in production as the WSDL generation process can be quite slow. An approach I’ve used in the past is to copy the generated WSDL from your browser to your project and expose it using Springs static WSDL support with <static-wsdl>.

Web.xml

Now for the final bit of configuration before we test out our service. Web.xml is defined as follows.

<?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">  
     <context-param>  
          <param-name>contextConfigLocation</param-name>  
           <param-value>  
                /WEB-INF/config/spring-config.xml  
           </param-value>  
      </context-param>  
      <listener>  
           <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
      </listener>  
      <servlet>  
           <servlet-name>webservices</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>  
           <init-param>  
                <param-name>contextConfigLocation</param-name>  
                <param-value></param-value>  
           </init-param>  
           <load-on-startup>1</load-on-startup>  
      </servlet>  
      <servlet-mapping>  
           <servlet-name>webservices</servlet-name>  
           <url-pattern>*.wsdl</url-pattern>  
      </servlet-mapping>  
      <servlet-mapping>  
           <servlet-name>webservices</servlet-name>  
           <url-pattern>/endpoints/*</url-pattern>  
      </servlet-mapping>  
 </web-app>  
  • Line 8 to 13 – Path for Spring configuration to be loaded on application start-up.
  • Line 14 to 16 – Loads the Spring application context using the configuration file defined above
  • Line 18 to 19 – Spring Web Service Servlet that intercepts incoming HTTP requests.
  • Line 21 to 22 – Ensures WSDL is context aware. Transforms SOAP address so that it isn’t hard coded to localhost:8080.Address updates depending on the application context and port that the application is deployed at.
  • Line 25 to 26 – ContextConfigLocation set with an empty parameter means that Spring won’t try to load the default webservices-servlet.xml configuration.
  • Line 35 to 36 – Configures the URLs that our newly configured Web Services Servlet will handle.

Deploying the Service

We’re now ready to deploy our application – I use Tomcat but feel free to use any Servlet container. Once the application is deployed, just browse to http://localhost:8080/spring-webservices-sample/endpoints/AccountDetailsService.wsdl and the application should generate and display the following WSDL.

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch0="http://com/blog/samples/webservices/accountservice" xmlns:sch
     <wsdl:types>  
          <xsd:schema xmlns="http://com/blog/samples/webservices/accountservice" xmlns:account="http://webservices.samples.blog.com" xmln
               <xsd:import namespace="http://webservices.samples.blog.com"/>  
               <xsd:element name="AccountDetailsRequest">  
                    <xsd:complexType>  
                         <xsd:sequence>  
                              <xsd:element name="accountNumber" type="xsd:string"/>  
                         </xsd:sequence>  
                     </xsd:complexType>  
                </xsd:element>  
                <xsd:element name="AccountDetailsResponse">  
                     <xsd:complexType>  
                          <xsd:sequence>  
                               <xsd:element name="AccountDetails" type="account:Account"/>  
                          </xsd:sequence>  
                     </xsd:complexType>  
                </xsd:element>  
           </xsd:schema>  
           <xs:schema xmlns="http://webservices.samples.blog.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqua
                <xs:element name="Account" type="Account"/>  
                <xs:complexType name="Account">  
                     <xs:sequence>  
                          <xs:element name="AccountNumber" type="xs:string"/>  
                          <xs:element name="AccountName" type="xs:string"/>  
                          <xs:element name="AccountBalance" type="xs:double"/>  
                          <xs:element name="AccountStatus" type="EnumAccountStatus"/>  
                     </xs:sequence>  
                </xs:complexType>  
                <xs:simpleType name="EnumAccountStatus">  
                     <xs:restriction base="xs:string">  
                          <xs:enumeration value="Active"/>  
                          <xs:enumeration value="Inactive"/>  
                     </xs:restriction>  
                </xs:simpleType>  
           </xs:schema>  
      </wsdl:types>  
      <wsdl:message name="AccountDetailsResponse">  
           <wsdl:part element="tns:AccountDetailsResponse" name="AccountDetailsResponse"/>  
      </wsdl:message>  
      <wsdl:message name="AccountDetailsRequest">  
           <wsdl:part element="tns:AccountDetailsRequest" name="AccountDetailsRequest"/>  
      </wsdl:message>  
      <wsdl:portType name="AccountDetailsService">  
           <wsdl:operation name="AccountDetails">  
                <wsdl:input message="tns:AccountDetailsRequest" name="AccountDetailsRequest"/>  
                <wsdl:output message="tns:AccountDetailsResponse" name="AccountDetailsResponse"/>  
           </wsdl:operation>  
      </wsdl:portType>  
      <wsdl:binding name="AccountDetailsServiceSoap11" type="tns:AccountDetailsService">  
           <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>  
           <wsdl:operation name="AccountDetails">  
                <soap:operation soapAction=""/>  
                <wsdl:input name="AccountDetailsRequest">  
                     <soap:body use="literal"/>  
                </wsdl:input>  
                <wsdl:output name="AccountDetailsResponse">  
                     <soap:body use="literal"/>  
                </wsdl:output>  
           </wsdl:operation>  
      </wsdl:binding>  
      <wsdl:service name="AccountDetailsServices">  
           <wsdl:port binding="tns:AccountDetailsServiceSoap11" name="AccountDetailsServiceSoap11">  
                <soap:address location="http://localhost:8080/spring-webservices-sample/endpoints"/>  
           </wsdl:port>  
      </wsdl:service>  
 </wsdl:definitions>

Testing the Service

The simplest way to test a SOAP service is using SoapUI. For anyone who hasn’t used it before, SoapUI is an open source functional testing tool for testing SOAP web services. It saves us having to write a web service client and means that in just a few clicks we can have a test harness in place to test our service.

To test our serviced using SoapUI follow the steps below.

Figure 6.0 SoapUI Test Project

  • SoapUI will parse the exposed WSDL (make sure your application is deployed and the WSDL is exposed!) and use it to build a sample SOAP request.
  • When the new project opens click AccountServiceTest -> AccountDetails -> request and you’ll see a SOAP request for the AccountDetails service in the left hand pane. Set the account number and press the green arrow in the top left hand corner to call the service.
  • If the request is successful you should see a SOAP response containing the requested account data in the right hand pane. See figure 7.0 below

Figure 7.0 SoapUI AccountDetaills Service Test

Wrapping Up

The sample code in this post took you through the steps required to build, deploy and test a contract first web service using the Spring framework. Don’t forget that you can download the full source code for this tutorial and play around with it. If you found this tutorial useful then feel free to share it with others or leave a comment below. The full source code can be found on GitHub at https://github.com/briansjavablog/spring-webservices-tutorial.