SOAP Web Services using CXF with Spring

In this tutorial we are going to see how to implement SOAP Web Services using Spring Framework.
In this example we’ll use a Contract-First approach. This approach encourages you to think of the service contract first, in terms of XML, using XML schema (.xsd) and WSDL. In this approach, you design the request and response messages for your service first. The messages are designed with XML, which is very good at representing complex data structures in a platform-and language-independent way. The next step is to implement this contract in a particular platform and programming language, in this case Java.

There are two possible ways to develop a SOAP web service: application servers incorporating or stand-alone web application.
In the first case a SOAP Engine or JAX-WS Runtime (like Axis, CXF or Metro) needs to be installed in the Application Server.
A stand-alone application instead it’s not Application Server dependent and it can be deployed in a Servlet Container like Tomcat. Tomcat doesn’t support JAX-WS by itself, so we need to help it by embedding a JAX-WS runtime.

What we will see is a stand-alone web application that expose a SOAP web service that calculate the sum of a list of integer.

Ingredients

  • Spring 3.1.1
  • CXF 2.2.3

1. WSDL file

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.madbit.org/MyService/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MyService" targetNamespace="http://www.madbit.org/MyService/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://www.madbit.org/MyService/">
      <xsd:element name="SumRequest">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="addend" type="xsd:int" minOccurs="2" maxOccurs="unbounded"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="SumResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="sum" type="xsd:int"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="SumIn">
    <wsdl:part element="tns:SumRequest" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="SumOut">
    <wsdl:part element="tns:SumResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:portType name="MyService">
    <wsdl:operation name="Sum">
      <wsdl:input message="tns:SumIn"/>
      <wsdl:output message="tns:SumOut"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="MyServiceSOAP" type="tns:MyService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="Sum">
      <soap:operation soapAction="http://www.madbit.org/MyService/Sum"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="MyService">
    <wsdl:port binding="tns:MyServiceSOAP" name="MyServiceSOAP">
      <soap:address location="http://www.madbit.org/"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

2. The POM file

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.madbit.soap</groupId>
	<artifactId>spring-soap</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>Spring SOAP</name>

	<properties>
		<spring.version>3.1.1.RELEASE</spring.version>
		<cxf.version>2.2.3</cxf.version>
	</properties>

	<dependencies>
		<!-- Spring framework -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-web</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency> <!-- Used for REST -->
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-oxm</artifactId>
		    <version>${spring.version}</version>
		</dependency>

		<!-- Web Service runtime -->
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>${cxf.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>${cxf.version}</version>
		</dependency>
	</dependencies>

	<build>
	    <plugins>
              <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>${cxf.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>${basedir}/target/generated/</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${basedir}/src/main/resources/wsdl/MyService.wsdl</wsdl>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
              </plugin>
	    </plugins>
	</build>
</project>

3. Generate the classes

Now, let’s execute the command mvn generate-sources in the project directory. The cxf-codegen-plugin will generate the Web Service interface MyService.java, with the JAX-WS annotations. Will be also generated a JAXB2 annotated classes for every type defined in the WSDL file (in this case SumRequest and SumResponse). All those files will be placed under /target/generated.

Below the code of the generated classes.

MyService.java

package org.madbit.myservice;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.xml.bind.annotation.XmlSeeAlso;

@WebService(targetNamespace = "http://www.madbit.org/MyService/", name = "MyService")
@XmlSeeAlso({ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface MyService {

    @WebResult(name = "SumResponse", targetNamespace = "http://www.madbit.org/MyService/", partName = "parameters")
    @WebMethod(operationName = "Sum", action = "http://www.madbit.org/MyService/Sum")
    public SumResponse sum(
        @WebParam(partName = "parameters", name = "SumRequest", targetNamespace = "http://www.madbit.org/MyService/")
        SumRequest parameters
    );
}

SumRequest.java

package org.madbit.myservice;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "addend"
})
@XmlRootElement(name = "SumRequest")
public class SumRequest {

    @XmlElement(type = Integer.class)
    protected List<Integer> addend;

    public List<Integer> getAddend() {
        if (addend == null) {
            addend = new ArrayList<Integer>();
        }
        return this.addend;
    }
}

SumResponse.java

package org.madbit.myservice;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "sum"
})
@XmlRootElement(name = "SumResponse")
public class SumResponse {

    protected int sum;

    public int getSum() {
        return sum;
    }

    public void setSum(int value) {
        this.sum = value;
    }
}

4. Implement the Web Service endpoint

Now we it’s time to create the Web Service implementation, that is the class which contains the business logic of the service. Let’s call this class MyServiceImpl.java. It implements the MyService interface previously generated as following:

package org.madbit.soap;

import org.madbit.myservice.MyService;
import org.madbit.myservice.SumRequest;
import org.madbit.myservice.SumResponse;

public class MyServiceImpl implements MyService {

