Contract-first web services with JBoss 5, Metro & Spring

In this article I want to show how to build a simple application deployed on JBoss 5 using Metro web services stack and Spring. The main purpose of this article is to show the configuration that needs to be done in order to create project structure, define dependencies and prepare deployable artifacts. That’s why you will find here much more XML than Java code (unfortunately). I chose to package the whole application as an EAR archive that in turn contains web application with our web services. Of course a WAR would be perfectly enough to show web services, but I want this project to be a base for future enhancement.

The complete source code from this example is available here.

What you need?

Step 1- Create Maven project

First of all let’s create the main project using Maven

mvn archetype:create -DgroupId=com.krzysztofadamczyk.playground.springmetrodemo -DartifactId=spring-metro-demo

Next we’ll changing packaging to “pom” in the main pom file

<packaging>pom</packaging>

We also have to add dependencyManagement section, where we put our dependencies settings:

For Spring:

		<dependency>
		   <groupId>org.springframework</groupId>
		   <artifactId>spring</artifactId>
		   <version>2.5.6.SEC01</version>
		</dependency>

Metro:

		<dependency>
			<groupId>com.sun.xml.ws</groupId>
			<artifactId>webservices-rt</artifactId>
			<version>1.5</version>
		</dependency>
		<dependency>
			<groupId>com.sun.tools.ws</groupId>
			<artifactId>webservices-tools</artifactId>
			<version>1.5</version>
		</dependency>
		<dependency>
			<groupId>javax.xml</groupId>
			<artifactId>webservices-api</artifactId>
			<version>1.5</version>
		</dependency>
		<dependency>
			<groupId>javax.activation</groupId>
			<artifactId>activation</artifactId>
			<version>1.1</version>
			<scope>provided</scope>
		</dependency>

For JAXWS-Spring integration we will exclude transitive dependencies to JAX-WS, since we already have JAX-WS RI in Metro:

		<dependency>
			<groupId>org.jvnet.jax-ws-commons.spring
			</groupId>
			<artifactId>jaxws-spring</artifactId>
			<scope>runtime</scope>
			<version>1.8</version>
			<exclusions>
				<exclusion>
					<groupId>com.sun.xml.ws</groupId>
					<artifactId>jaxws-rt</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.xml.ws</groupId>
					<artifactId>jaxws-local-transport</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.servlet</groupId>
					<artifactId>servlet-api</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
				</exclusion>
				<exclusion>
					<groupId>junit</groupId>
					<artifactId>junit</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jws</groupId>
					<artifactId>jsr181-api</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.xml.bind</groupId>
					<artifactId>jaxb-impl</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.xml.soap</groupId>
					<artifactId>saaj-api</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.xml.messaging.saaj</groupId>
					<artifactId>saaj-impl</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.xml.stream.buffer</groupId>
					<artifactId>streambuffer</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.xml.stream</groupId>
					<artifactId>sjsxp</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.org.apache.xml.internal</groupId>
					<artifactId>resolver</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.jvnet.staxex</groupId>
					<artifactId>stax-ex</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.activation</groupId>
					<artifactId>activation</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

We must also declare repository for JAXWS-Spring integration:

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Maven 2 Repository</name>
            <url>http://download.java.net/maven/2</url>
        </repository>
    </repositories>

Step 2 – Create web application project

mvn archetype:create -DartifactId=services -DarchetypeArtifactId=maven-archetype-webapp

Add dependencies (dependencies are marked optional, because I don’t want them to go into WAR file – I’ll keep them on EAR level for future use by other modules):

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring</artifactId>
			<optional>true</optional>
		</dependency>
		<!-- JAX-WS-Spring integration -->
		<dependency>
			<groupId>org.jvnet.jax-ws-commons.spring</groupId>
			<artifactId>jaxws-spring</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.sun.xml.ws</groupId>
			<artifactId>webservices-rt</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>javax.xml</groupId>
			<artifactId>webservices-api</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.sun.tools.ws</groupId>
			<artifactId>webservices-tools</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>javax.activation</groupId>
			<artifactId>activation</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

