Simple Spring-WS Application [with JPA and Jersey REST]

Introduction

In this article we will add a SOAP service utilizing Spring-WS to our evolving project.  This is a continuing series of articles whose purpose is to provide working examples of technologies integrated in a Spring based web application. Our latest edition will join the previously integrated technologies which include: Jersey (REST), JPA, Hibernate, JavaScript, AJAX and Groovy.  These are developed in a Maven2 build environment with testing through JUnit4 and reporting via PMD and Cobertura. This installment will add a SOAP service but we will put off writing the client until the next article and in its place we will utilize an useful application called SoapUI to test our SOAP service.  The following article will utilize XmlBeans and Spring MVC to create a web client for the SOAP service.

Using Spring-WS 1.5

Contract-First versus Contract-Last

There are two ways to implement web services:

  1. The contact-last approach allows for the generation of the xsd and wsdl directly from the Java classes.
  2. The contract-first utilize the wsdl as the contract.  The java classes are constructed with respect to that contract.

Spring has chosen to implement the contract-first approach.  They have,discussed this point in the Spring Web Services Reference in Chapter 2. Why Contract First? To summarize, from their article, the key advantages to this approach are:

  • Easing the Object/XML impedance mismatch by allowing  explict handling of:
    • xsd extensions
    • unportable types
    • cyclic graphs
  • Additionally the contract-approach provides a less fragile solution with reusable components.

Describing the Schema

In the schema we will model the entity classes and the messages that we wish to send through our SOAP service. Our domain model is simple and small consisting of two class Person and Company.  Below is what they look like as encoded in the XMLSchema:

<complexType name="Company">
	<sequence>
		<element name="id" type="long" minOccurs="1" />
		<element name="name" type="string" minOccurs="1" />
		<element name="employees" type="tns:Person" minOccurs="0" maxOccurs="unbounded" />
	</sequence>
</complexType>

<complexType name="Person">
	<sequence>
		<element name="id" type="long" minOccurs="1"></element>
		<element name="name" type="string" minOccurs="1"></element>
		<element name="age" type="short" minOccurs="1"></element>
		<element name="company" type="tns:Company" minOccurs="1" maxOccurs="1"></element>
	</sequence>
</complexType>

We also need describe the messages that will request data.  We are limiting our web service operations to the following service api:

CompaniesResponse getCompanies(CompaniesRequest companiesRequest);
PersonResponse getPerson(PersonRequest personRequest);
PersonsResponse getPersons(PersonsRequest personSearchRequest);
TableCountResponse getTableCount(TableCountRequest tableCountRequest);

Through these methods we will be able to service the functions that we have been using in our previous REST implementation.  The functions include:

  • get all persons
  • get all companies
  • get persons in a company
  • update person
  • create person
  • delete person

Note in the service api, above, we are utilizing the following classes:

  • CompanyRequest/CompanyRequest
  • PersonRequest/PersonResponse
  • TableCountRequest/TableCountResponse
  • PersonSearch

These are utility classes that are generated to pass the request and response information through our SOAP messages.  These classes are not actually written directly in java but are generated from their XSD definition via the SUN xjc compiler.  These elements are defined in the CompanySearch.xsd.  To get a feel for writing the XSD definitions we will work through the definition of the PersonRequest/PersonResponse elements. First note that there are xml elements to deal with the Person class both singly and in a collection.  The PersonRequest/PersonResponse elements deal with single Person operations such as: create, read, update and delete (crud). Looking first at the PersonRequest it contains a sequence that requires one-and-only-one operation defined and optionally a PersonCriteria or/and Person element.  So first we have an Operation, this operation has been encoded as an enumeration with the CRUD methods we wish to apply to a Person element/entity . The operations that the PersonRequest can make are:

  • CREATE operation require that the person element be supplied
  • READ operation this will be done with a PersonCriteria element, this PersonCriteria element gives us the criteria for the search for the appropriate Person.
  • UPDATE operation requires the person element
  • DELETE operation requires the person element

Schema definitions of PersonRequest, PersonResponse and Operation