	@Override
	public SumResponse sum(SumRequest parameters) {
		int sum = 0;
		for(Integer i: parameters.getAddend()){
			sum += i;
		}
		SumResponse response = new SumResponse();
		response.setSum(sum);
		return response;
	}
}

5. Exposing a Web Service Using CXF

Exposing a stand-alone SOAP endpoint using the SimpleJaxWsServiceExporter or the support for JAX-WS in a Java EE container in conjunction with Spring is simple, but these solutions ignore the largest
cross-section of developers—people developing on Tomcat. Here again, we have plenty of options.
Tomcat doesn’t support JAX-WS by itself, so we needto help it by embedding a JAX-WS runtime. There are many choices, and you’re free to take your pick. Two popular choices are Axis2 and CXF, both of which are Apache projects. CXF represents the consolidation of the Celtix and XFire projects, which each had useful SOAP support. For our example, we’ll embed CXF since it’s robust, fairly well tested, and provides support for other important standards like JAX-RS, the API for REST-ful endpoints. Setup is fairly straightforward. You’ll need to include the CXFdependencies on your classpath, as well as the Spring dependencies.
Let’s walk through the moving pieces for configuration. A good place to start is the web.xml file. In our simple example, web.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<display-name>AppStore Gateway</display-name>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/myservice-servlet.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Servlet for SOAP -->
	<servlet>
		<servlet-name>cxf</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>cxf</servlet-name>
		<url-pattern>/soap/*</url-pattern>
	</servlet-mapping>
</web-app>

This web.xml file will look pretty much as all Spring MVC applications do. The only exception here is that we’ve also configured a CXFServlet, which handles a lot of the heavy lifting required to expose our service. In the Spring MVC configuration file, weather-servlet.xml, we’ll declare a bean for the weather service implementation and export it as a web service by using the Spring namespace support that CXF provides for configuring services (as well as clients, which we’ll soon see).
The Spring context file is underwhelming; most of it is boilerplate XML namespace and Spring context file imports. The only two salient stanzas are below, where we first configure the service itself as usual. Finally, we use the CXF jaxws:endpointnamespace to configure our endpoint.

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:simple="http://cxf.apache.org/simple"
	xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/simple http://cxf.apache.org/schemas/simple.xsd
http://cxf.apache.org/bindings/soap
http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
">
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
	<bean id="myServiceImpl" class="org.madbit.soap.MyServiceImpl" />
	<jaxws:endpoint implementor="#myServiceImpl"
		address="/MyService">
		<jaxws:binding>
			<soap:soapBinding style="document" use="literal" version="1.1" />
		</jaxws:binding>
	</jaxws:endpoint>
</beans>

We tell the jaxws:endpointfactory to use our Spring bean as the implementation. We tell it at what address to publish the service using the address element. Note that we’ve found that using a forward slash at the beginning of the address is the only way toget the endpoint to work reliably across both Jetty and Tomcat; your mileage may vary. We also specify some extra niceties like what binding style to use.
You don’t need to, of course. This is mainly for illustration. The Java code stays the same as before, with the javax.jws.WebServicemethod and javax.jws.WebMethodannotations in place. Launch the application and your web container, and then bring upthe application in your browser. In this example, the application is deployed at the root context (/), so the SOAP endpoint is available at http://localhost:8080/spring-soap/soap/MyService. If you bring upthe page at http://localhost:8080/, you’ll see a directory of the available services and their operations. Click the linkfor the service’s WSDL—or simply append ?wsdlto the service endpoint—to see the WSDL for the service. WSDL describes the messages and endpoint to clients. You can use it to infer a client to the service on most platforms.

Invoking a Web Service Using CXF

Let’s now use CXF to define a web service client. We’ll want one to work with our new weather service, after all! Our client is the same as in previous recipes, and there is no special Java configuration or coding to be done. We simply need the interface of the service on the classpath. Once that’s done, you can use CXF’s namespace support to create a client.

<?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:aop="http://www.springframework.org/schema/aop"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans?
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop ?
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
">
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
	<import resource="classpath:META-INF/cxf/cxf.xml"/>
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
	<jaxws:client serviceClass="org.madbit.soap.MyService" address="http://localhost:8080/spring-soap/soap/MyService" id="myService"/>
	<bean class="org.madbit.soap.MyServiceClient" id="client">
		<property name="myService" ref="myService"/>
	</bean>
</beans>

We use the jaxws:clientnamespace support to define to which interface the proxy should be bound, and the endpoint of the service itself. That is all that’s required. Our examples from previous
recipes works otherwise unchanged: here we inject the client into the MyServiceClient and invoke it.

Resources

Source code

git://github.com/madbit/spring-soap.git

2 Comments

  1. Matt

    You introduces this tutorial with: “In this example we’ll use a Contract-Last approach”, however, the code is generated from the wsdl which is a top-down, or contract-first approach.

    Cheers,
    Matt

    Reply
    1. madbit (Post author)

      Sorry for the mistake. I’ve corrected it.
      Thanks 😉

      Reply

Leave a Comment

Your email address will not be published. Required fields are marked *