| Implementation Details
There are few Java classes are involved in the implementaion of whole example and it is important to know the role and requiremetns of
each Java class. Our example is based on the Implied Resource Pattern which is the recommended way of implementing stateful Web Services;
therefore we have one Factory Service and two Instance Service. Below is the list of all classes used in the implementation along with
their brief description.
Table 1. List of the Java Classes implemented in the Tutorial
Java Classes | Their Role in the Application |
---|
PersonFactoryService |
There is only one Factory Service which is deployed twice to instantiate both Instance Services (PersonNameService and
PersonAddressService). Technically there is no need to deploy the Factory Service twice for each service; better approach is to change
the createResource method which takes the String and based on the String it initialize the corresponding service.
In this tutorial we have followed crude approach to keep the code simple; but in the next tutorial I will discuss the other option w
hich fairly complicates the code.
| PersonNameService | PersonNameService Service is simple service only wrapping the operations related to the Name of the Person resource. | PersonAddressService |
Similar to PersonNameService with only difference that PersonAddressService has logic to change the Address property of the Person
resource.
| PersonResourceHome |
The Resource Home class is used to initialize the Person resource. Be sure we have only one Person resource shared among two services
therefore we have only one Resource Home.
| PersonResource | The Person resource is shared among different services and has very simple implementation. | PersonResourceQNames | The PersonResourceQNames is the interface to create the Qualified Names for our resource with static and final varaibles. |
| Important Tip
One important thing to remember, which can be considered as bug, the qualified names declared in this interface only matters and client
should use the same qualified names for different Resource Properties accordingly. In other words if the WSDL file has Resource Property
with local name as "address" and in the interface the same Resource Property has local name "myAddress" even then
Server Side implementation will work, but now client has to use "myAddress" rather than "address" to access the
Resource Property.
|
|
Implementation Details of PersonServiceQNames
PersonServiceQNames is simple interface with final and static variables; which are QName of the ResourceProperties declared in the WSDL.
The interface is using two different namespaces declared in the WSDL, one for our separate schema i.e. http://test.wsrf.resource.sharing,
which represents that data type of the Person Resource and one for the namespace of the services declared in the WSDL
i.e. http://test.wsrf.dl.ac.uk/name/person which is used for the fully qualified name of the Person Resource.
Technically these namespaces don't make too much difference as they can be anything even different from the what are declared in the WSDL
of Instance Service and the XML schema but using different namespaces will kill the purpose of the WSDL as then client has to use the same
namespace and QNames as used by the server side implementation but the client can only guess the QNames of the ResourceProperties from
the WSDL. Therefore, namespace and QNames on the server side implementation should match namespace and QNames declared in the WSDL,
although the WS-Core; the Globus implementation of the WS-RF doesn’t impose this restriction on the server side implementation.
Listing 5. Source Code of the PersonServiceQNames
package uk.ac.dl.wsrf.sharing.name.person;
import javax.xml.namespace.QName;
public interface PersonServiceQNames {
public static final String NS = "http://test.wsrf.dl.ac.uk/name/person";
public static final String NS2 = "http://test.wsrf.resource.sharing";
public static final QName RPNAME = new QName(NS2, "myName");
public static final QName RPAddress = new QName(NS2, "myAddress");
public static final QName RESOURCEPROPERTIES = new QName(NS,
"NameResourcePropertiesSet");
}
|
Implementation Details of PersonFactoryService
Implementation of PersonFactoryService is very similar to any other Factory Services with just single method createResource which creates
the EndpointReference for the Resource and returns to the client. In reality Factory Service has nothing to do with Instance service,
Factory Service only instantiate the resource which it shares with Instance Service (through "deploy-jndi-config.xml")and manually
creates the EndpointReference for the instance service and returns. The createResource operation relies heavily on the
deploy-jndi-config.xml and deploy-server.wsdd to instantiate the appropriate resource. The configuration fiels deploy-jndi-config.xml
and deploy-server.wsdd are discussed in the next section.
Listing 6. Source Code of the PersonFactoryService
import java.rmi.RemoteException;
import java.net.URL;
import org.globus.wsrf.ResourceContext;
import org.globus.wsrf.ResourceKey;
import org.apache.axis.message.addressing.EndpointReferenceType;
import org.apache.axis.MessageContext;
import org.globus.wsrf.utils.AddressingUtils;
import org.globus.wsrf.container.ServiceHost;
import uk.ac.dl.wsrf.sharing.name.person.*;
public class PersonFactoryService {
/* Implementation of createResource Operation */
public CreateResourceResponse createResource(CreateResource request) throws
RemoteException {
System.out.println("PersonFactoryService create");
ResourceContext ctx = null;
PersonResourceHome home = null;
ResourceKey key = null;
/* First, we create a new PersonResource through the PersonResourceHome */
try {
ctx = ResourceContext.getResourceContext();
home = (PersonResourceHome) ctx.getResourceHome();
key = home.create();
} catch (Exception e) {
e.printStackTrace();
}
EndpointReferenceType epr = null;
/* We construct the instance's endpoint reference. The instance's service
* path can be found in the WSDD file as a parameter. */
try {
URL baseURL = ServiceHost.getBaseURL();
String instanceService = (String) MessageContext
.getCurrentContext().getService().getOption("instance");
String instanceURI = baseURL.toString() + instanceService;
// The endpoint reference includes the instance's URI and the resource key
epr = AddressingUtils.createEndpointReference(instanceURI, key);
} catch (Exception e) {
e.printStackTrace();
}
/* Finally, return the endpoint reference in a CreateResourceResponse */
CreateResourceResponse response = new CreateResourceResponse();
response.setEndpointReference(epr);
return response;
}
}
|
In deploy-jndi-config.xml (discussed later) for Factory Service we have something similar to the following where Factory Service
doesn't have its own resource but has link to the resource declared in the "AnyService":
Listing 7. Resource Linkage in "deploy-jndi-config.xml"
<service name="AnyService">
<resource name="home" type="XXX.XXX.XXX">
<resourceParams>
.........
</resourceParams>
</resource>
</service>
<service name="FactoryService">
<resourceLink name="home" target="java:comp/env/services/AnyService/home"/>
</service>
|
If the name of resource in the "AnyService" is changed to "myResource" from home then FactoryService will have something
similar to following code in the "deploy-jndi-config.xml":
Listing 8. Another way of Resource Linkage in "deploy-jndi-config.xml"
< resourceLink name="home" target="java:comp/env/services/AnyService/myResource"/ >
|
The following code fragment for the Factory Service, which is normally used in different WS-RF online examples and tutorials, will only
fetch that resource whose name is "home" in the deploy-jndi-config.xml for the Instance Service; if resource has any other
name then it can't be fetched or instantiated directly and the only option is to use InitialContext which will be dicussed in the next
part of this series. "FactoryService" is only instantiating the resource which it
is sharing with the Instance service (in above example it is "AnyService").
Listing 9. Source COde to Retrieve Resource Home
ctx = ResourceContext.getResourceContext();
home = (PersonResourceHome) ctx.getResourceHome();
|
Implementation of PersonResource
Implementation of PersonResource is very simple; it is encapsulating the Name and the Address part of our Person resource. Axis will
create the matching Java classes for the Name and the Address part of our Person resource in the package according to namespace used
in the XML schema. The sample source code has few additional code related to Topic, TopicList and Notification, which is not used in
this example but is the basis of the forthcoming tutorials on the Web Services Notification and is discussed in those tutorials.
PersonResource.java is the implementation of the Person resource to declare and create instances of the resource, and in fact it is also
initializing the resources with the Unique Key, which is used in the EPR. Direct access to PersonResource is prohibited and all calls
to initialize the instances of resource should be through PersonResourceHome.java which is discussed in next section. Below is the
implementation of PersonResource.java.
Listing 10. Source Code of the PersonResource
package uk.ac.dl.wsrf.sharing.name.person;
import org.globus.wsrf.Resource;
import org.globus.wsrf.ResourceProperties;
import org.globus.wsrf.ResourceProperty;
import org.globus.wsrf.ResourcePropertySet;
import org.globus.wsrf.ResourceIdentifier;
import org.globus.wsrf.impl.SimpleResourceProperty;
import org.globus.wsrf.impl.SimpleResourcePropertySet;
import org.globus.wsrf.TopicListAccessor;
import uk.ac.dl.wsrf.sharing.name.person.*;
import sharing.resource.wsrf.test.*;
import org.globus.wsrf.TopicList;
import org.globus.wsrf.impl.SimpleTopicList;
import org.globus.wsrf.impl.ResourcePropertyTopic;
import org.globus.wsrf.Topic;
public class PersonResource implements Resource, ResourceProperties,
ResourceIdentifier, TopicListAccessor {
/** the identifier of this Resource */
private Object id;
/** Stores the ResourceProperties */
private ResourcePropertySet propSet;
/* Variable for Resource property */
private Name name;
private Address address;
/** Resource property name and address */
private ResourceProperty nameRP;
private ResourceProperty addressRP;
/** Create Topic List**/
private TopicList topicList;
/** initializes the PersonResource. */
public void initialize() throws Exception {
System.out.println("PersonResource.initialize() PersonService");
// choose an ID
this.id = new Integer(hashCode());
// create the resource property set
this.propSet = new SimpleResourcePropertySet(
PersonServiceQNames.RESOURCEPROPERTIES);
// create resource properties
this.nameRP = new SimpleResourceProperty(PersonServiceQNames.RPNAME);
this.addressRP = new SimpleResourceProperty(PersonServiceQNames.
RPAddress);
//Initialize TopicList
this.topicList = new SimpleTopicList(this);
// Wrap ResourceProperty to make it Topic
this.nameRP = new ResourcePropertyTopic(this.nameRP);
this.addressRP = new ResourcePropertyTopic(this.addressRP);
// Add RP in the TopicList
this.topicList.addTopic((Topic)this.nameRP);
this.topicList.addTopic((Topic)this.addressRP);
this.propSet.add(this.nameRP);
setName(new Name("Asif ", "Akram", " ", "Mr."));
this.nameRP.add(name);
this.propSet.add(this.addressRP);
setAddress(new Address("Daresbury", "United kingdom", "Cheshire", "23",
"Wilson Patten"));
this.addressRP.add(address);
}
public ResourcePropertySet getResourcePropertySet() {
return propSet;
}
public void setNameRP(Name nameValue) {
setName(nameValue);
this.nameRP.set(0, name);
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public void setAddressRP(Address addressValue) {
setAddress(addressValue);
this.addressRP.set(0, address);
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Object getID() {
return id;
}
public TopicList getTopicList() {
return topicList;
}
}
|
| Important Tip
In the WSDL for Instance Services there is one element PersonResourcePropertiesSet, which has two sub-elements of the type Name and Address.
PersonResourcePropertiesSet is the resource and in the programming API it is represented as ResourcePropertySet and sub-elements of
ResourcePropertySet are called ResourceProperties.WS-RF based Web Service can have any number of the Resource Properties and all of them are
wrapped in different ResourcePropertySet for later retrieval, modification or deletion. It is always nice to follow good naming conventions
which should be self explanatory, in the source code any data type, which will encapsulate the "resource" for our Instance
Service ends with ResourcePropertiesSet to indicate that it is ResourcePropertySet, and the name of all sub-elements (Resource Property)
of the resource ends with "RP" (Resource Property) to keep things clear and simple.
|
|
In the code the variable "propSet" is initialized as the object of the SimpleResourcePropertySet, and you may have figured out that
the ResourcePropertySet is the interface and the "SimpleResourcePropertySet" is the concrete implementation of the
ResourcePropertySet.
The constructor of the SimpleResourcePropertySet takes the QName i.e. the qualified name of our resource property as declared in the
WSDL file. Similarly the ResourceProperty is an interface and the ReflectionResourceProperty and the SimpleResourceProperty are its
implementation used to declare and initialize the variable nameRP (name Resource Property) and addressRP (address Resource Property).
ReflectionResourceProperty has three different constructors and the constructor normally used is: ReflectionResourceProperty(QName name,
String propertyName, Object obj), QName should match the qualified name of the Resource Property in the WSDL and
"String propertyName"
is the name of the variable in the implementation. It can be called anything like myName different from the name used in the WSDL.
SimpleResourceProperty(QName name) only takes QName of the Resource Property and the variable encapsulating the value of the Resource
Property has to be coupled manually with the Resource Property. In the source code SimpleResourceProperty is used.
Finally we have to add these newly created Resource Property into our ResourcePropertySet, e.g. this.propSet.add(nameRP) and assign
the initial values
to the Resource Property by calling utility set method for private variables.
One important thing in the source code is to import the package "sharing.resource.wsrf.test.*", in which WSDL2Java will create
all Java mappings for the Name and the Address data types which are later used in the resource. Following are the key points related to the
PersonResource:
- Local variables for Name and Address
/* Variable for Resource property */
private Name name;
private Address address;
|
- Declaring ResourcePropertySet
/** Stores the ResourceProperties */
private ResourcePropertySet propSet;
|
- Declaring ResourceProperty objects for Name and Address
/** Resource property name and address */
private ResourceProperty addressRP;
private ResourceProperty nameRP;
|
- Initializing the ResourcePropertySet
this.propSet = new SimpleResourcePropertySet(PersonServiceQNames.RESOURCEPROPERTIES);
|
- Initializing the ResourceProperty for Name and Address
this.nameRP = new SimpleResourceProperty(PersonServiceQNames.RPNAME);
this.addressRP = new SimpleResourceProperty(PersonServiceQNames. RPAddress);
|
- Initializing Address and Name ResourceProperty after adding them in the ResourcePropertySet
/*Initializing Name and adding in the ResourcePropertySet*/
this.propSet.add(this.nameRP);
setName(new Name("Asif ", "Akram", " ", "Mr."));
this.nameRP.add(name);
/*Initializing Name and adding in the ResourcePropertySet*/
this.propSet.add(this.addressRP);
setAddress(new Address("Daresbury", "United kingdom", "Cheshire", "23",
"Wilson Patten"));
this.addressRP.add(address);
|
- get and set methods for Name and Address objects
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address }
public Name getName() { return name; }
public void setName(Name name) { this.name = name; }
|
- get method for Name and Address ResourcePropety
public void setNameRP(Name nameValue) {
setName(nameValue);
this.nameRP.set(0, name);
}
public void setAddressRP(Address addressValue) {
setAddress(addressValue);
this.addressRP.set(0, address);
}
|
The one main issue in the initializion of the Name and the Address part of the Person resource is that variable in the Resource Property
like title, firstName or lastName are not in the same order as they are declared in the schema, therefore as a developer you have to
check the order of the variables in the constructor of the corresponding class. Better solution is to use the default constructor to
create the object i.e. Name/Address and set different values through their set methods.
Implementation of PersonResourceHome
PersonResourceHome.java is only to initialize our PersonResource.java. PersonResourceHome extends ResourceHomeImpl and has create() method
to instantiate the resource which returns the ResourceKey. The ResourceKey is the Unique Key for the resource instance and is used to
identify specific instance of the resource in the EPR. Below is the complete implementation of the PersonResourceHome
Listing 11. Source Code of the PersonResourceHome
import org.globus.wsrf.ResourceKey;
import org.globus.wsrf.impl.ResourceHomeImpl;
import org.globus.wsrf.impl.SimpleResourceKey;
import org.globus.wsrf.Resource;
import org.globus.wsrf.ResourceContext;
public class PersonResourceHome extends ResourceHomeImpl {
private static java.util.Hashtable resources = new java.util.Hashtable ();
private int counter=0;
public ResourceKey create() {
System.out.println("PersonResourceHomeFactory create() " + counter++);
try {
PersonResource resource = (PersonResource) createNewInstance();
resource.initialize();
ResourceKey key = new SimpleResourceKey(keyTypeName, resource.getID());
this.add(key, resource);
resources.put(key,resource);
return key;
} catch (Exception e) {
System.out.println("Exception when creating PersonResource: ");
e.printStackTrace();
return null;
}
}
}
|
Implementation of PersonNameService
In the actual implementation both Instance Services are named as PersonService but service which is referred as PersonNameService in the
tutorial is in the package "uk.ac.dl.wsrf.sharing.name.person" and the service termed as PersonAddressService is in the package
"uk.ac.dl.wsrf.sharing.address.person". It was better to name both services differently but I preferred to put them in different
packages to distinguish them.
PersonService has the business logic to manage the Person resource according to the corresponding WSDL's. The PersonService in the package
"uk.ac.dl.wsrf.sharing.name.person" has two public methods the getNameRP() and the changeName(); similarly PersonService in the
package "uk.ac.dl.wsrf.sharing.address.person" has two public methods the getAddressRP() and the changeAddress() along with the
utility method getResource() to locate the resource referenced in the EPR.
Listing 12. Source Code of the PersonNameService
package uk.ac.dl.wsrf.sharing.name.person;
import java.rmi.RemoteException;
import org.globus.wsrf.ResourceContext;
import uk.ac.dl.wsrf.sharing.name.person.*;
import sharing.resource.wsrf.test.Name;
public class PersonService {
public PersonService() throws RemoteException { }
public String changeName(Name name) throws RemoteException {
System.out.println("PersonNameService changeName Method Called");
PersonResource nameResource = getResource();
nameResource.setNameRP(name);
return "Name Changed " + name.getFirstName() + " "+name.getLastName() + " !";
}
public Name getNameRP(GetNameRP params) throws RemoteException {
System.out.println("PersonNameService getNameRP Method Called");
PersonResource personResource = getResource();
return personResource.getName();
}
public PersonResource getResource() throws RemoteException {
System.out.println("PersonNameService getResource() Method Called");
Object resource = null;
try {
resource = ResourceContext.getResourceContext().getResource();
} catch (Exception e) {
throw new RemoteException("", e);
}
PersonResource personResource = (
PersonResource) resource;
return personResource;
}
}
|
|
Implementation of PersonAddressService
The implementation of the PersonService in the package uk.ac.dl.wsrf.sharing.address.person, is very similar to the PersonService
discussed in the last section and is exactly on the same lines. The only difference in this PersonService is that it is manipulating the
Address part of the Person resource. Below is the complete code of PersonService in the package uk.ac.dl.wsrf.sharing.address.person.
Listing 13. Source Code of the PersonAddressService
package uk.ac.dl.wsrf.sharing.address.person;
import java.rmi.RemoteException;
import org.apache.axis.message.addressing.EndpointReferenceType;
import org.globus.wsrf.ResourceContext;
import org.globus.wsrf.ResourceKey;
import org.globus.wsrf.utils.AddressingUtils;
import org.apache.axis.MessageContext;
import uk.ac.dl.wsrf.sharing.name.person.*;
import sharing.resource.wsrf.test.*;
import javax.xml.soap.SOAPElement;
import javax.xml.namespace.QName;
import org.globus.wsrf.impl.SimpleResourceKey;
public class PersonService {
public PersonService() throws RemoteException {
}
public String changeAddress(Address address) throws RemoteException {
System.out.println("PersonService changeAddress Method Called");
PersonResource resource = getResource();
resource.setAddressRP(address);
return "Address Changed " + address.getCity() + " " +
address.getCountry() + " !";
}
public Address getAddressRP(GetAddressRP params) throws RemoteException {
System.out.println("PersonService getAddressRP Method Called");
PersonResource resource = getResource();
return resource.getAddress();
}
public PersonResource getResource() throws RemoteException {
System.out.println("PersonService getResource() Method Called");
Object resource = null;
try {
resource = ResourceContext.getResourceContext().getResource();
} catch (Exception e) {
throw new RemoteException("", e);
}
PersonResource personResource = (
PersonResource) resource;
return personResource;
}
}
|
|
| Page 4 of 11 | |
| |