Contexts

From GCube System
Revision as of 12:02, 28 April 2009 by Fabio.simeoni (Talk | contribs) (Lifetime Management)

Jump to: navigation, search

Within service implementations, some functionality needs to be accessed by different components and for different purposes.

The most obvious case of shared functionality is the functionality required to expose the configuration of the service, or the configuration of its RI, or the configuration of the gHN on which the RI is deployed. There is also information related to scope and security which is dynamically acquired and simultaneously needed by different components. And moving beyond information access, there are utilities and behaviours, both general-purpose and service-specific, which may be called upon from multiple and functionally unrelated components.

The gCF approach to shared functionality is to centralise it in distinguished components, called contexts, which are directly accessible from any other implementation component. gCF contexts are singleton classes, in that they are designed to be instantiated only once and to expose the single instance to all their clients in a thread-safe manner.

Overview

There are different type of contexts in gCF, each of which centralises functionality that is conceptually associated with key entities of service implementations: the service and its RI, the gHN on which the RI is deployed, the port-types of the service, and even the port-types of the target services of the RI. Some are fully defined within gCF (e.g. the context of the gHN). Others are only partially implemented and need to be specialised within service implementations. Collectively, the context form a inheritance hierarchy contained in the contexts package:

Contexts.jpg

As shown in the diagram, the entire framework has dependencies on the contexts packages, in line with the definition of contexts as repositories of shared functionality. The high-level roles of the components in the package is summarised below:

  • GCUBEContext: the base implementation for all contexts in gCF.
  • GCUBEServiceContext: a concrete specialisation of GCUBEContext for a gCube Service and its RIs.
  • GCUBEPortTypeContext: a concrete specialisation of GCUBEContext for a port-type of gCube Service.
  • GCUBEStatefulPortTypeContext: a concrete specialisation of GCUBEPortTypeContext for a stateful port-type of a gCube Service.
  • GCUBERemotePortTypeContext: a concrete specialisation of GCUBEContext for a port-type of a target gCube Service.
  • GHNContext: a concrete specialisation of GCUBEContext for a running gCube Hosting Node.
  • GHNClientContext: a concrete specialisation of GCUBEContext for a gCube Hosting Node that is not runnning.

Before discussing each context class in detail, we refine our the service model to introduce key context classes from the perspective of the service developer:

Model&contexts.jpg

The figure shows that the GHNContext, the GHNClientContext, and GCUBERemotePortTypeContext are fully implemented by the framework and live outside the boundary of specific service implementations. In contrast, service and port-type contexts fall in the scope of service implementations as specialisations of gCF context classes.

Common Facilities

All gCF contexts offer basic facilities for configuration management and local file management.

The primary form of configuration supported by gCF contexts is name-based lookup of arbitrary objects. The underlying configuration mechanism relies on a dedicated and read-only implementation of the JNDI interface.In particular, gCF contexts act as wrappers of standard JNDI contexts to offer a simplified lookup interface.

The underlying JNDI implementation remains opaque to both clients and implementers of context classes. Developers are only exposed to the configuration files used to populate the JNDI implementation wrapped by the contexts. The direct subclasses of GCUBEContext are responsible for establishing conventions on the naming and location of such files, for the definition and documentation of pre-defined configuration properties, and for loading all the configuration properties from the files. As most service developers specialise in turn these subclasses, they need only to author the configuration files.


The syntax for authoring configuration files is based on XML, and its definition is lifted from technologies that underlie gCF. Conventions on specific configuration elements are discussed below in relation to each type of context. Here, we summarise only the 'atomic' types of configuration elements that may occur in configuration files:

  • environments: elements that bind names to values of a set of pre-defined classes, such as Integer and String.
An environment is an empty element named environment with name and type attributes. The name attribute names the bound value and the type attributes gives the fully qualified name of its class.
<environment name="foo" value="bar" type="java.lang.String" /> 
  • resources: elements that bind names to values of custom classes.
A resource is a complex element named resource and with name and type attributes, and a single resourceParams child element. The attributes retain the semantics they have in environments, except that type can identify any class in principle. The resourceParams elements contains in turn one ore more parameter elements, where each parameter contains a name element and a value element. One of the parameters must have a name of factory, and its value must be the qualified name of a class capable to generate a value of the resource's type and to initialise it with the values of the remaining parameters (as Strings).
<resource name="foo" type="org.acme.Foo">
  <resourceParams>	
    <parameter>
       <name>factory</name>
          <value>org.acme.FooFactory</value>
    </parameter>
    <parameter>
         <name>bar</name>
         <value>baz</value>
    </parameter>               
 </resourceParams>
