The following classes are commonly used when dealing with managed objects.
The left panel of the IBM Director Console depicts groups. These are categorizations of managed objects that make it easier to work with distributed systems. For example, systems might be grouped by their OS type.
A filter is a set of selection criteria for managed systems. Groups are populated by applying filter criteria to the whole set of managed objects.
A dynamic filter defines the criteria used to query the IBM Director database tables and determine the managed objects that meet the criteria. The criteria can be combined using Boolean operations such as AND (all true) and OR (any true). A group is populated by the set of managed objects determined by these constraints and operations. For example, one group might be the set of managed objects that have Pentium III processors and have a particular device chipset. Your extension can create a new group using the TWGFilter class in the SDK.
In the context of the SDK, a task is a container of data. A task is a set of code that you write using the TWGTask class and install on the IBM Director Server. Tasks are organized into subtasks which are individual units of work. Every task has at least one subtask. Every TWGTask has a properties file associated with it that configures the task to either run all the time or run only when needed. In the properties file, you also list the SubTasks your Task can handle. The SubTasks are the "tasks" that appear in the right pane of the IBM Director Console. When you use the IBM Director Console to drag a task from the right pane onto an object in the middle pane, IBM Director makes sure that the appropriate TWGTask is loaded, and it invokes the Task's subtaskActivate() method. subtaskActivate() receives a parameter which is a TWGTaskActivation object. The TWGTaskActivation object contains the SubTask and the list of managed objects the SubTask is to operate with.
Task functions are performed by code that resides on the console, the server, and the agent. These distributed components can work alone or in combination depending on how the task is structured and what function is being performed.
The IBM Director's task infrastructure handles the coordination of distributed processing among the console, server, and agent.
The base IBM Director product contains many tasks that perform common systems management functions. However, you can implement your own tasks to provide other management functions.
If a task has a console component, this is specified in the properties file for the task. The "GUI" parameter in the properties file indicates the class to load for the task's console component. In the FileExplorer properties file this line is
GUI = class:com.BobCo.FileExplorer.BobCoFileExplorerGUI
The console component of the task is created by extending the TWGTaskFrame. This class ensures that the console component of your task interacts correctly with the rest of the IBM Director Console.
In the FileExplorer sample, you will see that BobCoFileExplorerGUI extends TWGTaskFrame. When the BobCoFileExplorerGUI class is loaded, the resulting GUI panel will have colors that match the rest of IBM Director and will automatically have a status bar at the bottom of the panel. Most of the work of actually creating the new console panel is done in the buildView() method of TWGTaskFrame. This is where JPanels and other Java visual objects are created.
The FileExplorer sample implements a task that runs in the same JVM as the IBM Director Server. The code examples that follow will depict that case. There is also the FileExplorerJVM sample that runs in its own JVM.
The class that implements your server task is specified by the "Server" parameter in the properties file. This line has other parameters which are described under Task Properties and in the FileExplorer sample properties file. In the FileExplorer properties file this line is the following.
Server = class:com.BobCo.FileExplorer.BobCoFileExplorerServer | auto-load:true | sameJVM:true
Recall that on the IBM Director Console, the right pane contains a list of operations a user can perform. Each operation is implemented as a subtask. A single server task can implement multiple subtasks. The FileExplorer sample has just one subtask and its name is specified in the properties file with this line.
Subtask.0.ID = ExploreFiles
The process of starting your task on the IBM Director Server is called task activation. When your task is started, its TWGTaskServer implementation is passed a reference to a TWGTaskActivation object. The TWGTaskActivation object contains a reference to the TWGSubtask being activated. Your server task might implement several subtasks, so the TWGTaskActivation object lets you know which one was started. The TWGTaskActivation object also contains a reference to a TWGLocalTaskClients object that contains the TWGLocalManagedObject objects being targeted by the user.
A simple way to think of this is the following:
Your subtask can query the state of the specified managed object(s). If the object is currently online, your subtask can perform its function.
A service requester initiates the communication and a service provider responds to it. Typically, the service requester is a console or server task, and the service provider is an agent; however, other combinations can be implemented as well. A service requester and a service provider communicate by sending messages through IBM Director's inter-process communication (IPC) facility. The service requester and provider can reside on the same machine or on different machines across a network.
IPC communications are implemented with ServiceNodes and Commands. Each end point of IPC communications is called a ServiceNode. Commands are message packages that are sent between ServiceNodes.
The ServiceNode class must be used for communication between:
The basic concepts for all IPC communications are:
A service requester and service provider each create their own service node to send and receive commands. Commands are sent, received, and replied to using methods of the ServiceNode class.
In this section, examples of service node communication are in the context of a server task requesting a service from an agent. Keep in mind that a server task might need to make requests of another server task. A given task can act as both a service requester and a service provider.
The following table summarizes the process flow for synchronous communications between a server requester and service provider. Asynchronous communication is similar and is described below.
(console/server) |
(server/agent) |
|
Create ServiceNodeFactory * | ||
Create ServiceNode | Create ServiceNode | |
Create Command
|
Wait for commands ServiceNode.ProcessCommands() |
|
Send Command
ServiceNode.SendCommand() |
|
|
CommandComplete()
|
|
ServiceNode.CommandReceived()
|
*Note: A service node factory needs to be created only if your service requester is a server task running in its own JVM.
Here is how this is done.
// Set ServiceNode support to use local IPC implementation ServiceNodeLocalImplFactory snlif = new ServiceNodeLocalImplFactory(); ServiceNode.SetServiceNodeFactory(snlif);
IBM Director services must be active on the system before you can create a service node factory. The service node factory should be set only once in any given JVM. Therefore, the following rules apply.
BobCoFileExplorerJVMServer me = new BobCoFileExplorerJVMServer(); ServiceNode sn = new ServiceNode( serviceNodeName ); TWGRemoteTaskManager.setServiceNode( sn, RTMcommandCode, me );
The FileExplorer sample uses the following constructor to instantiate a service node.
public static final String serviceNodeName = "BobCoFileExplorer"; ServiceNode sn = new ServiceNode(serviceNodeName);
The serviceNodeName must be a unique service node name on the local system.
The syntax for instantiating a Command is:
Command(long cmdCode); where cmdCode is a unique command code used by the recipient to distinguish commands.
The destination address for the command is set using the SetDestinationAddress() method. The syntax for this method is:
cmd.SetDestinationAddress(address);
- The address parameter is a String of the form "protocol::proto_addr::servicenodename"
- protocol can be either TCPIP, IPX, or NETBIOS.
- proto_addr is an appropriate address for the protocol.
For example, if the protocol is TCPIP, then the proto_addr would be a dotted-decimal IP address.- servicenodename is the name of the destination service node.
The following example shows how to specify a destination address when sending a command to an IBM Director Agent. When sending commands to your task's console, to your task's server, or from your task's server to another IBM Director Server, you need only specify a service node name as the destination address. The service node name for the console and server must be unique. Service nodes that are created on the IBM Director Console or the IBM Director Server can be viewed as having the same logical address. IBM Director knows which protocol and which address to use when routing commands to these non-agent components. In the example below, the destination address would be only "BobSN".
Example:
cmd.SetDestinationAddress("TCPIP::127.0.0.1::BobSN");
Below is the FileExplorer code to set the destination address. In the FileExplorer sample, these steps are implemented across several methods. They are combined here to illustrate more clearly how a destination address is created.
Information about each TWGNativeManagedObject is maintained by the IBM Director Server. A task can create a shadow of that information. This allows the task to work with some attributes of the native managed object while not duplicating the entire managed object in the task. Changes to the managed object are reflected back to the shadow object.
public static final String clientAddressSuffix = "::BobCoB"; private static TWGNativeAddressEntryShadow nativeAddrShadow = new TWGNativeAddressEntryShadow(); public static final int getClientLocalFixedDrives = 0xB0BC0001; BobCoTaskActivation(TWGRemoteTaskActivation act) { // Command cmd was instantiated elsewhere with command code. // Command cmd = new Command(getClientLocalFixedDrives); Enumeration e = act.getClients().enumerateClients(); TWGRemoteManagedObject rmo = (TWGRemoteManagedObject)e.nextElement(); ... byte[] addrEntry = rmo.getAddressEntryRecord(); nativeAddrShadow.initAddressEntry(addrEntry, 0, addrEntry.length); String ipcPath = nativeAddrShadow.getIPCPath(); cmd.SetDestinationAddress(ipcPath+clientAddressSuffix); }
The simplest syntax of the AddInputParm() method is:
void AddInputParm(byte[] data);Example:
String msg = "Hello world"; byte[] msgBuf = new byte[ IntelByteBuffer.GetUTF8(msg) ]; cmd.AddInputParm(msgBuf);
Note: When communicating with C++ service nodes, the IntelByteBuffer class must be used for command input and output parameters. IntelByteBuffer is a class for creating or interpreting Intel byte ordered arrays of C data types. The IntelByteBuffer class "flattens" Java objects so they can be passed as byte arrays.
If the sending and receiving service nodes are both implemented in Java, then using the IntelByteBuffer class is not required. Instead, you can use Java's built-in serialization ("flattening") mechanism to add a Java object to a command. The Command class method is AddObjectAsInputParm(). The only requirement to use this method is that the object you add as a parameter must implement the java.io.Serializable interface. This makes it very convenient for sending Java objects between service nodes.
Example:
public Command buildMyCommand( long cmdCode, String parameter ) { Command cmd = new Command( cmdCode ); cmd.AddObjectAsInputParm( parameter ); return cmd; }
The ServiceNode SendCommand() method sends a command synchronously. It executes in the caller's thread, and therefore blocks until the command's CommandComplete() method returns and any Command Complete Listeners have returned. We'll talk about CommandCompleteListeners a bit later.
The ServiceNode SendAsynchCommand() method sends the command and returns immediately, allowing the calling thread to continue. Just as with the synchronous case, the command's CommandComplete() method is called after the service provider sends a reply to the command.
Both SendCommand() and SendAsynchCommand() take a reference to a Command object as a parameter. References to the Command object are dropped when the CommandComplete() method completes. Garbage collection can then occur on the Command object. In Java, Commands should not be reused. A new Command should be instantiated each time one is sent.
In most cases, SendAsynchCommand() is the recommended method because the sending process can continue to do work while the command is being processed.
Example of synchronous send:
sn.SendCommand(cmd); where sn is a ServiceNode and cmd is a Command.
Example of asynchronous send:
sn.SendAsynchCommand(cmd);
The following table summarizes the process flow for asynchronous communication between a service requester and service provider.
(console/server) |
(server/agent) |
|
Create ServiceNodeFactory * | ||
Create ServiceNode | Create ServiceNode | |
Create Command
|
Wait for commands ServiceNode.ProcessCommands() |
|
Send Command Asynchronously
ServiceNode.SendAsynchCommand() Continue other processing. |
|
|
CommandComplete()
|
|
ServiceNode.CommandReceived()
|
*Note: A service node factory needs to be created only if your service requester is a server task running in its own JVM.
When the CommandComplete() method is invoked, a return code, set by the service provider in its ServiceNode ProcessCommands() method, is made available. The return code can be retrieved with the following method:
long ReturnCode();
The service provider can set any return code it desires; however, the Command class defines return codes for common errors. Some of these predefined return codes are the following.
CMDRET_SEND_FAILED CMDRET_SECURE_FAIL CMDRET_SEND_TIMEOUT CMDRET_SERVICEFAILEDThese predefined return codes are set by IBM Director, and your extension should never set them explicitly. Your extension can set its own return codes using different values.
The service provider can also attach parameters to the reply. The service requester can determine how many data parameters have been returned with the following Command method.
int NumOutputParms();
Individual output parameters can be retrieved with the OutputParm() method of the Command class.
Example of a CommandComplete() method:
public void CommandComplete() { // Zero return code means success. if (ReturnCode() == 0) { System.out.println("Command succeeded.\n"); byte [] buffer; for (int i=0; i < NumOutputParms(); i++){ buffer = OutputParm(i); System.out.println("OutputParm(" + i + ") = " + buffer + "\n"); } else { System.out.println("Command failed with ReturnCode = " + Long.toHexString(ReturnCode()) + "\n"); } }
When sending a command synchronously, the CommandComplete() method is not essential because the ServiceNode SendCommand() method returns to the next line of code only when the command is complete. The service requester can process the command reply in-line following the SendCommand() call. However, when sending a command asynchronously, the CommandComplete() method is essential. This is because the command will not be complete when the SendAsynchCommand() call returns. The CommandComplete() method is the callback method IBM Director invokes when the command reply is received.
In addition to the Command object itself handling the command completion, you can register a CommandCompleteListener object to be invoked when the command completes. To do this, you implement the CommandCompleteListener interface.
The CommandCompleteListener interface defines a single method that is invoked when a command completes.
void CommandComplete( Command );
To register your CommandCompleteListener class to handle the completion of a command, you call the following Command method.
cmd.setCommandCompleteListener( CommandCompleteListener ccl );
Here is a summary of the steps:
The FileExplorer sample implements the CommandCompleteListener interface as shown below.
public class GetClientDirectoryContentsCommand extends Command implements BobCoFileExplorerConstants, CommandCompleteListener { BobCoTaskActivation act; Command consoleCmd; /** * Constructor * @param act the BobCoTaskActivation that this command is being issued under * @param consoleCmd the ConsoleRequestDirectoryContentsCommand received * from the console that is causing this command to be * sent to the client. Input parameters are taken from * this received Command, and output parameters received * from the client are sent back as output parameters in * the consoleCmd. */ GetClientDirectoryContentsCommand(BobCoTaskActivation act, Command consoleCmd) { consoleCmd.PostponeReply(); setCommandCompleteListener(this); ... } /** * Command completion callback. This listener method is called when a Command instance * registered to this listener is completed. * @param cmd - Command instance of command which has completed */ public void CommandComplete(Command cmd) { long rc = cmd.ReturnCode(); if (rc == RETURN_OKAY) { for (int i=0; i < cmd.NumOutputParms(); i++) { ... } } ... } }
Often, the agent will contain a class that extends the ServiceNode class. The following examples show how a ServiceNode is created for an agent. The first example shows how a ServiceNode is created using Java. The second example is from the FileExplorer sample and uses C++.
public class MySubagent extends ServiceNode { private static final String BOBCO_SVCNODE_NAME = "BobCoB"; public MySubagent() throws BadServiceNodeImplException, ServiceNodeException { super(BOBCO_SVCNODE_NAME); } }
#define BOBCO_SVCNODE_NAME "BobCoB" BobCoSvcNode *svcnode = NULL; class BobCoSvcNode : public ServiceNode { public: /* Constructor : just create service node with needed name */ BobCoSvcNode() : ServiceNode(BOBCO_SVCNODE_NAME, FALSE) {}; ... } int main() { /* Register to prevent unload on user logoff */ ServiceNode::RegisterAsServiceBase(); /* Create the service node for the process */ svcnode = new BobCoSvcNode; /* If error creating service node, exit with error */ if(!svcnode->Create()) { return 1; } /* Once service node is created, start processing commands */ svcnode->ProcessCommands(); /* Clean up service node */ delete svcnode; svcnode = NULL; return 0; }
Note that the C++ agent code contains a main() function. Tasks implemented in C++ must be implemented to run as a separate executable. Therefore, they need a main() function. Additionally, C++ agents need to be configured using the SDK's twgsvcee.exe utility. With this utility you specify the name of your executable as well as other options for how to invoke the program. See Registering an Agent Service for more information on this utility. Agents implemented in Java do not need a main() method nor be configured with the twgsvcee utility.
In the Java example, IBM Director will call the service provider's ProcessCommands() method on a new JVM thread. IBM Director obtains the name of the class that extends the ServiceNode class and implements the ProcessCommands() method from an agent properties file. For IBM Director Agents, this file is named class.Subagent, where class is the Java class that extends ServiceNode. For more information on setting up IBM Director Agents, see Extending the Java-based Agent.
The service node in the previous C++ example processes commands in the caller's thread. For C++ service providers, IBM Director invokes the service provider's main() function which then must call the ProcessCommands() method itself.
To enable command processing in the service provider, the ServiceNode class is extended and the CommandReceived() method is implemented. Commands received from other processes are routed to the service provider's ServiceNode.CommandReceived() method.
The incoming command is passed in as a parameter. After processing a command, CommandReceived() sets a return code using the SetReturnCode() method of the Command class.
The CommandReceived() method returns TRUE if the service node should continue processing and returns FALSE if the service node should be closed.
Here is an example, using the FileExplorer agent's CommandReceived() method.
class BobCoSvcNode : public ServiceNode { /****************************************************************************/ /* Process any received commands, and route to needed handlers */ /****************************************************************************/ BOOL BobCoSvcNode::CommandReceived(Command &cmd) { char msg[100]; sprintf(msg, "Command received: %08x", cmd.CommandCode()); debug(msg); switch(cmd.CommandCode()) { case BOBCO_GET_LOCAL_FIXED_DRIVES: debug("Calling HandleGetDrives()"); HandleGetDrives(cmd); break; case BOBCO_GET_DIRECTORY_CONTENTS: debug("Calling HandleGetDir()"); HandleGetDir(cmd); break; default: return(FALSE); // This will cause the service node to close, // thus returning from the ProcessCommands call in main(), // which will end the agent service. } return TRUE; } }
The NumInputParms() method returns a count of the number of input parameters. The InputParm() method returns individual parameters passed in with the command. The AddOutputParm() method allows the service provider to pass data back to the service requester. The SetReturnCode() method can be used to indicate if the command succeeded or failed.
The following example shows how these methods can be used together.
public void MyCommandHandler( Command cmd ) { IntelByteBuffer input; String strOdd = "Odd number"; String strEven = "Even number"; byte[] oddBuf = strOdd.getBytes(); byte[] evenBuf = strEven.getBytes(); // Loop over all input data and determine if each is an odd or even number. for (int i=0; i < cmd.NumInputParms(); ++i) { input = new IntelByteBuffer( InputParm(i) ); int val = input.ReadLONG(); if ( ( (val / 2) * 2 ) == val ) AddOutputParm( evenBuf ); else AddOutputParm( oddBuf ); } cmd.SetReturnCode( 0 ); return; }
This is useful when you want to process a command asynchronously on another thread or need to communicate with another computer. Within the CommandReceived() method you can call PostponeReply() on the Command object. You can then process the command on another thread. When command processing is complete, you call SendPostponedReply() on the receiver's service node and pass the command object whose reply was postponed. Calling SendPostponedReply() causes the command complete listeners to be called.
This situation commonly occurs when a console command is sent to a server, but the server must acquire some data before replying to the console. The server might have to issue its own command and receive data before responding to the console.
In the following code from the FileExplorer sample, the reply to consoleCmd was postponed. When processing of a different command (GetClientDirectoryContentsCommand()) is done, the reply to consoleCmd is completed.
Here is where the original consoleCmd reply is postponed.
GetClientDirectoryContentsCommand(BobCoTaskActivation act, Command consoleCmd) { super(getClientDirectoryContents); this.act = act; this.consoleCmd = consoleCmd; consoleCmd.PostponeReply(); setCommandCompleteListener(this); // copy path parameter from console's input parameter to the client's command byte[] buffer = consoleCmd.InputParm(0); AddInputParm(buffer, IntelByteBuffer.SIZEOF_LONG64, buffer.length-IntelByteBuffer.SIZEOF_LONG64); System.out.println("BobCo sending GetClientDirectoryContentsCommand to client"); act.sendAsynchToClient(this); }
And here is where the postponed reply to consoleCmd is finally sent.
/** * Command completion callback. This listener method is called when a Command instance * registered to this listener is completed. * @param cmd - Command instance of command which has completed */ public void CommandComplete(Command cmd) { long rc = ReturnCode(); System.out.println("BobCo GetClientLocalFixedDrivesCommand complete, rc="+rc+", NumOutputParms="+NumOutputParms()); consoleCmd.SetReturnCode(rc); if (rc == RETURN_OKAY) { for (int i=0; i < NumOutputParms(); i++) { consoleCmd.AddOutputParm(OutputParm(i)); } } sn.SendPostponedReply(consoleCmd); }
When the IBM Director Server starts, it searches for files of the form install path\classes\extensions\*.TWGExt. TWGExt files are text files that define extensions. While there are many properties that can be specified in this file, the only required property is twg.extension.classname. This property specifies the fully qualified name of a class that extends the TWGExtension class. See Task Properties for more information.
For the FileExplorer sample, this property is
twg.extension.classname=com.BobCo.FileExplorer.BobCoFileExplorerExtension public class BobCoFileExplorerExtension extends TWGExtension { ... }
The specified class is loaded and instantiated during the IBM Director Server's initialization and the required methods are called.
The TWGTask class is an abstract class that contains data essential to the instantiation of a task. For example, it contains the name of the task, the name of the Java class or executable file that implements the task, the state of the task, and other task attributes. The TWGDefaultTask class is a default implementation of TWGTask that provides the basic methods needed by many tasks. TWGDefaultTask is a good place to start when creating a new task. In the FileExplorer sample, TWGDefaultTask is used. The FileExplorer task is initialized with values specified in a properties file. The fully qualified name of that properties file is passed to the TWGDefaultTask constructor.
// If the task has not already been defined, create it now. // Construct the task using the values in the File Explorer properties file. if (!TWGTask.isTaskID("BobCo|FileExplorer")) { try { TWGDefaultTask bobCoTask = new TWGDefaultTask("BobCoFileExplorerTask.properties"); } catch (TWGTaskCreationException e) { System.out.println("Task creation exception."); e.printStackTrace(); } }
Here is code from the FileExplorer sample where the TWGDefaultTask class is instantiated.
public void InitClassInstances() throws TWGExtensionInitException { ... try { TWGDefaultTask bobCoTask = new TWGDefaultTask("BobCoFileExplorerTask.properties"); } catch (TWGTaskCreationException e) { System.out.println("Task creation exception!!!"); e.printStackTrace(); err = true; } }
For the FileExplorer sample here is how this is coded.
BobCoFileExplorerTask.properties contains the following line.
GUI = class:com.BobCo.FileExplorer.BobCoFileExplorerGUI
The BobCoFileExplorerGUI class starts out as follows.
public class BobCoFileExplorerGUI extends TWGTaskFrame implements ActionListener { ... }
When IBM Director loads your GUI class, the TWGTaskFrame constructor is called. After the constructor completes, IBM Director will call the setTaskActivator() method. That method in turn calls the pInit() method. pInit is used to initialize non-GUI items. In fact, in the pInit() method you can control whether the GUI frame is actually displayed or not. For example, if initialization fails, you can prevent the GUI from appearing. The FileExplorer sample uses the pInit() method to create the console's service node.
Finally, IBM Director calls the consoleStart() method which in turn calls the buildView() method. The FileExplorer sample implements the buildView() method to create the Java panels, buttons, and so on for its GUI.
Here is the FileExplorer pInit() method.
/** * Initialization routine for setting up non-gui items. */ public boolean pInit( ) { boolean rc = true; try { sn = new ServiceNode(); } catch (BadServiceNodeImplException snix) { System.out.println("BobCo GUI Error: BadServiceNodeImplException while creating ServiceNode"); rc = false; } catch (ServiceNodeClosedException sncx) { System.out.println("BobCo GUI Error: ServiceNodeClosedException while creating ServiceNode"); rc = false; } catch (ServiceNodeException snx) { System.out.println("BobCo GUI Error: ServiceNodeException while creating ServiceNode"); rc = false; } return rc; } // end pInit
Here is the FileExplorer buildView() method.
/** * Create the client pane and return it. */ public Container buildView( ) { // Set the frame's icon to the flashlight setIconImage(UFImageLoader.getImage("/images/find16.gif")); // Construct the top level client area pane that all panes are // placed into. This code is provided as a temp for frames. JPanel returnPanel = new JPanel( ); returnPanel.setBorder( new BevelBorder( BevelBorder.LOWERED )); returnPanel.setLayout(new BorderLayout()); // Load and set the frame's title from the task resources. TWGTaskActivator ta = getTaskActivator(); setTitle(ta.getTask().getName()+ta.getTargetTitle()); LongValueSet moid = ta.getMoid(); /* Put the Tree in a scroller. */ tree = new BobCoFileExplorerTree(this, moid.GetValue(0)); JScrollPane sp = new JScrollPane(); sp.setPreferredSize(new Dimension(300, 300)); sp.getViewport().add(tree); /* And show it. */ returnPanel.setLayout(new BorderLayout()); returnPanel.add("Center", sp); // Create a horizontal panel for the buttons. JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS)); // Put glue in first so that all buttons are justified left. buttonPane.add(Box.createHorizontalGlue()); // Create cancel button and add to button panel. JButton cancelBtn = new JButton( "Cancel" ); buttonPane.add( cancelBtn ); cancelBtn.addActionListener( this ); returnPanel.add( "South", buttonPane ); return( returnPanel ); } // end buildView
For most tasks, it is more efficient and preferable to run in the same JVM as the IBM Director Server. However, in some cases it may be preferable to run a task in its own JVM. Specifically, if your task uses large amounts of memory, runs to completion quickly, and does not need to stay resident, then it may be preferable to run the task in a separate JVM. This section describes the implementation differences when running in the same or a separate JVM.
com.tivoli.twg.engine.TWGTask com.tivoli.twg.engine.TWGTaskServer com.tivoli.twg.engine.TWGTaskActivation com.tivoli.twg.engine.TWGLocalTaskClients
com.tivoli.twg.engine.RemoteTaskActivationListener com.tivoli.twg.engine.TWGRemoteTaskClients
// The server extension class implements the TWGTaskServer interface. public class BobCoFileExplorerServer implements CommandReceivedListener, TWGTaskServer, BobCoFileExplorerConstants {...} // In addition, you will need to implement the following method. public boolean serverActivate(TWGTask task) { System.out.println("BobCoFileExplorerServer.serverActivate()" +" to log\\com.tivoli.twg.engine.TWGServerMain.stdout"); task.setServiceNode(serviceNodeName); return true; }
// The server extension class implements the RemoteTaskActivationListener interface. public class BobCoFileExplorerJVMServer implements CommandReceivedListener, RemoteTaskActivationListener, BobCoFileExplorerJVMConstants {...}
TWGTaskServer
RemoteTaskActivationListener
A service node should be created in the task constructor and its presence communicated to IBM Director in the serverActivate() method.
public BobCoFileExplorerServer() { ... try { sn = new ServiceNode(serviceNodeName); BobCoTaskActivation.setServiceNode(sn); sn.RegisterDefaultCommandListener(this); keepGoing = sn.Create(); if (keepGoing) { ... continue ... } else { ... terminate ... } } catch( ... exceptions ... ) { ... process exceptions ... } ... } public boolean serverActivate(TWGTask task) { ... task.setServiceNode(serviceNodeName); return true; }
If an error occurs during subtaskActivate(), you must not deactivate the server. Rather, your method should return an error code. IBM Director will then call your task's other activate and deactivate methods as appropriate.
public int subtaskActivate(TWGTaskActivation act) { ... int rc = TWGTask.SUBTASK_ACT_COMPLETE; String subtaskID = act.getSubtask().getID(); if (subtaskID.equals("ExploreFiles")) { TWGLocalTaskClients clients = act.getClients(); Object[] allClients = clients.getAllClients(); if (allClients.length != 1) { // Activation must be targeted for exactly one client. rc = TWGTask.SUBTASK_ACT_FAILED; } else { BobCoTaskActivation myAct = new BobCoTaskActivation(act); } ... } ... return rc; }
A ServiceNodeFactory must first be created in your task's main() method.
public static void main(String argv[]) { ... BobCoFileExplorerJVMServer me = new BobCoFileExplorerJVMServer(); ServiceNodeLocalImplFactory snlif = new ServiceNodeLocalImplFactory(); ServiceNode.SetServiceNodeFactory(snlif); try { sn = new ServiceNode(serviceNodeName); TWGRemoteTaskManager.setServiceNode(sn, RTMcommandCode, me); BobCoJVMTaskActivation.setServiceNode(sn); sn.RegisterDefaultCommandListener(me); keepGoing = sn.Create(); if (keepGoing) { ... continue ... } else { ... terminate ... } } catch( ... exceptions ... ) { ... process exceptions ... } ... }
Termination processing for your task is done in the run() method.
public class BobCoFileExplorerServer { ... public void run() { ... // Begin termination processing sn.DeregisterDefaultCommandListener(); TWGRemoteTaskManager.unsetServiceNode(); try{ sn.Close(); } catch(ServiceNodeException err){ ... process exception ... } ... } }
Termination processing for your task is done in the main() method.
public class BobCoFileExploreJVMServer { ... public static void main(String argv[]) { ... // Begin termination processing sn.DeregisterDefaultCommandListener(); TWGRemoteTaskManager.unsetServiceNode(); try{ sn.Close(); } catch(ServiceNodeException err){ ... process exception ... } ... } }
If a service provider is processing a command and the command's time out value has elapsed, the service requester's command complete processing occurs immediately for that command and the command's return code is set to CMDRET_SEND_TIMEOUT by IBM Director. Postponing a command reply has no effect on the time out processing for the command. A postponed reply allows command processing to continue asynchronously, but the command still must complete within the time out limit.
You can change a command's time out value using the Command class's SetTimeOut() method.
If you need to communicate with the targeted managed object, you perform the following steps.
Example:
class MyCommand extends Command { public MyCommand ( long objectID ) { ServiceNode sn = new ServiceNode(); ... int pid = TWGPersistentObject.toPersistID( objectID ); TWGManagedObject manObj = TWGManagedObject.getManagedObjectByID( pid ); if (manObj.getState != TWGManagedObject.MOSTATE_NORMAL_ONLINE){ ... handle error ... } byte[] address = manObj.getAddressEntryRecord(); TWGNativeAddressEntryShadow shadow = new TWGNativeAddressEntryShadow(); String ipcPath; try { shadow.initAddressEntry( address, 0, address.length ); ipcPath = shadow.getIPCPath(); } catch (Exception x) { ... handle exception ... } SetDestinationAddress( ipcPath + "::MyAgent" ); SetTimeOut( 20000 ); ... attach parameters to command ... try { sn.SendAsynchCommand( this ); } catch (Exception x) { ... handle exception ... } } ... }
TWGConManagedObject instantiates a shadow object. Here is an example of how it might be used.
public String getObjectName( long objectID ) { TWGConManagedObject cmo = null; String name = ""; cmo = (TWGConManagedObject)TWGConObject.FindObject( objectID ); name = cmo.getName(); return name; }
One way to address this is to use the SendAuthorizationCommand class. SendAuthorizationCommand is a special command to temporarily authorize one system to send commands to another system.
In Java:
public SendAuthorizationCommand(int timeout);
In C++:
virtual SendAuthorizationCommand(ULONG time_out);
The sending system is authorized to send further commands until the time out occurs. If a time out of zero is specified, an outstanding authorization is canceled.