There is a downloadable zipped Eclipse project at the end of this post.
An interesting almost new subproject in the Spring environment is Spring Web Services.
In short: in finest Spring manner, DI and small little helper form a contract-first Web Service framework. The pros and cons about contract-first vs. contract-last Web Services is explained in the tutorial. From personal experience, contract-first requires more initial effort, but pays a lot afterwards. In our application, the aspect of versioning was underestimated first. By using contract-first, it was really easy to introduce detailed versioning (on basis of namespaces with the PayloadRootAnnotationMethodEndpointMapping)
Depending on the data structure which should be transferred, it may be better to choose the XPathParamAnnotationMethodEndpointAdapter
(for little payloads) over the GenericMarshallingMethodEndpointAdapter (for huge, object oriented payloads).
Excursion
When marshalling should be used, the Java classes for the XML Schema must be generated. There exist several framework to ease this process. In this sample, JAXB2 is used. There are several helper for using JAXB and automize the process of class generation.
- Create a quickstart Maven project (using the Eclipse-Maven plugin, compare this post).
- Overwrite the generated POM with the following
<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>javabinding</groupId>
<artifactId>javabinding</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>javabinding</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>java.net</id>
<name>java.net Maven Repository</name>
<url>https://maven-repository.dev.java.net/nonav/repository</url>
<layout>legacy</layout>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>com.sun.tools.xjc.maven2</groupId>
<artifactId>maven-jaxb-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
This POM includes several configuration and dependency information which allows for using the JAXB-Maven2-Plugin
- You can put your XSDs into the subdirectory
/src/main/resources
.
- Below is a sample XML Schema which defines a Request and Response object (this convention is necessary for automatic WSDL generation).
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/WSSchema"
xmlns:tns="http://www.example.org/WSSchema" elementFormDefault="qualified">
<element name="SampleRequest">
<complexType>
<sequence>
<element name="StringArgument" type="string"></element>
</sequence>
</complexType>
</element>
<element name="SampleResponse">
<complexType>
<sequence>
<element name="StringArgument" type="string"></element>
</sequence>
</complexType>
</element>
</schema>
- Run Maven with goals
clean,install
.
Afterwards (and a refresh (F5) on the project), there should be a generated-resources
directory in subdirectory target
. The compiled classes reside in classes
.
- Add
target/generated-sources/xjc
to the project’s buildpath.
- You can now include the generated, compiled classes in other projects with this dependency in the POM.
<dependency>
<groupId>javabinding</groupId>
<artifactId>javabinding</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Setup
- Following the tutorial, create the Maven project first
mvn archetype:create -DarchetypeGroupId=org.springframework.ws -DarchetypeArtifactId=spring-ws-archetype -DarchetypeVersion=1.5.2 -DgroupId=de.wssample -DartifactId=wssample
- Afterwards, create a eclipse project from the maven resources.
cd wssample
mvn eclipse:eclipse
- The generated eclipse project can now be imported as a Eclipse project into eclipse.
- Remove the M2_REPO entries from the project’s build path.
- Use the follwing POM which includes the javabinding we generated above and the jetty-plugin for fast deployments.
<?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>de.wssample</groupId>
<artifactId>wssample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>wssample Spring-WS Application</name>
<url>http://www.springframework.org/spring-ws</url>
<build>
<finalName>wssample</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<inherited>true</inherited>
<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.4</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>10</scanIntervalSeconds>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm-tiger</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core-tiger</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>javabinding</groupId>
<artifactId>javabinding</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- Put your XSD in the WEB-INF directory (in the sample above, the WSSample.xsd). This is necessary for the automatic WSDL generation.
The project structure should look like this:
The 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">
<!-- Register PayloadRootAnnotationMethodEndpointMapping -->
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" />
<!-- Register Endpoint -->
<bean id="sampleEndpoint" class="de.wssample.SampleEndpoint"></bean>
<!-- Configure XML Marshaller -->
<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>org.example.wsschema.SampleRequest</value>
<value>org.example.wsschema.SampleResponse</value>
</list>
</property>
</bean>
<!-- Add automatic WSDL generation support -->
<bean id="sample" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
<property name="schema" ref="schema" />
<property name="portTypeName" value="SampleWS" />
<property name="locationUri" value="http://localhost:8080/samplews/" />
<property name="targetNamespace" value="http://samplews" />
</bean>
<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="/WEB-INF/WSSchema.xsd" />
</bean>
</beans>
The web.xml
was generated automatically, the Endpoint is as follows
package de.wssample;
import org.example.wsschema.SampleRequest;
import org.example.wsschema.SampleResponse;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
@Endpoint
public class SampleEndpoint {
@PayloadRoot(localPart = "SampleRequest", namespace = "http://www.example.org/WSSchema")
public SampleResponse doit(SampleRequest request) {
SampleResponse response = new SampleResponse();
response.setStringArgument("response");
return response;
}
}
- Run mvn with goals
clean,compile,jetty:run
.
- Point the browser to the generated WSDL.
- It is important to note that with this setup, the root context can be used and the WSDL is generated if the org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition bean id + .wsdl is called (“sample” in this case).
You can now test the Web Service with the great tool soapUI, which generates test request data for you, let’s you modify the data and allows for creating and executing test cases.
Sampleproject: A mail web service
In the project a web services around the velocitymailsample is created which results in a mail web service.
Instructions:
- Download the velocitymail-sample
- Download the mail-databinding-sample
- Download the mail-service-sample
server
in /velocitymail/src/main/resources/applicationContext.xml
has to be set and access to a (secured) SMTP mail server is necessary to run the sample.
If correctly installed into the same eclipse workspace, the dependencies should be resolved automatically. However, to deploy the application as a WAR
, you will need to mvn clean install
sampledatabinding, velocitymail and mailservice in this order.
To run the web service with the jetty-maven-plugin, you can run the mailservice project with goals mvn clean install jetty:run
.