Difference between revisions of "Contexts"

From GCube System
Jump to: navigation, search
(Lifetime Management)
(Lifetime Management)
 
(28 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
Within service implementations, some functionality needs to be accessed by different components and for different purposes.  
 
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 most obvious case of ''shared functionality'' is that 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. Information related to scope and security is also 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.
+
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 ==
 
== 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 <code>contexts</code> package:
+
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;
 +
* the port-types of target services.  
 +
 
 +
Some contexts are fully defined within gCF. Others are only partially implemented and need to be specialised within service implementations. Collectively, the context form a inheritance hierarchy contained in the package <code>org.gcube.common.core.contexts</code>:
  
 
[[Image:contexts.jpg]]
 
[[Image:contexts.jpg]]
  
As shown in the diagram, the entire framework has dependencies on the <code>contexts</code> 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:
+
As shown in the diagram, the entire framework has dependencies on the <code>contexts</code> packages, in line with the definition of contexts as repositories of shared functionality. The high-level roles of package components is summarised below:
  
* <code>GCUBEContext</code>: the base implementation for all contexts in gCF.
+
* <code>GCUBEContext</code>: the base implementation of all contexts.
  
* <code>GCUBEServiceContext</code>: a concrete specialisation of <code>GCUBEContext</code> for a gCube Service and its RIs.
+
* <code>GCUBEServiceContext</code>: a concrete specialisation of <code>GCUBEContext</code> for gCube Services and their RIs.
  
* <code>GCUBEPortTypeContext</code>: a concrete specialisation of <code>GCUBEContext</code> for a port-type of gCube Service.
+
* <code>GCUBEPortTypeContext</code>: a concrete specialisation of <code>GCUBEContext</code> for the port-types of gCube Services.
  
* <code>GCUBEStatefulPortTypeContext</code>: a concrete specialisation of <code>GCUBEPortTypeContext</code> for a stateful port-type of a gCube Service.
+
* <code>GCUBEStatefulPortTypeContext</code>: a concrete specialisation of <code>GCUBEPortTypeContext</code> for the ''stateful'' port-types of gCube Services.
  
* <code>GCUBERemotePortTypeContext</code>: a concrete specialisation of <code>GCUBEContext</code> for a port-type of a target gCube Service.
+
* <code>GCUBERemotePortTypeContext</code>: a concrete specialisation of <code>GCUBEContext</code> for port-typea of target services.
  
* <code>GHNContext</code>: a concrete specialisation of <code>GCUBEContext</code> for a running gCube Hosting Node.
+
* <code>GHNContext</code>: a concrete specialisation of <code>GCUBEContext</code> for running gCube Hosting Nodes.
  
* <code>GHNClientContext</code>: a concrete specialisation of <code>GCUBEContext</code> for a gCube Hosting Node that is not runnning.
+
* <code>GHNClientContext</code>: a concrete specialisation of <code> GHNContext </code> for gCube Hosting Node that are 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:
+
Before discussing each context class in detail, we refine the [[Service Model|service model]] to introduce key context classes from the perspective of the service developer:
  
 
[[Image:model&contexts.jpg]]
 
[[Image:model&contexts.jpg]]
Line 37: Line 44:
 
All gCF contexts offer basic facilities for ''configuration management'' and local ''file management''.  
 
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 [http://java.sun.com/products/jndi/ JNDI] interface.In particular, gCF contexts act as wrappers of standard JNDI contexts to offer a simplified lookup interface.   
+
The primary form of configuration supported by gCF contexts is name-based lookup of arbitrary objects. As discussed [[Configuration_Components#The JNDI Configuration|above]], the underlying configuration mechanism relies on a dedicated and read-only implementation of the [http://java.sun.com/products/jndi/ 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 <code>GCUBEContext</code> 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.  
 
+
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 <code>GCUBEContext</code> 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.  
 
As most service developers specialise in turn these subclasses, they need only to author the configuration files.  
  
----<div id="configsyntax"></div>
+
The basic operation for configuration management is the following:
  
The syntax for authoring configuration files is based on XML, and its [http://www.globus.org/toolkit/docs/4.0/common/javawscore/developer-index.html#s-javawscore-developer-JNDIDetails 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:
+
* '''<code>getProperty(name,mandatory?)</code>''': 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.
*'''environments''': elements that bind names to values of a set of pre-defined classes, such as <code>Integer</code> and <code>String</code>.
+
:An environment is an empty element named <code>environment</code> with <code>name</code> and <code>type</code> attributes. The <code>name</code> attribute names the bound value and the <code>type</code> attributes gives the fully qualified name of its class.
+
  
 
<pre>
 
<pre>
<environment name="foo" value="bar" type="java.lang.String" />
+
String bar = (String) someContext.getProperty("foo");  //lookup optional environment
</pre> 
+
Foo foo = (Foo) someContext.getProperty("foo",true);    //lookup mandatory resource
 
+
*'''resources''': elements that bind names to values of custom classes.
+
: A resource is a complex element named <code>resource</code> and with <code>name</code> and <code>type</code> attributes, and a single <code>resourceParams</code> child element. The attributes retain the semantics they have in environments, except that <code>type</code> can identify any class in principle. The <code>resourceParams</code> elements contains in turn one ore more <code>parameter</code> elements, where each <code>parameter</code> contains a <code>name</code> element and a <code>value</code> element. One of the parameters ''must'' have a name of <code>factory</code>, and its value ''must'' be the qualified name of a class capable to generate a value of the resource's <code>type</code> and to initialise it with the values of the remaining parameters (as <code>String</code>s).  
+
 
+
<pre>
+
<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>
+
 
</pre>
 
</pre>
  
All the pre-defined resources use the <code>globus.wsrf.jndi.BeanFactory</code> 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. <code>setBar(String)</code>). Developers can defined their own factories, though the <code>BeanFactory</code> is expected to satisfy most resource configuration requirements.
+
As to file management, the gCF contexts offers facilities to access:
 
+
----
+
 
+
As to file management, the gCF contexts offers facilities to:
+
  
*access files stored in the local file system, for reading or writing purposes.  
+
*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.
 
: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.
+
*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.
 
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):
+
The basic operations for file management are the following:
 
+
* '''<code>getProperty(name,mandatory?)</code>''': 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.
+
 
+
<pre>
+
String bar = (String) someContext.getProperty("foo");  //lookup optional environment
+
Foo foo = (Foo) someContext.getProperty("foo",true);    //lookup mandatory resource
+
</pre>
+
  
 
*'''<code>getResource(path)</code>''': returns a classpath resource from its path.  
 
*'''<code>getResource(path)</code>''': returns a classpath resource from its path.  
Line 115: Line 87:
 
== The gHN Context ==
 
== The gHN Context ==
  
<code>GHNContext</code> specialises <code>GCUBEContext</code> as the gCF context of the local gHN. The gCF context:
+
<code>GHNContext</code> specialises <code>GCUBEContext</code> as the context of the local gHN. The gHN context:
  
 
*exposes the configuration of the gHN and its hosting environment.
 
*exposes the configuration of the gHN and its hosting environment.
Line 132: Line 104:
 
</pre>
 
</pre>
  
'''Note:''' The [[#Common Facilities|facilities]] for file management that the gHN context inherits from <code>GCUBEContext</code> 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 [[Contexts#Service Contexts|service contexts]].
+
'''Note:''' The [[#Common Facilities|facilities]] for file management that the gHN context inherits from <code>GCUBEContext</code> 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 [[Contexts#Service Contexts|service contexts]] or [[Contexts#PortType Contexts|port-type 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 <code>IllegalAccessException</code> at runtime.
 
'''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 <code>IllegalAccessException</code> at runtime.
Line 138: Line 110:
 
=== Configuration Management ===
 
=== Configuration Management ===
  
The gHN exposes different types of configuration:
+
The gHN exposes the following forms of configuration:
  
 
*descriptive information contained in the gHN profile.
 
*descriptive information contained in the gHN profile.
Line 149: Line 121:
  
 
*configuration related to the implementation of core gCF interfaces.  
 
*configuration related to the implementation of core gCF interfaces.  
:This includes implementations of components of discovery, publishing, and remote notification [[Client_Libraries|interfaces]].
+
:This includes implementations of discovery, publishing, and remote notification [[Client_Libraries|interfaces]], among others.
The implementations are obtained by invoking the <code>getImplementation()</code> method with <code>Class</code> object of the required interface:
+
The implementations are obtained by invoking the following method:
 +
 
 +
*<code>getImplementation(interfaceclass)</code>: returns the local implementation  of the interface represented by the given <code>Class</code> object.
  
 
<pre>
 
<pre>
Line 157: Line 131:
  
 
*statistics and other information related to the hosting environment.
 
*statistics and other information related to the hosting environment.
:This includes methods such as <code>getFreeSpace)</code>, <code>getUptime()</code>, <code>getMemoryUsage()</code>, <code>getCPUInfo()</code>, <code>getLoadStatistics()</code>, and many others.
+
:This includes methods such as <code>getFreeSpace)</code>, <code>getUptime()</code>, <code>getMemoryUsage()</code>, <code>getCPUInfo()</code>, <code>getLoadStatistics()</code>, and many others (consult the <code>GHNContext</code> documentation for details).
  
 
<pre>
 
<pre>
Line 186: Line 160:
 
: 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 and File Management|configuration management]] purposes. This mode of operation of the context is called ''client mode''.  
 
: 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 and File Management|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 <code>GHNClientContext</code>, a subclass of <code>GHNContext</code>.  
+
::''' Note''': In client mode, the gHN context returned by <code>GHNContext.getContext()</code> is an instance of <code>GHNClientContext</code>.  
  
 
*'''<code>STARTED</code>''': this is the state of a running gHN that is in the process of initialisation.  
 
*'''<code>STARTED</code>''': this is the state of a running gHN that is in the process of initialisation.  
Line 201: Line 175:
 
: This is an operational state that marks the absence of problems on the gHN.  
 
: This is an operational state that marks the absence of problems on the gHN.  
  
*'''<code>UPDATE</code>''': this is the state of a gHN that has changed some its properties.  
+
*'''<code>UPDATED</code>''': 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 before returning to its previous state. One distinguished client is the <code>GHNManager</code>, the Local Service that is responsible for publishing the updated profile of the gHN in the infrastructure. The gHN transitions to this state in the following cases:
+
: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 <code>GHNManager</code>, 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'': <code>CERTIFIED</code>, <code>DOWN</code>, and <code>FAILED</code>.
 
:*when it transitions to one of its ''public states'': <code>CERTIFIED</code>, <code>DOWN</code>, and <code>FAILED</code>.
 
:*in response to key lifetime event, such as the addition and removal of scopes.
 
:*in response to key lifetime event, such as the addition and removal of scopes.
Line 213: Line 191:
 
:This is a non-operational state in which the gHN is about to terminate the current process.
 
: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:
+
:'''Note:''' Only privileged clients and the gHN itself can trigger state transitions (cf. <code>setStatus()</code>). 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 <code>getStatus()</code> method:
 +
 
 +
<pre>
 +
if (GHNContext.getContext().getStatus()==Status.CERTIFIED) {...}
 +
</pre>
 +
 
 +
=== Event Management ===
 +
 
 +
The gHN context acts in the role of a producer and consumer of events.
  
*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 (<code>registerService()</code>). 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 <code>CERTIFIED</code> state when all of them become operational, or return to the <code>READY</code> state when one of them fails.   
+
As a consumer, it subscribes with all the RIs that register their presence with it during their own their own initialisation (<code>registerService()</code>).  
  
::'''Note''' Any client of the gHN context can then access the service context of some or all the RIs that have registered (cf. <code>getServiceContext()</code>, <code>getServiceContexts()</code>). 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 <code>GCUBEHandler</code>, 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. 
+
:'''Note''' Any client of the gHN context can then access the service context of some or all the RIs that have registered (cf. '''<code>getServiceContext()</code>'''). Any service, in particular, can rely on this functionality to interact with other, co-deployed services without having static dependencies on them.  
  
 
<pre>
 
<pre>
Line 223: Line 209:
 
</pre>
 
</pre>
  
*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 (<code>subscribeForCredential()</code>). 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|security management]]), In particular, the RI of the Delegation also subscribes with the gHN context to be notified of credential renewal request (<code>subscribeForCredentialRequest()</code>) and, when credentials are delivered by the infrastructure, it asks the gHN to pass them on to the RI which originally requested them ((<code>delegateCredentials()</code>)). The mediation of the gHN context is based on dedicated topics (<code>CREDENTIAL_REQUEST</code>,<code>CREDENTIAL_DELEGATION</code>) and events (<code>CredentialRequestEvent</code>,<code>CredentialDelegationEvent</code>). The gHN context defines also partial implementations for prospective consumers of security-related events (<code>CredentialConsumer</code>,<code>CredentialRequestConsumer</code>). Although in some special cases service developers ''may'' wish to extend <code>CredentialConsumer</code>, normally they won't need to as - as discussed [[#Service Contexts|below]] - the context of their services will do it on their behalf.
+
:'''Note''' One distinguished gCF component, the <code>GCUBEHandler</code>, relies on <code>getServiceContext()</code> 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.
  
*The lifetime of the gHN can be monitored too. The gHN context defines topics (<code>RIREGISTRATION</code>, <code>SHUTDOWN</code>, <code>READY</code>, <code>UPDATE</code>) and events (<code>GHNRIRegistrationEvent</code>,<code>GHNLifetTimeEvent</code>) for which clients can subscribe and unsubscribe (<code>subscribeGHNEvents()</code>, <code>unsubscribeGHNEvents()</code>). The gHN defines also a partial implementation for prospective consumer of lifetime events (<code>GHNConsumer</code>); the following consumer exemplifies the extension pattern by overriding callbacks corresponding to two events of interest:
+
As a producer, the gHN context can notify clients of the occurrence of two different classes of events.
 +
 
 +
The first class of events relates to the lifetime of the gHN. In particular the gHN defines:
 +
* a number of <code>GHNTopic</code>s, including '''<code>RIREGISTRATION</code>''', '''<code>READY</code>''', '''<code>UPDATED</code>''','''<code>SHUTDOWN</code>'''.
 +
* a generic '''<code>GHNLifetTimeEvent</code>''' that carries en empty payload, and a '''<code>GHNRIRegistrationEvent</code>''' specialisation of <code>GHNLifetTimeEvent</code> which carries the service context of the registered RI as its payload.
 +
* a pre-defined consumer implementation, '''<code>GHNConsumer</code>''', that prospective consumers can derive by implementing the event-specific callback of interest.
 +
* methods to subscribe for and unsubscribe from the occurrence of events of interest ('''subscribeGHNEvents(GHNConsumer,GHNLifetimeEvent*)''', '''unsubscribeGHNEvents(GHNConsumer,GHNLifetimeEvent*)''').
  
 
<pre>
 
<pre>
Line 233: Line 225:
 
   synchronized protected void onRIRegistration(GHNRIRegistrationEvent event) {
 
   synchronized protected void onRIRegistration(GHNRIRegistrationEvent event) {
 
      
 
      
     GCUBEServiceContext ctxt = event.getPayload(); //the event payload is the context of the registered RI
+
     GCUBEServiceContext ctxt = event.getPayload();  
 
     ...
 
     ...
  
Line 246: Line 238:
  
 
GHNContext.getContext().subscribeGHNEvents(new MyConsumer(), GHNTopic.RIREGISTRATION, GHNTopic.READY);
 
GHNContext.getContext().subscribeGHNEvents(new MyConsumer(), GHNTopic.RIREGISTRATION, GHNTopic.READY);
 
+
GHNContext.getContext().subscribeGHNEvents(new MyConsumer()); ///subscribe to all events
 
</pre>
 
</pre>
 
:'''Note:''' Only privileged clients and the gHN itself can trigger state transitions (cf. <code>setStatus()</code>). 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 <code>getStatus()</code> method:
 
  
<pre>
+
The second class of events related to the renewal of credentials that RIs might need to operate in a secure infrastructure. In particular, the gHN context acts as a mediator between the RIs and the RI of distinguished Local Service, the <code>Delegation</code> service, which is responsible for arranging the periodic renewal of credentials with remote Security Services (cf. [[Security_Management|security management]]). The process subsumes the following steps:
if (GHNContext.getContext().getStatus()==Status.CERTIFIED) {...}
+
* RIs subscribe with the gHN context to request credentials (<code>subscribeForCredential()</code>).
</pre>
+
* the RI of the Delegation also subscribes with the gHN context to be notified of credential renewal request (<code>subscribeForCredentialRequest()</code>).
 +
* When credentials are delivered by the infrastructure, the RI of the <code>Delegation</code> service asks the gHN to pass them on to the RI which originally requested them ((<code>delegateCredentials()</code>)).  
 +
 
 +
The mediation of the gHN context is based on dedicated topics (<code>CREDENTIAL_REQUEST</code>,<code>CREDENTIAL_DELEGATION</code>) and events (<code>CredentialRequestEvent</code>,<code>CredentialDelegationEvent</code>). The gHN context pre-defined consumer implementations that prospective consumers can derive by implementing the event-specific callback of interests (<code>CredentialConsumer</code>,<code>CredentialRequestConsumer</code>). In some special cases service developers ''may'' wish to extend <code>CredentialConsumer</code>, but normally they won't need to as - as discussed [[#Service Contexts|below]] - the context of their services will do it on their behalf.
  
 
== Service Contexts ==
 
== Service Contexts ==
  
<code>GCUBEServiceContext</code> specialises the <code>GCUBEContext</code> as the context of a service and its RI.  
+
<code>GCUBEServiceContext</code> specialises <code>GCUBEContext</code> 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:
+
The specialisation remains abstract, and developers must specialise it further for their services. By doing so, developers obtains a component that:
  
 
*exposes the configuration of the service and its RI.
 
*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.
+
:This functionality allows service developers to inspect the properties of the service and its RI at runtime.
  
 
*manages the lifetime of the RI, from its initialisation and staging to its updates and failure.
 
*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 correct operation of the RI within the infrastructure and can be extended by service developers.
  
 
* acts as a centralised [[Scope Management#Scope Managers|scope manager]] and [[Security Management#Handling Credentials: Security Managers|security manager]] for the entire service implementation.
 
* acts as a centralised [[Scope Management#Scope Managers|scope manager]] and [[Security Management#Handling Credentials: Security Managers|security manager]] for the entire service implementation.
Line 271: Line 264:
 
To specialise <code>GCUBEServiceContext</code>, developers must derive it and implement the following method:
 
To specialise <code>GCUBEServiceContext</code>, developers must derive it and implement the following method:
  
* '''<code>getJNDIName()</code>''': the method returns the name of the global section of the [[Configuration_Components#The_JNDI_Configuration|JNDI configuration file]] of the service.
+
* '''<code>getJNDIName()</code>''': the method returns the name of the global section of the [[Configuration_Components#The_JNDI_Configuration|JNDI configuration file]].
  
 
Developers are also responsible for the implementation of the service context as a singleton class. The recommended approach can be exemplified as follows:
 
Developers are also responsible for the implementation of the service context as a singleton class. The recommended approach can be exemplified as follows:
Line 320: Line 313:
 
Bar bar - (Bar) MyServiceContext.getContext().getProperty("bar");    //general access to user-defined binding  
 
Bar bar - (Bar) MyServiceContext.getContext().getProperty("bar");    //general access to user-defined binding  
 
</pre>
 
</pre>
 
  
 
As to file management, the service context offers the following methods:
 
As to file management, the service context offers the following methods:
Line 334: Line 326:
 
For lifetime management purposes, the service context models the RIs as the following finite state machine:
 
For lifetime management purposes, the service context models the RIs as the following finite state machine:
  
[[Image:RIStatesjpg]]
+
[[Image:RIStates.jpg]]
  
 
The state of the RI can can be described as follows:  
 
The state of the RI can can be described as follows:  
Line 343: Line 335:
 
:*deriving a <code>GCUBEService</code> resource from the profile of service.
 
:*deriving a <code>GCUBEService</code> resource from the profile of service.
 
:*deriving a <code>GCUBERunningInstance</code> resource, either from scratch of from past serialisations of the RI profile.
 
:*deriving a <code>GCUBERunningInstance</code> resource, either from scratch of from past serialisations of the RI profile.
:*initialising the [[Security_Management#Handling Credentials: Security Managers|<code>GCUBEServiceSecurityManager</code>]] that handles service credentials on behalf of the RI.
+
:*initialising the [[Security_Management#Handling Credentials: Security Managers|security manager]] that handles service credentials on behalf of the RI. The security manager is identified from the JNDI configuration of the service. In the lack of explicit configuration, the service context initialises a <code>GCUBESimpleServiceSecurityManager</code>.  
:*initialising the [[Scope_Management#Scope_Managers|<code>GCUBEScopeManager</code>]] that handles scope information on behalf of the RI.
+
:*initialising the [[Scope_Management#Scope_Managers|scope manager]] that handles scope information on behalf of the RI.
:*initialising the [[State_Management#Remote_Persistence|<code>GCUBERIPersistenceManager</code>]] that manages the remote persistence of the state of the RI.
+
:*initialising the [[State_Management#Remote_Persistence|persistence manager]] that manages the remote persistence of the state of the RI, if one is specified in the JNDI configuration of the service.
 +
:*initialising the plugin manager of the service, if one is specified in the JNDI configuration of the service.  
 
:*registering with [[#The gHN Context|the gHN context]].  
 
:*registering with [[#The gHN Context|the gHN context]].  
 
:The successful completion of these activities triggers a transition of the RI to the <code>INITIALISED</code> state.  
 
:The successful completion of these activities triggers a transition of the RI to the <code>INITIALISED</code> state.  
Line 363: Line 356:
 
:This is the operational state in which the RI can service incoming requests.  
 
:This is the operational state in which the RI can service incoming requests.  
  
*'''<code>UPDATED</code>''': this is the state of a RI that has undergone some changes.
+
::'''Note:''' A RI which is not this state will not receive calls. A distinguished gCF component, the <code>GGCUBEHandler</code> will intercept all calls and refuse to forward them (see also [[#The gHN Context"| the lifetime of the gHN context]] for further information.
 +
 
 +
*'''<code>UPDATED</code>''': this is the state of a RI that has changed its profile.
 
:This is a temporary state in which the service context:
 
:This is a temporary state in which the service context:
 
:* notifies all interested clients of the change.
 
:* notifies all interested clients of the change.
Line 371: Line 366:
 
:* in response to key lifetime events, such as addition and removal of scopes.
 
:* in response to key lifetime events, such as addition and removal of scopes.
  
*<code>Status.FAILED</code>: 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.
+
*'''<code>STATECHANGED</code>''': this is the state of a RI that has changed its persistent state.
:* As the RI enters this state, the service context informs subscribed consumers, including the <code>GHNContext</code>. As a result, future calls to the RI are blocked.
+
:This is a temporary state in which the service context notifies all interested clients of the change before returning to its previous state. The RI may transition to this state from the <code>INITIALISED</code> and <code>READY</code> states and does so when the method '''<code>notifyStateChange()</code>''' is explicitly invoked by clients.
 +
::'''Note:''' the persistence manager of the RI is the primary consumer of state change events. Upon notification, the manager will attempt to store the updated state of the RI.
 +
::'''Note:''' [[State_Management#Adding_Persistence|persistence delegates]] of stateful port-types are distinguished producers of state change events. They invoke <code>notifyStateChange()</code> in correspondence with the serialisation of [[State_Management|stateful resources]].  
 +
::'''Note:''' any client may invoke <code>notifyStateChange()</code> to notify of a change in the persistent state of the RI. A common case is when the client has successfully stored a file 'obtained' by invoking the method <code>getPersistentFile()</code>. 
  
'''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 <code>GHNContext</code>. The subscription topics correspond to states the RI transitions to and the service context models them with the values of the inner enumeration <code>GCUBEServiceContext.RILifetimeTopic</code> (e.g. <code>RILifetimeTopic.INITIALISATION</code> and <code>RILifetimeTopic.READY</code>). It then models events with the inner classe <code>GCUBEServiceContext.RILifetimeEvent</code>. To subscribe to one or more lifetime topics, clients needs to register a concrete implementation of the inner class <code>GCUBEServiceContext.RIConsumer</code> (cf. <code>subscribeLifetTime()</code>). Clients subclass <code>GCUBEServiceContext.RIConsumer</code> by implementing the callbacks that correspond to the events of interest (e.g. <code>GCUBEServiceContext.RIConsumer.onRIReady()</code>).
+
*'''<code>DOWN</code>''': this is the state of a RI managed by a gHN that has experienced failure or is about shutdown.
 +
:This is a non-operational state in which the RI cannot satisfy client requests. Upon transitioning to this state, the service context notifies all interested clients.  
  
'''Note''': The facilities and activities described above are entirely implemented in <code>GCUBEServiceContext</code> 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 <code>GCUBEServiceContext</code> immediately before RI transitions to key stages (cf. <code>onInitialisation()</code>, <code>onReady()</code>, <code>onUpdate()</code>, <code>onStatesChange()</code>, <code>onFailure()</code>).
+
*'''<code>FAILED</code>''': this is the state of a RI that has experienced a failure.
+
:This is a non-operational state in which the RI cannot satisfy client requests. Upon transitioning to this state, the service context notifies all interested clients.
'''Note''': A failure in any of the activities described above - including callbacks - causes a transition of the RI to <code>Status.FAILED</code>.
+
 
 +
=== Event Management ===
 +
 
 +
All the state transitions of a RI subsume a callback on the service context that the developer can implement in its specialisation of <code>GCUBEServiceContext</code>. The available callbacks are: '''<code>onInitalisation()</code>''', '''<code>onReady()</code>''', '''<code>onUpdate()</code>''', '''<code>onStateChange()</code>''', '''<code>onShutdown()</code>''', '''<code>onFailure()</code>'''. Most transitions are conditional to the faultless execution of the corresponding callback.
 +
 
 +
<pre>
 +
/** {@inheritDoc} */
 +
protected void onReady() throws Exception {
 +
  if (...) throw new Exception();          //would fail the transition to READY
 +
}
 +
</pre>
 +
 
 +
Other components of the service implementation - and in fact any component that has a reference to the service context - can be notified of state transitions by subscribing with the RI for the occurrence of lifetime events. In particular, the service context defines:
 +
 
 +
:* a number of '''<code>RILifetimeTopic</code>'''s, including '''<code>DEPLOYED</code>''','''<code>INITIALISED</code>''','''<code>READY</code>''','''<code>FAILED</code>''','''<code>UPDATED</code>''','''<code>STATECHANGE</code>''','''<code>DOWN</code>'''.
 +
:* a generic '''<code>RILifetimeEvent</code>''' event that carries the service context as its payload.
 +
:* a pre-defined implementation of a '''<code>Consumer</code>''' that prospective consumers can derive by implementing the event-specific callback of interest.
 +
:* methods to subscribe for and unsubscribe from the occurrence of events of interest ('''<code>subscribeLifeTime(Consumer,RILifetimeTopic*)</code>''','''<code>unsubscribeLifeTime(Consumer,RILifetimeTopic*)</code>''').
 +
 
 +
<pre>
 +
class MyConsumer extends Consumer {
 +
 
 +
  /**{@inheritDoc} */
 +
  synchronized protected void onReady(GHNRIRegistrationEvent event) {
 +
   
 +
    GCUBEServiceContext ctxt = event.getPayload();
 +
    ...
 +
  }
 +
 
 +
/**{@inheritDoc} */
 +
  synchronized protected void onFailure(GHNLifeTimeEvent event) {...}
 +
 
 +
}
 +
 
 +
...
 +
 
 +
MyContext.getContext().subscribeLifeTime(new MyConsumer(), RILifetimeTopic.READY, RILifetimeTopic.FAILED);
 +
MyContext.getContext().subscribeLifeTime(new MyConsumer());  //subscribes to all lifetime topics
 +
</pre>
 +
 
 +
=== Scope and Security Management ===
 +
 
 +
The service context implements the <code>GCUBESecurityManager</code> interface and the <code>GCUBEScopeManager</code> interface, thereby serving as a thread-indexed repository of [[Scope_Management#Scope_Managers|scope information]] and [[Security_Management#Handling_Credentials:Security_Managers|service credentials]] for the whole service implementation. Accordingly, developers can pass their service context into all the methods of gCF that expects either type of manager, e.g.:
 +
 
 +
<pre>
 +
ISClient client = GHNContext.getContext(ISClient.class);
 +
GCUBERIQuery query = client.getQuery(GCUBERIQuery.class);
 +
 
 +
...prepare query...
 +
 
 +
client.execute(query,MyContext.getContext()) //service context used as a scope manager
 +
</pre>
 +
 
 +
The service context implements scope the <code>GCUBESecurityManager</code> and the <code>GCUBEScopeManager</code> interfaces by delegating to internal implementations of the interfaces. The following methods in <code>GCUBEServiceContext</code> expose the internal implementations to subclasses (i.e. they are <code>protected</code> methods):
 +
 
 +
*<code>getSecurityManager()</code>: returns the internal security manager.
 +
*<code>getScopeManager()</code>: returns the internal scope manager.
 +
 
 +
For security management, the service context also complements the interface with the following convenience methods:
 +
 
 +
*<code>useCallerCredentials(thread?)</code>: If security is enabled, it indicates that outgoing calls in a given thread must use the credentials associated with the incoming call. It has no effect otherwise. If the thread parameter is omitted, an invocation of this method is equivalent to:
 +
 
 +
<pre>
 +
MyContext.getContext().useCredentials(MyContext.getContext().getCallerCredentials())</code>  
 +
</pre>
 +
 
 +
:or, if the thread is specified:
 +
 
 +
<pre>
 +
MyContext.getContext().useCredentials(thread, MyContext.getContext().getCallerCredentials())</code>
 +
</pre>
 +
 
 +
For scope management, the service context does not add convenience methods but enforces some pre-conditions before delegating to the inner scope manager. In particular:
 +
 
 +
*<code>setScope(scope)</code>, <code>setScope(thread,scope)</code>, and <code>prepareCall(remote,class,name,scope?)</code> reject any scope that is not compatible with those of the RI.
 +
 
 +
For similar reasons, the service context adds pre-conditions and post-conditions to the methods of the inner <code>GCUBERunningInstance</code> that add or remove scopes. In particular:
 +
 
 +
*<code>addScope(scope*)</code> filters out any scope that is not compatible with the scope of the gHN context ''and'' the scope of the service of the RI. Furthermore, it triggers a transition of the RI to the <code>UPDATED</code> state if some scope has actually been added post-filitering.
 +
 
 +
*<code>removeScope(scope*)</code> unpublishes the RI from all the scopes from which it has been removed and triggers a transition of the RI to the <code>UPDATED</code> state.
 +
 
 +
Finally, the service context can expose the scopes with which the RI was first activated on the gHN. In particular:
 +
 
 +
*<code>getStartScopes()</code>: returns the scopes configured for the RI, or the start scopes of the gHN if no specific scopes were configured for the RI.
 +
:The start scopes for the RI can be configured by including the following environment in the global section of the [[#The JNDI Configuration|JNDI configuration]] file:
 +
 
 +
<pre>
 +
<environment name="startScopes" value="/some/scope,/some/other/scope,.." type="java.lang.String"/>
 +
</pre>
 +
 
 +
where the value of the environment is a comma-separated list of well-formed and gHN-compatible scope expressions.
  
 
== PortType Contexts ==
 
== PortType Contexts ==
  
[coming soon]
+
<code>GCUBEPortTypeContext</code> specialises the <code>GCUBEContext</code> as the context of a single port-type of the service. <code>GCUBEStatefulPortTypeContext</code> specialises further <code>GCUBEPortTypeContext</code> as the context of a stateful port-type.
 +
 
 +
<code>GCUBEPortTypeContext</code> and <code>GCUBEStatefulPortTypeContext</code> are abstract and developers must specialise them further for their port-types.
 +
By doing so, they obtain components that:
 +
 
 +
* expose the configuration of the service which relates specifically to a port-type rather than the service as a whole.
 +
:This functionality allows service developers to inspect the properties of the port-type at runtime.
 +
 
 +
* manage the lifetime of a port-type in the broader context of the RI lifetime.
 +
:This functionality supports the correct operation of the port-type and can be extended by service developers.
 +
 
 +
Developers that derive <code>GCUBEPortTypeContext</code> or <code>GCUBEStatefulPortTypeContext</code> must implement the following methods:
 +
 
 +
* '''<code>getJNDIName()</code>''': returns the name of the corresponding port-type section in the [[Configuration_Components#The_JNDI_Configuration|JNDI configuration file]].
 +
 
 +
* '''<code>getNamespace()</code>''': returns the namespace of the port-type, as  specified in its WSDL.
 +
 
 +
* '''<code>getServiceContext()</code>''': returns the context of the service.
 +
 
 +
Like with the service context, developers are responsible for the implementation of port-type contexts as singleton classes. The recommended approach is the same:
 +
 
 +
<pre>
 +
public class MyPortTypeContext extends GCUBEPortTypeContext {
 +
 
 +
  private static MyPortTypeContext singleton = new MyPortTypeContext();
 +
 
 +
  private MyPortTypeContext(){}
 +
 
 +
  public static MyPortTypeContext getContext() {return singleton;}
 +
 
 +
  /** {@inheritDoc} */
 +
  public MyServiceContext getContext() {...}
 +
 
 +
  /** {@inheritDoc} */
 +
  protected String getJNDIName() {...}
 +
 
 +
  /** {@inheritDoc} */
 +
  protected String getNamespace() {...}
 +
 
 +
}
 +
</pre>
 +
 
 +
=== Configuration and File Management ===
 +
 
 +
All port-type contexts expose the following forms of configuration:
 +
 
 +
* the object bindings in the section of the [[Configuration_Components#The_JNDI_Configuration|JNDI configuration file]] dedicated to the port-type.
 +
 
 +
<pre>
 +
Foo foo = MyPortTypeContext.getContext().getFoo();                    //accessor specific to user-defined binding
 +
Bar bar - (Bar) MyPortTypeContext.getContext().getProperty("bar");    //general access to user-defined binding
 +
</pre>
 +
 
 +
* the [[#The Deployment Descriptor|deployment descriptor]] and the [https://technical.wiki.d4science.research-infrastructures.eu/documentation/index.php/How_To_Configure_Service_Security#Create_Web_Service_Security_Descriptor_.28WSSD.29 security descriptor]  of the port-type.
 +
 
 +
<pre>
 +
WSDDService ddescriptor = MyPortTypeContext.getContext().getDeploymentDescriptor();
 +
ServiceSecurityDescriptor sdescriptor = MyPortTypeContext.getContext().getSecurityDescriptor();
 +
</pre>
 +
 
 +
* the endpoint of the port-type.
 +
 
 +
<pre>
 +
EndpointReferenceType epr = MyPortTypeContext.getContext().getEPR();
 +
</pre>
 +
 
 +
In addition, stateful port-type contexts expose configuration and utility methods related to the management of the stateful resources associated with the port-type (cf. [[State_Management|State Management]]):
 +
 
 +
* <code>getRPDName()</code>: returns the name of the Resource Property Document (RPD) of (public) stateful resources.
 +
 
 +
*<code>getPublicationProfile()</code>: returns the publication profile of (public) stateful resources.
 +
 
 +
*<code>getResourceLifeTime()</code>: returns the lifetime of stateful resources (in seconds).
 +
 
 +
These methods reflect mandatory elements of the port-type configuration and are used mostly by gCF. More immediately relevant to developers are the following methods:
 +
 
 +
*<code>getWSHome()</code>: returns the home of public stateful resources.
 +
 
 +
*<code>getHome()</code>: returns the home of private stateful resources.
 +
 
 +
*<code>makeKey()</code>: returns a new qualified key for stateful resources.
 +
 
 +
As to file management, all port-type contexts delegate the methods <code>getFile()</code> and <code>getPersistentFile()</code> inherited from <code>GCUBEContext</code> to the corresponding methods of the service context. Port-types thus share configuration directory and persistent root with their service.
 +
 
 +
=== Lifetime Management ===
 +
 
 +
The life-time of a port-type is regulated by the [[#Lifetime Management_2|lifetime of the RI]]. In particular, all port-types:
 +
 
 +
* register a RI <code>Consumer</code> with the service context (discussed [[#Event Management_2|here]])
 +
: This allows port-types to perform basic lifetime operations in sync with the RI.
 +
 
 +
* define callbacks for RI lifetime events that echo those defined by <code>Consumer</code>: <code>onInitalisation()</code>, <code>onReady()</code>, <code>onUpdate()</code>, <code>onStateChange()</code>, <code>onShutdown()</code>, <code>onFailure()</code>.
 +
: This gives a convenient place for developers to add port-type specific reactions to lifetime events of the RI.

Latest revision as of 13:16, 11 September 2009

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

The most obvious case of shared functionality is that 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. Information related to scope and security is also 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;
  • the port-types of target services.

Some contexts are fully defined within gCF. Others are only partially implemented and need to be specialised within service implementations. Collectively, the context form a inheritance hierarchy contained in the package org.gcube.common.core.contexts:

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 package components is summarised below:

  • GCUBEContext: the base implementation of all contexts.
  • GCUBEServiceContext: a concrete specialisation of GCUBEContext for gCube Services and their RIs.
  • GCUBEPortTypeContext: a concrete specialisation of GCUBEContext for the port-types of gCube Services.
  • GCUBEStatefulPortTypeContext: a concrete specialisation of GCUBEPortTypeContext for the stateful port-types of gCube Services.
  • GCUBERemotePortTypeContext: a concrete specialisation of GCUBEContext for port-typea of target services.
  • GHNContext: a concrete specialisation of GCUBEContext for running gCube Hosting Nodes.
  • GHNClientContext: a concrete specialisation of GHNContext for gCube Hosting Node that are not runnning.

Before discussing each context class in detail, we refine 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. As discussed above, 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 basic operation for configuration management is the following:

  • 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

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.
  • 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 file management are the following:

  • 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 context of the local gHN. The gHN 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 or port-type 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 the following forms 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 discovery, publishing, and remote notification interfaces, among others.

The implementations are obtained by invoking the following method:

  • getImplementation(interfaceclass): returns the local implementation of the interface represented by the given Class object.
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 (consult the GHNContext documentation for details).
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 returned by GHNContext.getContext() is an instance of GHNClientContext.
  • 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.
  • UPDATED: 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.
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) {...}

Event Management

The gHN context acts in the role of a producer and consumer of events.

As a consumer, it subscribes with all the RIs that register their presence with it during their own their own initialisation (registerService()).

Note Any client of the gHN context can then access the service context of some or all the RIs that have registered (cf. getServiceContext()). Any service, in particular, can rely on this functionality to interact with other, co-deployed services without having static dependencies on them.
GCUBEServiceContext ctxt = GHNContext.getContext().getServiceContext("...service class...","...service name...");
Note One distinguished gCF component, the GCUBEHandler, relies on getServiceContext() 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.

As a producer, the gHN context can notify clients of the occurrence of two different classes of events.

The first class of events relates to the lifetime of the gHN. In particular the gHN defines:

  • a number of GHNTopics, including RIREGISTRATION, READY, UPDATED,SHUTDOWN.
  • a generic GHNLifetTimeEvent that carries en empty payload, and a GHNRIRegistrationEvent specialisation of GHNLifetTimeEvent which carries the service context of the registered RI as its payload.
  • a pre-defined consumer implementation, GHNConsumer, that prospective consumers can derive by implementing the event-specific callback of interest.
  • methods to subscribe for and unsubscribe from the occurrence of events of interest (subscribeGHNEvents(GHNConsumer,GHNLifetimeEvent*), unsubscribeGHNEvents(GHNConsumer,GHNLifetimeEvent*)).
class MyConsumer extends GHNConsumer {

  /**{@inheritDoc} */
  synchronized protected void onRIRegistration(GHNRIRegistrationEvent event) {
     
     GCUBEServiceContext ctxt = event.getPayload(); 
     ...

  }

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

}

...

GHNContext.getContext().subscribeGHNEvents(new MyConsumer(), GHNTopic.RIREGISTRATION, GHNTopic.READY);
GHNContext.getContext().subscribeGHNEvents(new MyConsumer()); ///subscribe to all events

The second class of events related to the renewal of credentials that RIs might need to operate in a secure infrastructure. In particular, 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). The process subsumes the following steps:

  • RIs subscribe with the gHN context to request credentials (subscribeForCredential()).
  • the RI of the Delegation also subscribes with the gHN context to be notified of credential renewal request (subscribeForCredentialRequest()).
  • When credentials are delivered by the infrastructure, the RI of the Delegation service 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 pre-defined consumer implementations that prospective consumers can derive by implementing the event-specific callback of interests (CredentialConsumer,CredentialRequestConsumer). In some special cases service developers may wish to extend CredentialConsumer, but normally they won't need to as - as discussed below - the context of their services will do it on their behalf.

Service Contexts

GCUBEServiceContext specialises 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, developers obtains a component that:

  • exposes the configuration of the service and its RI.
This functionality allows service developers to inspect the properties of the service and its RI at runtime.
  • 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 be 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:

RIStates.jpg

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 security manager that handles service credentials on behalf of the RI. The security manager is identified from the JNDI configuration of the service. In the lack of explicit configuration, the service context initialises a GCUBESimpleServiceSecurityManager.
  • initialising the scope manager that handles scope information on behalf of the RI.
  • initialising the persistence manager that manages the remote persistence of the state of the RI, if one is specified in the JNDI configuration of the service.
  • initialising the plugin manager of the service, if one is specified in the JNDI configuration of the service.
  • 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.
Note: A RI which is not this state will not receive calls. A distinguished gCF component, the GGCUBEHandler will intercept all calls and refuse to forward them (see also the lifetime of the gHN context for further information.
  • UPDATED: this is the state of a RI that has changed its profile.
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.
  • STATECHANGED: this is the state of a RI that has changed its persistent state.
This is a temporary state in which the service context notifies all interested clients of the change before returning to its previous state. The RI may transition to this state from the INITIALISED and READY states and does so when the method notifyStateChange() is explicitly invoked by clients.
Note: the persistence manager of the RI is the primary consumer of state change events. Upon notification, the manager will attempt to store the updated state of the RI.
Note: persistence delegates of stateful port-types are distinguished producers of state change events. They invoke notifyStateChange() in correspondence with the serialisation of stateful resources.
Note: any client may invoke notifyStateChange() to notify of a change in the persistent state of the RI. A common case is when the client has successfully stored a file 'obtained' by invoking the method getPersistentFile().
  • DOWN: this is the state of a RI managed by a gHN that has experienced failure or is about shutdown.
This is a non-operational state in which the RI cannot satisfy client requests. Upon transitioning to this state, the service context notifies all interested clients.
  • FAILED: this is the state of a RI that has experienced a failure.
This is a non-operational state in which the RI cannot satisfy client requests. Upon transitioning to this state, the service context notifies all interested clients.

Event Management

All the state transitions of a RI subsume a callback on the service context that the developer can implement in its specialisation of GCUBEServiceContext. The available callbacks are: onInitalisation(), onReady(), onUpdate(), onStateChange(), onShutdown(), onFailure(). Most transitions are conditional to the faultless execution of the corresponding callback.

/** {@inheritDoc} */
protected void onReady() throws Exception {
  if (...) throw new Exception();           //would fail the transition to READY
}

Other components of the service implementation - and in fact any component that has a reference to the service context - can be notified of state transitions by subscribing with the RI for the occurrence of lifetime events. In particular, the service context defines:

  • a number of RILifetimeTopics, including DEPLOYED,INITIALISED,READY,FAILED,UPDATED,STATECHANGE,DOWN.
  • a generic RILifetimeEvent event that carries the service context as its payload.
  • a pre-defined implementation of a Consumer that prospective consumers can derive by implementing the event-specific callback of interest.
  • methods to subscribe for and unsubscribe from the occurrence of events of interest (subscribeLifeTime(Consumer,RILifetimeTopic*),unsubscribeLifeTime(Consumer,RILifetimeTopic*)).
class MyConsumer extends Consumer {

  /**{@inheritDoc} */
  synchronized protected void onReady(GHNRIRegistrationEvent event) {
     
     GCUBEServiceContext ctxt = event.getPayload();
     ...
  }

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

}

...

MyContext.getContext().subscribeLifeTime(new MyConsumer(), RILifetimeTopic.READY, RILifetimeTopic.FAILED);
MyContext.getContext().subscribeLifeTime(new MyConsumer());  //subscribes to all lifetime topics

Scope and Security Management

The service context implements the GCUBESecurityManager interface and the GCUBEScopeManager interface, thereby serving as a thread-indexed repository of scope information and service credentials for the whole service implementation. Accordingly, developers can pass their service context into all the methods of gCF that expects either type of manager, e.g.:

ISClient client = GHNContext.getContext(ISClient.class);
GCUBERIQuery query = client.getQuery(GCUBERIQuery.class);

...prepare query...

client.execute(query,MyContext.getContext()) //service context used as a scope manager

The service context implements scope the GCUBESecurityManager and the GCUBEScopeManager interfaces by delegating to internal implementations of the interfaces. The following methods in GCUBEServiceContext expose the internal implementations to subclasses (i.e. they are protected methods):

  • getSecurityManager(): returns the internal security manager.
  • getScopeManager(): returns the internal scope manager.

For security management, the service context also complements the interface with the following convenience methods:

  • useCallerCredentials(thread?): If security is enabled, it indicates that outgoing calls in a given thread must use the credentials associated with the incoming call. It has no effect otherwise. If the thread parameter is omitted, an invocation of this method is equivalent to:
MyContext.getContext().useCredentials(MyContext.getContext().getCallerCredentials())</code> 
or, if the thread is specified:
MyContext.getContext().useCredentials(thread, MyContext.getContext().getCallerCredentials())</code>

For scope management, the service context does not add convenience methods but enforces some pre-conditions before delegating to the inner scope manager. In particular:

  • setScope(scope), setScope(thread,scope), and prepareCall(remote,class,name,scope?) reject any scope that is not compatible with those of the RI.

For similar reasons, the service context adds pre-conditions and post-conditions to the methods of the inner GCUBERunningInstance that add or remove scopes. In particular:

  • addScope(scope*) filters out any scope that is not compatible with the scope of the gHN context and the scope of the service of the RI. Furthermore, it triggers a transition of the RI to the UPDATED state if some scope has actually been added post-filitering.
  • removeScope(scope*) unpublishes the RI from all the scopes from which it has been removed and triggers a transition of the RI to the UPDATED state.

Finally, the service context can expose the scopes with which the RI was first activated on the gHN. In particular:

  • getStartScopes(): returns the scopes configured for the RI, or the start scopes of the gHN if no specific scopes were configured for the RI.
The start scopes for the RI can be configured by including the following environment in the global section of the JNDI configuration file:
<environment name="startScopes" value="/some/scope,/some/other/scope,.." type="java.lang.String"/>

where the value of the environment is a comma-separated list of well-formed and gHN-compatible scope expressions.

PortType Contexts

GCUBEPortTypeContext specialises the GCUBEContext as the context of a single port-type of the service. GCUBEStatefulPortTypeContext specialises further GCUBEPortTypeContext as the context of a stateful port-type.

GCUBEPortTypeContext and GCUBEStatefulPortTypeContext are abstract and developers must specialise them further for their port-types. By doing so, they obtain components that:

  • expose the configuration of the service which relates specifically to a port-type rather than the service as a whole.
This functionality allows service developers to inspect the properties of the port-type at runtime.
  • manage the lifetime of a port-type in the broader context of the RI lifetime.
This functionality supports the correct operation of the port-type and can be extended by service developers.

Developers that derive GCUBEPortTypeContext or GCUBEStatefulPortTypeContext must implement the following methods:

  • getNamespace(): returns the namespace of the port-type, as specified in its WSDL.
  • getServiceContext(): returns the context of the service.

Like with the service context, developers are responsible for the implementation of port-type contexts as singleton classes. The recommended approach is the same:

public class MyPortTypeContext extends GCUBEPortTypeContext {

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

   public static MyPortTypeContext getContext() {return singleton;}

  /** {@inheritDoc} */
   public MyServiceContext getContext() {...}

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

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

}

Configuration and File Management

All port-type contexts expose the following forms of configuration:

Foo foo = MyPortTypeContext.getContext().getFoo();                     //accessor specific to user-defined binding 
Bar bar - (Bar) MyPortTypeContext.getContext().getProperty("bar");     //general access to user-defined binding 
WSDDService ddescriptor = MyPortTypeContext.getContext().getDeploymentDescriptor();
ServiceSecurityDescriptor sdescriptor = MyPortTypeContext.getContext().getSecurityDescriptor();
  • the endpoint of the port-type.
EndpointReferenceType epr = MyPortTypeContext.getContext().getEPR();

In addition, stateful port-type contexts expose configuration and utility methods related to the management of the stateful resources associated with the port-type (cf. State Management):

  • getRPDName(): returns the name of the Resource Property Document (RPD) of (public) stateful resources.
  • getPublicationProfile(): returns the publication profile of (public) stateful resources.
  • getResourceLifeTime(): returns the lifetime of stateful resources (in seconds).

These methods reflect mandatory elements of the port-type configuration and are used mostly by gCF. More immediately relevant to developers are the following methods:

  • getWSHome(): returns the home of public stateful resources.
  • getHome(): returns the home of private stateful resources.
  • makeKey(): returns a new qualified key for stateful resources.

As to file management, all port-type contexts delegate the methods getFile() and getPersistentFile() inherited from GCUBEContext to the corresponding methods of the service context. Port-types thus share configuration directory and persistent root with their service.

Lifetime Management

The life-time of a port-type is regulated by the lifetime of the RI. In particular, all port-types:

  • register a RI Consumer with the service context (discussed here)
This allows port-types to perform basic lifetime operations in sync with the RI.
  • define callbacks for RI lifetime events that echo those defined by Consumer: onInitalisation(), onReady(), onUpdate(), onStateChange(), onShutdown(), onFailure().
This gives a convenient place for developers to add port-type specific reactions to lifetime events of the RI.