3.2. Step 2: Implementing the service in Java

After defining the service interface ("what the service does"), the next step is implementing that interface. The implementation is "how the service does what it says it does".

3.2.1. The QNames interface

The first bit of code we need is a very simple Java interface that will make our life a bit easier. When we have to refer to just about anything related to a service, we will need to do so using its qualified name, or QName for short. This is a name which includes a namespace and a local name. For example, the QName of the Value RP is:

	{http://www.globus.org/namespaces/examples/core/MathService_instance}Value
	
[Note]

This is a common string representation of a QName. The namespace is placed between curly braces, and the local name is placed right after the namespace.

A qualified name is represented in Java using the QName class. Since we'll be referring to the service's qualified names frequently, it is a good practice to put them all in a separate interface:

package org.globus.examples.services.core.first.impl;

import javax.xml.namespace.QName;

public interface MathQNames {
	public static final String NS = "http://www.globus.org/namespaces/examples/core/MathService_instance";

	public static final QName RP_VALUE = new QName(NS, "Value");

	public static final QName RP_LASTOP = new QName(NS, "LastOp");

	public static final QName RESOURCE_PROPERTIES = new QName(NS,
			"MathResourceProperties");
}
[Note]

This file is $EXAMPLES_DIR/org/globus/examples/services/core/first/impl/MathQNames.java.

We are not really required to write this interface, but in the long run it's much better to have all the QNames in a single spot, so we can avoid making mistakes when manually writing QNames in our other classes.

3.2.2. The service implementation

In this first simple example, our service implementation will consist of a single Java class with the code for both the service and the resource. We will see in the following chapters that it is more common (in fact, desirable) to split the implementation into at least two classes: one for the service and another one for the resource. You should only use the approach described in this first example when coding very simple services.

Writing the code for the service is actually very mechanical. The only non-trivial piece of code is the method that will be in charge of initializing our service's single resource.

The bare bones of our resource class will be the following:

package org.globus.examples.services.core.first.impl;

import java.rmi.RemoteException;

import org.globus.wsrf.Resource;
import org.globus.wsrf.ResourceProperties;
import org.globus.wsrf.ResourceProperty;
import org.globus.wsrf.ResourcePropertySet;
import org.globus.wsrf.impl.ReflectionResourceProperty;
import org.globus.wsrf.impl.SimpleResourcePropertySet;
import org.globus.examples.stubs.MathService_instance.AddResponse;
import org.globus.examples.stubs.MathService_instance.SubtractResponse;
import org.globus.examples.stubs.MathService_instance.GetValueRP;

public class MathService implements Resource1, ResourceProperties2 {


}
1

Since our Java class will implement both the service and the resource, we need to implement the Resource interface. However, this interface doesn't require any methods. It is simply a way of tagging a class as being a resource.

2

By implementing the ResourceProperties interface we are indicating that our class has a set of resource properties which we want to make available. This interface requires that we add the following to our class:

private ResourcePropertySet propSet;

public ResourcePropertySet getResourcePropertySet() {
	return this.propSet;
}

Now, remember that our resource has two resource properties: Value of type xsd:int and LastOp of type xsd:string. We need to add an attribute for each resource property along with a get/set method pair for each resource property:

package org.globus.examples.services.core.first.impl;

import java.rmi.RemoteException;

import org.globus.wsrf.Resource;
import org.globus.wsrf.ResourceProperties;
import org.globus.wsrf.ResourceProperty;
import org.globus.wsrf.ResourcePropertySet;
import org.globus.wsrf.impl.ReflectionResourceProperty;
import org.globus.wsrf.impl.SimpleResourcePropertySet;
import org.globus.examples.stubs.MathService_instance.AddResponse;
import org.globus.examples.stubs.MathService_instance.SubtractResponse;
import org.globus.examples.stubs.MathService_instance.GetValueRP;

public class MathService implements Resource, ResourceProperties {

	/* Resource Property set */
	private ResourcePropertySet propSet;

	/* Resource properties */
	private int value;
	private String lastOp;

	/* Get/Setters for the RPs */
	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	public String getLastOp() {
		return lastOp;
	}

	public void setLastOp(String lastOp) {
		this.lastOp = lastOp;
	}

	/* Required by interface ResourceProperties */
	public ResourcePropertySet getResourcePropertySet() {
		return this.propSet;
	}
}
[Caution]

It is important for the attributes to have the same name that was given to the resource properties in the WSDL file (but with the first letter in lowercase). Remember:

<xsd:element name="Value" type="xsd:int"/>
<xsd:element name="LastOp" type="xsd:string"/>

This translates to:

private int value;
private String lastOp;

As for the get/set methods, we again have to use the same name used in the WSDL file (keeping the first letter in uppercase):

public int getValue() {...}
public void setValue(int value) {...}
public String getLastOp() {...}
public void setLastOp(String lastOp) {...}

Next, we have to implement the constructor. Here we will initialize the resource properties.

/* Constructor. Initializes RPs */
public MathService() throws RemoteException {
	1
	this.propSet = new SimpleResourcePropertySet(
			MathQNames.RESOURCE_PROPERTIES);
		
	/* Initialize the RP's */
	try {
		2
		ResourceProperty valueRP = new ReflectionResourceProperty(
				MathQNames.RP_VALUE, "Value", this);
		this.propSet.add(valueRP);
		setValue(0);
			
		ResourceProperty lastOpRP = new ReflectionResourceProperty(
				MathQNames.RP_LASTOP, "LastOp", this);
		this.propSet.add(lastOpRP);
		setLastOp("NONE");
	} catch (Exception e) {
		throw new RuntimeException(e.getMessage());
	}
}
1

We create the resource property set. To do so, we need to provide the qualified name of the resource properties. In our case, that QName is:

{http://www.globus.org/namespaces/examples/core/MathService_instance}MathResourceProperties

This was specified in the WSDL file. Remember that we put this QName in the MathQNames interface so we could access it with ease.

2

We create the individual resource properties, and initialize them (again, notice how we're referring to each resource property's QName: RP_VALUE and RP_LASTOP)

[Tip]ReflectionResourceProperty

What we have just seen is the simplest type of resource creation: using ReflectionResourceProperty to represent each resource property. This makes the implementation much simpler, but also adds quite a bit of restrictions on our resource implementation (such as the need for get/set methods, as outlined in the caution box above). GT4 includes other classes to deal with more complex resource scenarios (such as SimpleResourceProperty, PersistentResourceProperty, etc.). In fact, we will start using SimpleResourceProperty in Chapter 8, Notifications.

Finally, we need to provide the implementation of our remotely-accessible methods (add, subtract, and getValueRP). These are pretty straightforward, except for some peculiarities in how the parameters and return types have to be declared. For example, take a look at the add method: (the subtract method is similar)

public AddResponse add(int a) throws RemoteException {
	value += a;
	lastOp = "ADDITION";

	return new AddResponse();
}

You'll notice that, even though we defined the add operation as having no return type in the WSDL file, now the return type is AddResponse. A similar thing happens in the getValueRP method:

public int getValueRP(GetValueRP params) throws RemoteException {
	return value;
}

Even though getValueRP was defined as having no parameters, it turns out our method has a single "GetValueRP params" parameter. Don't get nervous... this all has a very logical explanation. This is due to the fact that WSRF uses document/literal bindings, which requires that the parameters be implemented in a very particular way.

[Tip]How document/literal bindings affect our parameters

Whenever we write an operation which is part of our WSDL interface (such as add, subtract, or getValueRP), the parameters and the return values will in some cases be 'boxed' inside stub classes (which are generated automatically from the WSDL file). This is more evident when we have several parameters. For example, if we declared the following operation in our WSDL file:

void multiply(int a1, int a2);

The actual Java code would look like this:

public MultiplyResponse multiply(Multiply params) throws RemoteException
{
	int a1 = params.getA1()
	int a2 = params.getA2()
	
	// Do something
	
	return new MultiplyResponse();
}

Multiply and MultiplyResponse are stub classes. Notice how the two parameters (a1 and a2) are 'boxed' inside a single Multiply parameter, and how we return a MultiplyResponse object, even though we don't really want to return anything.

The tricky thing about this is that, as mentioned earlier, this 'boxing' process only happens in some cases:

  • When the number of parameters is more than one. For example, our add method has a single parameter, so it is not 'boxed'.

  • When the return type is void or a complex type. For example, our getValueRP method returns an int value, so it is not 'boxed'. On the other hand, both add and subtract return void, so what we really have to return is AddResponse and SubtractResponse.

The complete class would look like this:

package org.globus.examples.services.core.first.impl;

import java.rmi.RemoteException;

import org.globus.wsrf.Resource;
import org.globus.wsrf.ResourceProperties;
import org.globus.wsrf.ResourceProperty;
import org.globus.wsrf.ResourcePropertySet;
import org.globus.wsrf.impl.ReflectionResourceProperty;
import org.globus.wsrf.impl.SimpleResourcePropertySet;
import org.globus.examples.stubs.MathService_instance.AddResponse;
import org.globus.examples.stubs.MathService_instance.SubtractResponse;
import org.globus.examples.stubs.MathService_instance.GetValueRP;

public class MathService implements Resource, ResourceProperties {

	/* Resource Property set */
	private ResourcePropertySet propSet;

	/* Resource properties */
	private int value;
	private String lastOp;

	/* Constructor. Initializes RPs */
	public MathService() throws RemoteException {
		/* Create RP set */
		this.propSet = new SimpleResourcePropertySet(
				MathQNames.RESOURCE_PROPERTIES);

		/* Initialize the RP's */
		try {
			ResourceProperty valueRP = new ReflectionResourceProperty(
					MathQNames.RP_VALUE, "Value", this);
			this.propSet.add(valueRP);
			setValue(0);

			ResourceProperty lastOpRP = new ReflectionResourceProperty(
					MathQNames.RP_LASTOP, "LastOp", this);
			this.propSet.add(lastOpRP);
			setLastOp("NONE");
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	/* Get/Setters for the RPs */
	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	public String getLastOp() {
		return lastOp;
	}

	public void setLastOp(String lastOp) {
		this.lastOp = lastOp;
	}

	/* Remotely-accessible operations */

	public AddResponse add(int a) throws RemoteException {
		value += a;
		lastOp = "ADDITION";

		return new AddResponse();
	}

	public SubtractResponse subtract(int a) throws RemoteException {
		value -= a;
		lastOp = "SUBTRACTION";

		return new SubtractResponse();
	}

	public int getValueRP(GetValueRP params) throws RemoteException {
		return value;
	}

	/* Required by interface ResourceProperties */
	public ResourcePropertySet getResourcePropertySet() {
		return this.propSet;
	}
}
[Note]

This file is $EXAMPLE_DIR/org/globus/examples/services/core/first/impl/MathService.java.