A service with Service Data

We're now going to modify the MathService example to add some Service Data. Since some of the files are a bit long, we won't show the whole code in this page, only the important parts. For the complete code, please download the tutorial files in the tutorial website.

The MathData SDE

We are going to add a single SDE to MathService:

This SDE will have one and only one value (cardinality 1..1). The datatype of this SDE will be a complex datatype called MathDataType, which will contain the internal value of MathService, the name of the last operation invoked (addition or subtraction), and the number of times either of the operations has been invoked. Although we could define MathDataType in the GWSDL file, we'll define it instead in a separate XML Schema file called MathDataType.xsd:

<complexType name="MathDataType">
  <sequence>
    <element name="value" type="int"/>
    <element name="lastOp" type="string"/>
    <element name="numOps" type="int"/>
  </sequence>
</complexType>
[Note]

This is a part of $TUTORIAL_DIR/schema/progtutorial/MathService_sd/MathSDE.xsd

If you take a close look at the MathSDE.xsd file, you'll see that it isn't actually pure XML Schema. It is a very simple GWSDL description with a <types> tag (no messages, PortTypes or bindings) with just one type: the MathDataType. If you need to create your own SDDs, the only change you would need to make to the file (besides defining your own schema) would be to define a new target namespace.

The Java Bean generated from the XML Schema would look something like this:

public class MathDataType  implements java.io.Serializable {
    private int value; // attribute
    private java.lang.String lastOp; // attribute
    private int numOps; // attribute
 
    public MathDataType() {
    }
 
    public int getValue() {
        return value;
    }
 
    public void setValue(int value) {
        this.value = value;
    }
 
    public java.lang.String getLastOp() {
        return lastOp;
    }
 
    public void setLastOp(java.lang.String lastOp) {
        this.lastOp = lastOp;
    }
 
    public int getNumOps() {
        return numOps;
    }
 
    public void setNumOps(int numOps) {
        this.numOps = numOps;
    }
}
[Note]

This is an extract from the code generated from the XML Schema file. This Java Bean is generated in the compile/deploy process by Ant, so don't use this code directly.

Service Interface

In this example, we are modifying the service interface, not so much because we'll be changing the specification of the operations (slightly), but mainly because we'll be adding service data. Adding service data to a service is an interface issue, so we need to create a new GWSDL file (along with a new client that will be capable of accessing the service data). However, the GWSDL code is going to be very similar to the one from the previous two examples, so it won't be hard to understand. The new GWSDL interface can be found in $TUTORIAL_DIR/schema/progtutorial/MathService_sd/Math.gwsdl.

