Difference between revisions of "Contexts"
(→Lifetime Management) |
|||
(128 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | Within | + | 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 <code>org.gcube.common.core.contexts</code>: | |
− | + | [[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 package components is summarised below: | |
− | + | ||
− | + | ||
+ | * <code>GCUBEContext</code>: the base implementation of all contexts. | ||
+ | |||
+ | * <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 the port-types of gCube Services. | ||
+ | |||
+ | * <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 port-typea of target services. | ||
+ | |||
+ | * <code>GHNContext</code>: a concrete specialisation of <code>GCUBEContext</code> for running gCube Hosting Nodes. | ||
+ | |||
+ | * <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 the [[Service Model|service model]] to introduce key context classes from the perspective of the service developer: | ||
+ | |||
+ | [[Image:model&contexts.jpg]] | ||
+ | |||
+ | The figure shows that the <code>GHNContext</code>, the <code>GHNClientContext</code>, and <code>GCUBERemotePortTypeContext</code> 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 [[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. | ||
+ | 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: | ||
+ | |||
+ | * '''<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> | ||
+ | |||
+ | 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: | ||
+ | |||
+ | *'''<code>getResource(path)</code>''': 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. | ||
+ | |||
+ | <pre> | ||
+ | InputStream foo = someContext.getResource("resources/foo.properties"); //relative path | ||
+ | InputStream bar = someContext.getResource("/org/acme/resources/bar.properties"); //absolute path | ||
+ | </pre> | ||
+ | |||
+ | *'''<code>getFile(path,mode)</code>''': returns a <code>File</code> 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. | ||
+ | |||
+ | <pre> | ||
+ | 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' | ||
+ | </pre> | ||
== The gHN Context == | == The gHN 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. | ||
+ | :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 <code>getContext()</code> method of the <code>GHNContext</code> class: | ||
+ | |||
+ | <pre> | ||
+ | GHNContext context = GHNContext.getContext(); | ||
+ | </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]] 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. | ||
+ | |||
+ | === 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 <code>$GLOBUS_LOCATION/share/schema/gcube/common/core/profiles/node.xsd</code>. The information can be accessed via the <code>GCUBEHostingNode</code> resource returned by the method <code>getGHN()</code>. | ||
+ | |||
+ | <pre> | ||
+ | Iterator<Package> iterator = GHNContext.getContext().getGHN().getDeployedPackages().iterator(); | ||
+ | String version = GHNContext.getContext().getGHN().getNodeDescription().getOS().getVersion(); | ||
+ | </pre> | ||
+ | |||
+ | *configuration related to the implementation of core gCF interfaces. | ||
+ | :This includes implementations of discovery, publishing, and remote notification [[Client_Libraries|interfaces]], among others. | ||
+ | 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> | ||
+ | ISCLient client = GHNContext.getContext().getImplementation(ISClient.class); | ||
+ | </pre> | ||
+ | |||
+ | *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 (consult the <code>GHNContext</code> documentation for details). | ||
+ | |||
+ | <pre> | ||
+ | String url = GHNContext.getContext().getBaseURL(); | ||
+ | Integer port = GHNContext.getContext().getFreePort(); | ||
+ | Map<String,Double> stats = GHNContext.getContext().getLoadStatistics(); | ||
+ | </pre> | ||
+ | |||
+ | *information contained in the gHN [[Administrator Guide#Configuring the gHN|configuration files]]. | ||
+ | :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 [[#Common Facilities|facilities]] that the gHN context inherits from <code>GCUBEContext</code>. | ||
+ | |||
+ | <pre> | ||
+ | GCUBEScope[] scopes = GHNContext.getContext().getStartScopes(); | ||
+ | GHNContext.Type type = GHNContext.getContext().getType(); | ||
+ | GHNContext.Mode mode = GHNContext.getContext().getMode(); | ||
+ | String country = (String) GHNContext.getContext().getProperty("country"); | ||
+ | </pre> | ||
+ | |||
+ | === Lifetime Management === | ||
+ | |||
+ | For lifetime management purposes, the gHN context models the gHN as the following finite state machine: | ||
+ | |||
+ | [[Image: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: | ||
+ | |||
+ | *'''<code>DEPLOYED</code>''': 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 and File Management|configuration management]] purposes. This mode of operation of the context is called ''client mode''. | ||
+ | |||
+ | ::''' 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. | ||
+ | : This is a non-operational state in which the gHN context initialises the gHN. This involves: | ||
+ | :* loading its [[Administrator_Guide#Configuring_the_gHN|configuration]]. | ||
+ | :* deriving a <code>GCUBEHostingNode</code> [[Resource Model|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). | ||
+ | |||
+ | *'''<code>READY</code>''': 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. | ||
+ | |||
+ | *'''<code>CERTIFIED</code>''': 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. | ||
+ | |||
+ | *'''<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. | ||
+ | :* 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>. | ||
+ | :*in response to key lifetime event, such as the addition and removal of scopes. | ||
+ | :*after the periodic regeneration of the gHN profile. | ||
+ | |||
+ | *'''<code>DOWN</code>''': 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. | ||
+ | |||
+ | *'''<code>FAILED</code>''': 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. <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. | ||
+ | |||
+ | 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>'''). Any service, in particular, can rely on this functionality to interact with other, co-deployed services without having static dependencies on them. | ||
+ | |||
+ | <pre> | ||
+ | GCUBEServiceContext ctxt = GHNContext.getContext().getServiceContext("...service class...","...service name..."); | ||
+ | </pre> | ||
+ | |||
+ | :'''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. | ||
+ | |||
+ | 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> | ||
+ | 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 | ||
+ | </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: | ||
+ | * RIs subscribe with the gHN context to request credentials (<code>subscribeForCredential()</code>). | ||
+ | * 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 <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, 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. | ||
+ | |||
+ | * acts as a centralised [[Scope Management#Scope Managers|scope manager]] and [[Security Management#Handling Credentials: Security Managers|security manager]] for the entire service implementation. | ||
+ | :This functionality supports the loosely-coupled propagation of scope and credentials across components of the service implementation. | ||
+ | |||
+ | 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]]. | ||
+ | |||
+ | Developers are also responsible for the implementation of the service context as a singleton class. The recommended approach can be exemplified as follows: | ||
+ | |||
+ | <pre> | ||
+ | public class MyServiceContext extends GCUBEServiceContext { | ||
+ | |||
+ | private static MyServiceContext singleton = new MyServiceContext(); | ||
+ | |||
+ | private MyServiceContext(){} | ||
+ | |||
+ | public static MyServiceContext getContext() {return singleton;} | ||
+ | |||
+ | /** {@inheritDoc} */ | ||
+ | protected String getJNDIName() {...} | ||
+ | |||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | === 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 <code>$GLOBUS_LOCATION/share/schema/gcube/common/core/profiles/service.xsd</code>. For convenience, the service context exposes some profile information with dedicated accessors. The rest is available via the <code>GCUBEService</code> resource returned by the <code>getService()</code> method. | ||
+ | |||
+ | <pre> | ||
+ | String id = MyServiceContext.getContext().getID(); | ||
+ | String name = MyServiceContext.getContext().getName(); | ||
+ | List<Dependencies> dependencies = MyServiceContext.getContext().getService().getDependencies(); | ||
+ | </pre> | ||
+ | |||
+ | * descriptive information contained in the RI profile. | ||
+ | : This information is available via the <code>GCUBERunningInstance</code> resource returned by the <code>getInstance()</code> method. | ||
+ | |||
+ | <pre> | ||
+ | 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(); | ||
+ | </pre> | ||
+ | |||
+ | * the object bindings in the global section of the [[Configuration_Components#The_JNDI_Configuration|JNDI configuration file]]. | ||
+ | |||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | |||
+ | As to file management, the service context offers the following methods: | ||
+ | |||
+ | * '''<code>getFile()</code>''': overrides the method in <code>GCUBEContext</code> 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. | ||
+ | |||
+ | * '''<code>getPersistentFile()</code>''': retains the same usage model of <code>getFile()</code> 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 [[State Management#Adding Persistence|different gHN]]. | ||
+ | |||
+ | === Lifetime Management === | ||
+ | |||
+ | For lifetime management purposes, the service context models the RIs as the following finite state machine: | ||
+ | |||
+ | [[Image:RIStates.jpg]] | ||
+ | |||
+ | The state of the RI can can be described as follows: | ||
+ | |||
+ | *'''<code>DEPLOYED</code>''': 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 [[#The JNDI configuration | JNDI configuration]] of the 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. | ||
+ | :*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|scope manager]] that handles scope information on behalf 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]]. | ||
+ | :The successful completion of these activities triggers a transition of the RI to the <code>INITIALISED</code> state. | ||
+ | |||
+ | *'''<code>INITIALISED</code>''': 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 <code>READY</code> or <code>FAILED</code>, 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: | ||
+ | :* <code>GHNTopic.SHUTDOWN</code> events: when this event occurs, the RI transitions to the state <code>DOWN</code>. | ||
+ | :* <code>GHNTopic.REMOVESCOPE</code> 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 <code>READY</code> state. | ||
+ | |||
+ | *'''<code>READIED</code>''': 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 <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: | ||
+ | :* 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'': <code>READIED</code>, <code>FAILED</code>, and <code>DOWN</code>. | ||
+ | :* in response to key lifetime events, such as addition and removal of scopes. | ||
+ | |||
+ | *'''<code>STATECHANGED</code>''': 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 <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>. | ||
+ | |||
+ | *'''<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. | ||
+ | |||
+ | *'''<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. | ||
+ | |||
+ | === 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 == | ||
− | [ | + | <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 12: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
:
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 ofGCUBEContext
for gCube Services and their RIs.
-
GCUBEPortTypeContext
: a concrete specialisation ofGCUBEContext
for the port-types of gCube Services.
-
GCUBEStatefulPortTypeContext
: a concrete specialisation ofGCUBEPortTypeContext
for the stateful port-types of gCube Services.
-
GCUBERemotePortTypeContext
: a concrete specialisation ofGCUBEContext
for port-typea of target services.
-
GHNContext
: a concrete specialisation ofGCUBEContext
for running gCube Hosting Nodes.
-
GHNClientContext
: a concrete specialisation ofGHNContext
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:
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 aFile
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 theGCUBEHostingNode
resource returned by the methodgetGHN()
.
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 givenClass
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 theGHNContext
documentation for details).
String url = GHNContext.getContext().getBaseURL(); Integer port = GHNContext.getContext().getFreePort(); Map<String,Double> stats = GHNContext.getContext().getLoadStatistics();
- information contained in the gHN configuration files.
- 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:
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 ofGHNClientContext
.
- Note: In client mode, the gHN context returned by
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
, andFAILED
. - in response to key lifetime event, such as the addition and removal of scopes.
- after the periodic regeneration of the gHN profile.
- when it transitions to one of its public states:
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 thegetStatus()
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 ongetServiceContext()
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
GHNTopic
s, includingRIREGISTRATION
,READY
,UPDATED
,SHUTDOWN
. - a generic
GHNLifetTimeEvent
that carries en empty payload, and aGHNRIRegistrationEvent
specialisation ofGHNLifetTimeEvent
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.
- acts as a centralised scope manager and security manager for the entire service implementation.
- 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:
-
getJNDIName()
: the method returns the name of the global section of the 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:
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 theGCUBEService
resource returned by thegetService()
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 thegetInstance()
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();
- the object bindings in the global section of the JNDI configuration file.
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 inGCUBEContext
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 ofgetFile()
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:
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
orFAILED
, 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 stateDOWN
. -
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.
- Note: A RI which is not this state will not receive calls. A distinguished gCF component, the
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
, andDOWN
. - in response to key lifetime events, such as addition and removal of scopes.
- when it transitions to one of its public states:
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
andREADY
states and does so when the methodnotifyStateChange()
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 methodgetPersistentFile()
.
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
RILifetimeTopic
s, includingDEPLOYED
,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*)
).
- a number of
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)
, andprepareCall(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 theUPDATED
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 theUPDATED
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:
-
getJNDIName()
: returns the name of the corresponding port-type section in the JNDI configuration file.
-
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:
- the object bindings in the section of the JNDI configuration file dedicated to the port-type.
Foo foo = MyPortTypeContext.getContext().getFoo(); //accessor specific to user-defined binding Bar bar - (Bar) MyPortTypeContext.getContext().getProperty("bar"); //general access to user-defined binding
- the deployment descriptor and the security descriptor of the port-type.
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.