Difference between revisions of "The Development Cycle"

From GCube System
Jump to: navigation, search
(Undeploying Stubs & Service)
(Building & Deploying the Service)
Line 509: Line 509:
  
  
 +
When need arises, you can also undeploy the service:
 +
 +
ant -f SampleService/build.xml undeployService.
 +
 +
Alternatively, you can undeploy the service by passing the name of its GAR to a dedicated gCore script:
 +
 +
gcore-undeploy-service org.acme.sample
  
 
== Starting the gHN ==
 
== Starting the gHN ==

Revision as of 16:43, 14 April 2008

Let us go through a quick tour of development with SampleService.

My First Profile

A good starting point with service development is its configuration. And a good starting point when configuring a service is the definition of its profile. The role and structure of gCube service profiles is described in detail elsewhere. Here we just show one of the smallest and yet perfectly well-formed profile a service may have:

<?xml version="1.0" encoding="UTF-8"?>
<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>
		<Version>1.0</Version>
		<Packages>
			<Main>
				<Name>Main</Name>
				<Version>1.0</Version>
				<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>...</WSDL>
				</PortType>
			</Main>
			<Software>
				<Description>Describes port-type stubs</Description>
				<Name>Stubs</Name>
				<Version>1.0</Version>
				<Files><File>org.acme.sample.stubs.jar</File></Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

By necessity, this profile reveals things yet to come. For the time being, be happy with a conceptual summary:

  • SampleService is a a particular type of gCube Resource, one of type Service. Like all gCube Resources has an ID, which it receives when it is formally registered with the infrastructure. Until then, SampleService does not have a proper identifier. No worries though, a temporary one will be generated by gCF at runtime.
  • The profile of SampleService specifies its Name and specifies its membership to a Class of functionally related gCube services. Classes are not pre-defined, and here we settle on Samples. A free-form Description and a Version complement the preliminary description of SampleService.
  • SampleService is physically comprised of Packages. A Main package identifies the main artefact to emerge from the build of the service, a GARArchive, and provides information about the service port-types, including their WSDLs (here omitted for mercy). The profile unveils our initial plans for a single stateless port-type, which we name by identifying the relative part of the URI at which it will be accessible on the gHN (more on this later).
  • Another Software element identifies the secondary build artefact of the service, its own stubs. The Main package depends always upon its stubs and this dependency must be explicit.

Note: If the notion of build artefact is not obvious to you, bide your time for a little longer or jump ahead.

Save the profile in a file called profile.xml and place it in the etc folder under the service location:

|-SampleService
|--etc
|---profile.xml
|--src
|--schema
|
|-Dependencies
|--SampleService

A Tiny JNDI

Next we move to the configuration of the code itself, for which we use JNDI technology. Again, the details and granularity of JNDI configuration are discussed elsewhere. Here we simply notice that, while the JNDI configuration might contain any information which the code needs to consult at runtime, the gCF raises precise requirements against the existence and shape of some of that information. In particular, the configuration should at least contain the following information:

<?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>

</jndiConfig>


Essentially, the configuration must have a service element entirely dedicated to SampleService, rather than more specific components yet to come. Furthermore, the element must:

  • specify the name of the service. This is not particularly constrained but must distinguish SampleService from any other which may be deployed on the same gHN. As we already do with Java and URI namespaces, we can approach uniqueness through a hierarchical structure, here of /-separated strings. In our case, the name acme/sample will suffice as a unique identifier.
  • include an environment element which specifies the absolute path to the configuration folder of SampleService. Contrary to expectations, this is not our etc folder under the service location. It is in fact another configuration folder allocated to SampleService at the point of service deployment. This will become clearer later. For the time being, we use the wildcard @config.dir@ to abstract over the precise location of this yet-to-be folder, and leave to gCore the task of resolving it at the point of deployment.

