Notes on the Spring-WS tutorial

Today I have reviewed a proof of concept I have done in order to decide which Web Services framework matches the requirements of a bioinformatics project. Because of forgetting to store a file in the versioning system, I had to review the whole tutorial and to look for the file within the Spring tutorial. What is interesting, until now nobody published the full content of the Spring-WS tutorial.

So, here is my pom.xml. I am using Apache Maven2 in order to strictly enforce the versions of the JAR files. Nobody is constrained to use Maven, but without it, it would be very difficult to sort out the transient dependencies.

<?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.razvan.hr</groupId>
 <artifactId>holidayService</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>holidayService Spring-WS Application</name>
 <url>http://www.springframework.org/spring-ws</url>
 <build>
 <finalName>holidayService</finalName>
 <plugins>
 <plugin>
 <groupId>org.mortbay.jetty</groupId>
 <version>6.1.9</version>
 <artifactId>maven-jetty-plugin</artifactId>
 </plugin>
 </plugins>
 </build>
 <dependencies>
 <dependency>
 <groupId>org.springframework.ws</groupId>
 <artifactId>spring-ws-core</artifactId>
 <version>1.5.8</version>
 </dependency>
 <dependency>
 <groupId>jdom</groupId>
 <artifactId>jdom</artifactId>
 <version>1.0</version>
 </dependency>
 <dependency> <!-- required by AbstractJDomPayloadEndpoint -->
 <groupId>jaxen</groupId>
 <artifactId>jaxen</artifactId>
 <version>1.1</version>
 <scope>runtime</scope>
 </dependency>
 <dependency>
 <groupId>javax.xml.soap</groupId>
 <artifactId>saaj-api</artifactId>
 <version>1.3</version>
 <scope>runtime</scope>
 </dependency>
 <dependency>
 <groupId>com.sun.xml.messaging.saaj</groupId>
 <artifactId>saaj-impl</artifactId>
 <version>1.3</version>
 <scope>runtime</scope>
 </dependency>
 <dependency>
 <groupId>xalan</groupId>
 <artifactId>xalan</artifactId>
 <version>2.7.1</version>
 </dependency>
 </dependencies>
</project>

I am using the jetty plugin in order to be able to run the application right from the command line; xalan dependency is required in order to avoid the exception “java.lang.IllegalStateException: Could not find SAAJ on the classpath” thrown by Spring-WS, corrected in a newer version of xalan.

Also according to the tutorial, the file spring-ws-servlet.xml, a regular Spring context file, describes the external interface of the Web Service and the associated Java classes. This file has to be placed in the src/main/webapp/WEB-INF directory.

<?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">

 <bean id="holidayEndpoint" class="com.razvan.hr.ws.HolidayEndpoint">
 </bean>

 <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
 <property name="mappings">
 <props>
 <prop key="{http://mycompany.com/hr/schemas}HolidayRequest">holidayEndpoint</prop>
 </props>
 </property>
 <property name="interceptors">
 <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />
 </property>
 </bean>

 <bean id="holiday" 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/hr.xsd" />
 <property name="portTypeName" value="HumanResource" />
 <property name="locationUri" value="http://localhost:8080/holidayService/" />
 </bean>
 </property>
 </bean>

</beans>

And here is the implementation of com.razvan.hr.ws.HolidayEndpoint. According to Maven customaries, this have to be placed in the src/main/java subdirectory of the project. I have tried to keep the implementation minimal by avoiding further service instantiations, unrelated to Spring-WS:

package com.razvan.hr.ws;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;
import org.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint;

