You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 19 Next »

Orchestration with JSR181

This tutorial will explain how you can leverage the servicemix-jsr181 component to orchestrate web services. We will use two public web services:

  • USZip which returns the Zip code for a US city
  • LocalTime which gives the local time given a zip code

We will call them in a simple way to provide an aggregate web service which will return the local time for a given city and expose it through as an HTTP/SOAP service.

For this tutorial, you will need a 3.1-incubating version of ServiceMix built from sources.

Project structure, SUs and SA

In this example, we will only use two components:

Thus we will have to create two service units and a service assembly.

First, we need to create the root maven project which will hold our SUs and SA. Launch the following commands:

mkdir citytime
cd citytime

And create a file named pom.xml with the following content:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.servicemix.samples</groupId>
  <artifactId>citytime</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>
</project>
citytime-sa

Then, we can use maven archetypes to create the two SUs and the SA. In the citytime directory, launch the following commands:

cd citytime

mvn archetype:create \
        -DarchetypeGroupId=org.apache.servicemix.tooling \
        -DarchetypeArtifactId=servicemix-http-consumer-service-unit \
        -DarchetypeVersion=3.1-incubating \
        -DgroupId=org.apache.servicemix.samples.citytime \
        -DartifactId=citytime-http-su

mvn archetype:create \
        -DarchetypeGroupId=org.apache.servicemix.tooling \
        -DarchetypeArtifactId=servicemix-jsr181-wsdl-first-service-unit \
        -DarchetypeVersion=3.1-incubating \
        -DgroupId=org.apache.servicemix.samples.citytime \
        -DartifactId=citytime-jsr181-su

mvn archetype:create \
        -DarchetypeGroupId=org.apache.servicemix.tooling \
        -DarchetypeArtifactId=servicemix-service-assembly \
        -DarchetypeVersion=3.1-incubating \
        -DgroupId=org.apache.servicemix.samples.citytime \
        -DartifactId=citytime-sa

Due to a bug in the 3.1 archetypes, you need to edit the citytime-jsr181-su/pom.xml file and edit the last xml section with the following:

<properties>
  <servicemix-version>3.1-incubating</servicemix-version>
  <xfire-version>1.2.2</xfire-version>
</properties>

This will create the following directory structure:

citytime\
  pom.xml
  citytime-http-su\
    ...
  citytime-jsr181-su\
    ...
  citytime-sa\
    ...

Generating Eclipse projects

Now that we have the projects created, be can import them in Eclipse. Run the following command to build the eclipse project files:

cd citytime
mvn eclipse:eclipse

Now, we can import the projects in Eclipse.

The jsr181 SU

The first thing to do is to design the WSDL that will be exposed as a service, so that we can generate the needed classes and implement the service.

Edit the citytime/citytime-jsr181-su/src/main/resources/service.wsdl and replace it by the following one:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CityTime"
	targetNamespace="http://servicemix.apache.org/samples/citytime"
	xmlns:tns="http://servicemix.apache.org/samples/citytime">

	<wsdl:types>
		<xsd:schema
			targetNamespace="http://servicemix.apache.org/samples/citytime"
			xmlns:xsd="http://www.w3.org/2001/XMLSchema"
			xmlns:tns="http://servicemix.apache.org/samples/citytime">
			<xsd:element name="GetCityTimeRequest">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="City" type="xsd:string"></xsd:element>
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
			<xsd:element name="GetCityTimeResponse">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="string" type="xsd:string"
							minOccurs="0" maxOccurs="unbounded">
						</xsd:element>
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="GetCityTimeRequest">
		<wsdl:part name="GetCityTimeRequest"
			element="tns:GetCityTimeRequest">
		</wsdl:part>
	</wsdl:message>
	<wsdl:message name="GetCityTimeResponse">
		<wsdl:part name="GetCityTimeResponse"
			element="tns:GetCityTimeResponse">
		</wsdl:part>
	</wsdl:message>
	<wsdl:portType name="CityTimePortType">
		<wsdl:operation name="GetCityTime">
			<wsdl:input message="tns:GetCityTimeRequest"></wsdl:input>
			<wsdl:output message="tns:GetCityTimeResponse"></wsdl:output>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="CityTimeSoapBinding"
		type="tns:CityTimePortType">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="GetCityTime">
			<soap:operation
				soapAction="http://servicemix.apache.org/samples/citytime/GetCityTime" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="CityTime">
		<wsdl:port name="Soap" binding="tns:CityTimeSoapBinding">
			<soap:address location="http://www.example.org/" />
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>