Note: The wildcard @config.dir is surely convenient at this stage. It is in fact necessary for any real gCube service. This is because the service may be dynamically deployed on any gHN and the absolute path of the configuration folder depends on the file system of that gHN. During service development, that location is an absolute unknown.

So far, there is little in our configuration which is specific to SampleService. Indeed, the JNDI above can be considered as boilerplate for any gCube service. We will see shortly how to inject more information into the JNDI configuration, but until that moment this minimal configuration will suffice.

Save the configuration in a 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
|--src
|--schema
|
|-Dependencies
|--SampleService

A PortType Interface

Enough configuration for now. Let us move to the definition of the interface for the first port-type we plan to add to SampleService. The general expectation is that all your interfaces will have this high-level structure (up to the choice of namespace prefixes and other stylistic matters of course):

<?xml version="1.0" encoding="UTF-8"?>
<definitions 
               xmlns:tns="<PORT-TYPE NAMESPACE>" targetNamespace="<PORT-TYPE NAMESPACE>" name="<PORT-TYPE NAME>"
               xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >

	<types>
	   <xsd:schema targetNamespace=".....">
			<!-- REQUEST & RESPONSE TYPE DEFINITIONS -->
	    </xsd:schema>
	</types>
	
	<!-- MESSAGE DEFINITIONS  -->

	<portType name="<PORT_TYPE NAME>portType">
		<!--OPERATIONS DEFINITIONS -->
	</portType>

</definitions>

All pretty standard WSDL, of course. And yet notice what follows:

  • we dedicate the WSDL to a single port-type (even though multiple port-types are admitted by the WSDL specs). This simplifies our building configuration and suggests than any form of sharing across port-types (type definition, messages, faults, etc.) be achieved through imports/includes (as it really should).
  • the name of the WSDL definitions does not play much of a role during development. Use anything that looks appropriate. Given that there is a one-to-one correspondence between WSDLs and port-types, we suggest you decide upon some name for the port-type and use it here.
  • the name of the portType element also refers to the port-type, though this has a very important role during the build of the service. In particular, it must end in 'portType'. We suggest you append this suffix to the name of the port-type you have chosen one bullet above.

The WSDL of our first port-type illustrates these conventions:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:tns="http://acme.org/sample" name="Stateless" targetNamespace="http://acme.org/sample"
    xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"  >

	<types>
		<xsd:schema targetNamespace="http://acme.org/sample">

			<xsd:element name="about" type="xsd:string"/>
			<xsd:element name="aboutResponse" type="xsd:string"/>

		</xsd:schema>
	</types>

	<message name="aboutInputMessage">
		<part name="request" element="tns:about"/>
	</message>
	<message name="aboutOutputMessage">
		<part name="response" element="tns:aboutResponse"/>
	</message>

	<portType name="StatelessPortType">
		    
             <operation name="about">
			<input message="tns:aboutInputMessage"/>
			<output message="tns:aboutOutputMessage"/>			
		</operation>

	</portType>
</definitions>

As you can see:

  • we have used the namespace http://acme.org/sample for our port-type.
  • we have settled on the name Stateless for it, and used it twice. The first time to name the whole interface definition and a second time in the name StatelessPortType of the portType element.
  • we have defined a single operation about for Stateless, which takes and returns a string. The idea is to expect the name of the caller as input and produce some information about the port-type as output.


Hard to imagine a simpler port-type, really. Now the last important convention:

Note: The file in which you save the interface must have the same name as the port-type.

Accordingly, save the interface in a file Stateless.wsdl in the schema folder under the service location:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|
|--src
|
|--schema
|---Stateless.wsdl
|
|-Dependencies
|--SampleService

A Simple Implementation

So far, the development process has been dominated by simple but numerous conventions upon the use of not necessarily simple but equally numerous formats. Frankly, all very unexciting and error-prone. Welcome (or welcome back) to the grim reality of web service development. gCF cannot take much of this pain away, unfortunately. In fact, gCF has only added to those conventions with no visible returns. Delving into the implementation of SampleService hopefully will give you the first glimpse of rewards to come.

