The Handler Framework

From GCube System
Revision as of 15:17, 16 September 2009 by Fabio.simeoni (Talk | contribs)

Jump to: navigation, search

One way to characterise the overall behaviour of a service is in terms of the tasks that the service is designed to carry out. Interacting with a target service in the process of satisfying a client request is a common example of a task; monitoring some of the local resources at regular intervals is another. In many cases, modelling such tasks with dedicated components improves their maintainability and reusability. In particular, the same task components can be customised and reused in different parts of the service implementation or assembled in some guise to form more complex tasks. In recognition of these benefits, gCF defines a framework for task definition and management that builds upon a standardisation of tasks as gCube handlers.

Overview

The handler framework is defined by the inheritance hierarchy contained in the package org.gcube.common.core.utils.handlers:

Handlers.jpg

The high-level roles of package components are summarised below:

  • GCUBEIHandler: the interface of all handlers.
  • GCUBEHandler: the abstract base implementation of GCUBEIHandler.
  • GCUBEComplexHandler: an abstract specialisation of GCUBEHandler for complex handlers, i.e handlers that manage other handlers.
  • GCUBEScheduledHandler: a concrete specialisation of GCUBEComplexHandler for handlers that schedule the execution of other handlers.
  • GCUBESequentialHandler: a concrete specialisation of GCUBEComplexHandler for handlers that chain the execution of other handlers.
  • GCUBEHandlerConnector: a concrete specialisation of GCUBEHandler for handlers that connect the inputs and outputs of handlers chained by a GCUBEScheduledHandler.
  • GCUBEParallelHandler: a concrete specialisation of GCUBEComplexHandler for handlers that parallelise the execution of other handlers.
  • GCUBEServiceHandler: an abstract specialisation of GCUBEHandler for handlers that engage in interactions with remote service port-types.
  • GCUBEStatefulServiceHandler: an abstract specialisation of GCUBEServiceHandler for handlers that engage in interactions with remote WS-Resources.
  • GCUBEStagingServiceHandler: an abstract specialisation of GCUBEStatefulServiceHandler for handlers that can stage the WS-resource with which they need to interact.

The framework also includes the following auxiliary packages:

  • handlers.events: a package of components related to the management of events produced by handlers.
  • handlers.lifetime: a package of components related to the management of the lifetime of handlers.

Basics Concepts

The basic behaviour of all handlers is defined by the GCUBEIHandler interface. The class GCUBEHandler provides a base implementation for it which most handlers are expected to directly or indirectly extend.

class MyHandler extends GCUBEHandler {
 
...
 
}

The first and foremost method of all handlers is the run() method, which implements the task associated with the handler. This is in fact the only method of GCUBEIHandler that GCUBEHandler does not implement. The following is thus one of the simplest handlers:

class MyHandler extends GCUBEHandler {
 
 /** {@inheritDoc} **/
 public void run() throws Exception {
   System.out.println("carrying out a task..");
 }
 
}
 
...
 
MyHandler handler;
...
handler.run();

Note: The framework makes no assumption about how run() is implemented, expect for the fact that it may generate an exception. For some handlers it may block while for others it may return immediately. Again, for some handlers it may be invoked only once while for others it may be invoke an arbitrary number of times.

Most handlers will need inputs and produce outputs. Since run() does not cater for them, handlers need some form of state in which clients can put inputs and find outputs. Handlers are free to define any form of state and access to state (dedicated getters, setters, and constructors). The two forms of state discussed next, however, are predefined in GCUBEIHandler to enable generic state management strategies across handlers of different types. Their use is entirely optional.

The Handled Object

The first form of state is an arbitrary object upon or on behalf of which the handler is expected to execute, the handled object. GCUBEIHandler defines accessors for it and GCUBEHandler implements them (setHandled(), getHandled()). GCUBEIHandler and GCUBEHandler are in fact parametric in the type of the handled object:

public interface GCUBEHandler<T> {
 ...
 public void setHandled(T h);
 public T getHandled(T h);
 ...
}

Note: The parametrisation allows the framework to be specialised in contexts where handlers are expected to handle objects of a give type, at no loss of static typechecking.

In spite of their name, not all handlers need a handler object of course. MyHandler above does not for example and derives GCUBEHandler as a raw type. To avoid compiler's warnings, it could have been defined as:

class MyHandler extends GCUBEHandler<Object> {...}

The following variation of MyHandler exemplifies the use of handled objects:

class MyHandler extends GCUBEHandler<String> {
 
 /** {@inheritDoc} **/
 public void run() throws Exception {
   System.out.println("executing with "+this.getHandled());
 }
 
}
 
...
 
MyHandler handler;
...
handled.setHandled("some task");
handled.run();

Note: In practice, the handled object is often an instance of some distinguished component of the service implementation (e.g. a stateful resource). One ore more handlers are then used to implement tasks associated with the instance as if they were separate extensions of its class:

class SomeServiceComponent {.....}
...
class SomeComponentTask extends GCUBEHandler<SomeServiceComponent> {
 
 /** {@inheritDoc} **/
 public void run() throws Exception {
    ...
    ... this.getHandled().getSomeProperty()...
    ... // do something important .....
    ... this.getHandled().setSomeOtherProperty(...)...
    ... 
 }
 
}

Note: For convenience, GCUBEHandler has a constructor parametric in the handled object to which subclasses may wish to delegate.

