Difference between revisions of "Adding State"

From GCube System
Jump to: navigation, search
(Adding State)
(Adding State)
Line 1: Line 1:
 
= Adding State =
 
= Adding State =
In the [[The_Development_Cycle|first part]] of this tutorial we have defined a stateless service: its responses to client requests depended solely on the requests. This is all well but in practice services may need to maintain some form of ''state'' that pre-exists, persists, and changes as a result of client invocations. Many gCube services are indeed, ''stateful''.
+
The service we defined in the [[The_Development_Cycle|first part]] of this tutorial was stateless: its responses to client requests depended solely on the requests. This is all well, but in practice services may need to maintain some form of ''state'' that pre-exists, persists, and changes as a result of client invocations. Many gCube services are indeed ''stateful''.
  
 
In this second part of the tutorial we will learn how to add state to our <code>SampleService</code>. In the dumb spirit of the service, the idea is to keep track of the number of visits of any user that has previously 'logged on' with the service.  
 
In this second part of the tutorial we will learn how to add state to our <code>SampleService</code>. In the dumb spirit of the service, the idea is to keep track of the number of visits of any user that has previously 'logged on' with the service.  
Line 8: Line 8:
 
* a <code>Stateful</code> port-type that allows the users to visit the service and thus updates the state of the the service. In particular, we plan a single operation <code>visit()</code> for this port-type.
 
* a <code>Stateful</code> port-type that allows the users to visit the service and thus updates the state of the the service. In particular, we plan a single operation <code>visit()</code> for this port-type.
  