We will also define ant task that will import our WSDL and create corresponding Java classes:

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-antrun-plugin</artifactId>
				<executions>
					<execution>
						<id>generate-bindings</id>
						<phase>generate-sources</phase>
						<configuration>
							<tasks>
								<mkdir dir="${basedir}/target/generated-sources"></mkdir>
								<mkdir dir="${basedir}/target/throwaway-classes"></mkdir>

								<taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
									<classpath refid="maven.compile.classpath" />
									<classpath refid="maven.plugin.classpath" />
								</taskdef>

								<wsimport
									wsdl="${basedir}/src/main/webapp/WEB-INF/wsdl/EchoService.wsdl"
									sourcedestdir="${basedir}/target/generated-sources"
									destdir="${basedir}/target/throwaway-classes" fork="true"
									extension="true"></wsimport>

							</tasks>
							<sourceRoot>${basedir}/target/generated-sources</sourceRoot>
						</configuration>
						<goals>
							<goal>run</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

We don’t want to mix “hand-written” and generated classes, and therefore we will use separate source directories to keep them. As there can be only one sourceDirectory defined in build section, we have to use the following plug-in to define second source directory:

	<build>
		<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
		<plugins>
             <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                  <execution>
                    <id>add-source</id>
                    <phase>generate-sources</phase>
                    <goals>
                      <goal>add-source</goal>
                    </goals>
                    <configuration>
                      <sources>
                          <source>${basedir}/target/generated-sources</source>
                      </sources>
                    </configuration>
                  </execution>
                </executions>
            </plugin>

We have to configure our web application in web.xml. Things to notice here:

  • org.springframework.web.context.ContextLoaderListener
    Which initializes Spring application context
  • com.sun.xml.ws.transport.http.servlet.WSSpringServlet
    which handles requests and integrates JAX-WS with Spring
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
	<display-name>Spring + Metro on JBoss demo</display-name>

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

	<servlet>
		<servlet-name>jaxws-servlet</servlet-name>
		<servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>jaxws-servlet</servlet-name>
		<url-pattern>/echoService</url-pattern>
	</servlet-mapping>

	<session-config>
		<session-timeout>
			30
        </session-timeout>
	</session-config>
	<welcome-file-list>
		<welcome-file>redirect.jsp</welcome-file>
	</welcome-file-list>
</web-app>

Step 3 – Create EAR project

mvn archetype:create -DartifactId=ear

Ear projects dependencies will be put into EAR file. The main parts of the pom file are shown below:

....
  <packaging>ear</packaging>
  <dependencies>
	<dependency>
		  <groupId>com.krzysztofadamczyk.playground.springmetrodemo</groupId>
		  <artifactId>services</artifactId>
		  <type>war</type>
	</dependency>

	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring</artifactId>
	</dependency>
    <dependency>
        <groupId>org.jvnet.jax-ws-commons.spring</groupId>
        <artifactId>jaxws-spring</artifactId>
	</dependency>
	<dependency>
		<groupId>com.sun.xml.ws</groupId>
		<artifactId>webservices-rt</artifactId>
	</dependency>
	<dependency>
		<groupId>com.sun.tools.ws</groupId>
		<artifactId>webservices-tools</artifactId>
	</dependency>
	<dependency>
		<groupId>javax.activation</groupId>
		<artifactId>activation</artifactId>
	</dependency>

  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-ear-plugin</artifactId>
        <configuration>
          <modules>
            <webModule>
              <groupId>com.krzysztofadamczyk.playground.springmetrodemo</groupId>
              <artifactId>services</artifactId>
              <contextRoot>/spring-metro-webservices</contextRoot>
            </webModule>
          </modules>
        </configuration>
      </plugin>
    </plugins>
  </build>

Step 4 – prepare contracts

Now as our project structure is more or less done we can focus on preparing contracts for the web service. In this example we will create a simple echo service which simply returns the given string with “echo” prefix. Types are defined into EchoTypes.xsd file:

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

	<xs:element name="EchoRequest">
		<xs:complexType>
			<xs:all>
				<xs:element name="text" type="xs:string" />
			</xs:all>
		</xs:complexType>
	</xs:element>

	<xs:element name="EchoResponse">
		<xs:complexType>
			<xs:all>
				<xs:element name="text" type="xs:string" />
			</xs:all>
		</xs:complexType>
	</xs:element>
</schema>

The WSDL file is presented below:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://www.krzysztofadamczyk.com/echo/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:echo="http://www.krzysztofadamczyk.com/echo/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	name="echo" targetNamespace="http://www.krzysztofadamczyk.com/echo/">
	<wsdl:types>
		<xsd:schema>
			<xsd:import namespace="http://www.krzysztofadamczyk.com/echo/"
				schemaLocation="EchoTypes.xsd" />
		</xsd:schema>
	</wsdl:types>

	<wsdl:message name="EchoRequest">
		<wsdl:part name="EchoRequest" element="echo:EchoRequest" />
	</wsdl:message>
	<wsdl:message name="EchoResponse">
		<wsdl:part name="EchoResponse" element="echo:EchoResponse"  />
	</wsdl:message>

	<wsdl:portType name="EchoPort">
		<wsdl:operation name="sayEcho">
			<wsdl:input message="tns:EchoRequest" />
			<wsdl:output message="tns:EchoResponse" />
		</wsdl:operation>
	</wsdl:portType>

	<wsdl:binding name="echoSOAP" type="tns:EchoPort">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="sayEcho">
			<soap:operation soapAction="http://www.krzysztofadamczyk.com/echo/SayEcho" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="EchoService">
		<wsdl:port binding="tns:echoSOAP" name="echoSOAP">
			<soap:address location="http://www.example.org/" />
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>

Step 5 – create service endpoint implementation

After building the project with mvn install you can see a few Java classes generated in target\generated-sources\. EchoPort is our generated Service Endpoint Interface. We will now create the service implementation as follows:

package com.krzysztofadamczyk.playground.springmetrodemo;

import javax.jws.WebService;

import com.krzysztofadamczyk.echo.EchoPort;
import com.krzysztofadamczyk.echo.EchoRequest;
import com.krzysztofadamczyk.echo.EchoResponse;

@WebService(name = "EchoPort", targetNamespace = "http://www.krzysztofadamczyk.com/echo/",
endpointInterface = "com.krzysztofadamczyk.echo.EchoPort")
public class EchoServiceImpl implements EchoPort {

    public EchoResponse sayEcho(final EchoRequest echoRequest) {
        final EchoResponse result = new EchoResponse();
        result.setText("echo: " + echoRequest.getText());
        return result;
    }
}

Step 6 – define application context

Now it’s time to define application context. Here we take advantage of JAXWS-Spring integration using specialized schema to define our web service. Note that our endpoint implementation is a standard Spring bean and we can use here dependency injection or any other Spring technique.

<?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:ws="http://jax-ws.dev.java.net/spring/core"
	xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

	http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd

http://jax-ws.dev.java.net/spring/servlet

        http://jax-ws.dev.java.net/spring/servlet.xsd">

	<wss:binding url="/echoService">
		<wss:service>
			<ws:service bean="#echoService" />
		</wss:service>
	</wss:binding>

	<!-- this bean implements web service methods -->
	<bean id="echoService" class="com.krzysztofadamczyk.playground.springmetrodemo.EchoServiceImpl" />
</beans>

Step 7 – deploy and test project

After successfull build, copy EAR file (which can be found in ear/target) into jboss_home/server/default/deploy, and start server. Run SoapUI and create new project with the following WSDL location:

http://localhost:8080/spring-metro-webservices/echoService?wsdl

Where:

  • spring-metro-webservices – is the web application context root defined in EAR project pom
  • echoService – is an URL pattern (defined in web.xml, associated with WSSpringServlet), and also defined as binding URL in applicationContext.xml

SoapUI will generate a sample request for you. The result looks as follows:

From Blog
Advertisement

2 Responses to Contract-first web services with JBoss 5, Metro & Spring

  1. imiro says:

    Hi

    Nice tutorial!

    Since you have the metro-stack in order, can you easily enable ws-reliablemessaging policy on your webservice with this project setup?

  2. Jaleelpp says:

    Hi..
    Very nice Tutorials

    but i can’t download the source code…

Leave a Reply

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

Gravatar
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 )

Connecting to %s

Follow

Get every new post delivered to your Inbox.