Then, grab the WSDL definitions for the two web services we will use. On Unix systems, you can use:

cd citytime/citytime-jsr181-su/src/main/resources/
wget http://www.webservicex.com/uszip.asmx?WSDL
mv uszip.asmx\@WSDL uszip.wsdl
wget http://www.ripedev.com/webservices/LocalTime.asmx?WSDL
mv LocalTime.asmx\@WSDL LocalTime.wsdl

If you use Windows, just download from them your web browser and put them in the above directory.

Then, we need to modify the pom.xml file to generate the classes for the web services. The archetype we used already has a definition for the main WSDL, we just need to copy it for the two new WSDLs:

<wsgen outputDirectory="${basedir}/target/generated-sources" 
       explicitAnnotation="true" 
       profile="org.codehaus.xfire.jaxws.gen.JAXWSProfile" 
       wsdl="${basedir}/src/main/resources/service.wsdl"></wsgen>
<wsgen outputDirectory="${basedir}/target/generated-sources" 
       explicitAnnotation="true" 
       profile="org.codehaus.xfire.jaxws.gen.JAXWSProfile" 
       wsdl="${basedir}/src/main/resources/LocalTime.wsdl"></wsgen>
<wsgen outputDirectory="${basedir}/target/generated-sources" 
       explicitAnnotation="true" 
       profile="org.codehaus.xfire.jaxws.gen.JAXWSProfile" 
       wsdl="${basedir}/src/main/resources/uszip.wsdl"></wsgen>

Unfortunately, the WSDL to Java generation does now handle well these WSDLs because they contain unsupported constructs.
You need the following service definitions:

uszip.wsdl
<wsdl:service name="USZip">
  <wsdl:port name="USZipSoap" binding="tns:USZipSoap">
    <soap:address location="http://www.webservicex.com/uszip.asmx" />
  </wsdl:port>
</wsdl:service>

Now, generate all the classes from these three WSDLs by launching:

cd citytime/citytime-jsr181-su
mvn generate-sources

Before refreshing your project, you can create the folder that will hold our implementation:

cd citytime/citytime-jsr181-su
mkdir src/main/java
mvn eclipse:eclipse

We will now create the service implementation. A skeleton has already been generated by the wsdl 2 java tool. We will just copy and rename this class in the src/main/java/org/apache/servicemix/samples/citytime folder so that it won't be erased when generating the project again.

package org.apache.servicemix.samples.citytime;

import javax.jws.WebService;

@WebService(serviceName = "CityTime", targetNamespace = "http://servicemix.apache.org/samples/citytime", 
            endpointInterface = "org.apache.servicemix.samples.citytime.CityTimePortType")
public class CityTimeImpl
    implements CityTimePortType
{


    public GetCityTimeResponse getCityTime(org.apache.servicemix.samples.citytime.GetCityTimeRequest GetCityTimeRequest) {
        throw new UnsupportedOperationException();
    }

}

But we need to remove the generation of this skeleton. In the pom.xml of the citytime-jsr181-su, add the generateServerStubs="false" attribute on the three c<wsgen/> tasks.

Now, create two properties for the services we use:

private USZipSoap usZip;
private LocalTimeSoap localTime;

public void setLocalTime(LocalTimeSoap localTime) {
    this.localTime = localTime;
}

public void setUsZip(USZipSoap usZip) {
    this.usZip = usZip;
}

We can now code our orchestration logic in the getWeather method:

public GetWeatherResponse getWeather(GetWeatherRequest getWeatherRequest) {
        GetInfoByCity GetInfoByCity = new GetInfoByCity();
        GetInfoByCity.setUSCity(GetCityTimeRequest.getCity());
        GetInfoByCityResponse r = usZip.getInfoByCity(GetInfoByCity);
        Element e = (Element) r.getGetInfoByCityResult().getContent().get(0);
        e = (Element) e.getElementsByTagName("Table").item(0);
        e = (Element) e.getElementsByTagName("ZIP").item(0);
        String ZipCode = e.getTextContent();
        String lt = localTime.localTimeByZipCode(ZipCode);
        GetCityTimeResponse rep = new GetCityTimeResponse();
        rep.setTime(lt);
        return rep;
}

Write JUnit test

  • No labels