For both, we need to repeat the [[The_Development_Cycle#A_PortType_Interface|steps]] shown for the <code>Stateless</code> port-type: add the port-types descriptions to the service profile, define the WSDL interface of each port-type, and provide the Java implementation of the two port-types. Additional steps will be required for state management.
+
(It will be [[WS-Resources and The Implied Resource Pattern|soon]] clear why we prefer two port-types with a single operation each over a single port-type with two operations.)
 +
 +
For both port-types, we need to repeat the [[The_Development_Cycle#A_PortType_Interface|steps]] shown for the <code>Stateless</code> port-type: add the port-types descriptions to the service profile, define the WSDL interface of each port-type, and provide the Java implementation of the two port-types. Additional steps will be required for state management.
  
 
== WS-Resources and The Implied Resource Pattern ==
 
== WS-Resources and The Implied Resource Pattern ==
  
Now, the overall state of our <code>SampleService</code> will be comprised of many 'pieces', one per user that logs on and then visits. We refer to these pieces somewhat more technically as ''stateful resources''. How should we go about creating and accessing stateful resources?   
+
The overall state of our <code>SampleService</code> will be comprised of many 'pieces', one per user that logs on and then visits. We refer to these pieces somewhat more technically as ''stateful resources''. How should we go about creating and accessing stateful resources?   
  
 
One approach would be to take some credentials from the users, say their name, when they log on with the <code>Factory</code> port-type. We could then create stateful resources that contain the count of their visits and identify such resources with the client name. When a client comes back to visit the service we could ask it to provide its name again so that we can identify the corresponding stateful resource and update the count of its visits.
 
One approach would be to take some credentials from the users, say their name, when they log on with the <code>Factory</code> port-type. We could then create stateful resources that contain the count of their visits and identify such resources with the client name. When a client comes back to visit the service we could ask it to provide its name again so that we can identify the corresponding stateful resource and update the count of its visits.

Revision as of 07:59, 25 March 2009

Adding State

The service we defined in the first part of this tutorial was stateless: its responses to client requests depended solely on the requests. This is all well, but in practice services may need to maintain some form of state that pre-exists, persists, and changes as a result of client invocations. Many gCube services are indeed stateful.

In this second part of the tutorial we will learn how to add state to our SampleService. In the dumb spirit of the service, the idea is to keep track of the number of visits of any user that has previously 'logged on' with the service. The identity of logged users and the number of their subsequent visits will then form the state of our augmented SampleService. In particular, we will add two new port-types:

  • a Factory port-type that allows users to log on and thus creates state within the service . In particular, we plan a single operation logon() for this port-type.
  • a Stateful port-type that allows the users to visit the service and thus updates the state of the the service. In particular, we plan a single operation visit() for this port-type.

(It will be soon clear why we prefer two port-types with a single operation each over a single port-type with two operations.)

For both port-types, we need to repeat the steps shown for the Stateless port-type: add the port-types descriptions to the service profile, define the WSDL interface of each port-type, and provide the Java implementation of the two port-types. Additional steps will be required for state management.

WS-Resources and The Implied Resource Pattern

The overall state of our SampleService will be comprised of many 'pieces', one per user that logs on and then visits. We refer to these pieces somewhat more technically as stateful resources. How should we go about creating and accessing stateful resources?

One approach would be to take some credentials from the users, say their name, when they log on with the Factory port-type. We could then create stateful resources that contain the count of their visits and identify such resources with the client name. When a client comes back to visit the service we could ask it to provide its name again so that we can identify the corresponding stateful resource and update the count of its visits.

In this approach, clients are a vital for state management because they need to explicitly identify it across requests. Unfortunately, identifiers service-specific; here we need a name, elsewhere we will need something else. The use of identifiers is also specific: here we suggested one parameter in the visit() operation, elsewhere could be two or three parameters in one or more other operations. This variability makes it impossible to build generic clients that can transparently access the state across of different services.

You may find this observation rather strange: what could a client do that does not require knowledge of the specific service it is talking to? Well, it turns out that if we can build sufficient conventions on how stateful resources are represented we can define generic clients capable of querying and changing the stateful resources of any service that complies with the conventions; we can also define generic clients that can uniformly destroy stateful resources, either immediately on based or some renewable expiry time; we can even define generic clients that allow others to subscribe for changes to the stateful resources. These are all key functionalities in a distributed system such as gCube. All we need to enable them is;

  • a uniform pattern to access stateful resources which does not change from service to service.
  • a standard that codifies this access pattern and builds other conventions on top of it.

Now, the Web Services Resource Framework (WSRF) is precisely one such standard and gCube adopts it. To access stateful resources, WSRF says: forget passing identifiers explicitly in operations such as visit(), which vary from port-type to port-type and from service to service, let us pass them instead implicitly as part of the endpoint of the port-type that exposes those operations. An invocation of visit() would thus be addressed to the endpoint of the Stateful port-type, but the address would include also the identifier of the stateful resource that is the target of the request, with no need to parameterise visit() with it. In fact, the client would not even have to know about this hidden identifier, would simply use the whole address as the address of the stateful resource. This is the access pattern that WSRF calls the implied resource pattern, for obvious reasons.

If you think about it, this address - or more appropriately, this qualified endpoint reference - identifies a pair (port-type,target stateful resource). WSRF calls this pair a WS-Resource and speaks of a qualified endpoint reference to the port-type as the endpoint reference of a WS-Resource. With the implied resource pattern and the corresponding terminology, we can now think of our new port-types as follows:

  • a Factory port-type that allows users to log on and thus creates WS-Resources. In particular, we plan a single operation for this port-type, logon(), which takes the name of the client and returns the endpoint reference of a WS-Resource dedicate to the client.
  • a Stateful port-type that allows the users to visit the service and thus updates its WS-Resources. In particular, we plan a single operation for this port-type, visit() which takes nothing and returns nothing but is invoked with the endpoint reference of a target WS-Resource.

A client will then invoke logon() on the Factory and use the resulting WS-Resource endpoint reference to invoke vist() on the Stateful port-type.

Extending the Profile

The following is the new profile that declares the new two port-types.

<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<ID></ID>
	<Type>Service</Type>
	<Profile>
		<Description>A very simple gCube Service</Description>
		<Class>Samples</Class>
		<Name>SampleService</Name>
		<Packages>
			<Main>
				<Description>Describes port-types</Description>
				<Name>Main</Name>
				<Dependencies>
					<Dependency>
						<Service>
							<Class>Samples</Class>
							<Name>SampleService</Name>
						</Service>
						<Package>Stubs</Package>
						<Version>1.0</Version>
						<Scope level="GHN"/>
						<Optional>false</Optional>
					</Dependency>
				</Dependencies>
				<GARArchive>org.acme.sample.gar</GARArchive>
				<PortType>
					<Name>acme/sample/stateless</Name>
					<WSDL/>
				</PortType>
				<PortType>
					<Name>acme/sample/stateful</Name>
					<WSDL/>
				</PortType>
				<PortType>
					<Name>acme/sample/factory</Name>
					<WSDL/>
				</PortType>
			</Main>
			<Software>
				<Description>Describes port-type stubs</Description>
				<Name>Stubs</Name>
				<Files><File>org.acme.sample.stubs.jar</File></Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

Save the profile as profile.xml and place it in the etc folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

Defining the port-type interfaces

WSDL interface for the factory service

Define a new WSDL interface for the Factory service:

<definitions name="Factory"
    targetNamespace="http://acme.org/sample"
    xmlns:tns="http://acme.org/sample"
  	xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:corefaults="http://gcube-system.org/namespaces/common/core/faults"
    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" >
    
    <import namespace="http://gcube-system.org/namespaces/common/core/faults" location="../gcube/common/core/faults/GCUBEFaults.wsdl"/>
   
 	<types>
	<xsd:schema targetNamespace="http://acme.org/sample">
		
		 <xsd:import namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing" 
schemaLocation="../ws/addressing/WS-Addressing.xsd" />
	
  		<xsd:element name="logon" type="xsd:string" />		
		<xsd:element name="logonResponse" type="wsa:EndpointReferenceType"/>
	</xsd:schema>
	</types>

	<message name="logonInputMessage">
		<part name="request" element="tns:logon"/>
	</message>
	<message name="logonOutputMessage">
		<part name="response" element="tns:logonResponse"/>
	</message>

	<portType name="FactoryPortType">
	
		<operation name="logon">
			<input message="tns:logonInputMessage"/>
			<output message="tns:logonOutputMessage"/>
			<fault name="fault" message="corefaults:GCUBEFaultMessage"></fault>
			<fault name="fault" message="corefaults:GCUBEUnrecoverableFaultMessage"></fault>
		</operation>
	
	</portType>

</definitions>

Notes:

  • the interface imports the WS-Addressing.xsd to make use of the WS-Adressing types' definitions
  • the interface exposes one single operation allowing to create a new stateful resource, the logon operation
  • the operation takes a string as input parameter
  • the operation returns an EndpointReferenceType that points to the stateful resource


Save the file as Factory.wsdl in the schema folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

WSDL interface for the service instance

Define a new WSDL interface for the service instance:

<definitions name="Stateful"
    targetNamespace="http://acme.org/sample"
    xmlns:tns="http://acme.org/sample"
  	xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:coretypes="http://gcube-system.org/namespaces/common/core/types"
    xmlns:corefaults="http://gcube-system.org/namespaces/common/core/faults">
    
    <import namespace="http://gcube-system.org/namespaces/common/core/faults" 
location="../gcube/common/core/faults/GCUBEFaults.wsdl"/>
    
 	<types>
	<xsd:schema targetNamespace="http://acme.org/sample">    	
	    <xsd:import namespace="http://gcube-system.org/namespaces/common/core/types" 
schemaLocation="../gcube/common/core/types/GCUBETypes.xsd"/>
	    	
  		<xsd:element name="aboutSF" type="coretypes:VOID" />
		<xsd:element name="aboutSFResponse" type="xsd:string" />				        
                    
	</xsd:schema>
	</types>

	<message name="aboutSFInputMessage">
		<part name="request" element="tns:aboutSF"/>
	</message>
	<message name="aboutSFOutputMessage">
		<part name="response" element="tns:aboutSFResponse"/>
	</message>

	<portType name="StatefulPortType">        
	
		<operation name="aboutSF">
			<input message="tns:aboutSFInputMessage"/>
			<output message="tns:aboutSFOutputMessage"/>
			<fault name="fault" message="corefaults:GCUBEFaultMessage"></fault>
		</operation>	
	
	</portType>

</definitions>

Notes:

  • the port-type exposes a single operation, the aboutSF operation
  • the operation does not take any input, in these cases it must be passed an element coretypes:VOID to avoid SOAP issues
  • the operation returns a string

Save the interface in a file Stateful.wsdl in the schema folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|---Stateful.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

Delving into the implementation

The implementation of the stateful part of the SampleService is a bit more complex than the stateless implementation and requires to code:

  1. the stateful context,
  2. the stateful resource,
  3. the resource home,
  4. and the two new port-types.

All the new classes implemented in this part will be placed in the new org.acme.sample.stateful package.

Important note: since we are leaving the stateless port-type as part of our example, we do not need to extend again the GCUBEStartupPortType (in charge of the initialisation of the GCUBEServiceContext) in one of the two new port-types. Instead, if we would start from scratch a complete stateful service, it is mandatory that one port-type extends the GCUBEStartupPortType (ideally, the Factory port-type, but it's up to developer.

The Stateful Context


We begin by implementing the Stateful Context. This context models the configuration of the acme/sample/stateful port-type. It transparently adds a lot of facilities to access the statefulness of the port-type.

package org.acme.sample.stateful;

import org.acme.sample.ServiceContext;
import org.gcube.common.core.contexts.GCUBEServiceContext;
import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext;

public class StatefulContext extends GCUBEStatefulPortTypeContext {

    public static String FREQUENT_USER_LIMIT_JNDI_NAME = "frequentUserLimit";
    
    private static GCUBEStatefulPortTypeContext cache = new StatefulContext();
    
    @Override
    public String getJNDIName() {return "acme/sample/stateful";}

    @Override
    public String getNamespace() {return "http://acme.org/sample";}

    @Override
    public GCUBEServiceContext getServiceContext() {
	return ServiceContext.getContext();
    }

    public static GCUBEStatefulPortTypeContext getPortTypeContext() {
	return cache;
    }        
}

Notes:

  • the class extends the GCUBEStatefulPortTypeContext
  • the class adopts the singleton pattern as suggested for any context
  • the getJNDIName() method returns the JNDI name of the port-type
  • the getServiceContext() method connects the port-type to the service context
  • the getNamespace() method returns the namespace of the port-type as defined in the WSDL interface

The Resource


The Resource class is the core of the statefulness. It models the state of a port-type and the way in which it can be accessed and modified. Our Resource will maintain the state in 2 properties:

  • the name of the user
  • a counter about how many times it accesses the resource
package org.acme.sample.stateful;

import org.gcube.common.core.state.GCUBEWSResource;
import org.globus.wsrf.ResourceException;

public class Resource extends GCUBEWSResource {

    protected static final String RP_NAME = "Name";
    protected static final String RP_VISITS = "Visits";
    protected static String[] RPNames = { RP_NAME, RP_VISITS };

    @Override
    protected void initialise(Object... args) throws ResourceException {
	if (args.length!=1) throw new IllegalArgumentException();
	
	try {
		String name = (String) args[0];
 		this.setName(name);       
 		this.setVisits(0);      
	}
	catch (Exception e) {
		throw new ResourceException(e);
	}
    }

    /**
     * {@inheritDoc}
     */
    public String[] getPropertyNames() {
	return RPNames;
    }

    /**
     * Returns the name of the RP stored on this Resource.
     * 
     * @return the name.
     */
    public String getName() {
	return (String) this.getResourcePropertySet().get(RP_NAME).get(0);
    }

    /**
     *  Returns the number of visits for the user
     * @return
     */
    public Integer getVisits() {
	return (Integer) this.getResourcePropertySet().get(RP_VISITS).get(0);
    }

    /**
     * Sets the name of the property.
     * 
     * @throws Exception
     */
    protected synchronized void setName(String name) throws Exception {
	this.getResourcePropertySet().get(RP_NAME).clear();
	this.getResourcePropertySet().get(RP_NAME).add(name);

    }

    /**
     * Sets the name of the property.
     * 
     * @throws Exception
     */
    protected synchronized void setVisits(Integer visits) throws Exception {
	this.getResourcePropertySet().get(RP_VISITS).clear();
	this.getResourcePropertySet().get(RP_VISITS).add(visits);
    }
    
}

Notes:

  • the class overrides the initialise() method to perform a custom initialisation of the properties
  • the two properties are not maintained as private members of the class, they are transparently managed by the superclass in a GCUBEWSResourcePropertySet instance. In order to do that the class:
    1. declares the name of the two properties
    2. overrides the getPropertyNames() method to return the property names
  • the getters and setters implementation of the properties reflects the GCUBEWSResourcePropertySet management of such properties

The Resource Home


The implementation of the Resource Home is very simple, since most of the work is performed by the superclass behind the scene. All it is needed is to override a method to connect the Home to the port-type context.

package org.acme.sample.stateful;

import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext;
import org.gcube.common.core.state.GCUBEWSHome;

public class Home extends GCUBEWSHome {

    @Override
    public GCUBEStatefulPortTypeContext getPortTypeContext() {	
	return StatefulContext.getPortTypeContext();
    }

}

Notes:

  • the class extends the GCUBEWSHome class
  • the getPortTypeContext() method connects the class to the stateful port-type

The Service instance port-type


The Service class provides an implementation of the Service instance port-type defined in the WSDL interface.

package org.acme.sample.stateful;

import org.acme.sample.ServiceContext;
import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext;
import org.gcube.common.core.contexts.GHNContext;
import org.gcube.common.core.faults.GCUBEFault;
import org.gcube.common.core.faults.GCUBEUnrecoverableException;
import org.gcube.common.core.types.VOID;
import org.globus.wsrf.ResourceException;

public class Service {

    public String aboutSF(VOID voidType) throws GCUBEFault {
	
	StringBuilder output = new StringBuilder();
	GHNContext nctx = GHNContext.getContext();
	ServiceContext sctx = ServiceContext.getContext();
	GCUBEStatefulPortTypeContext pctx = StatefulContext.getPortTypeContext();

	try {
		    final Resource resource = this.getResource();
		    resource.setVisits(resource.getVisits() + 1);
	    
		    output.append("Hello " + resource.getName()).append(", you have invoked porttype ").
		    	append(pctx.getName() + " \nof service " + sctx.getName()).
		    	append(", \nwhich you found at ").
		    	append(pctx.getEPR() + "in the gCube infrastructure " + nctx.getGHN().getInfrastructure()).	    
		    	append( " \nand you are in the Scope " + sctx.getScope()).
			append(" \nThis is your invocation N." + resource.getVisits() + "\n");
	
		    if (resource.getVisits() >= (Integer)pctx.getProperty(StatefulContext.FREQUENT_USER_LIMIT_JNDI_NAME, true)) {
		    	output.append("welcome in the frequent user club!");
		    }	    
		} catch (Exception e) {
			throw new GCUBEUnrecoverableException(e).toFault();
		}
		return output.toString();
    }

    /**
     * 
     * @return the stateful resource
     * @throws ResourceException if no resource was found in the current context
     */
    private Resource getResource() throws ResourceException {
	return (Resource) StatefulContext.getPortTypeContext().getWSHome().find();
    }    
}

Notes:

  • the class implements the aboutSF operation in the aboutSF() method
  • the method retrieves various contexts to use when producing the output string
  • the most interesting part of the class is the getResource() method demonstrating the way to access from the stateful context the actual Resource instance

The Factory port-type


The Factory class provides an implementation of the Factory port-type defined in the WSDL interface.

package org.acme.sample.stateful;

import org.apache.axis.message.addressing.EndpointReferenceType;
import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext;
import org.gcube.common.core.faults.GCUBEFault;
import org.gcube.common.core.faults.GCUBEUnrecoverableException;

public class Factory {    
    
    public EndpointReferenceType logon(String name) throws GCUBEFault {		
	//create/reuse the resource
	try {
		GCUBEStatefulPortTypeContext ptcxt = StatefulContext.getPortTypeContext();
		return ptcxt.getWSHome().create(ptcxt.makeKey(name), name).getEPR();
	} catch (Exception e) {	   
	    throw new GCUBEUnrecoverableException(e).toFault();
	} 
    }
}

Notes:

  • the class implements the logon operation in the logon method
  • the method retrieves the Stateful context and from it retrieves also the Home instance
  • by invoking the create method, the stateful resource is created with the given key (first input parameter) and with the given list of parameters (only the name variable here). The list is then passed to the initialise() method of the Resource instance
  • the makeKey() method of the StatefulContext instance creates a new GCUBEWSResourceKey
  • the getEPR() method of the Resource instance return the EndPointReference to use to access the new state from clients

Service folder tree after the implementation

All the java files described has to be placed in the src/org/acme/sample/stateful folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------stateful
|-------StatefulContext.java
|-------Resource.java
|-------Home.java
|-------Service.java
|-------Factory.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|---Stateful.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

JNDI configuration

The deploy-jndi-config.xml file must be updated in order to add the configuration of the stateful port-type. The stateful configuration has to declare the "home" resource that instructs gCore about how to create a new stateful resource. This configuration is automatically loaded by the gCore in the StatefulContext instance from which the configuration can be accessed programmatically.

<?xml version="1.0" encoding="UTF-8"?>
<jndiConfig xmlns="http://wsrf.globus.org/jndi/config">


	<service name="acme/sample">
		<environment 
		name="configDir" 
	 	value="@config.dir@" 
	 	type="java.lang.String"
	 	override="false" />
		 	
	</service>


	<service name="acme/sample/stateful">    
                <resource name="home" type="org.acme.sample.stateful.Home">
		 	<resourceParams>		                
		         <parameter>
		              <name>factory</name>
		              <value>org.globus.wsrf.jndi.BeanFactory</value>
		          </parameter>
		          <parameter>
		                <name>resourceClass</name>
		                <value>org.acme.sample.stateful.Resource</value>
		          </parameter>
		        </resourceParams>
	         </resource>
	 	
	   <environment 
		name="frequentUserLimit" 
	 	value="3" 
	 	type="java.lang.Integer"
	 	override="false" />
	</service>
  
</jndiConfig>

The following is worth noticing:

  • the JNDI still included the mandatory service element entirely dedicated to the SampleService as a whole
  • the service element named acme/sample/stateful groups the configuration of our stateful port-type; here it only includes the Home resource and a custom property. In the section How to publish the state we will see other configuration elements to add in this service element allowing to publish the service state
  • the frequentUserLimit environment element shows an example of custom property that can be programmatically read within the service code

Save the new configuration in the file called deploy-jndi-config.xml and place it in the etc folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------stateful
|-------StatefulContext.java
|-------Resource.java
|-------Home.java
|-------Service.java
|-------Factory.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|---Stateful.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

Building & Deploying

Towards Deployment: the WSDD descriptor

The two new port-types have to be declared in the deployment descriptor as follows.

<?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="acme/sample/stateless" provider="Handler" use="literal" style="document">
        <parameter name="className" value="org.acme.sample.stateless.Stateless"/>
        <wsdlFile>share/schema/org.acme.sample/Stateless_service.wsdl</wsdlFile>
        <parameter name="allowedMethods" value="*"/>
        <parameter name="handlerClass" value="org.globus.axis.providers.RPCProvider"/>
        <parameter name="scope" value="Application"/>
        <parameter name="loadOnStartup" value="true"/>
        <parameter name="securityDescriptor" value="@config.dir@/security_descriptor.xml"/> 
    </service>
  
  	<service name="acme/sample/stateful" provider="Handler" use="literal" style="document">
        <parameter name="className" value="org.acme.sample.stateful.Service"/>
        <wsdlFile>share/schema/org.acme.sample/Stateful_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="GCUBEProvider"/>
        <parameter name="loadOnStartup" value="true"/>
       	<parameter name="securityDescriptor" value="@config.dir@/security_descriptor.xml"/> 
    </service>
  
  	<service name="acme/sample/factory" provider="Handler" use="literal" style="document">
        <parameter name="className" value="org.acme.sample.stateful.Factory"/>
        <wsdlFile>share/schema/org.acme.sample/Factory_service.wsdl</wsdlFile>
        <parameter name="allowedMethods" value="*"/>
        <parameter name="handlerClass" value="org.globus.axis.providers.RPCProvider"/>
        <parameter name="scope" value="Application"/>
        <parameter name="loadOnStartup" value="true"/>
       	<parameter name="securityDescriptor" value="@config.dir@/security_descriptor.xml"/> 
    </service>
  
</deployment>

The deploy-server.wsdd file has to be updated with the above content in the etc folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------stateful
|-------StatefulContext.java
|-------Resource.java
|-------Home.java
|-------Service.java
|-------Factory.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|---Stateful.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

Configuring the build process

The build properties configuration file has now to declare the two new port-types to build.

package = org.acme.sample
lib.dir = Dependencies/SampleService
wsdl.1 = Stateless
wsdl.2 = Stateful
wsdl.3 = Factory
namespace.1=http://acme.org/sample

The build.properties file has to be updated with the above content in the etc folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------stateful
|-------StatefulContext.java
|-------Resource.java
|-------Home.java
|-------Service.java
|-------Factory.java
|------tests
|-------StatelessTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|---Stateful.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

Building the stubs and the service

The building process does not change. The same steps seen in the Stateless part of the tutorial have to be performed for building the stubs and the service.

(Un)Deploying the service

The (un)deploying process does not change. The same steps seen in the Stateless part of the tutorial have to be performed for deploying and undeploying the service.

A Test Client

package org.acme.sample.tests;

import org.acme.sample.stubs.FactoryPortType;
import org.acme.sample.stubs.StatefulPortType;
import org.acme.sample.stubs.service.FactoryServiceAddressingLocator;
import org.acme.sample.stubs.service.StatefulServiceAddressingLocator;
import org.apache.axis.message.addressing.AttributedURI;
import org.apache.axis.message.addressing.EndpointReferenceType;
import org.gcube.common.core.scope.GCUBEScope;
import org.gcube.common.core.types.VOID;
import org.gcube.common.core.utils.logging.GCUBEClientLog;
import org.gcube.common.core.contexts.GCUBERemotePortTypeContext;

public class StatefulTest {
    	
    	static GCUBEClientLog logger = new GCUBEClientLog(StatefulTest.class);
        
	public static void main(String[] args) throws Exception {
	    	
	    	logger.debug("Stateful client is running...");	    		    	
	    	EndpointReferenceType factory_endpoint = new EndpointReferenceType();
	    	factory_endpoint.setAddress(new AttributedURI(args[0]));
		FactoryPortType factoryPT = new FactoryServiceAddressingLocator().getFactoryPortTypePort(factory_endpoint);

		//we proxy the factory PT with the desired scope
		factoryPT = GCUBERemotePortTypeContext.getProxy(factoryPT, GCUBEScope.getScope(args[2]));
		EndpointReferenceType service_endpoint = factoryPT.logon(args[1]);				
		StatefulPortType statefulPT = new StatefulServiceAddressingLocator().getStatefulPortTypePort(service_endpoint);

		// then we have to proxy also the stateful PT... 				
		statefulPT = GCUBERemotePortTypeContext.getProxy(statefulPT, GCUBEScope.getScope(args[2]));
		System.out.println(statefulPT.aboutSF(new VOID()));

		//... but only once, if we use it again, there is no need to proxy again for subsequent uses
		System.out.println(statefulPT.aboutSF(new VOID()));
	}
}

Save the file StatefulTest.java under the src/org/acme/test folder under the service location.

Final structure of the service location

At the end of this section, the service location tree should look as follows:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|---deploy-server.wsdd
|---build.properties
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|------stateful
|-------StatefulContext.java
|-------Resource.java
|-------Home.java
|-------Service.java
|-------Factory.java
|------tests
|-------StatelessTest.java
|-------StatefulTest.java
|
|--schema
|---Stateless.wsdl
|---Factory.wsdl
|---Stateful.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService