Receiving notifications of node life cycle events

From JPPF Documentation version 3.x

Jump to: navigation, search

Contents

Main Page > Customizing JPPF > Node life cycle events

This plugin provides the ability to receive notifications of major events ocurring within a node, including node startup and termination as well as the start and completion of each job processing.

NodeLifeCycleListener interface

To achieve this, you only need to implement the interface NodeLifeCycleListener, which is defined as follows:

 public interface NodeLifeCycleListener extends EventListener {
   // Called when the node has finished initializing,
   // and before it starts processing jobs
   void nodeStarting(NodeLifeCycleEvent event);
 
   // Called when the node is terminating
   void nodeEnding(NodeLifeCycleEvent event);
 
   // Called before the node starts processing a job
   void jobStarting(NodeLifeCycleEvent event);
 
   // Called after the node finishes processing a job
   void jobEnding(NodeLifeCycleEvent event);
 }

Each method in the listener receives an event of type NodeLifeCycleEvent, which provides the following API:

 public class NodeLifeCycleEvent extends EventObject {
   // Get the object representing the current JPPF node
   public Node getNode()
 
   // The type of this event
   public NodeLifeCycleEventType getType()
 
   // Get the job currently being executed
   public JPPFDistributedJob getJob();
 
   // Get the tasks currently being executed
   public List<Task> getTasks();
 
   // Get the class loader used to load the tasks and
   // the classes they need from the client
   public AbstractJPPFClassLoader getTaskClassLoader()
 }

Please note that the methods getJob(), getTasks() and getTaskClassLoader() will return null for the events of type “nodeStarting()” and may return null for “nodeEnding()” events, as the node may not be processing any job at the time these events occur. The type of the event is available as an instance of the typesafe enum NodeLifeCycleEventType, defined as follows:

 public enum NodeLifeCycleEventType {
   // nodeStarting() notification
   NODE_STARTING,
   // nodeEnding() notification
   NODE_ENDING,
   // jobHeaderLoaded() notification
   JOB_HEADER_LOADED,
   // jobStarting() notification
   JOB_STARTING,
   // jobEnded() notification
   JOB_ENDING
}

You will also notice that the method getTasks() returns a list of Task<T> instances. Task<T> is the interface for all JPPF tasks, and can be safely cast to JPPFTask for all practical purposes.

JPPFDistributedJob is an interface common to client side jobs (see JPPFJob) and server/node side jobs (see JPPFTaskBundle). It provides the following methods, which can be used in the the NodeLifeCycleListener implementation:

 public interface JPPFDistributedJob {
    // Get the user-defined display name for this job
    // This is the name displayed in the administration console
   String getName();
 
   // Get the universal unique id for this job
   String getUuid();
 
   // Get the service level agreement between the job and the server
   JobSLA getSLA();
 
   // Get the user-defined metadata associated with this job
   JobMetadata getMetadata();
 }

Once the implementation is done, the listener is hooked up to JPPF using the service provider interface:

  • create a file in META-INF/services named org.jppf.node.event.NodeLifeCycleListener
  • in this file, add the fully qualified class name of your implementation of the interface
  • copy the jar file or class folder containing your implementation and service file to either the JPPF driver's class path, if you want it deployed to all nodes connected to that driver, or to the classpath of individual nodes, if you only wish specific nodes to have the add-on

Here is a simple example illustrating the process.

Our implementation of the NodeLifeCycleListener interface, which simply prints the events to the node's console:

 package myPackage;
 
 public class MyNodeListener implements NodeLifeCycleListener {
   public void nodeStarting(NodeLifeCycleEvent event) {
     System.out.println("node ready to process jobs");
   }
 
   public void nodeEnding(NodeLifeCycleEvent event) {
     System.out.println("node ending");
   }
 
   public void jobStarting(NodeLifeCycleEvent event {
     JPPFDistributedJob job = event.getJob();
     System.out.println("node starting job '" + job.getName() + "' with " +
       event.getTasks().size() + " tasks");
   }
 
   public void jobEnding(NodeLifeCycleEvent event) {
     System.out.println("node finished job '" + event.getJob().getName() + "'");
   }
 }

Once this is done, we create the file META-INF/services/org.jppf.node.event.NodeLifeCycleListener with the following content:

 myPackage.MyNodeListener

Our node listener is now ready to be deployed.


Related JPPF sample: “Node Life Cycle” in the JPPF samples pack.

Extended notifications: the NodeLifeCycleListenerEx interface

JPPF now provides an extension to the NodeLifeCycleListener interface, by the means of a new interface called NodeLifeCycleListenerEx, which is defined as follows:

 public interface NodeLifeCycleListenerEx extends NodeLifeCycleListener {
   // Called when the node has loaded a job header and before
   // the DataProvider or any of the tasks has been loaded
   void jobHeaderLoaded(NodeLifeCycleEvent event);
 }

At the time this method is called, neither the DataProvider (if any) nor the tasks have been deserialized. This means that the tasks can reference classes that are not yet in the classpath, and you can add these classes to the classpath on the fly, for instance by calling NodeLifeCycleEvent.getTaskClasLoader(), then invoking the addURL(URL) method of the resulting AbstractJPPFClassLoader.

To hook this extension up to the JPPF node, you must use the exact same service provider mechanism as for NodeLifeCycleListener. The JPPF service loader will automatically determine which services are instances of NodeLifeCycleListenerEx and emit the additional notifications accordingly.

Related JPPF sample: Extended Class Loading

Error handler

It is now possible to provide an error handler for each NodeLifeCycleListener implementation. This error handler will process all uncaught Throwables raised during the execution of any of the listener's methods.

To setup an error handler on a NodeLifeCycleListener implementation, you just have to implement the interface NodeLifeCycleErrorHandler, defined as follows:

 public interface NodeLifeCycleErrorHandler {
   // Handle the throwable raised for the specified event
   void handleError(NodeLifeCycleListener listener, NodeLifeCycleEvent event,
                    Throwable t);
 }

The listener parameter is the listener instance which threw the throwable. The event parameter is the notification that was sent to the listener; its getType() method will allow you to determine which method of the listener was called when the throwable was raised. The last parameter is the actual throwable that was raised.

When the NodeLifeCycleListener does not implement NodeLifeCycleErrorHandler, JPPF will delegate the error handling to a default implementation: DefaultLifeCycleErrorHandler.

Lastly, if an uncaught throwable is raised within the error handler itself, JPPF will handle it as follows:

  • if the logging level is “debug” or finer then the full stack trace of the throwable is logged
  • otherwise, only the throwable's class and message are logged
  • if the throwable is an instance of Error, it is propagated up the call stack
Main Page > Customizing JPPF > Node life cycle events

Support This Project Powered by MediaWiki