<element name="PersonRequest">
	<complexType>
		<sequence>
			<element name="operation" type="tns:Operation" minOccurs="1"
				maxOccurs="1" />
			<element ref="tns:PersonCriteria" minOccurs="0" maxOccurs="1" />
			<element name="person" type="tns:Person" minOccurs="0"
				maxOccurs="1" />
		</sequence>
	</complexType>
</element>

<simpleType name="Operation">
	<restriction base="string">
		<enumeration value="CREATE" />
		<enumeration value="UPDATE" />
		<enumeration value="READ" />
		<enumeration value="DELETE" />
	</restriction>
</simpleType>

The entire CompanySearch.xsd can viewed in the link below.

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

	<complexType name="Company">
		<sequence>
			<element name="id" type="long" minOccurs="1" />
			<element name="name" type="string" minOccurs="1" />
			<element name="employees" type="tns:Person" minOccurs="0"
				maxOccurs="unbounded" />
		</sequence>
	</complexType>

	<complexType name="Person">
		<sequence>
			<element name="id" type="long" minOccurs="1"></element>
			<element name="name" type="string" minOccurs="1"></element>
			<element name="age" type="short" minOccurs="1"></element>
			<element name="company" type="tns:Company" minOccurs="1"
				maxOccurs="1"></element>
		</sequence>
	</complexType>

	<element name="CompaniesRequest">
		<complexType>
			<sequence>
				<element ref="tns:CompanySearchCriteria" minOccurs="0"></element>
			</sequence>
		</complexType>
	</element>

	<simpleType name="Operation">
		<restriction base="string">
			<enumeration value="CREATE" />
			<enumeration value="UPDATE" />
			<enumeration value="READ" />
			<enumeration value="DELETE" />
		</restriction>
	</simpleType>

	<simpleType name="TableNames">
		<restriction base="string">
			<enumeration value="Person"></enumeration>
			<enumeration value="Company"></enumeration>
		</restriction>
	</simpleType>

	<element name="TableCountRequest">
		<complexType>
			<sequence>
				<element name="table" type="tns:TableNames" minOccurs="1" maxOccurs="1"/>
			</sequence>
		</complexType>
	</element>

	<element name="TableCountResponse">
		<complexType>
			<sequence>
				<element name="table" type="tns:TableNames" minOccurs="1" maxOccurs="1"/>
				<element name="count" type="integer" minOccurs="1" maxOccurs="1"/>
			</sequence>
		</complexType>
	</element>

	<element name="PersonRequest">
		<complexType>
			<sequence>
				<element name="operation" type="tns:Operation" minOccurs="1"
					maxOccurs="1" />
				<element ref="tns:PersonCriteria" minOccurs="0" maxOccurs="1" />
				<element name="person" type="tns:Person" minOccurs="0"
					maxOccurs="1" />
			</sequence>
		</complexType>
	</element>

	<element name="PersonsRequest">
		<complexType>
			<sequence>
				<element ref="tns:PersonSearchCriteria" minOccurs="0"></element>
			</sequence>
		</complexType>
	</element>

	<element name="PersonSearchCriteria">
		<complexType>
			<sequence>
				<element name="companyId" type="long" minOccurs="0"
					maxOccurs="1" />
			</sequence>
		</complexType>
	</element>

	<element name="PersonCriteria">
		<complexType>
			<sequence>
				<element name="id" type="long" minOccurs="1" maxOccurs="1" />
			</sequence>
		</complexType>
	</element>

	<element name="PersonResponse">
		<complexType>
			<sequence>
				<element name="person" type="tns:Person" nillable="true" />
			</sequence>
		</complexType>
	</element>

	<element name="PersonsResponse">
		<complexType>
			<sequence>
				<element name="personList" type="tns:Person" minOccurs="0"
					maxOccurs="unbounded"></element>
			</sequence>
		</complexType>
	</element>

	<element name="CompanySearchCriteria">
		<complexType>
			<sequence>
				<element name="name" type="string" nillable="true" />
				<element name="id" type="short" nillable="true" />
			</sequence>
		</complexType>
	</element>

	<element name="CompaniesResponse">
		<complexType>
			<sequence>
				<element name="companyList" type="tns:Company" minOccurs="0"
					maxOccurs="unbounded"></element>
			</sequence>
		</complexType>
	</element>