</resource>

All the pre-defined resources use the globus.wsrf.jndi.BeanFactory factory class. This is a generic factory that can instantiate any class that defines setter methods that match all the other resource parameters (e.g. setBar(String)). Developers can defined their own factories, though the BeanFactory is expected to satisfy most resource configuration requirements.


As to file management, the gCF contexts offers facilities to:

  • access files stored in the local file system, for reading or writing purposes.
File locations are abstracted over to avoid gHN-specific dependencies and all contexts offer basic backup facilities: transparently to their clients, they create copies of files that are about to be modified and used backup copies whenever the original files are corrupted.
  • access files packaged with the context as classpath resources, for read-only purposes.

In both cases, the gCF contexts are agnostic to the content of the files. This may be service-specific configuration that lives outside the standard JNDI framework, or any form of state that is deemed suitable for file storage.

The basic operations for configuration and file management are the following (see the code documentation for signature details):

  • getProperty(name,mandatory?): resolves a configuration property from its name.
An optional flag indicates whether the configuration of the property is mandatory or else is optional (default). The flag is used for error and log management purposes.
String bar = (String) someContext.getProperty("foo");   //lookup optional environment
Foo foo = (Foo) someContext.getProperty("foo",true);    //lookup mandatory resource
  • getResource(path): returns a classpath resource from its path.
A relative path is resolved with respect to the context implementation, while an absolute path is resolved directly against the classpath.
InputStream foo = someContext.getResource("resources/foo.properties");            //relative path
InputStream bar = someContext.getResource("/org/acme/resources/bar.properties");  //absolute path
  • getFile(path,mode): returns a File for read or write access.
A write mode induces backups and a read mode (default) relies on backups to recover from failures. In write mode, directories that are specified in the path but do not exist are automatically created, while paths that identify directories are disallowed. Paths are normally to some context-specific root.
File foo = someContext.getFile("foo.txt");              //read mode
File bar = someContext.getFile("bar");                  //read mode on directory
File baz = someContext.getFile("tests/baz.xml",true);   //write mode, may create directory 'tests'

The gHN Context

GHNContext specialises GCUBEContext as the gCF context of the local gHN. The gCF context:

  • exposes the configuration of the gHN and its hosting environment.
This functionality allows service developers to inspect at runtime the environment in which their code is deployed.
  • manages the lifetime of the gHN, from its initialisation to its shutdown and failure.
This functionality supports the internal operation of the gHN and remains transparent to most service developers.
  • mediates between RIs of deployed services.
This functionality supports mutual discovery of RIs and, while it is generically available to all service developers, it is key for the correct operation of the gHN and distinguished Local Services.

Clients obtain the gCF context by invoking the getContext() method of the GHNContext class:

 GHNContext context = GHNContext.getContext();

Note: The facilities for file management that the gHN context inherits from GCUBEContext are meant for internal use and service developers are not encouraged to use them. Service developers can access the file system using the facilities offered by service contexts.

Note: Some functionality offered by the gHN context is sensitive, and as such it is intended only for internal consumption. An attempt to invoke this functionality from code defined outside authorised namespaces may result in an IllegalAccessException at runtime.

Configuration Management

The gHN exposes different types of configuration:

  • descriptive information contained in the gHN profile.
The profile of the gHN is governed by the schema in $GLOBUS_LOCATION/share/schema/gcube/common/core/profiles/node.xsd. The information can be accessed via the GCUBEHostingNode resource returned by the method getGHN().
Iterator<Package> iterator = GHNContext.getContext().getGHN().getDeployedPackages().iterator();
String version = GHNContext.getContext().getGHN().getNodeDescription().getOS().getVersion();
  • configuration related to the implementation of core gCF interfaces.
This includes implementations of components of discovery, publishing, and remote notification interfaces.

The implementations are obtained by invoking the getImplementation() method with Class object of the required interface:

ISCLient client = GHNContext.getContext().getImplementation(ISClient.class);
  • statistics and other information related to the hosting environment.
This includes methods such as getFreeSpace), getUptime(), getMemoryUsage(), getCPUInfo(), getLoadStatistics(), and many others.
String url = GHNContext.getContext().getBaseURL();
Integer port = GHNContext.getContext().getFreePort();
Map<String,Double> stats = GHNContext.getContext().getLoadStatistics();
This configuration relates to the local operation of the gHN and its relationship with the infrastructure. Some of the configuration elements have dedicated accessors, for convenience or post-processing requirements. Other properties are accessible only with the facilities that the gHN context inherits from GCUBEContext.
GCUBEScope[] scopes = GHNContext.getContext().getStartScopes();
GHNContext.Type type = GHNContext.getContext().getType();
GHNContext.Mode mode = GHNContext.getContext().getMode();
String country = (String) GHNContext.getContext().getProperty("country");

Lifetime Management

For lifetime management purposes, the gHN context models the gHN as the following finite state machine:

GHNStates.jpg

Some of the states of the gHN are operational, in that the gHN can dispatch client calls to RIs of the deployed services, deploy new services, and undeploy currently deployed services. Other states are instead non-operational but characterise the transition of the gHN towards or from operational states. The gHN states can be described as follows:

  • DEPLOYED: this is the state of a gHN that is locally installed but not necessarily running within the current process (or not running at all).
This is a start and non-operational state in which the gHN context determines whether the gHN is running in the current process. If the gHN is not running, this state is also terminal and the gHN context can only be used for configuration management purposes. This mode of operation of the context is called client mode.
Note: In client mode, the gHN context is an instance of GHNClientContext, a subclass of GHNContext.
  • STARTED: this is the state of a running gHN that is in the process of initialisation.
This is a non-operational state in which the gHN context initialises the gHN. This involves:
  • loading its configuration.
  • deriving a GCUBEHostingNode resource that models the gHN, either from scratch or from past serialisations of its profile.
  • scheduling periodic updates to the state of the gHN which depends on the local hosting environment.
  • opening local interfaces for management and monitoring (if appropriately configured).
  • READY: this is the state of a gHN in which not all the RIs of the deployed services are operative.
This is an operational state in which some of the deployed RIs may still be in the process of initialisation or may have failed. The gHN enters this state after having triggered the initialisation of all the RIs. It may then transition back to it from after certification, if one of the RIs fails.
  • CERTIFIED: this is the state of a gHN in which all the deployed RIs are operative.
This is an operational state that marks the absence of problems on the gHN.
  • UPDATE: this is the state of a gHN that has changed some its properties.
This is a temporary state in which the gHN context:
  • notifies all interested clients of the change.
  • serialises an up-to-date version of the gHN profile.
Note: One distinguished consumer of update events is the GHNManager, the Local Service that is responsible for publishing the updated profile of the gHN in the infrastructure.
Upon successful completion of these activities, the gHN context reverts the gHN to its previous state. The gHN transitions to this state in the following cases:
  • when it transitions to one of its public states: CERTIFIED, DOWN, and FAILED.
  • in response to key lifetime event, such as the addition and removal of scopes.
  • after the periodic regeneration of the gHN profile.
  • DOWN: this is the state of a gHN that is about to terminate execution without having experienced a failure.
This is a non-operational state in which the gHN is about to terminate the current process, often to restart immediately after the deployment of new services.
  • FAILED: this is the state of a gHN that is about to terminate execution because it has experienced a failure.
This is a non-operational state in which the gHN is about to terminate the current process.

Key state transitions are regulated by the exchange of events between the gHN context and the service contexts of its RIs:

  • Service contexts register RIs with the gHN context, as part of their own initialisation and as early as during the initialisation of the gHN itself (registerService()). In response, the gHN context subscribes with the service contexts of the registered RIs as a consumer their lifetime events. It can then be notified of the current state of the RIs and act accordingly, e.g. move to the CERTIFIED state when all of them become operational, or return to the READY state when one of them fails.
Note Any client of the gHN context can then access the service context of some or all the RIs that have registered (cf. getServiceContext(), getServiceContexts()). Any service, in particular, can rely on this functionality to interact with other, co-deployed services without having static dependencies on them. One distinguished client in gCF, the GCUBEHandler, relies on it to block calls made to RIs that are no longer or not yet operative, but also to propagate the scope of the call to their service contexts.
GCUBEServiceContext ctxt = GHNContext.getContext().getServiceContext("...service class...","...service name...");
  • Service contexts also subscribe with the gHN to request the renewal of the credentials that their RIs may need to operate in a secure infrastructure (subscribeForCredential()). The gHN context acts as a mediator between the RIs and the RI of distinguished Local Service, the Delegation service, which is responsible for arranging the periodic renewal of credentials with remote Security Services (cf. security management), In particular, the RI of the Delegation also subscribes with the gHN context to be notified of credential renewal request (subscribeForCredentialRequest()) and, when credentials are delivered by the infrastructure, it asks the gHN to pass them on to the RI which originally requested them ((delegateCredentials())). The mediation of the gHN context is based on dedicated topics (CREDENTIAL_REQUEST,CREDENTIAL_DELEGATION) and events (CredentialRequestEvent,CredentialDelegationEvent). The gHN context defines also partial implementations for prospective consumers of security-related events (CredentialConsumer,CredentialRequestConsumer). Although in some special cases service developers may wish to extend CredentialConsumer, normally they won't need to as - as discussed below - the context of their services will do it on their behalf.
  • The lifetime of the gHN can be monitored too. The gHN context defines topics (RIREGISTRATION, SHUTDOWN, READY, UPDATE) and events (GHNRIRegistrationEvent,GHNLifetTimeEvent) for which clients can subscribe and unsubscribe (subscribeGHNEvents(), unsubscribeGHNEvents()). The gHN defines also a partial implementation for prospective consumer of lifetime events (GHNConsumer); the following consumer exemplifies the extension pattern by overriding callbacks corresponding to two events of interest:
class MyConsumer extends GHNConsumer {

  /**{@inheritDoc} */
  synchronized protected void onRIRegistration(GHNRIRegistrationEvent event) {
     
     GCUBEServiceContext ctxt = event.getPayload();  //the event payload is the context of the registered RI
     ...

  }

 /**{@inheritDoc} */	
  synchronized protected void onGHNReady(GHNLifeTimeEvent event) {...}

}

...

GHNContext.getContext().subscribeGHNEvents(new MyConsumer(), GHNTopic.RIREGISTRATION, GHNTopic.READY);

Note: Only privileged clients and the gHN itself can trigger state transitions (cf. setStatus()). All clients however can inspect the current state of the gHN, either asynchronously, via notification of state transitions as shown above, or synchronously, using the getStatus() method:
if (GHNContext.getContext().getStatus()==Status.CERTIFIED) {...}

Service Contexts

GCUBEServiceContext specialises the GCUBEContext as the context of a service and its RI. The specialisation remains abstract, and developers must specialise it further for their services. By doing so, the developer obtains a component that:

  • exposes the configuration of the service and its RI.
This functionality allows service developers to inspect the at runtime the properties of the service and its RI.
  • manages the lifetime of the RI, from its initialisation and staging to its updates and failure.
This functionality supports the correct operation of the RI within the infrastructure and can extended by service developers.
This functionality supports the loosely-coupled propagation of scope and credentials across components of the service implementation.

To specialise GCUBEServiceContext, developers must derive it and implement the following method:

Developers are also responsible for the implementation of the service context as a singleton class. The recommended approach can be exemplified as follows:

public class MyServiceContext extends GCUBEServiceContext {

   private static MyServiceContext singleton = new MyServiceContext();
   
   private MyServiceContext(){}

   public static MyServiceContext getContext() {return singleton;}

   /** {@inheritDoc} */
   protected String getJNDIName() {...}

}

Configuration and File Management

The service context exposes the following types of configuration:

  • descriptive information contained in the service profile.
The profile of the service is governed by the schema in $GLOBUS_LOCATION/share/schema/gcube/common/core/profiles/service.xsd. For convenience, the service context exposes some profile information with dedicated accessors. The rest is available via the GCUBEService resource returned by the getService() method.
String id = MyServiceContext.getContext().getID();
String name = MyServiceContext.getContext().getName(); 
List<Dependencies> dependencies = MyServiceContext.getContext().getService().getDependencies();
  • descriptive information contained in the RI profile.
This information is available via the GCUBERunningInstance resource returned by the getInstance() method.
Map<String,GCUBEScope> scopes = MyServiceContext.getContext().getInstance().getScopes();
List<Endpoints> epList = MyServiceContext.getContext().getInstance().getAccessPoint().getRunningInstanceInterfaces().getEndpoint();
Calendar time = MyServiceContext.getContext().getInstance().getDeploymentData().getActivationTime();
String data = MyServiceContext.getContext().getInstance().getSpecificData();
GCUBEScope[] scopes = MyServiceContext.getContext().getStartScopes(); //pre-defined binding
Foo foo = MyServiceContext.getContext().getFoo();                     //accessor specific to user-defined binding 
Bar bar - (Bar) MyServiceContext.getContext().getProperty("bar");     //general access to user-defined binding 


As to file management, the service context offers the following methods:

  • getFile(): overrides the method in GCUBEContext so as to target the configuration directory of the service.
The contents of this directory may outlive the current process but not the static or dynamic redeployment of the service. The developer should rely on this method only to store and access any kind of information that may be needed by the service in the scope of the current process.
  • getPersistentFile(): retains the same usage model of getFile() but targets a directory under the storage root of the gHN dedicated to longer-lived service-specific storage.
The contents of this directory outlive the current process and the redeployment of the service in the same or a different gHN.

Lifetime Management

For lifetime management purposes, the service context models the RIs as the following finite state machine:

File:RIStatesjpg

The state of the RI can can be described as follows:

  • DEPLOYED: this is the state of a RI that is activated but has not yet completed its initialisation.
This is a start and non-operational state in which the service context initialises the RI. This involves:
  • loading the JNDI configuration of the service.
  • deriving a GCUBEService resource from the profile of service.
  • deriving a GCUBERunningInstance resource, either from scratch of from past serialisations of the RI profile.
  • initialising the GCUBEServiceSecurityManager that handles service credentials on behalf of the RI.
  • initialising the GCUBEScopeManager that handles scope information on behalf of the RI.
  • initialising the GCUBERIPersistenceManager that manages the remote persistence of the state of the RI.
  • registering with the gHN context.
The successful completion of these activities triggers a transition of the RI to the INITIALISED state.
  • INITIALISED: this is the state of a RI that is fully initialised but not yet ready to process client requests.
This is a non-operational state in which the service context stages the RI. Staging requires the successful completion of the following parallel activities:
  • raising a request for credentials with the gHN context, if any are needed by the RI when it operates in a secure infrastructure, and waiting for their delivery.
  • recovering any state that the RI might have previously stored in the infrastructure from the current or another gHN (in case the RI has been dynamically redeployed).
  • waiting for any RI that is a logical dependency of the service READY or FAILED, assuming that the dependent RI has been activated and does not depend in turn on this RI (i.e. identifies a cyclic dependency). We refer to this task as co-dependency synchronisation and we perform it to ensure that RIs will always find their dependencies ready to use, even when they are co-deployed on the same gHN.
In parallel, the service context registers the RI with the gHN so as to be notified in occasion of:
  • GHNTopic.SHUTDOWN events: when this event occurs, the RI transitions to the state DOWN.
  • GHNTopic.REMOVESCOPE events: when this event occurs, the RIs aligns its scopes with the scopes of the gHN and removes itself from the scope from which the gHN has been removed (if it was in it in the first place).
The successful completion of these activities triggers a transition of the RI to the READY state.
  • READIED: this is the state of a RI that is fully operative within the infrastructure.
This is the operational state in which the RI can service incoming requests.
  • UPDATED: this is the state of a RI that has undergone some changes.
This is a temporary state in which the service context:
  • notifies all interested clients of the change.
  • publishes an updated RI profile in the infrastructure;
Upon completion of these activities, the service context reverts the RI to its previous state. The RI transitions to this state in the following cases:
  • when it transitions to one of its public states: READIED, FAILED, and DOWN.
  • in response to key lifetime events, such as addition and removal of scopes.
  • Status.FAILED: this is the terminal state of a RI that is no longer operative within the infrastructure, typically because of a fatal failure of the RI or the local gHN.
  • As the RI enters this state, the service context informs subscribed consumers, including the GHNContext. As a result, future calls to the RI are blocked.

Note: The service context accepts subscriptions from clients that wish to be notified of state transitions of the RI. Subscribers are typically distinguished components of the service implementations, although any they may also be components of co-deployed services that have dynamically discovered the service context through the GHNContext. The subscription topics correspond to states the RI transitions to and the service context models them with the values of the inner enumeration GCUBEServiceContext.RILifetimeTopic (e.g. RILifetimeTopic.INITIALISATION and RILifetimeTopic.READY). It then models events with the inner classe GCUBEServiceContext.RILifetimeEvent. To subscribe to one or more lifetime topics, clients needs to register a concrete implementation of the inner class GCUBEServiceContext.RIConsumer (cf. subscribeLifetTime()). Clients subclass GCUBEServiceContext.RIConsumer by implementing the callbacks that correspond to the events of interest (e.g. GCUBEServiceContext.RIConsumer.onRIReady()).

Note: The facilities and activities described above are entirely implemented in GCUBEServiceContext and remain transparent to its subclasses. However, subclasses can tailor them to the semantics of specific services by overriding a set of callbacks that are invoked by GCUBEServiceContext immediately before RI transitions to key stages (cf. onInitialisation(), onReady(), onUpdate(), onStatesChange(), onFailure()).

Note: A failure in any of the activities described above - including callbacks - causes a transition of the RI to Status.FAILED.

PortType Contexts

[coming soon]