public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {

 private XPath startDateExpression;

 private XPath endDateExpression;

 private XPath nameExpression;

 public HolidayEndpoint() throws JDOMException {
 Namespace namespace = Namespace.getNamespace("hr", "http://mycompany.com/hr/schemas");
 startDateExpression = XPath.newInstance("//hr:StartDate");
 startDateExpression.addNamespace(namespace);
 endDateExpression = XPath.newInstance("//hr:EndDate");
 endDateExpression.addNamespace(namespace);
 nameExpression = XPath.newInstance("concat(//hr:FirstName,' ',//hr:LastName)");
 nameExpression.addNamespace(namespace);
 }

 protected Element invokeInternal(Element holidayRequest) throws Exception {
 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
 Date startDate = dateFormat.parse(startDateExpression.valueOf(holidayRequest));
 Date endDate = dateFormat.parse(endDateExpression.valueOf(holidayRequest));
 String name = nameExpression.valueOf(holidayRequest);

 // do something meaningful with the extracted data.
 System.out.println("Start date: "+startDate+" end Date: "+ endDate+" Employee: "+name);
 return null;
 }
}

My final file structure is:

|-- pom.xml
`-- src
 `-- main
   |-- java
   |   `-- com
   |       `-- razvan
   |           `-- hr
   |               `-- ws
   |                   `-- HolidayEndpoint.java
   `-- webapp
     `-- WEB-INF
        |-- hr.xsd
        |-- spring-ws-servlet.xml
        `-- web.xml

The web.xml is pretty standard for Spring applications:

<?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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
 version="2.4">

 <display-name>Spring test 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>

This is the whole Web Service application. In order to run the application try from the command line:

mvn clean install jetty:run

This will compile the war file and start an instance of the jetty web server.

By accessing the address: http://localhost:8080/holidayService/holiday.wsdl you can see the generated WSDL associated to your described web service. While leaving the jetty running, you may attempt to create a client, in order to see the message printed within the invokeInternal method. Following it will be described a Java client, but it is possible to build your client using any other language or platform.

Using the Eclipse wizard create a new empty Java project, than create using the wizard a new “Web Service Client”. Provide the address of the wsdl and you will have a bunch of Java classes describing your web service and the required XML parsers dependencies in your newly created project. Create a new main class in the default package named TestWs.java, with the following content:

import java.math.BigInteger;
import java.util.Date;

import com.mycompany.hr.schemas.EmployeeType;
import com.mycompany.hr.schemas.HolidayRequest;
import com.mycompany.hr.schemas.HolidayType;
import com.mycompany.hr.schemas.HumanResource;
import com.mycompany.hr.schemas.HumanResourceServiceLocator;

public class TestWs {
 public static void main(String[] args) {
 try {
 HumanResourceServiceLocator loc = new HumanResourceServiceLocator();
 HumanResource myserv = loc.getHumanResourcePort();

 HolidayType ht = new HolidayType(new Date(), new Date());
 EmployeeType et = new EmployeeType(new BigInteger("100"), "Razvan",
 "Popovici");
 HolidayRequest req = new HolidayRequest(ht, et);

 myserv.holiday(req);
 } catch (Exception e) {
 e.printStackTrace();
 }
 System.out.println("OK!");

 }
}

Run the class, and you will be able to observe the message in the maven window.

A few conclusions of the test are:

– Spring-WS is a great approach if the schema definitions of the data model already exists.

– The required xalan dependency is a bug in Spring-WS dependency requirements, fortunately Maven made possible the correction.

– The element wsdl:definitions is optional, the tutorial worked without it as well.

Advertisements

Tags: , , , , , , , , , ,

4 Responses to “Notes on the Spring-WS tutorial”

  1. Wayan Parmana Says:

    thanks for the great script

  2. jack Says:

    nice for your tutorial, thanks

  3. camestres.com » Links for 15/2/2010 Says:

    […] Notes on the Spring-WS tutorial « Razvan’s Weblog – […]

  4. jude Says:

    Here’s the location of the Spring-WS full content which you use it in learning..

    http://s3.amazonaws.com/dist.springframework.org/release/SWS/spring-ws-2.0.1.RELEASE-full.zip

    and all the codes corcerning can be found here as well

    http://www.springsource.com/download/community

    hope this helps 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: