Developing a contract-first JAX-WS webservice

In this blog i’ll develop a simple webservice using JAX-WS. I’ll first start with the contract (wsdl and xsd’s). The contract will be used for generating the necessary JAXB artifacts. Getting the webservice up and running will be a piece of cake after that all thanks to Maven and JAX-WS.
First things first, let’s create a new Maven project. I’m using Netbeans as IDE for this.

New Maven Web Application

First Create a new Maven Web Application

and call it hello_person for example

The Contract

The webservice will accept a Person graph with a first and last name and will return a concatenated “Hello first name last name!”. Not very original but good enough for the example.
First we’ll define the xsd called helloPersonService.xsd for the request and response. The request will contain a Person object with a first and last name, the response will contain a Greetings string. We’ll store it in src/main/resources/xsd.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified"
            xmlns="http://example.nl/hellopersonservice/1.0"
            targetNamespace="http://example.nl/hellopersonservice/1.0">
    <xsd:element name="HelloPersonServiceRequest" type="HelloPersonServiceRequestType"/>
    <xsd:element name="HelloPersonServiceResponse" type="HelloPersonServiceResponseType"/>

    <xsd:complexType name="HelloPersonServiceRequestType">
        <xsd:element name="Person" type="PersonType"/>
    </xsd:complexType>
    
    <xsd:complexType name="HelloPersonServiceResponseType">
        <xsd:element name="Greetings" type="xsd:string"/>
    </xsd:complexType>

    <xsd:complexType name="PersonType">
        <xsd:sequence>
            <xsd:element name="FirstName" type="xsd:string"/>
            <xsd:element name="LastName" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

Now we’ll define a wsdl called helloPersonService.wsdl and we’ll store it in src/main/resources/wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
    name="helloPersonService"
    targetNamespace="http://example.nl/hellopersonservice/1.0"
    xmlns:tns="http://example.nl/hellopersonservice/1.0"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    >
    <wsdl:types>
        <xsd:schema targetNamespace="http://example.nl/hellopersonservice/1.0">
            <xsd:import schemaLocation="../xsd/helloPersonService.xsd"
                        namespace="http://example.nl/hellopersonservice/1.0"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="HelloPersonServiceRequest">
        <wsdl:part name="HelloPersonServiceRequest" element="tns:HelloPersonServiceRequest"/>
    </wsdl:message>
    <wsdl:message name="HelloPersonServiceResponse">
        <wsdl:part name="HelloPersonServiceResponse" element="tns:HelloPersonServiceResponse"/>
    </wsdl:message>
    <wsdl:portType name="HelloPersonServicePortType">
        <wsdl:operation name="greetPerson">
            <wsdl:input name="HelloPersonServiceRequest" message="tns:HelloPersonServiceRequest"/>
            <wsdl:output name="HelloPersonServiceResponse" message="tns:HelloPersonServiceResponse"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="HelloPersonServiceBinding" type="tns:HelloPersonServicePortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="greetPerson">
            <soap:operation style="document" soapAction="http://example.nl/HelloPersonService/greetPerson"/>
            <wsdl:input name="HelloPersonServiceRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="HelloPersonServiceResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="HelloPersonService">
        <wsdl:port name="HelloPersonServicePort" binding="tns:HelloPersonServiceBinding">
            <soap:address location="/service/helloPersonService" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Getting the pom right

Next up is adding the necessary component to the pom file. We need the jax-ws runtime libraries, the jetty plugin as we use jetty as the servlet container, and the jaxws-maven-plugin for generating the java code from the contract. I’ve highlighted these dependencies in the pom file below.

