Archiv für den Monat: November 2010

Creating a web service with Spring WS using JAXB2 marshalling

In the following the steps are shown how to create a simple web service with the Spring Web Service Framework using a JAXB2 marshalling. The example web service produces a list of random numbers. The amount of random numbers generated is specified in the request.

Prerequisites:

  • Eclipse Helios (3.6)
  • Installed m2eclipse plugin – this is not absolutely necessary, if you don’t want to use it, you can perform the Maven steps on the command line

First, we create a Maven project using the Spring WS archetype.

86a6a511406bc0f59463 221db397f3 c9807cc54e

Next, we change the dependency versions in the generated POM file to the current stable version (1.5.9) of Spring WS. Additionally we’re using Java version 1.5 and add the Jetty plugin for testing the service later.

<?xml version="1.0" encoding="UTF-8"?>
<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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dkaedv.testws</groupId>
  <artifactId>randomnumbers</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>randomnumbers Spring-WS Application</name>
  <url>http://www.springframework.org/spring-ws</url>
  <build>
    <finalName>randomnumbers</finalName>
    <plugins>
      <plugin>
        <inherited>true</inherited>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <version>6.1.10</version>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.springframework.ws</groupId>
      <artifactId>spring-oxm-tiger</artifactId>
      <version>1.5.9</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.ws</groupId>
      <artifactId>spring-ws-core-tiger</artifactId>
      <version>1.5.9</version>
    </dependency>
  </dependencies>
</project>

To let Eclipse know about the changes in the POM, we update the project configuration by right-clicking on the project -> Maven -> Update Project Configuration.

Next, we need to create an XML Schema for our service. Therefore we create a folder „xsd“ within src/main/webapp/WEB-INF and create the following „randomnumbers.xsd“ in there. The data structure itself is very simple. It contains two elements, the RandomNumbersRequest with the amount parameter and the RandomNumbersResponse containing a list of numbers.

Note, that the element names conform to the Spring naming conventions, i.e. the suffixes „Request“ and „Response“.

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

 <element name="RandomNumbersRequest">
    <complexType>
      <sequence>
        <element name="amount" type="int" />
      </sequence>
    </complexType>
  </element>

  <element name="RandomNumbersResponse">
    <complexType>
      <sequence>
        <element name="number" type="int" minOccurs="1" maxOccurs="unbounded"/>
      </sequence>
    </complexType>
  </element>
</schema>

From this XSD the interface classes for the request and response can be generated. These classes will be used by the JAXB2 marshaller to convert the web service requests and responses from/to XML. The code generation is configured by adding the appropriate build plugin in pom.xml:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>jaxb2-maven-plugin</artifactId>
  <version>1.3</version>
  <executions>
    <execution>
      <goals>
        <goal>xjc</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <packageName>com.dkaedv.testws.randomnumbers</packageName>
    <schemaDirectory>src/main/webapp/WEB-INF/xsd/</schemaDirectory>
    <schemaFiles>randomnumbers.xsd</schemaFiles>
    <outputDirectory>${basedir}/src/generated/java</outputDirectory>
  </configuration>
</plugin>

To actually generate code, run the generate-source target. This can be done in Eclipse by running the project as Maven build or on the command line:

mvn generate-sources

Afterwards, create the folder src/main/java to be able to implement the actual web service endpoint. Afterwards, refresh the project in Eclipse and update the Maven project configuration as before (Right-click the Project -> Maven -> Update Project Configuration).

Next, create the class RandomNumbersEndpoint as a subclass of AbstractMarshallingPayloadEndpoint that contains the implementation of the service:

973be1ffb8

package com.dkaedv.testws.randomnumbers;

import org.springframework.oxm.Marshaller;
import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint;

import com.dkaedv.testws.randomnumbers.RandomNumbersRequest;
import com.dkaedv.testws.randomnumbers.RandomNumbersResponse;

public class RandomNumbersEndpoint extends AbstractMarshallingPayloadEndpoint {

  public RandomNumbersEndpoint(Marshaller marshaller) {
    super(marshaller);
  }

  @Override
  protected Object invokeInternal(Object request) throws Exception {
    RandomNumbersRequest rnReq = (RandomNumbersRequest) request;
    RandomNumbersResponse response = new RandomNumbersResponse();

    int n = rnReq.getAmount();

    for (int i = 0; i < n; i++) {
      int randomNumber = (int) (Math.random() * 1000);
      response.getNumber().add(randomNumber);
    }

    return response;
  }
}

As the final step, we need to add the appropriate configuration to 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"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

  <!-- Publish the WSDL
         at http://localhost:8080/randomnumbers/randomNumbersService/randomNumbers.wsdl -->
  <bean id="randomNumbers" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
    <property name="schema" ref="schema" />
    <property name="portTypeName" value="RandomNumbersPT" />
    <property name="locationUri" value="http://localhost:8080/randomnumbers/randomNumbersService/" />
  </bean>

  <bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
    <property name="xsd" value="/WEB-INF/xsd/randomnumbers.xsd" />
  </bean>

  <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="classesToBeBound">
      <list>
        <value>com.dkaedv.testws.randomnumbers.RandomNumbersRequest</value>
        <value>com.dkaedv.testws.randomnumbers.RandomNumbersResponse</value>
      </list>
    </property>
    <property name="schema" value="/WEB-INF/xsd/RandomNumbers.xsd"/>
  </bean>

  <!-- Endpoint Definiton -->
  <bean id="randomNumbersEndpoint" class="com.dkaedv.testws.randomnumbers.RandomNumbersEndpoint">
    <constructor-arg ref="marshaller" />
  </bean>

  <!-- Routing -->
  <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
    <property name="mappings">
      <props>
        <prop key="{http://www.dka-edv.com/RandomNumbers}RandomNumbersRequest">randomNumbersEndpoint</prop>
      </props>
    </property>
    <property name="interceptors">
      <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />
    </property>
  </bean>
</beans>

That’s it. You can run the project using the Maven target „jetty:run“, e.g. on the command line:

mvn jetty:run

The WSDL should now be accessible at the address http://localhost:8080/randomnumbers/randomNumbersService/randomNumbers.wsdl. Note, that the WSDL was generated by Spring from the XML Schema created earlier – a manual creation is not required.

Of course you can now access and test the web service by using soapUI for example.