Previous Contents Index DocHome Next |
Process Manager 6.0 Programmer's Guide |
Chapter 2 Writing Custom Data Fields
This chapter describes how to write custom fields for use in Process Manager 6.0.This chapter includes the following sections:
Introduction
Defining Field Properties in a JSB File
Specifying Images for Use in Process Builder
Adding a Custom Field to an Application
BasicCustomField
Introduction
A data field contains information relevant to a process instance, such as the maximum value of the budget or the name of a document. Process Builder offers a set of predefined data field classes, such as Radio Buttons and Textfield. The predefined data fields store a single value per data field.In situations where you need behavior that is not provided by any of the predefined data field classes, you can define your own custom data field. Such situations include the need for:
supporting data types that are more complex than the data types available with built-in fields.
Process Manager allows you to define your own classes of data fields. Custom data fields are sometimes known as entity fields.representing multi-dimensional values, or other high-level data objects, in a process. For example, custom fields can represent a "shopping cart," an account, or a service order.
accessing data objects that are stored in resources external to Process Manager, such as PeopleSoft or CICS.
displaying the data field differently in an entrypoint and a workitem
Steps for Creating a Custom Field
The main steps for creating a custom field are as follows:
Create a JavaScript bean (JSB) file to specify the field properties that will be visible in Process Builder. For details, see "Defining Field Properties in a JSB File".
Write a Java class to determine the presentation and data management capabilities of the custom field. At a minimum, you must implement two interfaces, IDataElement and IPresentationElement. For details, see "Writing the Java Classes".
Optionally, create images to depict the data field in the Process Builder interface. For details, see "Specifying Images for Use in Process Builder".
Package the JSB and Java classes into a zip or jar archive. For details, see "Packaging a Custom Field".
In Process Builder, insert a data field and add the archive file as a new class. For details, see "Adding a Custom Field to an Application".
Defining Field Properties in a JSB File
You need to write a JSB file that defines which of the custom field's properties can be set at design time in Process Builder. In Process Builder, these properties are visible through the field's Inspector window. For each property shown in the Inspector window, a corresponding property must be defined in the JSB file.To create a JSB file for a new custom field class, you can copy an existing JSB file and modify it to suit your needs. For example, you can copy the JSB files for Process Builder's predefined data fields, or you can copy a template JSB file. These files are located in the following path of your Process Builder installation:
builder\com\netscape\workflow\fields
Warning: Do not modify the original JSB files for predefined data fields. If you do, the data fields may no longer work.
The JSB file and the custom field class must have the same name. For example, a custom field class named ShoppingCartField.class must have a JSB file named ShoppingCartField.jsb.
A JSB file has the following general structure:
<JSB>
<JSB_DESCRIPTOR ...>
<JSB_PROPERTY ...>
<JSB_PROPERTY ...>
...
</JSB>The file is surrounded by an opening <JSB> tag and a closing </JSB> tag. The other two tags are described in the following sections:
JSB_DESCRIPTOR Tag
JSB_DESCRIPTOR Tag
The <JSB_DESCRIPTOR> tag specifies the name, display name, and a short description of the data field class.For example, ShoppingCartField.jsb uses the following <JSB_DESCRIPTOR> tag:
NAME="com.netscape.pm.sample.ShoppingCartField"
DISPLAYNAME="Shopping Cart Field"
SHORTDESCRIPTION="Shopping Cart Field">
The NAME attribute is the full path name for the data field class, using a dot (.) as the directory separator.
The DISPLAYNAME attribute is the name that Process Builder uses for the field, such as the field's name in the Data Dictionary.
The SHORTDESCRIPTION attribute is a brief description of the field.
JSB_PROPERTY Tag
The JSB file contains a series of <JSB_PROPERTY> tags, one for each property that appears in the Inspector window. The following code shows some example <JSB_PROPERTY> tags. In this case, the Inspector window shows properties for dsidentifier, dbuser, dbpassword, dbuser and dbtablename as shown in Figure 2-1.
Figure 2-1    Inspector Window shows properties defined in the JSB file
The JSB_PROPERTY attributes and required property names are described in the next two sections.
JSB_PROPERTY Attributes
The attributes for the JSB_PROPERTY tag are shown in Table 2-1:
Required Data Field Properties
Each data field must have the properties listed in Table 2-2:
In addition to these required properties, each data field can have properties that are specific to itself. For example, a Textfield has properties for size and length; a radio button data field has a property for options; and so on.
When you define the properties for a custom field, consider the purpose of the field. For example, if the custom field must access an external database, you may want to define connection properties. These properties might include the database type (ORACLE, SYBASE), a username and password, or a connection string.
Not all properties you define in a JSB file will necessarily be used. It depends on how your Java class interprets these properties. For example, the JSB file could contain a color property that is totally ignored in the Java class. In this case, no matter what color the designer specifies for the field, it has no effect.
Writing the Java Classes
To write the Java classes for a custom field, perform the following steps:
Consider the Design Issues
There are several design issues to bear in mind as you write your custom field classes. This section describes the following design issues:
Consider Your Data and Data Sources
To write your Java classes, you must know something about the data these classes will work with. Consider the following questions:
What data types do you want the custom field to accept? For example, in what format will the data be? This could well depend on where the data is coming from.
What data sources will the custom field be required to access? For example, will the custom field access a PeopleSoft application? an SAP R/3 application? a relational database?
Design a Data Manager
Custom data fields are stateless, that is, you cannot use them to store information about a process instance from one workitem to another. Think of your custom data field as being a data manager. When a process instance arrives at a work item, the data field gets its data from an external data store. The data can be any Java object. When the process instance leaves the work item, the data field saves its data to an external store. The important idea is that the custom fields specify only the logic to manage the data, not the data itself.
Design a Thread-safe Class
Custom fields, like custom activities, are stateless entities. There is only one instance of a custom field per Java Virtual Machine. The custom field must be multithreaded. This means the field must safely handle calls from concurrent requests. As a result, you should not use instance variables on the data field object to keep persistent information about the data field's value. It is OK, however, to use instance variables to store read only information that is common across all process instances, such as the name of the external database where the data is stored.If you can't avoid using instance data, be sure to synchronize the data. With unsynchronized data, a variable set during one request might not exist for the next request.
Use an Entity Key
When a custom field loads data from an external data source, the custom field might need a key to identify the data it is looking for. This key, known as an entity key, can be stored with the process instance. Contrast this with built-in fields. They store the field value with the process instance, and there is no need to access external sources.To work with entity keys, use the following two methods on the IProcessInstance interface:
Define a Subclass of BasicCustomField
To implement a new data field class, create a Java subclass of BasicCustomField. This class provides methods that enable Process Manager to treat your custom field just like any other data field.The BasicCustomField class implements the IPresentationElement and IDataElement interfaces. The IPresentationElement interface specifies the presentation methods for the data field, which are display() and update(). The IDataElement interface specifies the methods for managing the loading and storing of data, which are create(), load(), store() and archive(). Your new subclass needs to provide definitions for each of the methods in these interfaces.
Note: You can find all the necessary classes in the pm60classes.jar file in the api directory on the Process Manager 6.0 CD.
Before looking at the methods in detail, here is a discussion of how and when the methods are called.
When a form is displayed in an entrypoint or a workitem the following happens:
The display() method displays the data field.
When an entrypoint or workitem form is submitted, the following happens:
If the display() method of a work item calls getData() to get the value of the data field, the load() method is invoked.
- If the form is displayed in an entrypoint, the process instance does not yet exist, therefore display() cannot access information about it. When the form is displayed in a workitem, the process instance exists, therefore the display() method can access information on it, such as the value of other data fields.
If the process instance is at an entrypoint, the system automatically calls create() on every data field, regardless of whether the field appears on the entry point form. The create() method initializes the value of the data field.
A JavaScript script (for example, an automation script, assignment script or completion script) can use getData() and setData() to get and set the data objects of a custom field. In this case, the invocation order is as follows:
If the form displayed the field is displayed in EDIT mode, the field's update() method is called to update the data field on the process instance. The update() method typically calls setData() to put the changed data into the data field.
- If the process instance is at a workitem, the process instance already exists, so the create() method is not called.
If the field's data was modified by a call to setData() (which might happen in the load(), create() or update() methods) the system calls the store() method to store the data.
When getData() is called, the load() method is invoked to fetch the data unless it has already been loaded for the current activity.
The next sections discuss in detail the methods that your new data field class must implement.The load() method typically uses setData()to load the fetched data in to the data field.
Whenever setData() is performed, the store() method is invoked when the process instance is ready to store itself. As a result, the store() method may be called even if the field's data has not changed.
The Methods in Detail
Define the following methods on your subclass of BasicCustomField:
display()
update()
create ()
load ()
store()
- Loads the value of the data field when an attempt is made to retrieve the value of a data field for which no value has been set yet in the current workitem.
archive()
loadDataElementProperties()
Define this method to process the properties that were set in the Builder during development of the process definition. The syntax is:protected void loadDataElementProperties( Hashtable entry )
This method is passed a hashtable of all the properties that can be set in the Builder (which correspond to the properties that are defined in the JSB file). Call the get() method on the hashtable to get the values of specific properties.
Typically, the loadDataElementProperties() method sets default values that are needed by the data field. For example, if the data field displays a table, loadDataElementProperties() might read the background color of the table from the properties hash table. If the data field uses an external database, the method might read the database table name from the properties hash table.
For example, suppose your JSB file contains properties for dbTableName and bgColor, as follows:
The dbTableName value is used to access a database, and the tableBackground color is used in an HTML TABLE tag when the data field is displayed.
Given the previous JSB code, the following Java code implements the loadDataElementProperties() method. This method reads the dbTableName property and sets a variable accordingly. If no value was specified in the Builder for this property, the method throws an exception. The loadDataElementProperties() method also gets the value for bgColor that was specified in the Builder. If the bgColor was not specified in the Builder, the method sets a default value for the corresponding variable.
display()
The display() method determines how the data field displays itself in an HTML form in an entrypoint or a work item.This method is invoked when the process instance reaches an activity that displays an HTML form, it is not invoked when the process instance reaches an automated or custom activity.
This method has two definitions -- one for an entrypoint and one for a workitem. When the user views an entrypoint form, the process instance does not exist yet, thus the display() method cannot access information on the process instance. When the user views a workitem form, the process instance does exist, therefore the display() method can access information about the process instance.
The syntax for an entrypoint is:
String displayFormat ) throws Exception
IProcessInstance pi,
String displayFormat ) throws Exception
Define the display() method to write the HTML code for displaying the data field. For example, if the field is to be displayed as a table, define the display() method to write the <TABLE> and </TABLE> tags, as well as the tags for the table rows and table cells.
Attributes that affect the appearance of the data field (such as the background color of a table) can be defined in the JSB file and set by the process designer in the Builder. The loadDataElementProperties() method can retrieve them and set them as values of instance variables to make them available to the display() method.
The display() method should consider whether the data field is in view, edit, or hidden mode, and display the data field accordingly. For view mode, it should display the value in such a way that the user cannot change it. For edit mode, it should display the value in some kind of form element, such as a text field or check box, so that the user can edit the value. There is no need to write the <FORM> and </FORM> tags -- these are written automatically.
The display() method should write a form element for every value that is associated with the data field, even if the user is not allowed to change the value. You can use hidden form elements to transmit data values that the user does not need to see or is not allowed to edit.
The workitem version of display() can retrieve the value of a data field by calling the getData() method on the process instance. This method gets the value out of the process instance if it has already been set for the current workitem, or loads it from external storage by calling the load() method if it has not already been set.
As far as the display() method is concerned, however, all it needs to do to get the value is to call getData() on the process instance, specifying the name of this data field. To get the name of this data field, use the getName() method as follows:
myDataObject myObject = (myDataObject) pi.getData( getName() );
When a process instance is loaded into a work item, the value of a data field can be any kind of object. The display() method might, for example, get the values of several instance variables on an object and display each one in a separate text field.
Example display() method
This example discusses a data field that manages information about employees, such as their name, phone number and email address. Each employee is uniquely identified by their employee ID.The data field presents itself as a table, as illustrated in Figure 2-3. Some of the table attributes, such as background color, can be specified in the Builder. When the data field is displayed in an entrypoint form, it does not know which employee it is associated with. The intent is that the employee would enter their employee ID number in the entrypoint. Given the employee ID number, the data field can retrieve information about the employee from the employee database. When the data field is displayed in a subsequent workitem, it has access to information about the employee, such as their name and phone number.
The code for the entrypoint version is shown here. At an entrypoint, the only thing that the user can enter is their employee ID number. This number is needed to uniquely identify the employee in the database. Figure 2-2 shows the data field in edit and modes in an entry point form.
Figure 2-2    Example data field in an entrypoint
When the data field appears in a work item form, it shows the ID number, name, phone number and email address for the employee. The user is not allowed to change the value of the ID once the process instance has progressed beyond the entrypoint, but they are allowed to change their name and phone number when the data field is in edit mode. Figure 2-3 shows the data field in edit mode in a work item.
Figure 2-3    Example data field in a workitem
The code for the work item version of display() is shown here:
For more information about display(), see the discussion of display() in the Class Reference.
update()
This method updates the value of the data field on the process instance when a form is submitted in an entrypoint or workitem. This method is not invoked when the process instance finishes an automated or custom activity.IProcessInstance pi,
IPMRequest rq ) throws Exception
Despite its name, the update() method is not the place where you update external databases when the value associated with the data field has changed. The update() method does not store values for persistence from one workitem to another, it just updates the process instance for the current work item only. The store() method stores the data field value in an external data store to make it persistent between workitems. You can define store() to store the data in whatever way you wish, for example in a database table of your choosing.
Typically, the update() method translates the name/value parameters sent by the form submission into an appropriate kind of data object for the field. If you do not define update(), all changes relevant to this data field that the user makes in the form are discarded when the form is submitted.
When a form is submitted, the value and name of every form element (such as text field or checkbox) on the form is packaged into a query string. One of the arguments to update() is an IPMRequest object that has a method for extracting individual values from the submitted parameter string. You can use the getParameter() method to get the value of a named parameter. You can use the isParameterDefined() method to test for the existence of a parameter before attempting to get its value.
When defining update(), you do not need to worry about whether the data field was in edit, view or hidden mode. The update() method is only called if the field was displayed in edit mode.
The update() method needs to know the names of the form elements that the display() method writes to the HTML page. For example, if display() displays a textfield called idFE, update() can access the value of that form element as follows:
String employeeID = rq.getParameter("idFE");
Conversely, if update() needs to receive a value from the form, it is the responsibility of the display() method to write an appropriately named form element to the page, even if the user is not allowed to change the value. You can use hidden form elements to transmit data values that the user does not need to see or is not allowed to edit.
WARNING -- Don't use variables on the data field itself to hold values that are specific to a process instance, since all process instances share a single instance of the data field.
In the following example, four form elements are used to represent the data field when it is in edit mode. These form elements are idFE, nameFE, phoneFE, and emailFE, which are form elements whose values specify employee ID number, employee name, phone, and email respectively.
This update() method creates a new instance of myDataObject. It extracts the values of the idFE, nameFE, phoneFE and emailFE form elements, and puts the values into corresponding variables on the object. Finally it calls setData() to put the object as the value of the data field on the process instance.
create ()
This method sets the default value for a data field when the process instance is created. The syntax is:public void create (IProcessInstance pi) throws Exception
The purpose of create() is to set a default value for a data field when the process instance is initialized in case the data field's value is not set in an entrypoint form. If the user sets the value of the data field in an entrypoint form, the user-specified value overrides the value set by the create() method (assuming that update() handles the user-specified value appropriately).
When a process instance is initialized, the create() method is called on all data fields, regardless of whether they appear in the entry point form or not.
The create() method puts values into the process instance which is created when the entrypoint completes. The store() method takes the values out of the process instance and stores them externally to make them persistent until the process instance reaches the next work item.
Typically, you would define the create() method to create a default value and put it in the process instance through a call to setData(). However, not all custom fields require these actions. This decision is up to the process designer. If a default value does not need to be set, it is recommended that you define create() to do nothing:
public void create( IProcessInstance pi )
throws Exception
{
}Default values can be defined in the JSB file and set by the process designer in the Builder. The loadDataElementProperties() method can retrieve them and set them as values of instance variables to make them available to the create() method.
The following code shows a generic example of create():
load ()
This method loads the value of the data field when the process instance is at a work item.public void load( IProcessInstance pi) throws Exception
This method is invoked when the getData() method of the process instance attempts to get the value of the data field but the value has not yet been set at the current work item. If the value has been set already at the current work item, then getData() returns the value -- it does not call load() again. Each time the process instance moves to a new work item, the first call to getData() at the new work item causes the value to be loaded again by a call to load().
Note that built-in fields are loaded whenever the process instance is loaded, but custom fields are loaded only when their data value is explicitly asked for. This behavior is called lazy loading.
Define the load() method to retrieve the data field's value from wherever it is stored by the store() method. It might, for example, retrieve a set of values from a database (maybe address, phone number and employee id) and create an object that stores those values as instance variables.
If the data is loaded from an external database, you might need a key to access the database tables. The store() method should set this key if necessary when the entry point or work item is completed.
If a key is needed (and has been set by the store() method), the load() method can retrieve it by calling getEntityKey() on the process instance, specifying the data field name. This method returns a String:
String myKey = pi.getEntityKey(getName());
// now that you know the key, you can access the databaseTo load the value into the data field, define the load() method to call setData() on the process instance, specifying the name of the data field and the value for it.Use getName() to get the name of this data field:
pi.setData( getName(), value );
NOTE: do not call getData() on this data field from within load() or you will end up in an infinite loop, since getData() invokes load().
The following example shows the basic structure for defining load(). The real work of extracting the values from the database is carried out in this case by the user-defined function retrieveMyData().
The following is a simple example of a function that extracts data from a database and returns an object that encapsulates that data. In this case, retrieveMyData() gets the name, phone number and email for an employee, given the key which is the employee's ID number.
This example assumes that MY_DB_TABLE has been defined as the database table name and that myDataSource has been bound to a data source for a database.
For more information about load(), see the discussion of load() in the Class Reference.
store()
This method stores the data associated with the custom field to make it persistent from one workitem to another.public void store (IProcessInstance pi) throws Exception
This method stores the data associated with the custom field to make it persistent from one workitem to another. This method is automatically invoked during completion of an activity if setData()was previously called. Typically, the setData() method is called during completion of an entrypoint or workitem in which the data field appeared in the form, but it can also be called in other situations such as by a completion script, an automated activity or a custom activity.
The setData() method is typically called in the following cases:
by create() to initialize the value of the data field when the process instance is created
It's up to the designer of the custom field to decide where and how to externally store the data. Note that data from a custom field cannot be stored in the application-specific table where built-in data fields are stored. That is, you cannot define store() to just "do the default thing" and save the value in the default way as done by the builtin data fields.by load() to load the value into the data field for a workitem. Often the value is loaded and displayed in a form in a workitem. In such cases, store()is called when the work item completes even if the value did not change.
by update() to update the process instance when the user enters relevant data in a form in an entrypoint or workitem
Do not define store() to save state by storing values in instance variables. The reason for this restriction is that for each instance of a data field in a process definition, Process Manager creates one instance of that class when the application is deployed. This instance is shared by all the process instances that use that process definition. For example, suppose you create and deploy a process definition whose data dictionary contains one instance of a custom data field, called employeeInfo. Three employees, Ann, Ben, and Carol, start processes instances. You might think that Ann, Ben and Carol's process instances would each create their own instance of employeeInfo, but you would be wrong. There is only a single instance of employeeInfo that is shared by all three process instances.
If the data field stores its data in an external database, it may need to use a key to identify the relevant data in the database. If applicable, defined the store() method to store a key to the database by calling setEntityKey() on the process instance. Later, when the load() method needs to load the data, it can get the key to identify the data it is looking for by calling getEntityKey().
The setEntityKey() method stores the key with the process instance. Contrast this with built-in fields. They store the field value with the process instance, and there is no need to access external sources.
Currently, Process Manager does not support global transactions. If the custom field stores its data in an external datasource that is both XA-compliant and managed by a resource manager, the custom field could participate in a global transaction. However, transactions initiated by Process Manager are not made through an XA resource manager, so they cannot be a part of the larger transaction.
The following example shows the basic structure for the store() method. In this example, the real work of storing the data gets done in the user-defined function storeMyData().
The following is a simple example of a user-defined method that serializes a data field value to an external database. In this case, the name, phone and email values are stored in corresponding columns in a database table. The key is the employeeID.
This example assumes that MY_DB_TABLE has been defined as the database table name and that myDataSource has been bound to a data source for a database.
For more information about the store() method, see the discussion of store() in the Class Reference.
archive()
When an archive operation is initiated from the administration pages, the archive() method for each data element associated with the process instance writes its data value to an output stream. The syntax for archive() is:public void archive(
IProcessInstance pi,
OutputStream os) throws ExceptionBuilt-in data elements archive themselves simply by writing their values out as bytes. By contrast, you can determine how custom fields write their data to an output stream. For example, you can stream bytes or encapsulate the values in XML.
For more information, see the discussion of archive() in the Class Reference.
Specifying Images for Use in Process Builder
You can optionally create images to represent data fields in the Process Builder. Name the image that represents the data field in edit mode as dataFieldName-EDIT.gif, and name the image that represents the data field in view mode as dataFieldName-VIEW.gif.For example, for the myNewCustomField data field, the edit mode image is myNewCustomField-EDIT.gif, and the view mode image is myNewCustomField-VIEW.gif.
Packaging a Custom Field
After you have compiled your custom field Java classes, defined the JSB file and optionally created images to represent the data field in the builder, the next step is to package these files into a zip or jar archive. Include any additional classes that your custom field uses in the archive.Figure 2-4 shows an example archive file for a custom data field called myNewCustomField. In this case, the data field is in a package customer.fields. The archive contains the following files:
myNewCustomField.jsb is the JSB file for this custom field.
myNewCustomField.class is the class file for this custom field.
myDataObject.class is the class of data objects that are used as the dat field values.
myNewCustomField-EDIT.gif and myNewCustomField-VIEW.gif. are GIF image files that are used as icons to represent the data field in edit and view mode in the Builder.
Figure 2-4    Archive file for myNewCustomField
Note that the archive file, JSB file, and custom field class must all have the same root name. In the example shown in Figure 2-4, this name is myNewCustomField.
As you create the archive, check that the directory structure reflects the package structure of the class. For example, if the class files are in the package customer.fields, the class files must be in the directory customer/fields, as shown in Figure 2-4. The JSB file must be at the same level as the class files.
Adding a Custom Field to an Application
After you package a custom field as an archive file, you can add the field in Process Builder, as described in Adding a Custom Field to an Application.The specific steps for adding a custom field are as follows:
From the Insert menu, choose Data Field.
In the "Create a New Data Field" dialog box click Add New Class. An example is shown in Figure 2-5:
Figure 2-5    Creating a data field from a new class
In the "Select the field JAR Package" dialog box, select the archive that represents your custom field class, then click Open. An example is shown in Figure 2-6:
Figure 2-6    Selecting the archive that represents a custom field class
In the Name field, enter the name of the new field.
Add the field to the Data Dictionary in either of two ways:
Click Add to add the field without setting its properties first. The dialog box remains open, and you can add more items.
Click Add & Define to add the field and set its properties immediately. The Inspector window appears for the data field you added, as shown in Figure 2-7
Figure 2-7    Setting properties for the new custom field The new data field, with the properties you defined, now appears in the Data Dictionary folder in the application tree view. You can now use the data field as you would use a typical data field in Process Builder.
Example Custom Data Field
This section discusses an example custom data field, myNewCustomField that manages information about an employee.
Appearance of the Data Field
At an entrypoint, the data field asks the user to enter their employee ID.Figure 2-8 shows the data field at an entry point.
Figure 2-8    The data field at an entry point
At a work item, the data field gets the information about the employee from a database, using the id as a key. The data field displays the id, name, phone and email for the employee. If the data field is in edit mode, the user can change the name, phone or email, but they cannot change the id. Figure 2-9 shows the data field at a workitem.
Figure 2-9    The data field at a work item
The Data Objects
The data field manages instances of the myDataObject class, which has employeeId, employeeName, phone and email variables.
package customer.fields;
public class myDataObject {
public String employeeID;
public String employeeName;
public String phone;
public String email;
public myDataObject( )
{
super();
}
//
End class
}
The Data Field Properties
The data field has several properties that the process designer can specify in the Builder. The designer can specify the background color for the table that displays the data field as well as specifying information that is needed to access the external database, such as the database name, user name and password.Figure 2-10 shows the data field in the Inspector window in the Process Builder.
Figure 2-10    Properties of the sample data field
How the Data Field Works
The name of the form element that displays the employee ID is different in an entry point than in a work item. In an entrypoint, a text field called idEntrypointFE solicits the user to enter their employee ID. In a work item, the employee ID is displayed as plain text that the user cannot edit. A hidden form element, called idFE, holds the value of the employee ID so that it is transmitted as a request parameter when the form is submitted. The update() method tests for the name of the form element containing the id to determine whether the process instance is at an entrypoint or a work item.At an entrypoint, the data field simply needs to get the employee's ID number to identify the employee in the database. There is nothing that needs to be written to the database yet. Thus the update() method simply calls setEntityKey() to set the id as the key. Since setData() is not called, the store() method is not invoked and consequently no changes are written to the database.
When the data field is displayed in a workitem, the load() method calls getEntityKey() to get the database key, which is the employee ID number. The load() method uses the key to retrieve information about the employee from an external database.
When the user submits the workitem form, the update() method sets the name, email and phone values on the data object, and then calls setData() to put the object into the data field. Since setData() is called, store() is invoked subsequently. The store() method uses the employee ID as the key to identify where to write the name, email and phone values in the external database.
The Code
To see the see the source code, click the following links:
myNewCustomField.java -- Java class that implements the data field
myNewCustomField.jsb -- JSB file that defines the properties that appear in the Process Builder
myDataObject.java -- Java class that implements the data that is managed by the data field
Development Hints and Tips
This section gives some hints and tips for developing and debugging custom fields:
Deploy the Custom Field to Test It
You can develop and test a new custom field by repeatedly going through the procedure of compiling the classes, creating the jar, importing the jar into the Builder and redeploying the application.If you take this approach, its best to change the name of the data field class at each iteration for the following reasons:
If you have already imported the data field archive into the Builder, you cannot import it again into the same application.
If you change the name of the class, don't forget to make the appropriate name changes in the JSB file.When you deploy the application, you need to restart the kjs on the Application Server if an existing class has changed, but you do not need to restart it if a new class is deployed.
Develop and Test on a Server Where Process Manager is Installed
If your server is on your development machine, you can develop and test your custom field without having to redeploy after each modification.
To start with, develop and compile your Java classes in your preferred Java development environment.
Create the JAR file, import it into the Process Builder, and then build and deploy an application that uses the custom field.
When you need to make changes to the Java file that defines the custom data field, edit and compile your Java classes in your preferred Java development environment as before.
If your output directory is not in the classpath of the server, copy the class file into the classpath of the server.
Restarting kjs on the Application Server to update your deployed applications to use the newly compiled data field class.
When you have completely finished debugging your custom field, create a new jar file for importing into the Process Builder.
Debugging Hints
To get debugging information, your Java methods can use System.out.println("Helpful debugging info goes here") to display debugging information in the kjs console on the Application Server if you start it from a DOS command prompt.You can also log debugging information to the Process Manager logs by using log() method on the IPMCluster bean or the log() method on the IPMApplication bean.
You can view the logs in the Process Administrator at http://yourPMserver:port#/Administrator.apm.
Class Reference
The remainder of this chapter provides a class and method reference for the classes needed for implementing a custom data field. The classes are:Several methods on these classes take an IProcessInstance object as an input argument. This class has methods for interacting with the process instance, such as getting the values of other data fields, getting the creation date and finding out who created the process instance. For details of the methods you can use to work with process instances, see the section IProcessInstance in Chapter 5 "Working with Applications, Process Instances and Work Items."
BasicCustomField
This class is the superclass for all custom data fields. To define a custom data field, create a subclass of BasicCustomField.
import com.netscape.pm.model.BasicCustomField;
public class myCustomField
extends BasicCustomField
{
...
}The BasicCustomField class provides methods that enable Process Manager to treat your custom field just like any other data field. Most of the methods in BasicCustomField are predefined and are used internally by Process Manager. However, you need to provide a definition for the following method in your custom data field class:
In addition to defining loadDataElementProperties(), your custom data field must implement the methods defined by the IDataElement and IPresentationElement.
BasicCustomField also implements the getName() and getPrettyName() methods which are specivied by the the IPMElement. Your custom data field can use these methods to get the name of the field itself.
Summary. Loads the design-time properties for the field specified in Process Builder's Inspector window.
protected void loadDataElementProperties(Hashtable entry ) throws Exception
The hashtable containing property/value pairs for the properties of this field that can be set in Process Builder .
Description. This method is called after the custom field has been created (while the application is being initialized). The hashtable entry parameter contains the field's configuration information, as it is stored in the LDAP repository. This information includes the properties you specified in the custom field's JSB file (which are the properties that appear in the inspector window in Process Builder).
If a call to this method fails, it can throw a java.lang.Exception at any time to signal an error. The error message will be displayed to the user, and the application will stop being initialized.
For more information about this method, see the section "loadDataElementProperties()."
Example. Suppose your JSB file contains the following entry:
<JSB_PROPERTY
NAME="dbidentifier"
TYPE="string"
DISPLAYNAME="External DB Identifier"
SHORTDESCRIPTION="Local alias for connecting to external DB"
ISEXPERT>
Given the previous JSB code, the following Java code implements the loadDataElementProperties() method. The method will first read the property and then, based on the value, set the instance variable mDBIdentifier.
IDataElement
The IDataElement interface specifies the following four methods which together implement the data handling functionality for custom data fields:Custom data field classes must implement these methods.
Summary. Writes the data associated with the custom field to an output stream. When you create a custom data field, you should define this method if you want your data field to be archivable.
public void archive (IProcessInstance pi,OutputStream os) throws Exception
Description. When an archive operation is initiated from the administration pages, the data elements associated with the process instance write their data values to an output stream. Built-in data elements archive themselves, simply by writing their values out as bytes. By contrast, you can determine how custom fields write their data to an output stream. For example, you can stream bytes or encapsulate the values in XML.
If a call to this method fails, you can throw a java.lang.Exception at any time to signal an error. The error message will be displayed to the administrator.
For more information about this method, see the section "archive()."
Summary. Loads, from a persistent resource, the data associated with the custom field. When you create a custom data field, you must implement this method.
public void load( IProcessInstance pi) throws Exception
Description. The load() method is invoked whenever the data value associated with the custom field is accessed through getData() off the process instance. Note that built-in fields are loaded whenever the process instance is loaded, but custom fields are loaded only when their data value is explicitly asked for. This behavior is called lazy loading.
Warning: Within the load() method, do not call getData() on the custom field. The load() method is already invoked as a result of a call to getData(). As a result, a call to getData() within the load() method causes an infinite loop.
If a user script accesses or modifies the data associated with a custom field, the script must implicitly know the object's data type. For example, a script would need to know the API for objects such as ItemSet and Item in a shopping cart custom field.
If a call to load() fails, you can throw a java.lang.Exception at any time to signal an error. If the current action is to display a form, an error message will be displayed to the user. If the user has completed a work item, an exception work item will be created.
For more information and an example, see the section "load ()."
Summary. Initializes a newly created process instance with a default value for the custom field. When you create a custom data field, you must implement this method if you want your custom data field to have a default value in cases where it does not appear on an entry point form.
public void create( IProcessInstance pi) throws Exception
Description. Most of the time, the create() method creates a default value and stores it in the process instance through a call to setData(). However, not all custom fields require these actions. This decision is up to the process designer.
If a default value does not need to be set, it is recommended that you do not implement the create() method. Leave it blank instead. The store() method for custom fields is called only when setData() has been performed on the field.
The create() method for all fields, whether predefined or custom fields, is called when the user initiates a process instance from the entry point.
If a call to this method fails, you can throw a java.lang.Exception at any time to signal an error. The error message will be displayed to the user, and the process instance will not be created.
For more information and an example, see the section "create ()."
Summary. Stores the data associated with the custom field to a persistent resource. When you create a custom data field, you must implement this method.
public void store( IProcessInstance pi) throws Exception
Description. It's up to the designer of the custom field to decide which external persistent datastore will store the custom field data. Note, however, that data from a custom field cannot be stored in the application-specific table, where built-in data fields are stored.
The custom field is responsible for storing the data, whereas Process Manager is responsible for storing the custom field's primary key. This key is stored in the application-specific database table.
The store() method is called only if the field's value has been modified, through a call to setData(). Note that the load() method typically calls setData(). As a result, the store() method is called whenever load() is called.
Currently, Process Manager does not support global transactions. If the custom field stores its data in an external datasource that is both XA-compliant and managed by a resource manager, the custom field could participate in a global transaction. However, transactions initiated by Process Manager are not made through an XA resource manager, so they cannot be a part of the larger transaction.
If a call to this method fails, you can throw a java.lang.Exception at any time to signal an error. The current work item is converted to an exception work item, and all data field values are reset to their values prior to the request.
For more information and an example, see the section "store()."
IPMElement
The IPMElement interface specifies the following two methods:The class BasicCustomField implements these two methods.
When defining custom data fields, you can use these methods to get the name of the data field. You do not need to define these methods on your custom data field class, since they are implemented by BasicCustomField.
Summary. Returns the name of the current element.
Return Value. A String object representing the name of the current element.
Description. The returned name is used to access the field's primary key and data value.
Example. The following code uses getName() inside the create() method:
public void create( IProcessInstance pi )
throws Exception
{
// Assign a default value for this field.
// Just an empty shopping cart...
pi.setData( getName(), new ShoppingCart() );
}
Summary. Returns the "pretty name" of the current element.
Return Value. A String object representing the pretty name of the current element.
Description. In previous releases of Process Manager, every element had a name as well as a "pretty name," the display name of the element. In the current release, an element's pretty name and its name are equivalent.
IPresentationElement
The IPresentationElement interface has the following two methods which together define how the a custom data field is represented in HTML and how this representation is processed when a form is submitted.Custom data field classes must implement these methods.
Summary. Displays the custom data field in the HTML page. When you create a custom data field, you must implement both versions of this method.
Syntax 1. This version displays the field when the user is viewing the entry point form, in which case the process instance does not yet exist.
public void display( IHTMLPage html, int displayMode, String displayFormat ) throws Exception
Syntax 2. This version displays the field in a workitem form, in which case the process instance does exist.
public void display( IProcessInstance pi,IHTMLPage html, int displayMode, String displayFormat ) throws Exception
Description. The version of display() shown in Syntax 1 will be called after the process instance has been created. In other words, it is called everywhere but the entry point node. The process instance will contain the data that is associated with your custom field; your implementation of display() will need to fetch the data object via the getData() method of the process instance class before displaying it.
The displayMode and displayFormat arguments are defined by the process designer through the Inspector window.
If a call to this method fails, you can throw a java.lang.Exception at any time to signal an error. The error message will be displayed to the user.
For more information about this method, see the section "display()."
For an example, see the section "Example display() method."
Summary. Determines how the HTML representation of a custom data field is processed when a form is submitted. Typically this method translates the form element value into the usual data object associated with the field. When you create a custom data field, you must implement this method.
public void update( IProcessInstance pi, IPMRequest rq ) throws Exception
Description. The update() method is called after the user has submitted a request to the Process Manager server. Since all requests take the form of an HTTP GET or HTTP POST, this method translates the form parameters of the request into the usual data object associated with your custom field. For example, suppose the form includes values for an item ID and an item quantity. The update() method would convert the item quantity to a numeric value, and the method would create an Item object out of the item ID. The Item object could then be bound to the process instance via setData().
If a call to this method fails, you can throw a java.lang.Exception at any time to signal an error. The error message will be displayed to the user.
For more information and an example, see the section "update()."
IPMRequest
The IPMRequest class represents requests sent by the browser to the Process Manager engine when a form is submitted. These requests contain the values of the form elements in the form. An IPMRequest object is automatically passed to the update() method of a custom data field class. The update() method can access the IPMRequest object to extract form element values and to find the authenticated user.The IPMRequest class has the following methods:
Summary. Gets the ID of the authenticated user who made the request.
public String getAuthenticatedUserId( ) throws XInvalidRequest;
Return Value. A String of the name of the authenticated user.
Description. Gets the ID of the authenticated user who made the request
Summary. Gets the value of a parameter in the request string. Typically, the parameter is the name of a form element in the form that was submitted.
public String getParameter(String parameter) throws XInvalidRequest;
Return Value. A String of the value of the parameter.
Description. Gets the value of a parameter in the request string. Typically, the parameter is the name of a form element in the form that was submitted. This method is typically invoked by the update() method of a custom data field to extract form element values.
Example. See the example for the update() method.
Summary. Returns true if a parameter is defined in the query string sent by a form submission, otherwise returns false.
public boolean isParameterDefined(String parameter)
Return Value. A Boolean indicating whether the named parameter exists or not..
Description. Returns true if a parameter is defined, otherwise returns false. The update() method can use this method to test for the existence of a parameter before attempting to retrieve its value. For example, if an entrypoint form displays different data fields than a work item form, update() can test for the existence of particular data fields to determine if the form came from an entry point or a work item.
Example. See the example for the update() method.
Previous Contents Index DocHome Next
Copyright © 2000 Sun Microsystems, Inc. Some preexisting portions Copyright © 2000 Netscape Communications Corp. All rights reserved.
Last Updated May 02, 2000