You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tuscany.apache.org by "Antollini, Mario" <ma...@intel.com> on 2007/11/04 20:37:44 UTC

Tutorial: Modeling and Implementing a Tuscany Application out of a WSDL File

Hello,

 

I just wanted to contribute to the community a Tutorial I recently wrote called "Modeling and Implementing a Tuscany Application out of a WSDL File".

 

I am just sending plain text, but after receiving any feedback (which will actually be greatly appreciated) me, or any Tuscany contributor/committer, can enrich it and make it available either via Tuscany's wikipage or as a  PDF file(?).

 

Thanks and best regards,

Mario 

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Modeling and Implementing a Tuscany Application out of a WSDL File

 

The main focus of this document is to report and share the experience gained after creating a new Tuscany application whose starting point was a WSDL file. In other words, this document will serve as guide for those interested in creating a Tuscany application out of a given WSDL. It not only describes the steps followed but also mentions the difficulties faced and their resolution. Moreover, it could also serve as the ground basis for creating a WSDL2Composite tool (à la Axis2's WSDL2Java).

 

Introduction

 

In the same way the business logic for a Web Service can be created following a top-down approach (i.e. specify the interface first, then write the code which sticks to it), we had the need to create a Tuscany application which adhered to a Web Service Interface already defined.

 

What we tried to accomplished was the challenge to model and implement an Amazon-like application running as a Tuscany Application (from now on Shopping Store). Of course, we wanted this application to be fully compatible to the Amazon's WSDL (http://ecs.amazonaws.com/AWSECommerceService/2007-05-14/AWSECommerceService.wsdl). This way, any given (Web) application written to interact with Amazon through its WS interface (e.g. http://www.openlaszlo.org/node/198) would be seamlessly communicating with the application entirely coded as a Tuscany application.

 

Having understood the intention of this document we can proceed to detail the steps carried out to build the application.

 

Specifying the Web Service Interface

 

Following a top-down approach, the first step is to define the interface of the Tuscany service/s which will be exposed as Web Services. Once the interfaces are defined, a WSDL file which expresses/describes the methods, its parameters and returned types must be written.

 

In the particular case of the application which gave birth to this document, the WSDL file was taken from Amazon. It is important to make clear that in its first phase of development the Shopping Store application was intended to provide only a Shopping Cart service, and this document only covers this initial phase. Therefore, since Amazon's WSDL is composed of a lot of different methods/operations it had to be reduced and only the operations related to the Shopping Cart remained. Here you can see the final version of the WSDL file called shoppingstore.wsdl: https://issues.apache.org/jira/secure/attachment/12368949/ShoppingStore.wsdl. It is worth mentioning that the soap address was modified in order for it to point to the location where the Shopping Cart service will be running.

 

Generate Stub and Skeleton Code

 

Now, what we need to do is to start writing the source code which offers the functionality/operations offered by the WSDL. 

 

The first natural thing to do is to translate the interfaces of the Shopping Store application (which only exposes the Shopping Cart service so far) to the Java language. In order to accomplish that we will use the wsdl2java tool available in Axis2 (version 1.2). We will use this tool for two purposes:

 

1 - Generate the stub code ("client-side") which will facilitate the effort required to develop a standalone Java client which will interact with the Shopping Cart via WS invocations.

2 - Generate the skeleton code ("server-side") which we will only use to understand the (Java) interface our Shopping Cart service will stick to.

 

What we need to do is to create an script file with the following content (assuming Axis2-1.2 was already downloaded, unzipped and the %AXIS2_HOME% environment variable has been set):

 

Windows:

set CURRENT_DIR=.

setlocal EnableDelayedExpansion

set AXIS2_CLASS_PATH=%AXIS2_HOME%

FOR %%c in ("%AXIS2_HOME%\lib\*.jar") DO set AXIS2_CLASS_PATH=!AXIS2_CLASS_PATH!;%%c

java -cp "%AXIS2_CLASS_PATH%" org.apache.axis2.wsdl.WSDL2Java -d jaxbri -ss -ssi -g -uri .\ShoppingStore.wsdl

endlocal

 

Linux:

AXIS2_CLASSPATH=""

for f in "$AXIS2_HOME/lib/*.jar"

do

   AXIS2_CLASSPATH="$AXIS2_CLASSPATH":$f

done

java -cp $AXIS2_CLASSPATH org.apache.axis2.wsdl.WSDL2Java -d jaxbri -ss -ssi -g -uri ./ShoppingStore.wsdl

 

Note that the data binding used was JAXB (denoted by the option "-d jaxbri"). Different errors/problems occurred at different stages of the development of the ToyApp when other data bindings were used. JAXB was the one which presented the less inconveniences throughout the development of the ToyApp.

 

Place the script file in the same directory where the WSDL file is located and then run it. Once the script finishes executing, the following directory structure will be created in the current directory:

 

<CURRENT_DIR>/src/com/amazon/webserices/awsecommerceservice/_2007_05_14

 

and many files will show up in there.

 

After running the script commands, it is necessary to modify the source code that has been generated, otherwise we will get runtime errors (very difficult to solve at that time actually). What we need to do is to look for the line * @XmlType(name = "", propOrder = { * in the following files and add their names within the empty quotes: CartAdd.java, CartAddResponse.java, CartClear.java, CartClearResponse.java, CartCreate.java, CartCreateResponse.java, CartGet.java and CartGetResponse.java. For example, in the case of CartAdd.java, the string * @XmlType(name = "", propOrder = { * must be replaced by * @XmlType(name = "CartAdd", propOrder = { *, and in the case of CartCreateResponse.java, it must look like * @XmlType(name = "CartCreateResponse", propOrder = { *.

 

All the generated files must be used for marshaling and unmarshaling purposes, therefore they must be included at both compile- and run-time. Thus, the best thing to do is to compile the generated code and pack it into a jar file. In order for it to compile, we need to include the following jars in a "lib" directory: axiom-api-1.2.4.jar, axis2-kernel-1.2.jar, jaxb-api-2.0.2.jar, stax-api-1.0.1.jar and wsdl4j-1.6.2.jar. Also create an empty directory called "build" and another one called "dist" and then add the following lines to a build script:

 

Windows:

javac .\src\com\amazon\webservices\awsecommerceservice\_2007_05_14\*.java -cp .\lib\jaxb-api-2.0.2.jar;.\lib\stax-api-1.0.1.jar;.\lib\axiom-api-1.2.4.jar;.\lib\axis2-kernel-1.2.jar;.\lib\wsdl4j-1.6.2.jar;.\build -d build

jar cvfM .\dist\AWS2007_05_14.jar -C .\build .

 

Linux:

javac ./src/com/amazon/webservices/awsecommerceservice/_2007_05_14/*.java -cp `cygpath -wp ./lib/jaxb-api-2.0.2.jar:./lib/stax-api-1.0.1.jar:./lib/axiom-api-1.2.4.jar:./lib/axis2-kernel-1.2.jar:./lib/wsdl4j-1.6.2.jar:./build` -d build -Xlint:unchecked

jar cvfM ./dist/AWS2007_05_14.jar -C ./build .

 

After executing it, a file called AWS2007_05_14.jar will be located in the "dist" directory. It can be used in both both client and server side projects to reduce coding effort.

 

Coding Server-Side Business Logic

 

Even though we now count with a library which is in charge of marshaling and unmarshalling the remote invocations, we still need to take a look at one of its source files: ShoppingStoreServiceSkeletonInterface.java. This file is very useful for the server-side code since it describes the method names, their parameters and the return type of the operations  the service will expose. Here you can see (a neat version of) the generated file:

 

    /**

     * ShoppingStoreServiceSkeletonInterface.java

     */

    package com.amazon.webservices.awsecommerceservice._2007_05_14;

 

    public interface ShoppingStoreServiceSkeletonInterface {

                                

                public CartGetResponse CartGet (CartGet cartGet);

        public CartAddResponse CartAdd(CartAdd cartAdd);

        public CartCreateResponse CartCreate(CartCreate cartCreate);

        public CartModifyResponse CartModify(CartModify cartModify);

        public CartClearResponse CartClear(CartClear cartClear);

    }

 

Take into account that this skeleton file was generated using the reduced version of the Amazon WSDL, therefore it only contains the methods related to the Shopping Cart service.

 

We are now ready to implement the business logic for the Shopping Cart service...

 

First, let us create the following directory structure (this will be a new "project", thus create it in a new location):

                

                - build                                                                                                                     -> this directory will contain everything needed to generate the jar containing the Shopping Store app.

                                - wsdl

                                                shoppingstore.wsdl                                                           -> the reduced Amazon WSDL

                                shoppingstore.composite                                                 -> the content of this file will be explained later

                - dist                                                                                                                                       -> this directory will contain everything needed to run the Shopping Store application

                - lib                                                                                                                                         -> lib required to compile and run the Shopping Store application

                                AWS2007_05_14.jar                                                                       -> the jar file we have just created

                - src                                                                                                                                        -> directory containing the source code of the Shopping Store application (we will be explain it later)

                                - shoppingstore

                                                - server

                                                                ShoppingStoreServer.java

                                                - services

                                                                - cart

                                                                                CartService.java

                                                                                CartServiceImpl.java

 

 

 

Now, let us specify its interface (which, in this case, will be identical to the skeleton shown before) in a java file named CartService.java:

 

package shoppingstore.services.cart;

 

import org.osoa.sca.annotations.Remotable;

 

import com.amazon.webservices.awsecommerceservice._2007_05_14.*;

 

@Remotable

public interface CartService {

                

                public CartCreateResponse CartCreate(CartCreate cartCreate);

                public CartAddResponse CartAdd(CartAdd cartAdd);

                public CartModifyResponse CartModify(CartModify cartModify);

                public CartClearResponse CartClear(CartClear cartClear);

                public CartGetResponse CartGet(CartGet cartGet);

}

 

The code was annotated with the @Remotable annotation. It indicates that the interface is designed to be used for remote communication. Remotable interfaces are intended to be used for coarse grained services. Operations parameters and return values are passed by-value.

 

Now, we need to implement the above methods. A proper implementation of the shopping cart logic is out of scope of this document; therefore, let us just implement a basic cart service which only expects one item per invocation to the CartAdd method and which just stores the cart and its items in main memory. Let us create a new file called CartServiceImpl.java:

 

package shoppingstore.services.cart;

 

import org.osoa.sca.annotations.Scope;

import java.util.HashMap;

import com.amazon.webservices.awsecommerceservice._2007_05_14.*;

 

@Scope("COMPOSITE")

public class CartServiceImpl implements CartService {

                

                private static long ID = 0;

                //Associate usedID with CartInstance

                private HashMap<String, Cart> cartsHash = new HashMap<String, Cart>();

                

                public CartCreateResponse CartCreate(CartCreate cartCreate) {

                                CartCreateResponse cartCreateResponse = new CartCreateResponse();

                                Cart cart = getCart(cartCreate.getAWSAccessKeyId());

                                //If user has already created a cart during this session

                                if(cart != null){

                                                cartCreateResponse.getCart().add(cart);

                                                return cartCreateResponse;

                                }

                                cart = new Cart();

                                cart.setCartId(this.generateID());

                                cart.setCartItems(new CartItems());                             

                                //Add Cart to the HashMap

                                addCart(cartCreate.getAWSAccessKeyId(), cart);

                                cartCreateResponse.getCart().add(cart);

                                return cartCreateResponse;

                }

                

                public CartAddResponse CartAdd(CartAdd cartAdd) {

                                CartAddResponse cartAddResponse = new CartAddResponse();

                                CartAddRequest cartAddRequest = cartAdd.getRequest().get(0);

                                //Retreive the cart associated with this user

                                Cart cart = getCart(cartAdd.getAWSAccessKeyId());

                                //If user did not create a Cart for this session

                                if(cart == null){

                                                cartAddResponse.getCart().add(new Cart());

                                                return cartAddResponse;

                                }

                                CartItem cartItem = new CartItem();

                                cartItem.setASIN(cartAddRequest.getItems().getItem().get(0).getASIN());

                                cartItem.setQuantity(cartAddRequest.getItems().getItem().get(0).getQuantity().toString());

                                cart.getCartItems().getCartItem().add(cartItem);

                                cartAddResponse.getCart().add(cart);

                                return cartAddResponse;

                }

 

                public CartGetResponse CartGet(CartGet cartGet) {

                                CartGetResponse cartGetResponse = new CartGetResponse();

                                Cart cart = getCart(cartGet.getAWSAccessKeyId());

                                if(cart == null){

                                                cartGetResponse.getCart().add(new Cart());

                                                return cartGetResponse;

                                }

                                cartGetResponse.getCart().add(cart);

                                return cartGetResponse;

                }

 

                public CartModifyResponse CartModify(CartModify cartModify) {

                                //Implementation here...

                }

                

                public CartClearResponse CartClear(CartClear cartClear) {

                                //Implementation here...

                }

                

                private synchronized String generateID(){

                                ID++;

                                return String.valueOf(ID);

                }

                

                private Cart getCart(String usedId){

                                Cart cart = null;

                                System.out.println(this.cartsHash.toString());

                                cart = this.cartsHash.get(cartId);

                                return cart;

                }

                

                private void addCart(String userId, Cart cart){

                                this.cartsHash.put(cartId, cart);

                }

}

 

It is worth highlighting the fact that this class was annotated with a @Scope("COMPOSITE") annotation. This annotation is important since component implementations can either manage their own state or allow the SCA runtime to do so. In the latter case, the implementation scope specifies a visibility and lifecycle contract an implementation has with the SCA runtime. Invocations on a service offered by a component will be dispatched by the SCA runtime to an implementation instance according to the semantics of its implementation scope. In this case, the @Scope("COMPOSITE") tells the runtime to dispatch all service requests to the same implementation instance for the lifetime of the containing composite. This way, we can be sure that we can maintain the session data for each user (i.e. retrieving the active cart and its items).

 

Shopping Cart SCA Composite

 

Now that our Shopping Cart service has been implemented, we need to integrate it into Tuscany. In order to do that we need to define a composite file; let us call it shoppingstore.composite.

 

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"

                                                targetNamespace="http://webservices.amazon.com/AWSECommerceService/2007-05-14"

                                                xmlns:tns="http://webservices.amazon.com/AWSECommerceService/2007-05-14"

                                                xmlns:db="http://tuscany.apache.org/xmlns/databinding/1.0"

           name="ShoppingStore">

    

    <component name="CartServiceComponent">

                                <service name="CartService" >

                        <interface.wsdl interface="http://webservices.amazon.com/AWSECommerceService/2007-05-14#wsdl.interface(AWSECommerceServicePortType)" />

                        <binding.ws />

                        <db:databinding name="jaxb" />

                    </service>

        <implementation.java class="shoppingstore.services.cart.CartServiceImpl" />

    </component>

</composite>

 

This composite file, called ShoppingStore, contains only one component, the CartServiceComponent, which is offering its service via a web service interface. It is worth mentioning that the name of the service being offered by the Cart component (i.e. CartService) must be equal to the Java interface of the cart service (i.e. CartService.java).

 

Setting the Service

 

Now that we have a Tuscany Service modeled and implemented we need to get it running. Therefore, what we are going to do is to use the embedded server Tuscany provides in its releases. Thus, let us code the Java class that will have the main method and will prepare the service to start "listening" for requests, let us call it ShoppingStoreServer.java: 

 

package shoppingstore.server;

 

import java.io.IOException;

import org.apache.tuscany.sca.host.embedded.SCADomain;

 

public class ShoppingStoreServer {

 

                public static void main(String[] args) {

 

                                SCADomain scaDomain = SCADomain.newInstance("shoppingstore.composite");

 

                                try {

                                                System.out.println("ShoppingStore server started (press enter to shutdown)");

                                                System.in.read();

                                } catch (IOException e) {

                                                e.printStackTrace();

                                }

 

                                scaDomain.close();

                                System.out.println("ShoppingStore server stopped");

                }

}

 

 

The code above creates a new instance of the ShoppingStore composite and leaves it running awaiting for requests through its web service interface (as described in its composite file).

 

Running the Composite

 

We have already coded everything we need. Now, we need to properly set the environment to run the composite...

 

1 - Install Tuscany: Download it (http://cwiki.apache.org/TUSCANY/sca-java-releases.html) and uncompress it on the directory where you want it to be located (e.g. /opt/tuscany-sca-1.0-incubating).

                                

2 - Compile the Shopping Store code (replace <TUSCANY_HOME> with the directory where Tuscany is located):

                Windows:

                #set TUSCANY_HOME=<TUSCANY_HOME>

                #javac .\src\shoppingstore\server\*.java .\src\shoppingstore\services\cart\*.java -cp .\lib\AWS2007_05_14.jar;%TUSCANY_HOME%\lib\tuscany-sca-manifest.jar -d build

                

                Linux:

                #TUSCANY_HOME="<TUSCANY_HOME>"

                #javac ./src/shoppingstore/server/*.java ./src/shoppingstore/services/cart/*.java -cp ./lib/AWS2007_05_14.jar:$TUSCANY_HOME/lib/tuscany-sca-manifest.jar -d build

 

3 - Create and arrange the required jar files: in order to run the Shopping Store composite, we first need to generate the required jar files. Let us begin:

                3.1 - Create shoppingstore.jar: this file must contain the compile code for the application, its composite file and the reduced version of the Amazon WSDL. We will generate the jar file out of the files contained in the build directory since it already has the files in the structure we need them to be. Run:

                                Windows:

                                                #jar cvfM .\dist\shoppingstore.jar -C .\build .

                                

                                Linux:

                                                #jar cvfM ./dist/shoppingstore.jar -C ./build .

                                

                3.2 - Now, copy the lib directory (along with the contained AWS2007_05_14.jar file) into the "dist" directory. It must look like this:

                                                -dist

                                                                - lib

                                                                                AWS2007_05_14.jar

                                                                shoppingstore.jar

                                                

4 - Run the service: In order to run the service, run the following command:

                Windows:

                                #set TUSCANY_HOME=<TUSCANY_HOME>

                                #java -cp %TUSCANY_HOME%\lib\tuscany-sca-manifest.jar;.\dist\shoppingstore.jar;.\dist\lib\AWS2007_05_14.jar shoppingstore.server.ShoppingStoreServer

                                

                Linux:

                                #TUSCANY_HOME="<TUSCANY_HOME>"

                                #java -cp $TUSCANY_HOME/lib/tuscany-sca-manifest.jar:./dist/shoppingstore.jar:./dist/lib/AWS2007_05_14.jar shoppingstore.server.ShoppingStoreServer

                                

5 - Test the service: Instead of writing a standalone Java client which will invoke our web service, we can test it is working correctly in a much easier way. Let us just create a telnet connection to the port where Tuscany is listening for requests and let us send a SOAP message over it. So, the steps would be:

                5.1 Execute

                                                #telnet <HOSTNAME> <PORT> (e.g. #telnet localhost 8080)

                                and a connection to Tuscany's web server will be established.

                5.2 Now, paste the following text into the terminal in order to create a Cart:

 

POST /CartServiceComponent HTTP/1.0

Accept-Language: en

Content-Type: text/xml; charset="utf-8"

Content-Id: soap-envelope

Content-Length: 328

SOAPAction: http://soap.amazon.com

 

<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

                <soapenv:Body>

                                <CartCreate xmlns="http://webservices.amazon.com/AWSECommerceService/2007-05-14">

                                                <AWSAccessKeyId>112233</AWSAccessKeyId>

                                                <Request/>

                                </CartCreate>

                </soapenv:Body>

</soapenv:Envelope>

 

There are only two relevant things the reader needs to understand about this XML text:

1 - The operation being executed is denoted by the tag "CartCreate"

2 - There is only one parameter this operation receives, the AWSAccessKeyId (which univocally identifies an Amazon user) value (equal to 112233 in this case)

 

After a few moments of pasting the XML text, the Cart service will respond with an XML where the ID of the Cart assigned to the user is shown (just for debugging purposes). It is an incremental value, so the first user will get CartId = 1, the second client to create a Cart will be assigned the Cart with ID number 2, an so on.

 

The Cart service running within Tuscany displays the activity being carried out upon each request, this information can be useful for the readers to double check that the service is properly processing the requests being sent.

 

After creating the Cart, Items can be added to it. The following message does that:

 

POST /CartServiceComponent HTTP/1.0

Accept-Language: en

Content-Type: text/xml; charset="utf-8"

Content-Id: soap-envelope

Content-Length: 428

SOAPAction: http://soap.amazon.com

 

<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

                <soapenv:Body>

                                <CartAdd xmlns="http://webservices.amazon.com/AWSECommerceService/2007-05-14">

                                                <AWSAccessKeyId>112233</AWSAccessKeyId>

                                                <Request>

                                                                <Items>

                                                                                <Item>

                                                                                                <ASIN>Item001</ASIN>

                                                                                                <Quantity>2</Quantity>

                                                                                </Item>

                                                                </Items>

                                                </Request>

                                </CartAdd>

                </soapenv:Body>

</soapenv:Envelope>

 

Let us try to understand this XML file:

1 - The operation being executed is denoted by the tag "CartAdd"

2 - The AWSAccessKeyId must have associated a Cart to it (i.e. a CartCreate operation has been executed for this same user)

3 - The Item is composed of ASIN (Amazon's ID for Items) and the quantity of those items to be added to the cart.

 

Again, the Cart service will respond with a XML text showing that the item has being received and processed.

 

Conclusions

 

What we have learnt after reading this document is to create a Tuscany application out of a WSDL file. Following this top-down approach is the natural way of modeling Tuscany applications, and is very convenient when the WSDL file is already available. 

 

Throughout this document, all the steps required to have a running Tuscany service were detailed. However, it is important to point that although we reached to a application offering its service via a WS interface, we could have exposed it via other interfaces and transports since Tuscany offers such intrinsic flexibility.

 

Even though we came across some inconveniences when coding the application which led us to write this document, Tuscany proved to be a good choice to model and implement a service-oriented application following a top down approach.


Re: Tutorial: Modeling and Implementing a Tuscany Application out of a WSDL File

Posted by Raymond Feng <en...@gmail.com>.
Hi, Mario.

Thank you for the great contribution!

I just converted the document to a wiki page @ 
http://cwiki.apache.org/confluence/display/TUSCANYWIKI/Modeling+and+Implementing+a+Tuscany+Application+out+of+a+WSDL+File 
with some minor formatting. The community can further enhance it.

Thanks,
Raymond

----- Original Message ----- 
From: "Antollini, Mario" <ma...@intel.com>
To: <tu...@ws.apache.org>
Sent: Sunday, November 04, 2007 11:37 AM
Subject: Tutorial: Modeling and Implementing a Tuscany Application out of a 
WSDL File


Hello,



I just wanted to contribute to the community a Tutorial I recently wrote 
called "Modeling and Implementing a Tuscany Application out of a WSDL File".



I am just sending plain text, but after receiving any feedback (which will 
actually be greatly appreciated) me, or any Tuscany contributor/committer, 
can enrich it and make it available either via Tuscany's wikipage or as a 
PDF file(?).



Thanks and best regards,

Mario



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



Modeling and Implementing a Tuscany Application out of a WSDL File



The main focus of this document is to report and share the experience gained 
after creating a new Tuscany application whose starting point was a WSDL 
file. In other words, this document will serve as guide for those interested 
in creating a Tuscany application out of a given WSDL. It not only describes 
the steps followed but also mentions the difficulties faced and their 
resolution. Moreover, it could also serve as the ground basis for creating a 
WSDL2Composite tool (à la Axis2's WSDL2Java).



Introduction



In the same way the business logic for a Web Service can be created 
following a top-down approach (i.e. specify the interface first, then write 
the code which sticks to it), we had the need to create a Tuscany 
application which adhered to a Web Service Interface already defined.



What we tried to accomplished was the challenge to model and implement an 
Amazon-like application running as a Tuscany Application (from now on 
Shopping Store). Of course, we wanted this application to be fully 
compatible to the Amazon's WSDL 
(http://ecs.amazonaws.com/AWSECommerceService/2007-05-14/AWSECommerceService.wsdl). 
This way, any given (Web) application written to interact with Amazon 
through its WS interface (e.g. http://www.openlaszlo.org/node/198) would be 
seamlessly communicating with the application entirely coded as a Tuscany 
application.



Having understood the intention of this document we can proceed to detail 
the steps carried out to build the application.



Specifying the Web Service Interface



Following a top-down approach, the first step is to define the interface of 
the Tuscany service/s which will be exposed as Web Services. Once the 
interfaces are defined, a WSDL file which expresses/describes the methods, 
its parameters and returned types must be written.



In the particular case of the application which gave birth to this document, 
the WSDL file was taken from Amazon. It is important to make clear that in 
its first phase of development the Shopping Store application was intended 
to provide only a Shopping Cart service, and this document only covers this 
initial phase. Therefore, since Amazon's WSDL is composed of a lot of 
different methods/operations it had to be reduced and only the operations 
related to the Shopping Cart remained. Here you can see the final version of 
the WSDL file called shoppingstore.wsdl: 
https://issues.apache.org/jira/secure/attachment/12368949/ShoppingStore.wsdl. 
It is worth mentioning that the soap address was modified in order for it to 
point to the location where the Shopping Cart service will be running.



Generate Stub and Skeleton Code



Now, what we need to do is to start writing the source code which offers the 
functionality/operations offered by the WSDL.



The first natural thing to do is to translate the interfaces of the Shopping 
Store application (which only exposes the Shopping Cart service so far) to 
the Java language. In order to accomplish that we will use the wsdl2java 
tool available in Axis2 (version 1.2). We will use this tool for two 
purposes:



1 - Generate the stub code ("client-side") which will facilitate the effort 
required to develop a standalone Java client which will interact with the 
Shopping Cart via WS invocations.

2 - Generate the skeleton code ("server-side") which we will only use to 
understand the (Java) interface our Shopping Cart service will stick to.



What we need to do is to create an script file with the following content 
(assuming Axis2-1.2 was already downloaded, unzipped and the %AXIS2_HOME% 
environment variable has been set):



Windows:

set CURRENT_DIR=.

setlocal EnableDelayedExpansion

set AXIS2_CLASS_PATH=%AXIS2_HOME%

FOR %%c in ("%AXIS2_HOME%\lib\*.jar") DO set 
AXIS2_CLASS_PATH=!AXIS2_CLASS_PATH!;%%c

java -cp "%AXIS2_CLASS_PATH%" org.apache.axis2.wsdl.WSDL2Java -d 
jaxbri -ss -ssi -g -uri .\ShoppingStore.wsdl

endlocal



Linux:

AXIS2_CLASSPATH=""

for f in "$AXIS2_HOME/lib/*.jar"

do

   AXIS2_CLASSPATH="$AXIS2_CLASSPATH":$f

done

java -cp $AXIS2_CLASSPATH org.apache.axis2.wsdl.WSDL2Java -d 
jaxbri -ss -ssi -g -uri ./ShoppingStore.wsdl



Note that the data binding used was JAXB (denoted by the option "-d 
jaxbri"). Different errors/problems occurred at different stages of the 
development of the ToyApp when other data bindings were used. JAXB was the 
one which presented the less inconveniences throughout the development of 
the ToyApp.



Place the script file in the same directory where the WSDL file is located 
and then run it. Once the script finishes executing, the following directory 
structure will be created in the current directory:



<CURRENT_DIR>/src/com/amazon/webserices/awsecommerceservice/_2007_05_14



and many files will show up in there.



After running the script commands, it is necessary to modify the source code 
that has been generated, otherwise we will get runtime errors (very 
difficult to solve at that time actually). What we need to do is to look for 
the line * @XmlType(name = "", propOrder = { * in the following files and 
add their names within the empty quotes: CartAdd.java, CartAddResponse.java, 
CartClear.java, CartClearResponse.java, CartCreate.java, 
CartCreateResponse.java, CartGet.java and CartGetResponse.java. For example, 
in the case of CartAdd.java, the string * @XmlType(name = "", propOrder = 
{ * must be replaced by * @XmlType(name = "CartAdd", propOrder = { *, and in 
the case of CartCreateResponse.java, it must look like * @XmlType(name = 
"CartCreateResponse", propOrder = { *.



All the generated files must be used for marshaling and unmarshaling 
purposes, therefore they must be included at both compile- and run-time. 
Thus, the best thing to do is to compile the generated code and pack it into 
a jar file. In order for it to compile, we need to include the following 
jars in a "lib" directory: axiom-api-1.2.4.jar, axis2-kernel-1.2.jar, 
jaxb-api-2.0.2.jar, stax-api-1.0.1.jar and wsdl4j-1.6.2.jar. Also create an 
empty directory called "build" and another one called "dist" and then add 
the following lines to a build script:



Windows:

javac 
.\src\com\amazon\webservices\awsecommerceservice\_2007_05_14\*.java -cp 
.\lib\jaxb-api-2.0.2.jar;.\lib\stax-api-1.0.1.jar;.\lib\axiom-api-1.2.4.jar;.\lib\axis2-kernel-1.2.jar;.\lib\wsdl4j-1.6.2.jar;.\build 
 -d build

jar cvfM .\dist\AWS2007_05_14.jar -C .\build .



Linux:

javac 
./src/com/amazon/webservices/awsecommerceservice/_2007_05_14/*.java -cp 
`cygpath -wp 
./lib/jaxb-api-2.0.2.jar:./lib/stax-api-1.0.1.jar:./lib/axiom-api-1.2.4.jar:./lib/axis2-kernel-1.2.jar:./lib/wsdl4j-1.6.2.jar:./build` 
 -d build -Xlint:unchecked

jar cvfM ./dist/AWS2007_05_14.jar -C ./build .



After executing it, a file called AWS2007_05_14.jar will be located in the 
"dist" directory. It can be used in both both client and server side 
projects to reduce coding effort.



Coding Server-Side Business Logic



Even though we now count with a library which is in charge of marshaling and 
unmarshalling the remote invocations, we still need to take a look at one of 
its source files: ShoppingStoreServiceSkeletonInterface.java. This file is 
very useful for the server-side code since it describes the method names, 
their parameters and the return type of the operations  the service will 
expose. Here you can see (a neat version of) the generated file:



    /**

     * ShoppingStoreServiceSkeletonInterface.java

     */

    package com.amazon.webservices.awsecommerceservice._2007_05_14;



    public interface ShoppingStoreServiceSkeletonInterface {



                public CartGetResponse CartGet (CartGet cartGet);

        public CartAddResponse CartAdd(CartAdd cartAdd);

        public CartCreateResponse CartCreate(CartCreate cartCreate);

        public CartModifyResponse CartModify(CartModify cartModify);

        public CartClearResponse CartClear(CartClear cartClear);

    }



Take into account that this skeleton file was generated using the reduced 
version of the Amazon WSDL, therefore it only contains the methods related 
to the Shopping Cart service.



We are now ready to implement the business logic for the Shopping Cart 
service...



First, let us create the following directory structure (this will be a new 
"project", thus create it in a new location):



                - 
      -> this directory will contain everything needed to generate the jar 
containing the Shopping Store app.

                                - wsdl

                                                shoppingstore.wsdl           
                                                 -> the reduced Amazon WSDL

                                shoppingstore.composite                      
                            -> the content of this file will be explained 
later

                - 
     -> this directory will contain everything needed to run the Shopping 
Store application

                - 
    -> lib required to compile and run the Shopping Store application

                                AWS2007_05_14.jar                            
                                            -> the jar file we have just 
created

                - 
    -> directory containing the source code of the Shopping Store 
application (we will be explain it later)

                                - shoppingstore

                                                - server

                                                                ShoppingStoreServer.java

                                                - services

                                                                - cart

                                                                             
    CartService.java

                                                                             
    CartServiceImpl.java







Now, let us specify its interface (which, in this case, will be identical to 
the skeleton shown before) in a java file named CartService.java:



package shoppingstore.services.cart;



import org.osoa.sca.annotations.Remotable;



import com.amazon.webservices.awsecommerceservice._2007_05_14.*;



@Remotable

public interface CartService {



                public CartCreateResponse CartCreate(CartCreate cartCreate);

                public CartAddResponse CartAdd(CartAdd cartAdd);

                public CartModifyResponse CartModify(CartModify cartModify);

                public CartClearResponse CartClear(CartClear cartClear);

                public CartGetResponse CartGet(CartGet cartGet);

}



The code was annotated with the @Remotable annotation. It indicates that the 
interface is designed to be used for remote communication. Remotable 
interfaces are intended to be used for coarse grained services. Operations 
parameters and return values are passed by-value.



Now, we need to implement the above methods. A proper implementation of the 
shopping cart logic is out of scope of this document; therefore, let us just 
implement a basic cart service which only expects one item per invocation to 
the CartAdd method and which just stores the cart and its items in main 
memory. Let us create a new file called CartServiceImpl.java:



package shoppingstore.services.cart;



import org.osoa.sca.annotations.Scope;

import java.util.HashMap;

import com.amazon.webservices.awsecommerceservice._2007_05_14.*;



@Scope("COMPOSITE")

public class CartServiceImpl implements CartService {



                private static long ID = 0;

                //Associate usedID with CartInstance

                private HashMap<String, Cart> cartsHash = new 
HashMap<String, Cart>();



                public CartCreateResponse CartCreate(CartCreate cartCreate) 
{

                                CartCreateResponse cartCreateResponse = new 
CartCreateResponse();

                                Cart cart = 
getCart(cartCreate.getAWSAccessKeyId());

                                //If user has already created a cart during 
this session

                                if(cart != null){

                                                cartCreateResponse.getCart().add(cart);

                                                return cartCreateResponse;

                                }

                                cart = new Cart();

                                cart.setCartId(this.generateID());

                                cart.setCartItems(new CartItems());

                                //Add Cart to the HashMap

                                addCart(cartCreate.getAWSAccessKeyId(), 
cart);

                                cartCreateResponse.getCart().add(cart);

                                return cartCreateResponse;

                }



                public CartAddResponse CartAdd(CartAdd cartAdd) {

                                CartAddResponse cartAddResponse = new 
CartAddResponse();

                                CartAddRequest cartAddRequest = 
cartAdd.getRequest().get(0);

                                //Retreive the cart associated with this 
user

                                Cart cart = 
getCart(cartAdd.getAWSAccessKeyId());

                                //If user did not create a Cart for this 
session

                                if(cart == null){

                                                cartAddResponse.getCart().add(new 
Cart());

                                                return cartAddResponse;

                                }

                                CartItem cartItem = new CartItem();

                                cartItem.setASIN(cartAddRequest.getItems().getItem().get(0).getASIN());

                                cartItem.setQuantity(cartAddRequest.getItems().getItem().get(0).getQuantity().toString());

                                cart.getCartItems().getCartItem().add(cartItem);

                                cartAddResponse.getCart().add(cart);

                                return cartAddResponse;

                }



                public CartGetResponse CartGet(CartGet cartGet) {

                                CartGetResponse cartGetResponse = new 
CartGetResponse();

                                Cart cart = 
getCart(cartGet.getAWSAccessKeyId());

                                if(cart == null){

                                                cartGetResponse.getCart().add(new 
Cart());

                                                return cartGetResponse;

                                }

                                cartGetResponse.getCart().add(cart);

                                return cartGetResponse;

                }



                public CartModifyResponse CartModify(CartModify cartModify) 
{

                                //Implementation here...

                }



                public CartClearResponse CartClear(CartClear cartClear) {

                                //Implementation here...

                }



                private synchronized String generateID(){

                                ID++;

                                return String.valueOf(ID);

                }



                private Cart getCart(String usedId){

                                Cart cart = null;

                                System.out.println(this.cartsHash.toString());

                                cart = this.cartsHash.get(cartId);

                                return cart;

                }



                private void addCart(String userId, Cart cart){

                                this.cartsHash.put(cartId, cart);

                }

}



It is worth highlighting the fact that this class was annotated with a 
@Scope("COMPOSITE") annotation. This annotation is important since component 
implementations can either manage their own state or allow the SCA runtime 
to do so. In the latter case, the implementation scope specifies a 
visibility and lifecycle contract an implementation has with the SCA 
runtime. Invocations on a service offered by a component will be dispatched 
by the SCA runtime to an implementation instance according to the semantics 
of its implementation scope. In this case, the @Scope("COMPOSITE") tells the 
runtime to dispatch all service requests to the same implementation instance 
for the lifetime of the containing composite. This way, we can be sure that 
we can maintain the session data for each user (i.e. retrieving the active 
cart and its items).



Shopping Cart SCA Composite



Now that our Shopping Cart service has been implemented, we need to 
integrate it into Tuscany. In order to do that we need to define a composite 
file; let us call it shoppingstore.composite.



<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"

                                                targetNamespace="http://webservices.amazon.com/AWSECommerceService/2007-05-14"

                                                xmlns:tns="http://webservices.amazon.com/AWSECommerceService/2007-05-14"

                                                xmlns:db="http://tuscany.apache.org/xmlns/databinding/1.0"

           name="ShoppingStore">



    <component name="CartServiceComponent">

                                <service name="CartService" >

                        <interface.wsdl 
interface="http://webservices.amazon.com/AWSECommerceService/2007-05-14#wsdl.interface(AWSECommerceServicePortType)" 
/>

                        <binding.ws />

                        <db:databinding name="jaxb" />

                    </service>

        <implementation.java 
class="shoppingstore.services.cart.CartServiceImpl" />

    </component>

</composite>



This composite file, called ShoppingStore, contains only one component, the 
CartServiceComponent, which is offering its service via a web service 
interface. It is worth mentioning that the name of the service being offered 
by the Cart component (i.e. CartService) must be equal to the Java interface 
of the cart service (i.e. CartService.java).



Setting the Service



Now that we have a Tuscany Service modeled and implemented we need to get it 
running. Therefore, what we are going to do is to use the embedded server 
Tuscany provides in its releases. Thus, let us code the Java class that will 
have the main method and will prepare the service to start "listening" for 
requests, let us call it ShoppingStoreServer.java:



package shoppingstore.server;



import java.io.IOException;

import org.apache.tuscany.sca.host.embedded.SCADomain;



public class ShoppingStoreServer {



                public static void main(String[] args) {



                                SCADomain scaDomain = 
SCADomain.newInstance("shoppingstore.composite");



                                try {

                                                System.out.println("ShoppingStore 
server started (press enter to shutdown)");

                                                System.in.read();

                                } catch (IOException e) {

                                                e.printStackTrace();

                                }



                                scaDomain.close();

                                System.out.println("ShoppingStore server 
stopped");

                }

}





The code above creates a new instance of the ShoppingStore composite and 
leaves it running awaiting for requests through its web service interface 
(as described in its composite file).



Running the Composite



We have already coded everything we need. Now, we need to properly set the 
environment to run the composite...



1 - Install Tuscany: Download it 
(http://cwiki.apache.org/TUSCANY/sca-java-releases.html) and uncompress it 
on the directory where you want it to be located (e.g. 
/opt/tuscany-sca-1.0-incubating).



2 - Compile the Shopping Store code (replace <TUSCANY_HOME> with the 
directory where Tuscany is located):

                Windows:

                #set TUSCANY_HOME=<TUSCANY_HOME>

                #javac .\src\shoppingstore\server\*.java 
.\src\shoppingstore\services\cart\*.java -cp 
.\lib\AWS2007_05_14.jar;%TUSCANY_HOME%\lib\tuscany-sca-manifest.jar -d build



                Linux:

                #TUSCANY_HOME="<TUSCANY_HOME>"

                #javac ./src/shoppingstore/server/*.java 
./src/shoppingstore/services/cart/*.java -cp 
./lib/AWS2007_05_14.jar:$TUSCANY_HOME/lib/tuscany-sca-manifest.jar -d build



3 - Create and arrange the required jar files: in order to run the Shopping 
Store composite, we first need to generate the required jar files. Let us 
begin:

                3.1 - Create shoppingstore.jar: this file must contain the 
compile code for the application, its composite file and the reduced version 
of the Amazon WSDL. We will generate the jar file out of the files contained 
in the build directory since it already has the files in the structure we 
need them to be. Run:

                                Windows:

                                                #jar cvfM 
.\dist\shoppingstore.jar -C .\build .



                                Linux:

                                                #jar cvfM 
./dist/shoppingstore.jar -C ./build .



                3.2 - Now, copy the lib directory (along with the contained 
AWS2007_05_14.jar file) into the "dist" directory. It must look like this:

                                                -dist

                                                                - lib

                                                                             
    AWS2007_05_14.jar

                                                                shoppingstore.jar



4 - Run the service: In order to run the service, run the following command:

                Windows:

                                #set TUSCANY_HOME=<TUSCANY_HOME>

                                #java -cp 
%TUSCANY_HOME%\lib\tuscany-sca-manifest.jar;.\dist\shoppingstore.jar;.\dist\lib\AWS2007_05_14.jar 
shoppingstore.server.ShoppingStoreServer



                Linux:

                                #TUSCANY_HOME="<TUSCANY_HOME>"

                                #java -cp 
$TUSCANY_HOME/lib/tuscany-sca-manifest.jar:./dist/shoppingstore.jar:./dist/lib/AWS2007_05_14.jar 
shoppingstore.server.ShoppingStoreServer



5 - Test the service: Instead of writing a standalone Java client which will 
invoke our web service, we can test it is working correctly in a much easier 
way. Let us just create a telnet connection to the port where Tuscany is 
listening for requests and let us send a SOAP message over it. So, the steps 
would be:

                5.1 Execute

                                                #telnet <HOSTNAME> <PORT> 
(e.g. #telnet localhost 8080)

                                and a connection to Tuscany's web server 
will be established.

                5.2 Now, paste the following text into the terminal in order 
to create a Cart:



POST /CartServiceComponent HTTP/1.0

Accept-Language: en

Content-Type: text/xml; charset="utf-8"

Content-Id: soap-envelope

Content-Length: 328

SOAPAction: http://soap.amazon.com



<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

                <soapenv:Body>

                                <CartCreate 
xmlns="http://webservices.amazon.com/AWSECommerceService/2007-05-14">

                                                <AWSAccessKeyId>112233</AWSAccessKeyId>

                                                <Request/>

                                </CartCreate>

                </soapenv:Body>

</soapenv:Envelope>



There are only two relevant things the reader needs to understand about this 
XML text:

1 - The operation being executed is denoted by the tag "CartCreate"

2 - There is only one parameter this operation receives, the AWSAccessKeyId 
(which univocally identifies an Amazon user) value (equal to 112233 in this 
case)



After a few moments of pasting the XML text, the Cart service will respond 
with an XML where the ID of the Cart assigned to the user is shown (just for 
debugging purposes). It is an incremental value, so the first user will get 
CartId = 1, the second client to create a Cart will be assigned the Cart 
with ID number 2, an so on.



The Cart service running within Tuscany displays the activity being carried 
out upon each request, this information can be useful for the readers to 
double check that the service is properly processing the requests being 
sent.



After creating the Cart, Items can be added to it. The following message 
does that:



POST /CartServiceComponent HTTP/1.0

Accept-Language: en

Content-Type: text/xml; charset="utf-8"

Content-Id: soap-envelope

Content-Length: 428

SOAPAction: http://soap.amazon.com



<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

                <soapenv:Body>

                                <CartAdd 
xmlns="http://webservices.amazon.com/AWSECommerceService/2007-05-14">

                                                <AWSAccessKeyId>112233</AWSAccessKeyId>

                                                <Request>

                                                                <Items>

                                                                             
    <Item>

                                                                             
                    <ASIN>Item001</ASIN>

                                                                             
                    <Quantity>2</Quantity>

                                                                             
    </Item>

                                                                </Items>

                                                </Request>

                                </CartAdd>

                </soapenv:Body>

</soapenv:Envelope>



Let us try to understand this XML file:

1 - The operation being executed is denoted by the tag "CartAdd"

2 - The AWSAccessKeyId must have associated a Cart to it (i.e. a CartCreate 
operation has been executed for this same user)

3 - The Item is composed of ASIN (Amazon's ID for Items) and the quantity of 
those items to be added to the cart.



Again, the Cart service will respond with a XML text showing that the item 
has being received and processed.



Conclusions



What we have learnt after reading this document is to create a Tuscany 
application out of a WSDL file. Following this top-down approach is the 
natural way of modeling Tuscany applications, and is very convenient when 
the WSDL file is already available.



Throughout this document, all the steps required to have a running Tuscany 
service were detailed. However, it is important to point that although we 
reached to a application offering its service via a WS interface, we could 
have exposed it via other interfaces and transports since Tuscany offers 
such intrinsic flexibility.



Even though we came across some inconveniences when coding the application 
which led us to write this document, Tuscany proved to be a good choice to 
model and implement a service-oriented application following a top down 
approach.



---------------------------------------------------------------------
To unsubscribe, e-mail: tuscany-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: tuscany-dev-help@ws.apache.org