Objective: The objective of this exercise is to understand the steps in using and developing Grid services, and to understand the similarities and differences between this programming tool and other, similar, tools such as Remote Procedure Call (RPC) and Web Services. The objective is also learn to use Globus Toolkit 3 (GT3) to write and deploy a stateful Grid Service. The exercise will consist of stepping through the development of an example GT3 application that performs some arithmetic transactions for a user, such as adding to a value, subtracting from a value, and obtaining the current state of the value.
Background: Writing a Grid Service is similar to Remote Procedure Call in that the first step is to define the service interface. The interface specifies what operations will be available to the users, but it does not implement the actual operations. The interface is used to build a client and server stub, and then the client and server are implemented in possibly different languages. A runtime system performs functions such as marshalling of operands, directing client requests to the correct server instance, and returning values to the client.
In Grid Services the interface is known as the PortType. The language used for defining the interface is Web Services Definition Language (WSDL). Since WSDL is not a very user-friendly language, the Globus Toolkit 3 provides tools for defining the interface in Java first and then generating the WSDL code using an Apache Axis tool called Java2WSDL. This approach is best suited for beginners.
Grid Services have much in common with Web Services. Both Grid Services and Web Services define an interface using WSDL and offer a service-oriented view to a requesting program. There are also some significant differences between Grid Service and Web Services. Web Services have instances that are stateless and persistent. In contrast, Grid Services can be either stateful or stateless, and can be either transient or persistent. A service instance is stateless if it cannot remember prior events, and persistent (non-transient) if the instances outlive all their clients. Unlike Web Services, Grid Services use a factory approach to maintain multiple service instances. The factory manages the instances and allows the Grid Service to be stateful and transient. These can be divided into types of services as follows:
Because WSDL is used to access the services, the clients of a Grid Service can be developed using any language that have bindings to WSDL. The GT3 examples and starting code use Java to develop Grid Service applications, and use a very handy build tool known as ant. Ant greatly simplifies the compilation and building process of Grid Services. GT3 provides a build file for ant to generate the necessary executables from the source files. The build file specifies the files to be compiled, the order and the way files should be compiled. Moreover, the build file is generic so that it can be used over and over for different Grid Services, and it can be modified to suite the purpose of the programmers. All that is needed by ant is the interface description generated from a Java interface, the implementation of service written in Java, and the deployment descriptor written in WSDD.
The example provided here is a step by step guide for implementing and running the programs in the online Globus Toolkit 3 tutorial found at http://www.casa-sotomayor.net/gt3-tutorial/. It is a short version of the online tutorial customized for our environment.
For best results, you should read through the tutorial materials along the way. However, you can do this short exercise by following the steps here. To start, login to your account at kite.csce.uark.edu and perform these steps starting from your home directory.
[aapon@kite] mkdir tutorial
[aapon@kite] cd tutorial
[aapon@kite tutorial] wget http://www.casa-sotomayor.net/gt3-tutorial/downloads/gt3tutorial-examples-0.3.tar.gz
Alternatively, you can also just copy the (older version of) the file from my home directory:
[aapon@kite tutorial] cp ~aapon/tutorial/gt3tutorial-examples-0.2.1.tar.gz .
[aapon@kite tutorial] tar -xvzf gt3tutorial-examples-0.2.1.tar.gz
[aapon@kite tutorial]$ ls
build.properties gt3tutorial namespace2package.mappings
build.xml gt3tutorial-examples-0.2.1.tar.gz tutorial_build.sh
[aapon@kite tuturial] vi build.properties
ogsa.root=/opt/globus3/
From the tutorial subdirectory, copy the gt3tutorial/core/factory to a subdirectory with the name "useridfactory". That is, whereever you see aapon in the following steps, replace it with your own userid.
[aapon@kite] cd tutorial
[aapon@kite tutorial] cp -R gt3tutorial/core/factory aaponfactory
[aapon@kite tutorial]$ ls -l aaponfactory/
total 12
drwxr-xr-x 2 aapon aapon 4096 Mar 2 02:55 client
drwxr-xr-x 2 aapon aapon 4096 Mar 2 02:55 impl
-rw-r--r-- 1 aapon aapon 1246 Mar 2 02:55 Math.wsdd
[aapon@kite tutorial] source $GLOBUS_LOCATION/setenv.sh
[aapon@kite tutorial] source $GLOBUS_LOCATION/etc/globus-user-env.sh
Important: if some of the required environmental variables are not set, you may get errors during compilation
or when you try to execute the client.
The interface specifies what operations will be available to the users. You will start with the files provided by the GT3 Tutorial and modify them to fit your userid. In this example Java is used to create the interface. The file should be saved with the '.java' extension.
[aapon@kite tutorial]$ cd aaponfactory/impl
[aapon@kite impl]$ ls
MathImpl.java Math.java
[aapon@kite impl]$ mv Math.java AaponMath.java
[aapon@kite impl]$ vi AaponMath.java
package aaponfactory.impl;
public interface AaponMath
{
//I will specify three operations in my interface.
//You can specify your own operations depending on
//the service you want to write.
public void add(int a);
public void subtract(int a);
public int getValue();
}
A copy of this file is available in aaponfactory/impl/AaponMath.java
Save this file as
/home/aapon/tutorial/aaponfactory/impl/AaponMath.java
[aapon@kite impl]$ mv MathImpl.java AaponMathImpl.java
[aapon@kite impl]$ ls
AaponMathImpl.java AaponMath.java
[aapon@kite impl]$ vi AaponMathImpl.java
package aaponfactory.impl;
import org.globus.ogsa.impl.ogsi.GridServiceImpl;
import aaponfactory.AaponMath.AaponMathPortType;
import java.rmi.RemoteException;
public class AaponMathImpl extends GridServiceImpl implements AaponMathPortType
{
private int value = 0;
public AaponMathImpl()
{
super("Aapon Math Factory Service");
}
public void add(int a) throws RemoteException
{
value = value + a;
}
public void subtract(int a) throws RemoteException
{
value = value - a;
}
public int getValue() throws RemoteException
{
return value;
}
}
Save this as
/home/aapon/tutorial/aaponfactory/impl/AaponMathImpl.javaA copy of this file is available in aaponfactory/impl/AaponMathImpl.java
/home/aapon/tutorial/aaponfactory/AaponMath.wsdd
[aapon@kite tutorial]$ ./tutorial_build.sh aaponfactory/impl/AaponMath.javaThe output from my run of this script is available at tutorial_build-output.txt
If you take a look at /home/aapon/tutorial/build/lib/ directory, you will see the gar and jar files that Ant created from the source files.
[aapon@kite] newgrp globus
[aapon@kite] cd $GLOBUS_LOCATION
[aapon@kite] ant deploy -Dgar.name=/home/aapon/tutorial/build/lib/aaponfactory.AaponMath.gar
The output from my run of ant deploy is in
ant-deploy-output.txt.
package aaponfactory.client;
import aaponfactory.AaponMath.AaponMathServiceGridLocator;
import aaponfactory.AaponMath.AaponMathPortType;
import java.net.URL;
public class AaponMathClient
{
public static void main(String[] args)
{
try
{
// Get command-line arguments
URL GSH = new java.net.URL(args[0]);
int a = Integer.parseInt(args[1]);
// Get a reference to the AaponMathService instance
AaponMathServiceGridLocator myServiceLocator = new
AaponMathServiceGridLocator();
AaponMathPortType myprog =
myServiceLocator.getAaponMathService(GSH);
// Call remote method 'add'
myprog.add(a);
System.out.println("Added " + a);
// Get current value through remote method 'getValue'
int value = myprog.getValue();
System.out.println("Current value: " + value);
}catch(Exception e)
{
System.out.println("ERROR!");
e.printStackTrace();
}
}
}
Save this as
/home/aapon/tutorial/aaponfactory/client/AaponMathClient.java
[aapon@kite tutorial] javac -classpath ./build/classes:$CLASSPATH aaponfactory/client/AaponMathClient.java
[aapon@kite] globus-start-container -p 8081
You will see a list of services that are available.
[aapon@kite] ogsi-create-service http://localhost:8081/ogsa/services/tutorial/aaponfactory/AaponMathFactoryService myprog
This ogsi-create-service has two arguments: the service handle GSH and the name of the instance we want to create.
Now there is an instance named myprog that you can contact.
[aapon@kite tutorial] java aaponfactory.client.AaponMathClient http://localhost:8081/ogsa/services/tutorial/aaponfactory/AaponMathFactoryService/myprog 4
If you get a NoClassDefFoundError, go back and source the .sh files at the end of Step 2.
You should see results such as:
Added 4
Current value: 4
Notice that at the beginning the current value starts out as zero, then gets incremented.
[aapon@kite tutorial] java aaponfactory.client.AaponMathClient http://localhost:8081/ogsa/services/tutorial/aaponfactory/AaponMathFactoryService/myprog 6
Added 6
Current value: 10
Run the client several times and you will see that the value changes.
The instance the client contacts keeps track of the operations that were performed and
remembers the value. If you create another instance and let the client contact this new instance, you
will notice that the current value starts out from zero and the number you specified is added.
Post exercise questions to think about:
Enjoy!