Difference between revisions of "Single Port-Type"

From GCube System
Jump to: navigation, search
(Refining the implementation)
(Refining the implementation)
Line 302: Line 302:
 
== Refining the implementation ==
 
== Refining the implementation ==
  
-Username
+
-user

Revision as of 15:46, 24 March 2008

From configuration to testing with a single Port-Type

Even if not compulsory, we strongly suggest to adopt the Eclipse 3.3 as development platform. In such an IDE, open a new workspace with a Java 1.5 compiler and create a new Java project by specifying a source folder (named from now on, SERVICE folder). Then, fill a user-library with all JARs in gCore lib and name it CONTAINERLIB.

Structuring the service code

Prepare the SERVICE folder structure as follows:

  1. create a etc folder where to place your configuration files
  2. create a org/acme/sample folder where to place your source code
  3. create a schema folder where to place the remote interface files
  4. copy the share/gcube_tools/build.xml file into your SERVICE folder

Profiling for the infrastructure

Create a new XML file named Profile.xml and place it in the SERVICE/etc folder. This file profiles the service in such a way that the instance of a service can be discovered by others and eventually dynamically deployed in a gCube infrastructure.

<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>
			</Main>
			<Software>
				<Description>Describes port-type stubs</Description>
				<Name>Stubs</Name>
				<Files><File>org.acme.sample.stubs.jar</File></Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

The file identifies our service by assigning it a ServiceClass and a ServiceName. It also describes the service decomposition: it is composed by two packages, the service itself and its stubs and that the first one has a GHN-scoped dependency against the second one. Finally, it indicates that the service has a single Port-Type named acme/sample/stateless.

JNDI configuration

Create a new XML file named deploy-jndi-config.xml and and place it in the SERVICE/etc folder. It will include either the global service configuration and the all the Port-Type ones. The file has a two-fold role:

  1. tells to the gCube framework about the service
  2. makes available to the service at runtime the information included there
<jndiConfig xmlns="http://wsrf.globus.org/jndi/config">
	<service name="acme/sample">
	
		<environment 
		name="profile" 
	 	value="@config.dir@/profile.xml" 
	 	type="java.lang.String"
	 	override="false" />
		 	
	</service>
</jndiConfig>

Notes:

  • at this stage, the file only include a service section reporting the name of the service ("acme/sample") and the name of the profile created
  • the @config.dir@ is a placeholder replaced at deployment time with the real path of the SERVICE/etc folder

Sketching Port-type Interfaces

WSDL structure

The following is the basic skeleton of any WSDL interface:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:tns="...." xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
name="Stateless" targetNamespace=".....">
	<types>
		<xsd:schema targetNamespace=".....">
			<!-- REQUEST AND RESPONS TYPE DEFINITIONS -->
		</xsd:schema>
	</types>
	
	<!-- MESSAGES  -->

	<portType name=".....">

		<!-- OPERATION -->

	</portType>
</definitions>

Defining the Stateless Port-Type

Create a new WSDL file, name it Stateless.wsdl and place the file in the SERVICE/etc folder.

<?xml version="1.0" encoding="UTF-8"?>
<definitions 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" name="Stateless" targetNamespace="http://acme.org/sample">
	<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: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"/>
			<fault name="fault" message="corefaults:GCUBEUnrecoverableFaultMessage"/>
			<fault name="fault" message="corefaults:GCUBERetrySameFaultMessage"/>
			<fault name="fault" message="corefaults:GCUBERetryEquivalentFaultMessage"/>
		</operation>
	</portType>
</definitions>

This file defines the interface of the first stateless Port-Type of the Sample Service. The interface has a single operation, named about that accepts as input a string and returns a string as output. Moreover, the operation can throw three types of fault, that are defined in the imported GCUBEFaults.wsdl. Each fault has a specific semantic within a gCube infrastructure:

  • GCUBEUnrecoverableFaultMessage ...
  • GCUBERetrySameFaultMessage...
  • GCUBERetryEquivalentFaultMessage ...

Namespace mappings

To successfully generate the stub classes from the WSDL inteface, we need to tell to gCore where (i.e. in what Java package) to place the stub classes. This is done with a mappings file, which maps WSDL namespaces to Java packages, named namespace2package.mappings and placed in the SERVICE root folder.

http\://acme.org/sample=org.acme.sample.stubs
http\://acme.org/sample/bindings=org.acme.sample.stubs.bindings
http\://acme.org/sample/service=org.acme.sample.stubs.service

Delving into the implementation

The Service Context

The fist thing to implement is the Service Context. It is a static component which represents the whole service within the GHN and the rest of its implementation. It has multi-fold role:

  • a provider of service-wide information & utilities
  • bootstraps the service within the GHN
  • gives access to JNDI configuration & profile information
  • manages security and scope
  • …and more

The role of the service context it's very complex role, but fortunately it’s all transparently catered for by the gCore Framework. What is needed it's just derive your context it from GCUBEServiceContext class and fill in the blanks.

Create a ServiceContext.java class in your SERVICE source root folder.

package org.acme.sample;

import org.gcube.common.core.contexts.GCUBEServiceContext;

public class ServiceContext extends GCUBEServiceContext {

	
	/** Single context instance, created eagerly */
	private static ServiceContext cache = new ServiceContext();
	
	/** Returns cached instance */
	public static ServiceContext getContext() {return cache;}
	
	/** Prevents accidental creation of more instances */
	private ServiceContext(){};
		
	/**
	 * {@inheritDoc}
	 */
	public String getJNDIName() {return "acme/sample";}
	
}

Notes:

  • the class adopts the singleton pattern. As best practice, it is strongly suggested to always adopt it for ANY context defined in a gCube service
  • the getJNDIName() method identifies the configuration in the JNDI file and must return the name given to the service there (acme/sample in this example). It MUST be implemented

A first implementation for the Stateless Port-Type

The implementation of the Stateless provides a mean to bootstrap the service by extending the GCUBEStartupPortType and to serve the Stateless Port-Type clients.

package org.acme.sample.stateless;
import org.acme.sample.ServiceContext;
import org.gcube.common.core.contexts.GCUBEPortTypeContext;
import org.gcube.common.core.contexts.GCUBEServiceContext;
import org.gcube.common.core.contexts.GHNContext;
import org.gcube.common.core.faults.GCUBEException;
import org.gcube.common.core.faults.GCUBEFault;
import org.gcube.common.core.faults.GCUBERetryEquivalentException;
import org.gcube.common.core.faults.GCUBERetrySameException;
import org.gcube.common.core.faults.GCUBEUnrecoverableException;
import org.gcube.common.core.porttypes.GCUBEStartupPortType;

public class Stateless extends GCUBEStartupPortType {

	/** {@inheritDoc} */
	protected GCUBEServiceContext getServiceContext() {return ServiceContext.getContext();}
	
	protected static void simulateProcessRequest() throws Exception {
		if (Math.random()<.20) { //simulating an error
			switch ((int) (Math.random()*4+1)) {//randomly choosing error type
				case 1 : throw new GCUBEUnrecoverableException("just give up");
				case 2 : throw new GCUBERetryEquivalentException("maybe someone else?");
				case 3: throw new GCUBERetrySameException("maybe in a bit?");
				case 4: throw new Exception("some problem with unclear semantics");
	}}}
	
	public String about(String name) throws GCUBEFault {
		
		StringBuilder output = new StringBuilder();		
		GHNContext nctx = GHNContext.getContext();
		ServiceContext sctx = ServiceContext.getContext();
		GCUBEPortTypeContext pctx = StatelessContext.getContext();
		try {
			simulateProcessRequest();
			output.append("Hello "+name).append(", you have invoked porttype ").
			append(pctx.getName()+" of service "+sctx.getName()).append(", which you found at ").
			append(pctx.getEPR()+" in the gCube infrastructure "+nctx.getGHN().getInfrastructure());
		}
		catch(GCUBEException e) {throw e.toFault();}
		catch(Exception e) {sctx.getDefaultException("Problem of unknown semantics", e).toFault();}
		return output.toString();
	}

}

Notes:

  • the GCUBEStartupPortType is a class defined in the gCore Framework performing the Service instance initialisation. In any gCube service, there MUST be ONE (and only ONE) Port-Type that extends this class.
  • the getServiceContext() method MUST be implemented. It connects the Port-Type to the Service Context
  • the about(String name) method provides an implementation of the about operation defined in the WSDL interface
  • the simulateProcessRequest() method is just a trick to randomly choose an error type (since there is no way to get a real error inside the about method)

Towards Deployment: the WSDD descriptor

A gCube service is deployed into an Axis message processing node using an XML-based deployment descriptor file known as a Web Service Deployment Descriptor (WSDD). WSDD describes how the various components installed in the Axis node are to be chained together to process incoming and outgoing messages to the service. These chain definitions are (somehow) compiled and made available at runtime through registries. Each service section reports deployment instructions about a Port-Type. In our Sample service, the first version of the WSDD instructs the Axis engine about how to deploy and activate the Stateless Port-Type of the Sample Service. It must be placed in the SERVICE/etc folder and named deploy-server.wsdd.

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

Notes:

  • the value of the name attribute of the service element, together with the base URL of gCore, will form the URI at which our Port-Type listens the incoming requests (in this example, the complete URL will result http://localhost:8080/wsrf/services/acme/sample/stateless). This value MUST be the same reported as name of the Port-Type in the service profile.
  • the value of the className parameter is the Port-Type implementation class
  • the loadOnStartup parameter MUST be set to true for the bootstrapping Port-Type (i.e. the one that implements the GCUBEStartupPortType)

Building & Deploying

Customisation

To configure the build process, create a build.properties file and place it into the SERVICE root folder.

name = SampleService
package = org.acme.sample
package.dir = org/acme/sample
lib.dir = SERVICELIBS/SampleService
wsdl.1 = Stateless

Notes:

  • the name parameter is the name of the service
  • the package parameter is the root Java package
  • the package.dir parameter is the root folder of the implementation files
  • the lib.dir points to external dependencies (relative to the BUILD_LOCATION)
  • the wsdl.X parameters provide a list of the service Port-Types


gCore Logging & Restart

A Test Client

Refining the implementation

-user