</schema>

SOAP Request to endpoint

Lets backup a bit and take a higher level view of what is happening here.  Below is a diagram that helps provide context to the code, xml and configuration.  In the diagram we see what happens as a message comes into our web service and is mapped to an endpoint in the application which services the requests and returns a response message. Endpoint mapping We will now step through these processes to get an understanding of how this sequence of events is encoded in our application.

Message to MessageDispacher

First the web server receives a SOAP Request.  For example: PersonRequest.  The web.xml file is configured as follows:

<servlet>
	<servlet-name>spring-ws</servlet-name>
	<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
	<init-param>
		<param-name>transformWsdlLocations</param-name>
		<param-value>true</param-value>
	</init-param>
</servlet>
<servlet-mapping>
	<servlet-name>spring-ws</servlet-name>
	<url-pattern>/companies/*</url-pattern>
</servlet-mapping>

Soap Message would look like this (it is a create Person request):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonRequest>
         <sch:operation>CREATE</sch:operation>

         <sch:person>
            <sch:id>-1</sch:id>
            <sch:name>New Name</sch:name>
            <sch:age>12</sch:age>
            <sch:company>
               <sch:id>1</sch:id>
               <sch:name>IBM</sch:name>
            </sch:company>
         </sch:person>
      </sch:PersonRequest>
   </soapenv:Body>
</soapenv:Envelope>

The soap message is passed to a web service url which would be: http://localhost:8080/general/companies. The servlet mapping would map the soap request passing it on to determine its endpoint mapping.

EndpointMapping, EndpointAdapter and Endpoint

EndpointMapping

The interface EndpointMapping defines the mapping between a message request and their associated endpoint objects. PayloadRootQNameEndpointMapping and SoapActionEndpointMapping are implementation of this interface additionally, and most importantly for our case, AbstractEndpointMapping implements the interface which will serve Spring’s endpoint interceptor. Looking at the spring-ws-servlet.xml we are utilizing a specializting of the AbstractEndpointMapping called PayloadRootAnnotationMethodEndpointMapping.  Below shows snippets from the configuration and a view of how the endpoint annotations are applied. From spring-ws-servlet.xml

<bean id="annotationMapping"
          class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
        <description>
            Detects @PayloadRoot annotations on @Endpoint bean methods. The CompanyDirectoryEndpoint
            has such annotations. It uses two interceptors: one that logs the message payload, and the other validates
            it according to the 'ComapnySearch.xsd' schema file.
        </description>
        <property name="interceptors">
            <list>
                <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
                <bean class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
                    <property name="xsdSchemaCollection" ref="schemaCollection"/>
                    <property name="validateRequest" value="true"/>
                    <property name="validateResponse" value="true"/>
                </bean>
            </list>
        </property>
        <property name="order" value="1"/>
</bean>

From CompanyDirectoryEndpoint.java

@Endpoint
public class CompanyDirectoryEndpoint implements CompanyDirectoryService {
@PayloadRoot(localPart = "PersonRequest", namespace = "http://persistent.com/schema")
	public PersonResponse getPerson(PersonRequest personRequest)
			throws DatatypeConfigurationException {
		Operation operation = personRequest.getOperation();
		PersonResponse personResponse = objectFactory.createPersonResponse();
		if( operation.equals(Operation.READ)) {
			long id = personRequest.getPersonCriteria().getId();
			Person person = companyDirectory.getPerson(id);
			personResponse.setPerson(SchemaConversionUtil.toSchemaType(person));
		} else 	if( operation.equals(Operation.DELETE)) {
			long theId = personRequest.getPersonCriteria().getId();
			Person person = companyDirectory.getPerson(personRequest.getPersonCriteria().getId());
			deletePerson(theId);
			personResponse.setPerson(SchemaConversionUtil.toSchemaType(person));
		} else if(operation.equals(Operation.CREATE)) {
			Person person = createPerson(personRequest.getPerson());
			personResponse.setPerson(SchemaConversionUtil.toSchemaType(person));
		}  else if(operation.equals(Operation.UPDATE)) {
			updatePerson(personRequest.getPerson());
		}
		return personResponse;
	}
EndpointAdapter

The application uses the GenericMarshallingMethodEndpointAdapter.  Given that the message is appropriate for the adapter the adapter transformes the message with the configured marshaller.  In our application we are utilizing a Jaxb2Marshaller.  Below is the configuration of the EndpointAdapter and its’ marshaller.   The Jaxb2Marshaller builds the approprate factories to map and build the schema objects from the incoming xml elements and types.

   <bean id="companyDirectoryEndpoint" class="com.persistent.ws.CompanyDirectoryEndpoint">
	      <description>
            This endpoint handles the Company Directory Service messages using JAXB2 marshalling.
        </description>
        <property name="companyDirectory" ref="companyDirectory"/>
	</bean>
 	<bean id="annotationMapping"
          class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
        <description>
            Detects @PayloadRoot annotations on @Endpoint bean methods. The CompanyDirectoryEndpoint
            has such annotations. It uses two interceptors: one that logs the message payload, and the other validates
            it according to the 'ComapnySearch.xsd' schema file.
        </description>
        <property name="interceptors">
            <list>
                <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
                <bean class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
                    <property name="xsdSchemaCollection" ref="schemaCollection"/>
                    <property name="validateRequest" value="true"/>
                    <property name="validateResponse" value="true"/>
                </bean>
            </list>
        </property>
        <property name="order" value="1"/>
    </bean>
Endpoint

The final destination is one of the CompanyDirectoryEndpoints methods or as annotated @PayloadRoot.  The java docs PayloadRoot marks an endpoint method as the handler for an incoming request. The annotation values signify the the request payload root element that is handled by the method.  You can see the @PayloadRoot used in CompanyDirectoryEndpoint.java above.

WSDL Creation

Although the Spring-WS philosophy is Contract-First we do still get some xml automation and this comes in a bean which creates are WSDL file.  All we need to do is provide a few parameters: the xsd file(s),  port name, location uri and its’ target namespace.  Here is the bean definition from spring-ws-servlet.xml:

<bean id="companySearch"
	class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
	<description>
		Builds a WSDL from the schema/CompanySearch.xsd.
	</description>

	<property name="schemaCollection" ref="schemaCollection" />
	<!-- wsdl:portType describes messages in an operation -->
	<property name="portTypeName" value="companies" />
	<!-- we use a relative uri that will be transformed by spring-ws dep-->
	<property name="locationUri" value="/companies/" />
	<property name="targetNamespace" value="http://com.persistent/definitions" />
</bean>

<bean id="schemaCollection"
	class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
	<description> This bean wrap the messages.xsd (which imports types.xsd), and inlines them as a one. </description>
	<property name="xsds" value="/schema/CompanySearch.xsd" />
	<property name="inline" value="true" />
</bean>

The entire spring-ws-servlet.xml can be viewed below:

<?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:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:sws="http://www.springframework.org/schema/web-services"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
       http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
       http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-1.5.xsd">
	<!-- START -->

	<bean id="companySearch"
		class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
		<description>
			Builds a WSDL from the schema/CompanySearch.xsd.
		</description>

		<property name="schemaCollection" ref="schemaCollection" />
		<!-- wsdl:portType describes messages in an operation -->
		<property name="portTypeName" value="companies" />
		<!-- we use a relative uri that will be transformed by spring-ws dep-->
		<property name="locationUri" value="/companies/" />
		<property name="targetNamespace" value="http://com.persistent/definitions" />
	</bean>

	<bean id="schemaCollection"
		class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
		<description> This bean wrap the messages.xsd (which imports
			types.xsd), and inlines them as a one. </description>
		<property name="xsds" value="/schema/CompanySearch.xsd" />
		<property name="inline" value="true" />
	</bean>

	<bean id="companyDirectoryEndpoint" class="com.persistent.ws.CompanyDirectoryEndpoint">
	      <description>
            This endpoint handles the Company Directory Service messages using JAXB2 marshalling.
        </description>
        <property name="companyDirectory" ref="companyDirectory"/>
	</bean>
 	<bean id="annotationMapping"
          class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
        <description>
            Detects @PayloadRoot annotations on @Endpoint bean methods. The CompanyDirectoryEndpoint
            has such annotations. It uses two interceptors: one that logs the message payload, and the other validates
            it according to the 'ComapnySearch.xsd' schema file.
        </description>
        <property name="interceptors">
            <list>
                <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
                <bean class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
                    <property name="xsdSchemaCollection" ref="schemaCollection"/>
                    <property name="validateRequest" value="true"/>
                    <property name="validateResponse" value="true"/>
                </bean>
            </list>
        </property>
        <property name="order" value="1"/>
    </bean>

    	<!--
		The GenericMarshallingMethodEndpointAdapter converts the incoming XML
		messages to marshalled objects used as parameters and return value;
		the PayloadRootAnnotationMethodEndpointMapping is the mapping that
		detects and handles the @PayloadRoot annotations.
		-->
    <bean
    	class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
			<constructor-arg ref="marshaller" index="0" />
		</bean>
    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
		<property name="classesToBeBound">
			<list>
				<value>com.persistent.schema.Company
				</value>
				<value>com.persistent.schema.CompaniesRequest
				</value>
				<value>com.persistent.schema.CompaniesResponse
				</value>
				<value>com.persistent.schema.CompanySearchCriteria
				</value>
				<value>com.persistent.schema.Person
				</value>
				<value>com.persistent.schema.PersonRequest
				</value>
				<value>com.persistent.schema.PersonResponse
				</value>
				<value>com.persistent.schema.PersonsResponse
				</value>
				<value>com.persistent.schema.PersonsRequest
				</value>
				<value>com.persistent.schema.PersonCriteria
				</value>
				<value>com.persistent.schema.PersonSearchCriteria
				</value>
				<value>com.persistent.schema.TableCountRequest</value>
				<value>com.persistent.schema.TableCountResponse</value>
			</list>
		</property>
		<property name="schema" value="/schema/CompanySearch.xsd" />
	</bean>
</beans>

Notes on the Web Services Build

In the build of the web application Sun’s XJC compiler builds the schema objects from the xsd file.  You can see the generated files off of the target/generated-sources/xjc directory.  These classes are utilized in the application as shown above in the snippet from CompanyDirectoryEndpoint.java where we are utilizing classes such as PersonRequest and PersonResponse which are both products of this generation process.  The xjc compiler is configured in the build section of our pom.xml:

<plugin>
	<groupId>com.sun.tools.xjc.maven2</groupId>
	<artifactId>maven-jaxb-plugin</artifactId>	<version>1.1</version>
	<executions>
		<execution>
			<phase>generate-sources</phase>
			<goals>
				<goal>generate</goal>
			</goals>
		</execution>
	</executions>
	<configuration><generatePackage>com.persistent.schema</generatePackage>
		<schemaDirectory>src/main/webapp/schema</schemaDirectory>
	</configuration>
</plugin>

Additionally we have added a section to the POM file to create a client jar that can be added to the repository to be used as a dependency is a client to this webservice.

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-jar-plugin</artifactId>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>jar</goal>
			</goals>
			<configuration>
				<description>
	This plugin is being used to capture the information that will be used
 	in our client application.  Creating this jar will also create an associated
 	pom which will carry all its required dependencies so we will not need to deal
 	with in the client application.
				</description>
 			<classifier>client</classifier>
			<classesDirectory>target/general/WEB-INF/classes</classesDirectory>
				<includes>
					<include>**/*.xsd</include>
					<include>**/persistent/entity/*</include>
					<include>**/persistent/datasource/*</include>
					<include>**/persistent/service/*</include>
				</includes>
			</configuration>
		</execution>
	</executions>
</plugin>

Installing SOAP Stubs for client

The maven-jar-plugin will create a jar file called general-client.jar.  I did see a way to automatically make it a first class repository item so I used the following script to install the jar file in the repository for easy access from a pom needing access to it.  The installit.sh is located in the resource directory and is to be run from that location.  Its contents are as follows: mvn install:install-file -Dfile=../../../target/general-client.jar -DgroupId=com.persistent -DartifactId=general-client -Dversion=1.0 -Dpackaging=jar -DgeneratePom=true

Two Models

There is a divergence from our REST implementation.  There we marked-up the Entity object directly with JAXB annotations to make them available for serialization to our clients (albeit also transformed to JSON).  To service the SOAP request we are using two models which we convert between.  One model is assocated with the schema where the class are generated through the XJC compiler we just mentioned above.  The other model the entity model which we have been working with.  This decoupling of the schema and entity model can be viewed having both positive and negative attributes.  There is a untility class that helps with the conversin between model called: SchemaConversionUtil. Its’ use can be seen in the CompanyDirectoryEndpoint.java class.

Running SoapUI against the SOAP Web Service

  1. You will find how to get the SOAP server up and running from the Installation Instructions section below.  To get the SoapUI client running we need to follow the following steps:
  2. Down load and install the SoapUI application.  There is a free version provided by EviWare located here.
  3. Start the web application and start SoapUI
  4. In the SoapUI interface select the menu item under File called New soapUI Project. Assign any project name you prefer and for the Inital WSDL/WADL enter: http://localhost:8080/general/companies/companySearch.wsdl as shown below:

New SoapUI Project

  1. On selecting OK the SoapUI application read the WSDL at the the URI provided.  With the information from the WSDL SoapUI will provide a tree menu of SOAP request that you can send.  Below is a picture of using the TableCountRequest where we have set the table name to the Enumeration (as defined in the CompanySearch.wsdl) Person.  On the right hand side we can see that the web service returned a value of 10.

SoapUI Table Count Sample

The other Soap requests of interest are:

<!-- Get all companies (the CompanyCriteria is not currently being utilized) -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:CompaniesRequest>
      </sch:CompaniesRequest>
   </soapenv:Body>
</soapenv:Envelope>

<!-- This PersonsRequest will get all Persons that are of company id 1 (This would map to IBM). -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonsRequest>
         <!--Optional:-->
         <sch:PersonSearchCriteria>
            <sch:companyId>1</sch:companyId>
         </sch:PersonSearchCriteria>
      </sch:PersonsRequest>
   </soapenv:Body>
</soapenv:Envelope>

<!-- This PersonsRequest will get all Persons: note no criteria required -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonsRequest>
      </sch:PersonsRequest>
   </soapenv:Body>
</soapenv:Envelope>

<!-- This PersonRequest will CREATE a new user.  Note it is helpful look at the xsd while understanding these requests -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonRequest>
         <sch:operation>CREATE</sch:operation>

         <sch:person>
            <sch:id>-1</sch:id>
            <sch:name>New Name</sch:name>
            <sch:age>12</sch:age>
            <sch:company>
               <sch:id>1</sch:id>
               <sch:name>IBM</sch:name>
            </sch:company>
         </sch:person>
      </sch:PersonRequest>
   </soapenv:Body>
</soapenv:Envelope>

<!-- This PersonRequest will retrieve (READ) a Person by Person Id -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonRequest>
         <sch:operation>READ</sch:operation>
         <!--Optional:-->
         <sch:PersonCriteria>
            <sch:id>10</sch:id>
         </sch:PersonCriteria>
      </sch:PersonRequest>
   </soapenv:Body>
</soapenv:Envelope>

<!-- The PersonRequest will UPDATE a Person.  It is the same as CREATE except that the person element's id is greater than 0 -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonRequest>
         <sch:operation>UPDATE</sch:operation>

         <sch:person>
            <sch:id>11</sch:id>
            <sch:name>New Name</sch:name>
            <sch:age>14</sch:age>
            <sch:company>
               <sch:id>1</sch:id>
               <sch:name>IBM</sch:name>
            </sch:company>
         </sch:person>
      </sch:PersonRequest>
   </soapenv:Body>
</soapenv:Envelope>

<!-- This PersonRequest will retrieve (DELETE) a Person by Person Id -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://persistent.com/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <sch:PersonRequest>
         <sch:operation>DELETE</sch:operation>
         <!--Optional:-->
         <sch:PersonCriteria>
            <sch:id>11</sch:id>
         </sch:PersonCriteria>
      </sch:PersonRequest>
   </soapenv:Body>
</soapenv:Envelope>

 

Class Diagram

The class diagram builds out only a couple of classes from our previous project.  It does however utilize the components available to it through programming to an interface and Springs IOC. Class Diagram

Installation Instructions

  1. On down loading the source code which is in a jar filed called: general.jar uncompress it:

jar xvf general.jar

There will now be a new directory called general.

  1. Database configuration.  In the directory general/main/resources the file general.properties contains the information regarding your database configuration including: user, password, Driver and database name.  If you are using MySQL locally then all that needs to change would be the username and password.  You will also either need to change the jdbc.url to a database you have already or create the database jpa used by the project.
  1. Go back to the general directory where the pom.xml file is and you are ready to compile and run the program.  Maven2 makes this simple.  To compile and run (the clear command is in here too. It isn’t important for the first build but is important as one begins development with the code).

mvn clean install

In this process the program will run through the JUnit test cases.  If a test fails you will not be able to run the program until the defect is fixed.  You can override the tests (and hope the program will run) by using a flag:

mvn clean install -Dmaven.test.skip=true

  1. Now we are ready to run the program. Enter:

mvn jetty:run

The jetty server will startup and you will then be able to interact with the application’s REST webservice using cURL or the html webpage. To use the html webpage use this URL:

http://localhost:8080/general/

Additionally you will be able to run the SoapUI against the SOAP web service.

  1. As descirbed above, for clients to this SOAP service, add the general-client jar into the repository to be available as a dependency:

The installit.sh is located in the resource directory and is to be run from that location.  Its contents are as follows:

mvn install:install-file -Dfile=../../../target/general-client.jar -DgroupId=com.persistent -DartifactId=general-client -Dversion=1.0 -Dpackaging=jar -DgeneratePom=true

Running Reports

There are three report plugins configured in the pom.xml.  Sample output of these reporting engines can be viewed through the links in the Project Reports section of the article. The simple Maven command to create each of these reports are as follows:

PMD Report

mvn pmd:pmd

Cobertura Report

mvn cobertura:cobertura

Surefire Report

surefire-report:report-only

Eclipse Integration

If you are interested in importing this project into your eclipse environment the following command will create the configuration files required to import the project into eclipse as an existing eclipse project.  This command is run, as the others are, from the root directory general:

mvn eclipse:eclipse

Also when the application is imported into the Eclipse environment insure that the generate code directory is included as a source directory to the project.  This is done by opening the properties dialog of the imported project and selecting the Java Build Path and from the Source tab select Add Folder and add the general/target/generated-sources/xjc directory.

 

Conclusion

That concludes this article.  We now have an application that makes available the Company Directory information through SOAP services.  This is in a environment which will also service REST requests.  In the next article we will supply the client portion to this SOAP service which will utilize XmlBeans and Spring MVC.

The Source Code

The complete code listing is here. Note there has been a refactoring on this code.  The refactoring pertains to the REST portion of the application but does effect the entity objects.  For more information please see this article: Separation of Concerns: Refactoring of Evolving Project Refactored Maven2 Project.

About The Author

David Sells is a computer consultant in Toronto, Ontario who specializing in Java Enterprise Development. He has  provided innovative solutions for clients including: IBM, Goldman Sachs, Merrill Lynch and Deutsche Bank.

He also holds the following certifications:

  • Sun Certified Enterprise Architect for the Java Platform, Enterprise Edition 5 (2009)
  • Sun Certified Web Component Developer
  • Sun Certified Java Programmer.

Contact: david@persistentdesigns.com

4 comments to Simple Spring-WS Application [with JPA and Jersey REST]

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>