This topic describes programming concepts and requirements needed to extend IBM Director to include user-defined tasks. To understand how an IBM Director task communicates with IBM Director components, refer to IBM Director Interprocess Communication.
IBM Director uses the concept of tasks, subtasks, and task activations as one of its primary extension points. Tasks are essentially containers of all data related to a task. This data is described in the subsequent sections. Subtasks are individual units of work that can be invoked or activated. A task has at least one subtask, but it can have many subtasks. A task is activated to perform a specific subtask primarily as a result of user actions initiated through the IBM Director Management Console (console), but it can also be activated through the scheduler or through event action plans.
The work of tasks is performed by code distributed on the console, IBM Director Server (server), and IBM Director managed system (agent). IBM Director's distributed code components can work alone or in conjunction depending on how the task is structured and what function it is implementing. IBM Director's task infrastructure handles the coordination and synchronization of distributed processing between the console, server, and agent.
TWGTask is an IBM Director abstract class used primarily as a data structure to contain all the data necessary to define a task. This data includes:
TWGDefaultTask is a concrete implementation of TWGTask. It has constructors that accept either the name of a properties file or a properties object to make it easier to initialize all the task's data. An example of how TWGDefaultTask can be invoked is described in Identifying your task to IBM Director at startup. Properties and their values are described in Task Properties. TWGDefaultTask also provides default implementations for the abstract methods in TWGTask. These implementations handle the complexity of interacting with the server's engine, including creating new threads or starting new processes to run the code that implements the task's behavior.
A task can have code that runs on the console, server, or agent in any combination. Native code running as an agent extension must accept, process, and respond to requests initiated by a server task.
Code running on the console that presents a GUI must be written in Java and extend the TWGTaskFrame class (for more information on extending TWGTaskFrame see the topic on Console task extensions.
Code running on the server can be implemented either as native code or as Java classes. Native code must be started in a separate process as a Win32 executable.
Note: This document does not describe how to implement native code.
Java classes can be written to run as a thread inside the IBM Director core server engine's JVM (core JVM), or as an external process running in a new, external JVM. It is almost always preferable to implement your server task code as a Java class that runs inside the main server JVM. You specify to run in the same JVM with the properties file of the task.
Code running as a thread inside the core server must implement the TWGTaskServer interface to provide a common way to interact with the server.
When your task server is running inside the IBM Director Server engine's JVM, it interacts directly with the engine's task related objects. Consequently, when a task server is activated, the TWGTaskServer implementor is passed a TWGTaskActivation object. This object has a reference to the TWGTask and TWGSubtask being activated. It also has a reference to a TWGLocalTaskClients object that contains a collection of TWGLocalManagedObject objects that represent the clients or agents that are the target(s) of the activation.
Java classes that implement a server task to run as an external JVM must:
public static void main(String argv[])
Your task server, running in its own JVM, must use setServiceNode() to register with TWGRemoteTaskManager as a RemoteTaskActivationListener. TWGRemoteTaskManager presents the set of methods required to communicate with the core server. These methods, in turn, use the ServiceNode you provide to send and receive commands represented by the Command class. Refer to IBM Director Interprocess Communication for more information on the ServiceNode and Command classes.
When a task activation occurs, your remote task server is passed a TWGRemoteTaskActivation object that contains:
When the IBM Director Server shuts down, ensure that your task server uses the unsetServiceNode() method to deregister with TWGRemoteTaskManager.
Some subtasks are targeted. In other words, they are intended to operate with the agents running on one or more managed objects. This section explains how to handle properly the set of targeted managed objects for a task activation.
If your task server is running inside IBM Director Server's JVM, the targeted managed objects are represented as TWGLocalTaskClients containing a set of TWGLocalManagedObject instances. You can use the getAllClients() method of the TWGLocalTaskClients instance to obtain an array of Objects containing the TWGLocalManagedObject instances. For each TWGLocalManagedObject, you can use the method getMoState() to get the current state of the managed object. Use the constants defined in TWGManagedObjectConstants to interpret the state of the managed object.
If you need to communicate with the managed object, you must first use the getMO() method of TWGLocalManagedObject to get a reference to the actual managed object. Next you must use the managed object's getAddressEntryShadowClass() method to get the name of the address entry shadow class and create an instance of it. Next you initialize the address shadow class instance by calling the initAddressEntry() method. This method requires an address entry record which you can get from the managed object with its getAddressEntryRecord() method. Finally, use the appropriate method from the address entry shadow to obtain the necessary addressing information. Using TWGNativeManagedObject instances as an example, the address entry shadow class is TWGNativeAddressEntryShadow and you use the getIPCPath() method to obtain the destination address to use for an IPC Command to send to the managed object. Other managed object classes use corresponding address entry shadow classes.
If your task server is running in an external JVM, the targeted managed objects are represented as TWGRemoteTaskClients containing a set of TWGRemoteManagedObject instances. Use the enumerateClients() method of TWGRemoteTaskClients to obtain an Enumeration of TWGRemoteManagedObject instances. For each TWGRemoteManagedObject, you must first check the managed object state by calling getMoState(). Again, you must handle the managed object according to its state. When the managed object's state is MOSTATE_NORMAL_ONLINE, modify the client's status as described previously and perform your subtask's actions, except in this case use the setClientTaskStatus() method of TWGRemoteTaskClients. When the managed object's state is anything else, do not communicate with it or change the client status.
If you need to communicate with the managed object, you do so using the address entry record and the address entry shadow as described previously. However, if you deal with multiple types of managed objects, determining which address entry shadow class to use is a little different:
Now you can instantiate the address entry shadow, initialize it and use it as described previously.
New tasks are added to IBM Director by extensions. A complete description of extensions can be found in Understanding IBM Director extensions. But for now, let's do a quick overview of how extensions work. When the server engine is initializing, it searches for files matching the wildcard x:\Program Files\IBM\Director\classes\extensions\*.TWGExt, where x is the drive letter on which the IBM Director server is installed. These are text files that define extensions. The only required line specifies the fully-qualified name of a subclass of the task's extension class.
Example: twg.extension.classname=com.BobCo.BobsExtension
The extension class is a subclass of the TWGExtension abstract base class. It is loaded and instantiated during the server's initialization. The methods that must be implemented by your subclass are called during various stages of initialization. Typically, new tasks are added in the InitClassInstances() method of the extension class by instantiating TWGDefaultTask using a constructor that accepts a properties file name:
if (!TWGTask.isTaskID("BobCo|BobsTask")) { try { TWGDefaultTask task = new TWGDefaultTask("/com/BobCo/server/BobsTask.properties"); } catch (TWGTaskCreationException e) { throw new TWGExtensionInitException("Error instantiating BobsTask: "+e.getMessage()); } }
The parameter to the constructor is a string containing a Java classpath relative name for the file containing the new task's properties. Because TWGTask and its subclasses are persistent objects, the task only needs to be instantiated one time which is ensured by using the call to isTaskID(). The string passed to isTaskID() is the task identifier defined by the TaskID property in the properties file. This method returns true if a task with this identifier has already been defined. The properties and values used to define a task using this constructor are described in Task properties.