Copyright (c) 1999, 2000 Silicon Graphics, Inc. All Rights Reserved.

Overview | Basic Concepts | GUI Components | Architecture | How To Write An App

Task Internals

This document covers topics of interest to Task writers. See How To Write a Task for the steps in writing a Task.

How Rhino creates and runs a Task

It is important for Task writers to understand the sequence of steps the Rhino infrastructure goes through to create, display, and ultimately perform a Task. This section will briefly describe those steps and the Rhino classes that are involved in the process. For details, please refer to the Rhino API documentation.

Establishing the HostContext

Rhino components need a HostContext in order to access system administration services and share data. The HostContext is created by the login process and typically persists until the last frame of the session has been closed. For example, when the User launches TaskManager, s/he will only have to log in once: any subsequent Task created will share the HostContext created at login time. By contrast, each time the User launches a Task from the command line s/he will have to log in to create a HostContext for the Task.

Creating and loading a Task

Tasks are loaded in a two-step process to allow the Task to be queried for static information such as its User-readable name, icon image, and privileges without having to instantiate the Task. This is especially useful for Task clients that display information about a large number of Tasks, such as TaskManager or TaskShelf, but which don't want the overhead of loading any Task class into memory until it is launched.

The static information about a Task is stored in a text file called a properties file, which is similar to an X Windows app-defaults file. The use of properties files for Task writers will be covered in more depth in a later section of this document.

The TaskLoader class is used to implement the two-step loading process. A Task client that wishes to display a Task icon and link but not immediately launch the Task instantiates a TaskLoader, passing in the HostContext and CLASSPATH relative name of the Task. When the User clicks on the link to launch the Task, the client calls TaskLoader.loadTask() to load the Task class and initialize the Task instance.

The TaskLoader.loadTask() method goes through several steps, in order, to initialize a Task and verify that it is ready to run. This sequence is important to Task writers who need to know when each Task method is called during the initialization phase.

  1. Create the TaskContext
    The TaskContext is used by Task subclasses and their components to share data and state information during the life of a Task. An example of data would be information entered by the User, while state could include information about the server connection.

    One component of TaskContext that is important to Task writers is TaskData. TaskData is a set of key/value pairs, also called attributes, representing the information entered by the User as well as other Task state. TaskData can be used to share information among different input components within a Task, as well as among different Tasks in a session. The use of TaskData will be covered in greater detail below.

  2. Load and instantiate the Task class
    The Task class is loaded into memory and the Task constructor is called.

  3. Load Product Attributes
    The optional Product Attributes for a Rhino application is a set of key/value pairs associated with a particular login session. For example, the Product Attributes for the FailSafe product has a single key/value pair "Cluster Name" that specifies the name of the Cluster being managed for a given session. The properties file for a Task specifies what products' Product Attributes must be loaded before the Task will run.

    Product Attributes are stored in the HostContext so that they can be shared by all components in a given session. When the Product Attributes are loaded for the first time, a product-specific plugin is invoked to set the attributes. The plugin may bring up a Frame that requests information from the User. The attribute values are then copied to the TaskData of the Task. Subsequent requests to load Product Attributes will not bring up a Frame, but will simply copy the attribute values cached in the HostContext into the TaskData of the requestor.

  4. Set TaskData attributes
    Some Task clients may wish to override the Product Attributes or share TaskData attributes among Tasks. For example, a Metatask may wish to pass a TaskData attribute from one Task to the next so that the User doesn't have to enter the data twice. If TaskData attributes are passed to TaskLoader.loadTask(), TaskLoader will attempt to copy those TaskData attributes to the Task being loaded using the Task.setTaskDataAttr() method.

    Not all TaskData attributes may be set by Task clients. Unless a Task has declared an attribute key public in its properties file, an attempt to call Task.setTaskDataAttr() or Task.getTaskDataAttr() on that attribute will cause the Task to exit with an assertion failure. This mechanism is in place to hide implementaiton details from Task clients. See the Task API documentation for more details about the Task.PUBLIC_DATA property.

  5. Pass operands to the Task
    Some Tasks may take an operand or operands on which to perform their operation. An operand is typically an Item selector, which is a string that uniquely identifies an administered object on the server. For example, the Modify User Account Task would take a single User Account as an operand, while Delete User Account might take one or more User Accounts as operands and Define User Account would not take any operands. Operands are passed after Product Attributes are loaded and after TaskData atributes are set to allow Product Attributes and TaskData attributes to be overridden, if desired. TaskLoader will pass operands to the Task being loaded using the method Task.setOperands().

    Because operands may be passed to Tasks by a class with no specific knowledge about the Task (for example, a TaskShelf), no ordering of operands should be assumed or required by the Task. See the Task API documentation for more details about operands.

  6. Verify prerequisites
    The final stage of loading a task is verifying that all of the prerequisites are in place to run the Task. This includes checking the TaskData attributes, operands, privileges, and state of the system being administered.

    The principle behind verifying prerequisites is to detect error conditions as early as possible. For example, a Task that requires special system software to be installed should check the system for that software at this stage of Task loading. It is extremely annoying for Users to enter data and then find out that the system is not in a state to perform the Task.

    TaskLoader calls three different verification methods. This three stage process is again aimed at providing error feedback to the User as early as possible.

    Stage One: Task.verifyPrereqsBeforeCheckPrivs()
    This is the stage where most verification should occur. Only checks that require privileges, such as accessing read-protected files, should be deferred to the third stage.

    Stage Two: Task.checkPrivs()
    Task.checkPrivs() automatically checks the privileges that are defined in the properties file of the Task. If the User does not have the required privileges, s/he will be asked to enter the root password to continue. See the Task API documentation for Task.checkPrivs() and Task.PRIV_LIST for more details.

    Stage Three: Task.verifyPrereqsAfterCheckPrivs()
    This final verification stage is optional, but is provided for those rare Tasks that need privileges to fully verify that the Task is ready to run. For example, Tasks that require access to read-protected files will need to have privileges before being able to verify that the Task prerequisites are met.

Creating the Visible Components of a Task

The visible components of a Task are not created until the Task has been added to a visible Frame or its Frame parent becomes visible for the first time. Even then, visible components are created on a just-in-time basis.

Because Tasks extend the Swing JPanel class, they can be displayed within an existing Frame. However, it is more common to create a new Frame for a Task that has been launched. The TaskFrame class is the canonical container for Tasks. TaskFrame takes care of keeping the Frame title in sync with the state of the Task as well as posting ResultView frames and disposing of the TaskFrame when the Task has been completed or cancelled. A single TaskFrame can even be used to display multiple Tasks sequentially. See the TaskFrame API documentation for more details.

When a Task is made visible for the first time, it calls the method Task.registerInterfaces(), which must be implemented by the Task subclass. registerInterfaces() should create the Form interface and/or Guide interface classes of the Task, and then call Task.setForm() and/or Task.setGuide() to register those interfaces.

At this point there are still no visible components. After the Task subclass has registered its interface(s), the Task must then decide which interface to display. If only one interface has been registered, then the choice is obvious. Otherwise, the property Task.PREFERRED_UI is used to determine which interface should be displayed. See the Task API documentation for more details about the preferred interface.

Next, Task calls either Form.showForm() or Guide.showGuide(), as appropriate.

Task writers need only implement the abstract methods of the Task, Form, Guide, and GuidePage classes as described in the API documentation. The complex process of just-in-time creation of visible components described above will happen automatically.

Performing the Task operation

When the User presses "OK", the Task base class first verifies the User data and then tells the subclass to perform the Task operation.

Other Rhino Classes of Interest to Task Writers