A natural place to start the implementation is right at its centre, with a service context. Among other things, a service context manages configuration, scoping, security, and lifetime on behalf of the rest of the code. In gCF, you can have your own wih a few lines of code:

package org.acme.sample;
import ...

public class ServiceContext extends GCUBEServiceContext {
		
	/** {@inheritDoc} */
	protected String getJNDIName() {
                  return "acme/sample";
        }
	
}

As you can see, it is enough to:

  • inherit your service context from GCUBEServiceContext.
  • let the inherited code know how to act on your behalf by implementing getJNDIName() and returning the name of the service element dedicated to the service in the JNDI configuration file.

Note: This is a first instance of the template pattern: you complete the interface by exposing service-specific information which the inherited code uses to customise the work it carries out for you. It is simple, and it's used a lot within gCF.

The rest of the implementation could use the service context by instantiating ServiceContext. For efficiency and data sharing, however, it is more appealing to force the use of a single ServiceContext object throughout the implementation. A simple way of doing this is as follows:

/** Single context instance, created eagerly */
private static ServiceContext singleton = new ServiceContext();
		
/** Prevents accidental creation of more instances */
private ServiceContext(){};

/** Returns cached instance */
public static ServiceContext getContext() {return singleton;}
  • a single context object is created the first time the class is loaded (see singleton field declaration).
  • no other such object can be created by clients (see private constructor).
  • the object is available on demand (see getContext()).

Note: This is an instance of the singleton pattern and gCF cannot 'force' it as a contract upon your code without taking away too much of its simplicity. It just recommends it.

We will go back to the service context in the rest of the Primer, as its feature will be needed. Even If we didn't, it would already carry out important work during service startup, as you will see later on. More information on the role and features of a service context in gCF can be found here.

Next, the implementation of the port-type interface. This is also straightforward:

package org.acme.sample.stateless;
import ...

public class Stateless extends GCUBEStartupPortType {

	/** {@inheritDoc} */
	protected GCUBEServiceContext getServiceContext() {return ServiceContext.getContext();}
	
	public String about(String name) {		
		return ("Hello " + name + ", you have invoked service ")+this.getServiceContext().getName() + " ("+this.getServiceContext().getServiceClass() + ")";				
	}
}

but do please note:

  • the implementation extends GCUBEStartupPortType, which initialises the service context when the port-type is acivated in turn at container startup. One service port-type must always assume the role of 'fire-starter', in case you have many. We will refer to it as the startup port-type from now onwards.
  • the name and class of the service as defined in the service profile can be obtained from the service context, as can any piece of information in the profile if it proves useful for dynamic introspection.

By now the service location should look as follows:

|-SampleService
|--etc
|---profile.xml
|---deploy-jndi-config.xml
|
|--src
|---org
|----acme
|-----sample
|------ServiceContext.java
|------stateless
|-------Stateless.java
|
|--schema
|---Stateless.wsdl
|
|-Dependencies
|--SampleService

Building & Deploying

By now, the definition of the service is nearly completed: most configuration, interfaces, and implementation components are all in place.

Some Java code is still missing actually. This is the code that we wish to generate automatically from the port-type interface so as to hide away the low-level details of communication between service and its clients. In a word, we need the service stubs.

Stubs are key offerings for clients, which can bind to instances of our service and invoke its methods with the simplicity (but not costs!) of local method calls. Stubs are key for the implementation of the service as well, which can also use them as high-level models of inputs and outputs.

Note: Of course, this is not the case for our Stateless port-type, which manipulates plain strings and so does not need to depend on its own stubs. The port-types we will consider later on, however, will illustrate this dependency more obviously.

Now, assume for a moment we have somehow generated the stub code. Would we be done then? Well, we would have to compile both stub and service code, of course. And then we would have to hand things over to the gHN, i.e. deploy them in key locations under the gCore location. Code generation, packaging, and deployment are the main steps of an overall process of build which makes the service operational within the gHN.

In web service development, the build process is far from trivial and requires the coordinated use of multiple utilities for code generation, compilation, packaging, and deployment. Fortunately, we can simplify the execution of the build process using Ant as our build technology. Ant lets us define the build process declaratively in a buildfile, and to outsource its execution to the generic ANT engine. Even better news, there is a pre-defined buildfile which ships with gCore ($GLOBUS_LOCATION/share/gcore_tools/build-sample.xml) and has been designed to work seamlessly for any gCube service which follows the structure recommended above.

All you need to do is configure it, of course.

The Deployment Descriptor

A first piece of configuration to produce concerns deployment. We configure deployment so as to tell the gHN how to manage a Running Instance of the service. In particular, we need to tell the gHN how to instantiate the port-type implementations and how to recognise and correctly dispatch calls to them. The result is a deployment descriptor. It will be embedded in the build artefacts to be deployed in the gHN.

Here is a deployment descriptor for SampleService:

<?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/SampleService/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>
    	  
</deployment>

The deployment descriptor ought to include a service element for each port-type. The following is worth noticing:

  • the name attribute tells the gHN how to distinguish calls to the port-type from calls to other port-types. It is the relative part of the URI at which the port-type is accessible, its endpoint. In particular, the port-type endpoint is formed by concatenating the value of the name parameter, i.e. its relative endpoint, with the endpoint of the gHN itself. Given that the endpoint of the gHN has always the following form:
http:// <hostname>:<port>/wsrf/services

then the endpoint of the Stateless port-type will be the following:

http://<hostname>:<port>/wsrf/services/acme/sample/stateless
  • the className parameter names the class which implements the port-type. This is used by the gHN to create an instance of that class and forward to it all the calls for the port-type that it receives.
  • the wsdlFile parameter specifies the location of the port-type interface after deployment. We will be able to explain its value later on.
  • the scope parameter tells the gHN how many port-type instances it ought to create in order to serve requests. A value of Application indicates that we wish all requests to be handled by single object of the implementation class. Alternatively, a value of Request indicates that we want a new port-type instance for each request.
  • the loadOnStartup parameter indicates that the gHN should create the port-type instance as soon as it starts up, rather than when it receives the first request for it.


Now, notice the following conventions:

  • the name of the service element must match that in the PortType element of the service profile.


Save the deployment descriptor in a file called deploy-server.wsdd under the etc folder of the service location:

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

The Build Properties

Once you have copied the pre-defined buildfile ($GLOBUS_LOCATION/share/gcore_tools/build-sample.xml) into the service location and renamed it to the classic build.xml, you have to configure it.

First of all, the buildfile needs to know the absolute path to the location from which you will build the artefacts, the build location. This is expected as the value of the environment variable BUILD_LOCATION. For convenience, we use the location of the Eclipse workspace as our build location:

export BUILD_LOCATION = ...workspace location...

Second, the buildfile requires you to specify a small set of build properties:

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

where:

  • package is the 'root package' of the service implementation, i.e. the first package in the implementation hierarchy which identifies the implementation unambiguously. The buildfile uses this property to guarantee unique names to the artefacts it produces. As we shall see, this guarantees the they can be safely deployed without any clashes with other service implementations. Let us assume that org.acme.sample identifies uniquely the implementation of SampleService within ACME and, through Java package conventions, pretty much everywhere.
  • lib.dir is the path to the custom dependency folder, relative to the build location. The buildfile needs to know where are your custom dependencies so as to put them on the classpath during compilation.
  • wsdl.1 is the name of the file which containes the WSDL interface of the first port-type. The buildfile uses this property to locate the interface under the service location, process it, and generate stubs from it.
  • namespace.1 is the namespace of the port-type, as specified in its WSDL. The buildfile uses this property to determine the Java package of the stubs generated from the WSDL interface.

Note: If you had many port-types, you would list them all in arbitrary order (wsdl.2,wsdl.3,...).

Note: If you had different namespaces across WSDLs and/or auxiliary XSDs, you would also list them all in arbitrary order.

Note: By default, all namespaces are mapped to ${package}/stubs. In our case, they would be placed org.gcube.sample.stubs. If you wish to map different namespaces onto different packages, you can do so with optional properties package.<n>. The buildfile will consider its value when generating stubs for interface elements in the namspace namespace.<n>. For example, if you specify the property:

package.1=stateless

then the buildfile would create stubs for the Stateless port-type under org.ample.stubs.stateless.

Save the configuration in a build.properties file 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
|
|--schema
|---Stateless.wsdl
|
|
|--build.xml
|
|-Dependencies
|--SampleService

Building & Deploying Stubs

Build time. From the build location, type:

ant -f SampleService/build.xml stubs

This invokes ANT and tells it to process the buildfile of SampleService, specifically the instructions for building the stub library which are grouped under the target stubs. If all goes according to plans, the buildfile:

  • generates stubs source code from the port-type interfaces, compiles it, and packages it in a org.acme.sample.stubs.jar file.
  • deploys org.acme.sample.stubs.jar in two locations:
    • ${GLOBUS_LOCATION}/lib under the gCore location, where it will be found as a run-time dependency of the service. Everything in here is on the classpath when the gHN is running.
    • ${BUILD_LOCATION}/Dependencies/SampleService, where it can be found as a compile-time dependency of the service. This is why we specified the custom dependency location in the build properties.

Note: The build process generates also three additional port-type interfaces from the original one: Stateless_bindings.wsdl, Stateless_flattened.wsdl, and Stateless_service.wsdl. For the time being, you do not need to be overly concerned with these, but simply notice that the latter, Stateless_service.wsdl, is the port-type interface pointed at from the deployment descriptor of the service.

Note: The build process creates a build folder under the build location, clearing it if it already exists. It then uses this folder to organise all the elements required for the build (code, configuration, interfaces). It copies most elements from the service location, of course, but it complements them with other elements which are cross-service and ship with gCore. In most cases, you should not concern yourself with the contents of the build folder but it is good to know who creates it and why.

Note: Sometimes you might want to generate the stubs or go as far as to package them, but not deploy them. Say you want to make sure they are generated correctly, and perhaps have a peek inside the jar file, but are not ready to replace the stub library previously deployed. In these cases, you can invoke the buildStubs or the jarStubs targets:

ant -f SampleService/build.xml buildStubs   ...generates and compile stubs but does not package them...
ant -f SampleService/build.xml jarStubs       ...generates and packages stubs but does not deploy them... 

Note: When we will build the service, the buildfile will automatically resolve the dependency of SampleService to its own stubs from the custom dependency folder. Eclipse needs instead to be told explicitly. For this, you should add ${BUILD__LOCATION}/Dependencies/SampleService/org.acme.sample.stubs.jar to the build path of your Eclipse project (Properties -> Java Build Path -> Libraries -> Add Jars...). The same is true for any other custom dependency, of course. For stubs, however, the good news is that we do not have to manually copy the library into the custom dependency folder.

Building & Deploying the Service

From the build location, type:

ant -f SampleService/build.xml

which points implicitly to the build instructions grouped under the target deployService. If all goes according to plans, the buildfile:

  • compiles the service implementation and packages it in a org.acme.sample.jar file
  • further packages org.acme.sample.jar along with service configuration and port-type interfaces - both manually and automatically generated - in a org.acme.sample.gar file.
  • deploys org.acme.sample.gar in the gHN, primarily by copying:
    • org.acme.sample.jar - and any other dependency $BUILD_LOCATION/Dependencies/SampleService, but not the service's own stub library - in the $GLOBUS_LOCATION/lib folder under the gCore location.
    • all the service configuration under the service location in a $GCUBE_LOCATION/etc/org.acme.sample folder under the gCore location.
    • all the port-type interfaces in the GAR file in a $GCUBE_LOCATION/share/schema/org.acme.sample folder under the gCore location.


Note: As with the stubs (and in fact more commonly), sometimes you might want to compile the service implementation, perhaps package it, and even go as far as to produce the GAR file, but not deploy it. In these cases, you can invoke the buildService, jarService, or garService targets:

ant -f SampleService/build.xml buildService      ...compiles the service implementation, but does not package it
ant -f SampleService/build.xml jarService          ...compiles and packages the service implementation but does create a GAR
ant -f SampleService/build.xml garService         ...compiles and packages the service implementation, creates a GAR but does not deploy it 

If you then want to deploy a still undeployed GAR, you can pass it to a dedicated gCore script. From the build location, type:

gcore-deploy-service SampleService/org.acme.sample.gar


When need arises, you can also undeploy the service:

ant -f SampleService/build.xml undeployService.

Alternatively, you can undeploy the service by passing the name of its GAR to a dedicated gCore script:

gcore-undeploy-service org.acme.sample

Starting the gHN

Believe it or not, our 'quick' tour of service development is over.

We can now start the gHN and make sure our SampleService is alive and kicking in it. Making sure of it means to look into the logs emitted by the gHN and the code which is deployed in it. There are essentially two logfiles you should be prepared to look into:

  • nohup.out offers a brief report of the endpoints of the services which are managed by the gHN and highlights any early startup problem. SampleService's single endpoint ought to appear in the list and no obvious error message should be visible.
  • container.log is the main gHN log and contains a trace of the activities of all the services and components which have cared to leave it. Now, by inheriting from core gCF classes, the implementation of SampleService will leave a trace of its main activities in the gHN log. However, you need to configure logging in order to show it.

gCore uses Log4j as its logging technology and expects its configuration in $GCUBE_LOCATION/container-log4j.properties. For now, you need only to specify that all the logs which are produced by code in the org.acme.sample package ought to be revealed. This is easily done:

# Display any warnings generated by our code
log4j.category.org.globus=WARN
log4j.category.org.gcube=DEBUG
...
log4j.category.org.acme.sample=DEBUG     

To start the gHN, now type:

${BUILD_LOCATION}> gcore-start-container 

or, to enable full log in case of early startup problems:

${BUILD_LOCATION}> gcore-start-container -debug

In both cases, any previous running instance of the gHN will automatically killed.

If all goes well, your nohup.out log should match this template:

Starting SOAP server at: http://<hostname>:<port>/wsrf/services/ 
With the following services:

[1] ....
...
...
[...]: http://<hostname>:<port>/wsrf/services/acme/sample/stateless
...
...

In particular, it should show the full endpoint of the Stateless port-type of SampleService.

Similarly, container.log should show trace of the startup activities of SampleService:

2008-04-11 16:25:41,562 INFO  contexts.GHNContext [main,info:108] GHNContext: Initialising GHN
2008-04-11 16:25:41,588 INFO  contexts.GHNContext [main,info:108] GHNContext: Creating the resource
2008-04-11 16:25:41,616 DEBUG contexts.GHNContext [main,debug:84] GHNContext: Parsing GHN from /Users/fabio/workspace/gcube/GCORE_DEV/config/GHNProfile.xml
2008-04-11 16:25:41,794 DEBUG builders.GHNBuilder [main,debug:84] GHNBuilder: Updating GHN
2008-04-11 16:25:41,795 DEBUG contexts.GHNContext [main,debug:84] GHNContext: Analysing memory status...
...
...
2008-04-11 16:25:43,146 INFO  sample.ServiceContext [main,info:108] SampleService: Initialising Running Instance
2008-04-11 16:25:43,149 DEBUG sample.ServiceContext [main,debug:84] SampleService: Parsing RI from ....(omitted)
2008-04-11 16:25:43,155 DEBUG builders.RIBuilder [main,debug:84] SampleService: Updating Running instance
2008-04-11 16:25:43,164 INFO  sample.ServiceContext [main,info:108] SampleService: Running Instance has been updated
2008-04-11 16:25:43,165 INFO  sample.ServiceContext [main,info:108] SampleService: Managing security with a GCUBEServiceSecurityManagerImpl
2008-04-11 16:25:43,166 INFO  contexts.GHNContext [main,info:108] GHNContext: Registering service Samples SampleService...
...
...
2008-04-11 16:25:43,168 INFO  sample.ServiceContext [main,info:108] SampleService: Running Instance has moved to status: initialised
2008-04-11 16:25:43,170 INFO  sample.ServiceContext [Thread-24,info:108] SampleService: Running Instance has moved to status: activated
2008-04-11 16:25:43,179 INFO  sample.ServiceContext [Thread-24,info:108] SampleService: Running Instance has been updated
...
...
2008-04-11 16:25:43,271 INFO  sample.ServiceContext [Thread-27,info:108] SampleService: ready event invoked on SampleService
2008-04-11 16:25:43,272 INFO  sample.ServiceContext [Thread-27,info:108] SampleService: Running Instance has moved to status: ready
2008-04-11 16:25:43,281 INFO  sample.ServiceContext [Thread-27,info:108] SampleService: Running Instance has been updated
...
...


Without going unnecessarily into details, the logs show the various stages of initialisation undergone by SampleService, from its initialisation and activation to the point in which the service is ready to receive requests at its port-types. Incidentally, this log shows the amount of work which gCF has already carried out on your behalf.

A Minimal Client

We conclude the tour with an invocation of SampleService. The simple client below exemplifies the use of stubs to bind to a Running Instance of SampleService and invoke the about() method of its Stateless port-type:

package org.acme.sample.tests;
import ...

public class StatelessTest {

	public static void main(String[] args) throws Exception {
		
		EndpointReferenceType endpoint = new EndpointReferenceType();
		endpoint.setAddress(new AttributedURI(args[0]));
		
		StatelessPortType stub = new StatelessServiceAddressingLocator().getStatelessPortTypePort(endpoint);
		stub=GCUBERemotePortTypeContext.getProxy(stub,GCUBEScope.getScope(args[1]));
                
                System.out.println(stub.about(args[2]));

		
	}
}

In brief, StatelessTest:

  • creates a EndpointReferenceType which points to the Stateless port-type of a Running Instance of SampleService. In particular, it initialises the EndpointReferenceType with the endpoint of the remote port-type, here obtained as command-line argument (http://<hostname>:<port>/wsrf/services/acme/sample/stateless).
  • uses the EndpointReferenceType to obtain a stub of the remote-port-type.
  • uses gCF facilities to obtain a proxy for the stub which knows how to scope future calls. In particular, it configures the proxy to issue calls in a scope parsed from a further command-line argument (e.g. /gcube/devsec). More information on scoping of calls to gCube services is available here.
  • invokes the about() method of the remote port-type against the stub proxy, using a third command-line argument for the expected input (e.g. John Doe.

StatelessTest depends on standard gCore libraries (e.g. for EndpointReferenceType, GCUBERemotePortTypeContext, and GCUBEScope) as well as on SampleService's stub library. Of course, these dependencies must be on the classpath during compilation and execution. How you place them there depends on whether you wish to compile and run StatelessTest from the shell or within Eclipse.

If you wish to compile and run StatelessTest from the shell, and you are on the same machine which hosts the gHN on which you have deployed SampleService, then all you need to do is to load the context of $GLOBUS_LOCATION/lib on the classpath (for the stubs library will have been already deployed there). There is a gCore scripts which does this for you:

source /Users/fabio/workspace/gcube/GCORE_DEV/bin/gcore-load-env

If instead you wish to compile and run from an Eclipse project, then you need to make sure the project has dependencies to the user libraries GCORELIBS and SAMPLESERVICEDEPS defined above. Of course, you can always place StatelessTest under the service location, where those dependencies are already defined.