class MyHandler extends GCUBEHandler<String> {
 
 public MyHandler(String s) {super(s);}
 ....
 
}

The Blackboard

A less constrained form of state associated with all handlers is in the forms of a map of arbitrary named objects, the blackboard. Again, GCUBEIHandler defines accessors for it GCUBEHandler implements them (getBlackboard(), setBlackboard(), clearBlackboard()). The names of the objects that handlers expect to find in their state or else put in their state is defined by the convention (i.e. it must be part in the documentation of the handler).

class MyHandler extends GCUBEHandler<Object> {
 
 /** {@inheritDoc} **/
 public void run() throws Exception {
   this.getBlackboard().put("output",this.getBlackboard().get("input")+1);
 }
 
}
 
...
 
MyHandler handler;
...
Map<String,Object> map = new HashMap<String,Object>();
map.put("input",5);
handler.setBlackboard(map);
handler.run();
System.out.println(handler.getBlackboard().get("output"));

This exemplifies the use of the blackboard but does not justify it. The blackboard is not intended for the exchange of information between handlers and their clients, where the handled object, getters, setters, and constructors fare better. It was primarily intended for the loosely-coupled exchange of information between handlers that are developed independently but under the same conventions. As we discuss below, this exchange may occur when handlers are chained in a GCUBESequentialHandler.

Miscellaneous Functionality

GCUBEIHandler defines a number of auxiliary methods that may be useful for the management of handlers and GCUEBHandler implements them. These include:

  • getID(): returns a unique identifier for the handler which may be used to discriminate between instances of the same class;
  • getName()/setName(): returns/sets a name for the handler (by default, this is the unqualified name of its class). This is used by the framework primarily for logging purposes;
  • getSecurityManager()/setSecurityManager(): returns/set a security manager for the execution of the handler. This methods allow security information to propagate from clients to handlers;
  • getScopeManager()/setScopeManager(): returns/set a scope manager for the execution of the handler. This methods allow scope information to propagate from clients to handlers;
  • getLogger(): returns a logger for the execution of the handler. It is recommended that handlers make use of the predefined loggers rather than define their own logger (this allows the injection of different loggers and the transparent redirection of logs based on context of usage).

Event and Lifetime Management

Interesting things may occur during the execution of a handler and their clients may wish to be notified of their occurrence. Events may be arbitrary, of course, but of common interest are those that reflect changes to the state of execution of a running handler, e.g. the execution has terminated successfully, or it has aborted due to some problem, or else it is momentarily suspended. These are lifetime events.

All handlers are event producers and accept subscriptions for events in one more topics of interests, first and foremost lifetime events. The handling of topics, events, subscriptions, and notifications follows standard gCF patterns (if you are not already familiar with the gCF Event Framework, this is a good time to have a look).

  • The class Topic in package org.gcube.common.core.utils.handlers.events is the abstract root of all topics associated with the execution of handlers. Topic.LifetimeTopic is a predefined subclass of Topic that 'groups' lifetime events.
public abstract class Topic implements GCUBETopic {
    ...
    public static final class LifetimeTopic extends Topic {
       public static final LifetimeTopic INSTANCE = new LifetimeTopic();
       private LifetimeTopic(){};
    }
 
}

Note that LifetimeTopic has a single instance LifetimeTopic.INSTANCE.

  • The class Event also in package org.gcube.common.core.utils.handlers.events is the abstract root of all events that are about Topics, whatever their payload. Event.LifetimeEvent is an abstract subclass of Event that models lifetime events. Event.Running,Event.Suspended, Event.Done, and Event.Failed are four predefined subclasses of Event.LifetimeEvent:
abstract public class Event<TOPIC extends Topic,PAYLOAD> extends GCUBEEvent<TOPIC,PAYLOAD>{
 
	...
       public abstract static class LifetimeEvent extends Event<LifetimeTopic,GCUBEHandler<?>>{}
 
	public static class Running extends LifetimeEvent {...}
	public static class Suspended extends LifetimeEvent {...}
	public static class Done extends LifetimeEvent {...}
	public static class Failed extends LifetimeEvent {...}
}

Note that LifetimeEvent has a GCUBEHandler as its payload. This is of course the vary handler the event is about (and whih has fired the event).

  • Monitor is a partial implementation of a consumer of Events about Topics. It exists for the convenience of actual consumers, which become easier to write if they extend Consumer and implement the callbacks of Consumer that interests them. Consumer defines callbacks for predefined lifetime events, as well as a generic callback for any user-defined event:
public abstract class Monitor implements GCUBEConsumer<Topic,Object> {
          ...
	 protected void onRunning(Running e) {}
	 protected void onCompletion(Done e) {}
	 protected void onFailure(Failed e) {}
	 protected void onSuspension(Suspended e) {}
	 protected void process(Event<?,?> e){}
}

Note that all callbacks receive the corresponding event and that their implementation does nothing by default. Note also the generic callback process() which receives an equallly generic event. Consumer will pass to process() all the events that it receives, including lifetime events (but after having passed them to more specific callbacks).


[in progress]

General-Purpose Handlers

[coming soon]

Sequential Handlers

[coming soon]

Parallel Handlers

[coming soon]

Scheduling Handlers

[coming soon]

Handlers for remote Interactions

[coming soon]

Service Handlers & the Client Interface

[coming soon]

Staging Service Handlers

[coming soon]