<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>nl.example</groupId>
    <artifactId>hello_person</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>hello_person Java EE 6 Webapp</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <id>java.net2</id>
            <name>Repository hosting the jee6 artifacts</name>
            <url>http://download.java.net/maven/2</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>java.net2</id>
            <name>Repository hosting the jee6 artifacts</name>
            <url>http://download.java.net/maven/2</url>
        </pluginRepository>
    </pluginRepositories>
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
            <version>2.2</version>
        </dependency>

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

    <build>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1-beta-1</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <configuration>
                    <webAppConfig>
                        <contextPath>hello_person</contextPath>
                    </webAppConfig>
                    <connectors>
                        <connector implementation="org.eclipse.jetty.nio.SelectChannelConnector">
                            <port>8083</port>
                        </connector>
                    </connectors>
                </configuration>
            </plugin>

            <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/src/main/java</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>1.12</version>
                <configuration>
                    <wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory>
                    <packageName>nl.example.hello_person.service.generated</packageName>
                    <keep>true</keep>
                    <sourceDestDir>${basedir}/target/generated/src/main/java</sourceDestDir>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <finalName>hello_person</finalName>
    </build>
</project>

Writing the implementation

First, let’s do a Clean and Build in NetBeans. This will generate the JAXB/JAX-WS artifacts from the contract.
Notice that the jaxws-maven-plugin has generated an interface called nl.example.hello_person.service.generated.HelloPersonServicePortType for the PortType in the wsdl. This is the interface we have to implement. The code for the implementation is pretty straightforward. I’ve added it below for convenience. Notice that the implementation class is called nl.example.hello_person.service.HelloPersonServiceImpl. We’ll need this in one of the next steps. Also notice the necessary @WebService annotation. The endpointInterface atribute points of course to the generated interface.

package nl.example.hello_person.service;

import javax.jws.WebService;
import nl.example.hello_person.service.generated.HelloPersonServiceRequestType;
import nl.example.hello_person.service.generated.HelloPersonServiceResponseType;

@WebService(endpointInterface="nl.example.hello_person.service.generated.HelloPersonServicePortType")
public class HelloPersonServiceImpl implements nl.example.hello_person.service.generated.HelloPersonServicePortType {

    @Override
    public HelloPersonServiceResponseType greetPerson(HelloPersonServiceRequestType helloPersonServiceRequest) {
        HelloPersonServiceResponseType helloPersonServiceResponse = new HelloPersonServiceResponseType();
        helloPersonServiceResponse.setGreetings("Hello " + helloPersonServiceRequest.getPerson().getFirstName() + " " + helloPersonServiceRequest.getPerson().getLastName() + "!");
        return helloPersonServiceResponse;
    }

}

The web.xml file

Now, add a web.xml file to the project (you can do this via New File > Web > Standard Deployment Descriptor (web.xml)) and add the proper configuration for the jax-ws servlet to it:

<?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_3_0.xsd"
         version="3.0">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <listener>
        <listener-class>
            com.sun.xml.ws.transport.http.servlet.WSServletContextListener
        </listener-class>
    </listener>
    <servlet>
        <description>JAX-WS endpoint</description>
        <display-name>The JAX-WS servlet</display-name>
        <servlet-name>jaxws</servlet-name>
        <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>jaxws</servlet-name>
        <url-pattern>/helloPersonService</url-pattern>
    </servlet-mapping>
</web-app>

The sun-jaxws.xml file

The last step is adding the sun-jaxws.xml file to the WEB-INF directory of the project. This is the content of the file:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints
    xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime'
    version='2.0'>
    <endpoint
        name='helloPersonService'
        implementation='nl.example.hello_person.service.HelloPersonServiceImpl'
        url-pattern='/helloPersonService' />
</endpoints>

Notice that the implementation attribute is pointing to our implementation class.

Running the service

Add jetty:run as custom action (via Run > Set Project Configuration > Customize > Actions) and run it.
Now open a browser and check the following url http://localhost:8083/hello_person/helloPersonService. If you see the following screen, the webservice is up and running!

Testing the service

For testing I’m using the soapUI plugin for Netbeans.
After you’ve installed the plugin, you can create a new Web Service Testing Project (New Project > SOA > Web Service Testing Project)

Point the initial WSDL property to the url you’ve seen on the Web Services page: http://localhost:8083/hello_person/helloPersonService?wsdl.

The testsuite now contains a greetPerson Test Step. You can now test if the service works. If all goes well, you should see something similar to the picture below.

%d bloggers like this: