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".
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
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"); }
This file is
|
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.
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 Resource, ResourceProperties { }
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;
}
}
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 { 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()); } }
ReflectionResourceProperty | |
---|---|
What we have just seen is the simplest type of resource creation: using |
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.
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; } }
This file is
|