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.

Reply via email to