6.2. Accessing resource properties the right way

We will now write and deploy a new service that exposes all the WS-ResourceProperty portTypes. Our client application will, in turn, make a call to some of these portTypes. For simplicity, our service will be based on the example presented in Chapter 3, Writing Your First Stateful Web Service in 5 Simple Steps (the example that uses ServiceResourceHome to confine our implementation to a single class). However, the steps described in this chapter are equally valid for the other two types of implementations we have seen in the previous two chapters (singleton resource homes, and factory/instance services).

6.2.1. The WSDL file

In the previous chapters, we were always able to reuse our original WSDL file because we were only modifying implementation details (for example, changing the implementation from a singleton resource home to a factory/service approach). However, in this chapter we do have to use a new WSDL file because we want to extend from new portTypes, which necessarily changes our service's interface. However, our changes will be minimal:

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="MathService"
    targetNamespace="http://www.globus.org/namespaces/examples/core/MathService_instance_rp" 1
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://www.globus.org/namespaces/examples/core/MathService_instance"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsrp="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
    xmlns:wsrpw="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl" 2
    xmlns:wsdlpp="http://www.globus.org/namespaces/2004/10/WSDLPreprocessor"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    
    3
<wsdl:import 
    namespace=
    "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
    location="../../wsrf/properties/WS-ResourceProperties.wsdl" />
    
<-- ... -->    

<portType name="MathPortType" 
    wsdlpp:extends="wsrpw:GetResourceProperty 
                    wsrpw:GetMultipleResourceProperties 
                    wsrpw:SetResourceProperties
                    wsrpw:QueryResourceProperties" 4
    wsrp:ResourceProperties="tns:MathResourceProperties">

	<operation name="add">
		<input message="tns:AddInputMessage"/>
		<output message="tns:AddOutputMessage"/>
	</operation>
        
	<operation name="subtract">
		<input message="tns:SubtractInputMessage"/>
		<output message="tns:SubtractOutputMessage"/>
	</operation>

	5
	
</portType>

</definitions>

[Note]

This is part of file $EXAMPLES_DIR/schema/examples/MathService_instance_rp/Math.wsdl.

1

Notice how we declare a new target namespace for our new WSDL interface.

2

This namespace declaration (wsrpw) was already present in previous examples. In general, we always need to declare the WS-ResourceProperties namespace if we want to use the portTypes defines in that specification.

3

Furthermore, we have to make sure we import the WS-ResourceProperties WSDL file, where the portTypes are actually defined.

4

Our example extends from the four WS-ResourceProperties portTypes. However, we strictly only need to extend from those portTypes we need to use in our service.

5

Finally, notice how we've eliminated the GetValueRP operation (although not shown above, the WSDL file also lacks the corresponding GetValueRP messages and elements). Since we are now going to use the GetResourceProperty portType, there is no need to expose an explicit GetValueRP operation.

Since we have added a new interface, we need to map the new WSDL namespaces to Java packages (as described in Section 3.1.3, “Namespace mappings”)

http\://www.globus.org/namespaces/examples/core/MathService_instance_rp=
                                     org.globus.examples.stubs.MathService_instance_rp
http\://www.globus.org/namespaces/examples/core/MathService_instance_rp/bindings=
                                     org.globus.examples.stubs.MathService_instance_rp.bindings
http\://www.globus.org/namespaces/examples/core/MathService_instance_rp/service=
                                     org.globus.examples.stubs.MathService_instance_rp.service
[Note]

These three lines must be present in $EXAMPLES_DIR/namespace2package.mappings.

6.2.2. The Java files

The implementation files only require minimal changes. The only noteworthy change is that we no longer need to implement the getValueRP operation. In general, using the WS-ResourceProperties portTypes doesn't require that we add any extra code to our Java files.

[Note]

This QNames interface for this example is $EXAMPLES_DIR/org/globus/examples/services/core/rp/impl/MathQNames.java.

[Note]

This service class for this example is $EXAMPLES_DIR/org/globus/examples/services/core/rp/impl/MathService.java.

6.2.3. The deployment files

To be able to use the WS-ResourceProperties portTypes we need to modify our WSDD file to make sure that our service relies on the Globus-supplied operation providers for those portTypes.

<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultServerConfig" 
    xmlns="http://xml.apache.org/axis/wsdd/" 
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <service name="examples/core/rp/MathService" provider="Handler" use="literal" style="document">
        <parameter name="className" value="org.globus.examples.services.core.rp.impl.MathService"/>
        <wsdlFile>share/schema/examples/MathService_instance_rp/Math_service.wsdl</wsdlFile>
        <parameter name="allowedMethods" value="*"/>
        <parameter name="handlerClass" value="org.globus.axis.providers.RPCProvider"/>
        <parameter name="scope" value="Application"/>
        <parameter name="providers" value="GetRPProvider GetMRPProvider SetRPProvider QueryRPProvider"/>
        <parameter name="loadOnStartup" value="true"/>
    </service>

</deployment>
[Note]

This is file $EXAMPLES_DIR/org/globus/examples/services/core/rp/deploy-server.wsdd.

[Tip]Operation Providers

GT4 Core uses a design pattern called operation providers that will make our lives as programmers much easier. To put it quite simply, an operation provider is a Java class, providing a set of operations, that we can easily plug into our service. For example, remember that the WSDL file we've used in the previous chapters included the following:

<portType name="MathPortType" 
    wsdlpp:extends="wsrpw:GetResourceProperty"
    wsrp:ResourceProperties="tns:MathResourceProperties">

We used the wsdlpp:extends attribute to specify that our service would also implement a standard WSRF portType: the GetResourceProperty portType. This means our service class would need to implement a GetResourceProperty method (which we'll see in more detail in the next chapter). However, instead of having to implement it ourselves, we can rely on the operation providers included with GT4 that provide an implementation of all the WSRF portTypes. To specify we wanted to use an operation provider in our service, we simply added the following to our WSDD file:

<parameter name="providers" value="GetRPProvider"/>

In the following chapters, each time we want our service to provide standard functionality specified in the WSRF specs, we will simply make our service extend from a standard WSRF portType and then 'plug in' a Globus operation provider that implements that portType.

The JNDI deployment file, on the other hand, doesn't require any changes.

6.2.4. Build and deploy

Build the service:

./globus-build-service.sh rp

And deploy it:

globus-deploy-gar $EXAMPLES_DIR/org_globus_examples_services_core_rp.gar

6.2.5. Client code

Our client application will make calls to some of the WS-ResourceProperties portTypes. The next example will use more complex resource properties and then we will see how to invoke the rest of the portTypes.

The code for this client is rather lengthy, so instead of seeing all the code all at once, we are going to run the client first, and then take a close look at what happens at each moment.

[Note]

This source code for the client is $EXAMPLES_DIR/org/globus/examples/clients/MathService_instance_rp/Client.java

Compile the client:

javac \
-classpath ./build/stubs/classes/:$CLASSPATH \
org/globus/examples/clients/MathService_instance_rp/Client.java

And run it:

java \
-classpath ./build/stubs/classes/:$CLASSPATH \
org.globus.examples.clients.MathService_instance_rp.Client \
http://127.0.0.1:8080/wsrf/services/examples/core/rp/MathService

The full output of the client should be the following:

Value RP: 0
LastOp RP: NONE
Value RP: 10
LastOp RP: ADDITION

Value RP: 100
LastOp RP: ADDITION

Value: 100
LastOp: ADDITION

Let's take a close look at what happens in each of these three blocks.

Invoking getResourceProperty

Value RP: 0
LastOp RP: NONE
Value RP: 10
LastOp RP: ADDITION

The first block of code prints out the initial values of the Value and LastOp RP's using the getResourceProperty operation, performs an addition, and then prints out the RP's again. All the getResourceProperty code is placed inside a printResourceProperties method.

printResourceProperties(math);
math.add(10);
printResourceProperties(math);

Let's take a close look at what happens in the printResourceProperties method:

/*
 * This method prints out MathService's resource properties by using the
 * GetResourceProperty operation.
 */
private void printResourceProperties(MathPortType math) throws Exception {
	GetResourcePropertyResponse valueRP, lastOpRP, lastLogRP;
	String value, lastOp, lastLog;

	1
	valueRP = math.getResourceProperty(MathQNames.RP_VALUE);
	lastOpRP = math.getResourceProperty(MathQNames.RP_LASTOP);

	2
	value = valueRP.get_any()[0].getValue();
	lastOp = lastOpRP.get_any()[0].getValue();

	3
	System.out.println("Value RP: " + value);
	System.out.println("LastOp RP: " + lastOp);
}
1

We first invoke the getResourceProperty operation on our portType. Take into account that, since our MathPortType portType extends from the standard GetResourceProperty portType, our portType also includes a getResourceProperty operation. The only parameter we have to include is the QName of the RP we want to retrieve. Notice how the return value is of type GetResourcePropertyResponse, a Globus-supplied stub class.

2

We must now extract the actual value of the RP's from the GetResourcePropertyResponse return value. This is when knowing about the resource property document (explained at the beginning of the chapter) comes in really handy. The GetResourcePropertyResponse object will contain zero, one, or many RPs in XML format (i.e. the same way they are represented in the RP document). To access these RPs, we need to use the get_any method, which returns an array of elements (in the XML sense of the word). In our case, the GetResourcePropertyResponse from requesting the Value RP will contain the following:

<ns1:Value xmlns:ns1="http://www.globus.org/namespaces/examples/core/MathService_instance_rp">0</ns1:Value>

To obtain the value 0 contained in that element, we simply need to access the first position of the array of elements (get_value()[0]) and get its value (getValue).

3

Finally, we print out the values.

Invoking SetResourceProperties to update

Value RP: 100
LastOp RP: ADDITION

The second block of code updates the value of the Value RP using the SetResourceProperties operation and requesting an Update action. All the update code is placed inside a updateRP method.

updateRP(endpoint, MathQNames.RP_VALUE, "100");
printResourceProperties(math);

Now, let's see how the update operation is actually carried out:

/*
 * This method updates resource property "rpQName" in the WS-Resource
 * pointed at by the endpoint reference "epr" with the new value "value".
 */
private void updateRP(EndpointReferenceType epr, QName rpQName, String value)
		throws Exception {
	1
	WSResourcePropertiesServiceAddressingLocator locator = new WSResourcePropertiesServiceAddressingLocator();
	SetResourceProperties_PortType port = locator
			.getSetResourcePropertiesPort(epr);

	2
	UpdateType update = new UpdateType();
	MessageElement msg = new MessageElement(rpQName, value);
	update.set_any(new MessageElement[] { msg });

	3
	SetResourceProperties_Element request = new SetResourceProperties_Element();
	request.setUpdate(update);

	4
	port.setResourceProperties(request);
}
1

First of all, we obtain a reference to a generic SetResourceProperties portType. This approach is different from the one used in the previous block of code, where we simply used our own MathPortType. Take into account that we could use our MathPortType to invoke the setResourceProperties operation. However, the approach followed here can come in handy when all we want to access is the standard WSRF operations, without having to get a reference to the full portType (in our case, MathPortType).

2

Since we are going to perform an update action through the SetResourceProperties operation, we first need to create an UpdateType object where we specify the update to carry out. Take into account that an UpdateType object can contain several update requests. We encapsulate each of these requests inside a MessageElement object. Then, we create an array of MessageElements and include that array in our UpdateType object (using the set_any method).

3

Now, we create a SetResourceProperties_Element object which will represent our SetResourceProperties request. This object can contain insert, update, and delete actions. In our case, we add the recently created UpdateType object to the request using the setUpdate method.

4

Finally, we invoke SetResourceProperties.

Invoking GetMultipleResourceProperties

Value: 100
LastOp: ADDITION

The third, and last, block of code prints out the values of the Value and LastOp RP's using the GetMultipleResourceProperties operation. All the GetMultipleResourceProperties code is placed inside a printMultipleResourceProperties method.

printMultipleResourceProperties(math);
/*
 * This method prints out MathService's resource properties by using the
 * GetMultipleResourceProperties operation.
 */
private void printMultipleResourceProperties(MathPortType math)
		throws Exception {
	GetMultipleResourceProperties_Element request;
	GetMultipleResourcePropertiesResponse response;

	1
	QName[] resourceProperties = new QName[] { MathQNames.RP_VALUE,
			MathQNames.RP_LASTOP };
	request = new GetMultipleResourceProperties_Element(resourceProperties);

	2
	response = math.getMultipleResourceProperties(request);

	3
	for(int i=0; i<response.get_any().length;i++)
	{
		String name = response.get_any()[i].getLocalName();
		String value = response.get_any()[i].getValue();
		System.out.println(name +": " + value);
	}
}
1

First, we need to create a GetMultipleResourceProperties_Element object that represents the request to getMultipleResourceProperties. The constructor expects an array of QNames. In our case, we specify the QNames for the Value and LastOp RPs

2

Next, we invoke the getMultipleResourceProperties. Notice how the return value is of type GetMultipleResourcePropertiesResponse.

3

As in getResourceProperty, the return of getMultipleResourceProperties encapsulates zero, one, or many RPs in XML format. In this case, the GetMultipleResourcePropertiesResponse will contain the following:

<ns1:Value xmlns:ns1="http://www.globus.org/namespaces/examples/core/MathService_instance_rp">100</ns1:Value>
<ns2:LastOp xmlns:ns2="http://www.globus.org/namespaces/examples/core/MathService_instance_rp">ADDITION</ns2:LastOp>

To extract the value of the RPs, we once again rely on the get_any method, which returns an array of elements. We simply have to iterate through this array, and write the value of each element using the getValue method. Here we are also printing out the name of the property using the getLocalName method.