The first change is that we need to specify a new target namespace. Notice how we're adding "_sd" after the previous namespace to denote that this is Math Service with service data (this is just for clarity, it's not mandated by GWSDL). Then, we need to include two new namespaces. The first corresponds to the SDE type (if you take a look at MathSDE.xsd, you'll see what the target namespace of the SDE type is). The second namespace is an OGSI namespace which contains a set of service data-related definitions.

<definitions name="MathService"
  targetNamespace="http://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd"
  xmlns:tns="http://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd"
  xmlns:data="http://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd/MathSDE"
  xmlns:ogsi="http://www.gridforum.org/namespaces/2003/03/OGSI"
  xmlns:gwsdl="http://www.gridforum.org/namespaces/2003/03/gridWSDLExtensions"
  xmlns:sd="http://www.gridforum.org/namespaces/2003/03/serviceData"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.xmlsoap.org/wsdl/">

Next, we'll need to import the schema file with the description of our MathDataType. Once again, notice how the namespace must correspond with the target namespace in MathSDE.xsd.

<import location="MathSDE.xsd"
  namespace="http://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd/MathSDE"/>

Finally, we have to add a new tag (from the service data namespace) inside the <gwsdl:portType> tag. This new tag is <sd:serviceData>, and allows us to specify the properties of each of the SDEs in this Grid Service.

<gwsdl:portType name="MathPortType" extends="ogsi:GridService">

  <!-- <operation>s -->

  <sd:serviceData name="MathData"
    type="data:MathDataType"
    minOccurs="1"
    maxOccurs="1"
    mutability="mutable"
    modifiable="false"
    nillable="false">
  </sd:serviceData>
</gwsdl:portType>

The first attribute is the name of the SDE. Remember, the name of an SDE must be locally unique (unique within this particular Grid Service). The following attribute specifies the type of this SDE, which is specified in MathSDE.xsd (notice how we're using the data namespace, which corresponds with the target namespace of MathSDE.xsd).

The following attributes refer to various properties of the SDE:

  • minOccurs : The minimum number of values that this SDE can have.

  • maxOccurs : The maximum number of values that this SDE can have. The value of this attribute can be unbounded, which indicates an array with no size limit.

  • modifiable : True or false. Specifies if the value of this SDE can be changed by a client.

  • nillable : True or false. Specifies if the value of this SDE can be NULL.

  • mutability : This attribute can have the following values:

    • static: The value of the SDE is provided in the GWSDL description.

    • constant: The value of the SDE is set when the Grid Service is created, but remains constant after that.

    • extendable: New elements can be added to the SDE, but not removed.

    • mutable: New elements can be added and removed.

In our example, the MathData SDE will have one and only one value (i.e. cardinality 1..1), will be allowed to change during the lifetime of the Grid Service, but cannot be NULL or be modified by the client.

Finally, since the internal value (which is modified by add and subtract) is now in the MathData SDE, there is no more need to keep the getValue method, since we'll be accessing the value through the SDE. If you take a look at the GWSDL file, you'll notice that getValue has been eliminated.

Namespace mappings

Since we have a new interface, with a new target namespace, we need to map that namespace to Java packages so the stubs are generated correctly. Notice how we also have to add a mapping for the MathDataType target namespace.

http\://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd=
  org.globus.progtutorial.stubs.MathService_sd

http\://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd/bindings=
  org.globus.progtutorial.stubs.MathService_sd.bindings

http\://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd/service=
  org.globus.progtutorial.stubs.MathService_sd.service

http\://www.globus.org/namespaces/2004/02/progtutorial/MathService_sd/MathSDE=
  org.globus.progtutorial.stubs.MathService_sd.servicedata
[Note]

These lines can be found in $TUTORIAL_DIR/namespace2package.mappings

Service Implementation

There's also a lot of new things in the service implementation ($TUTORIAL_DIR/org/globus/progtutorial/services/core/servicedata/impl/MathImpl.java). The class declaration, however, is still the same:

public class MathImpl extends GridServiceImpl implements MathPortType

The class will now have two new private attributes. One is the SDE (ServiceData mathDataSDE) and the other is the value of the SDE (MathDataType mathDataValue).

private ServiceData mathDataSDE;
private MathDataType mathDataValue;

The creation of the SDEs takes place in a special method called the postCreate method. This is called a callback method, which will be explained in greater detail in the lifecycle section. For now, suffice it to say that if we implement this method, the code in the method will be executed right after the service has been created. This is where we will set the initial value of the SDE.

public void postCreate(GridContext context) throws GridServiceException
{
  // Call base class's postCreate
  super.postCreate(context);

  // Create Service Data Element
  mathDataSDE = this.getServiceDataSet().create("MathData");

  // Create a MathDataType instance and set initial values
  mathDataValue = new MathDataType();
  mathDataValue.setLastOp("NONE");
  mathDataValue.setNumOps(0);
  mathDataValue.setValue(0);

  // Set the value of the SDE to the MathDataType instance
  mathDataSDE.setValue(mathDataValue);

  // Add SDE to Service Data Set
  this.getServiceDataSet().add(mathDataSDE);
}

The steps we must follow to create an SDE and add it to the Service Data Set are:

  1. Create a new SDE. Notice how we don't create it directly: we have to call the create method of the Service Data Set. This SDE is initially empty, it has no value. Also, the name of the SDE will be MathData

  2. Set a value for the SDE. The value of the SDE will be a MathDataType that we create ourselves.

  3. Set the initial values of MathDataType. In our example, the last operation is "NONE" and the number of operations done is zero.

  4. Add the SDE to the Service Data Set.

The add and subtract methods now have to modify the Service Data each time they are called (to update the 'value', the 'last operation' and the 'number of operations done'). We do this using the private mathDataValue attribute, and an extra method called incrementOps.

public void add(int a) throws RemoteException
{
  mathDataValue.setLastOp("Addition");
  incrementOps();
  mathDataValue.setValue(mathDataValue.getValue() + a);
}

The incrementOps method is a simple private method that increments the number of operations (in the Service Data) by one.

// This method updates the MathData SDE increasing the
// number of operations by one
private void incrementOps()
{
  int numOps = mathDataValue.getNumOps();
  mathDataValue.setNumOps(numOps + 1);
}

Deployment Descriptor

The deployment descriptor barely changes: new class, new baseClass, and new schemaPath (all corresponding to the files we've just created).

<?xml version="1.0"?>
<deployment name="defaultServerConfig" xmlns="http://xml.apache.org/axis/wsdd/"
  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

  <service name="progtutorial/core/servicedata/MathService" provider="Handler" style="wrapped">
    <parameter name="name" value="MathService"/>
    
    <parameter name="baseClassName" value="org.globus.progtutorial.services.core.servicedata.impl.MathImpl"/>
    <parameter name="className" value="org.globus.progtutorial.stubs.MathService_sd.MathPortType"/>
    <parameter name="schemaPath" value="schema/progtutorial/MathService_sd/Math_service.wsdl"/>

    <!-- Start common parameters -->
    <parameter name="allowedMethods" value="*"/>
    <parameter name="persistent" value="true"/>
    <parameter name="handlerClass" value="org.globus.ogsa.handlers.RPCURIProvider"/>
  </service>

</deployment>
[Note]

This file is $TUTORIAL_DIR/org/globus/progtutorial/services/core/servicedata/server-deploy.wsdd

Compile and deploy

Let's compile this new service. Notice how we're using the new GWSDL file.

./tutorial_build.sh \
org/globus/progtutorial/services/core/servicedata \
schema/progtutorial/MathService_sd/Math.gwsdl

Now, deploy the GAR file and start the service container:

ant deploy \
-Dgar.name=$TUTORIAL_DIR/build/lib/org_globus_progtutorial_services_core_servicedata.gar

globus-start-container

Remember, you have to run this from your GT3 installation directory, with a user with write permissions on that directory.