Sun ONE logo     Previous      Contents     Index      Next     
iPlanet Process Manager, Version 6.5 Programmer's Guide



Chapter 2   Writing Custom Data Fields

This chapter describes how to write custom fields for use in iPlanet Process Manager.

This chapter includes the following sections:

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.
  • 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

iPlanet Process Manager allows you to define your own classes of data fields. Custom data fields are sometimes known as entity fields.

Steps for Creating a Custom Field

The main steps for creating a custom field are as follows:

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-root\com\netscape\workflow\fields



Caution

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

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:

<JSB_DESCRIPTOR

   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.


<JSB_PROPERTY NAME="dsidentifier"
   TYPE="string"
   DISPLAYNAME="Data Source Identifier"
   SHORTDESCRIPTION="DS Identifier">

<JSB_PROPERTY NAME="dbname"
   TYPE="string"
   DISPLAYNAME="Database Name"
   SHORTDESCRIPTION="DB Type">

<JSB_PROPERTY NAME="dbpassword"
   TYPE="string"
   DISPLAYNAME="Database Password"
   SHORTDESCRIPTION="DB Password">

<JSB_PROPERTY NAME="dbuser"
   TYPE="string"
   DISPLAYNAME="Database User"
   SHORTDESCRIPTION="DB User">

<JSB_PROPERTY NAME="dbtablename"
   TYPE="string"
   DISPLAYNAME="DB table name"
   SHORTDESCRIPTION="DB User">


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:

Table 2-1    Attributes for the JSB_PROPERTY Tag 

Attribute Name

Purpose

NAME

 

The name of the property.

 

DISPLAYNAME

 

The display name for this property, as it appears in the Inspector window.

 

SHORTDESCRIPTION

 

A short description of the property.

 

DEFAULTVALUE

 

The default value of the property. This attribute is optional.

 

VALUESET

 

A comma-delimited list of the possible values for this property. These values appear as a pop up menu on the property in the Inspector window. This attribute is optional.

 

TYPE

 

The type of the data field column in the application table in the database.

 

ISDESIGNTIMEREADONLY

 

When specified, this attribute indicates that the property cannot be changed in Process Builder. This attribute is optional. By default, a property value can be changed any time.

This attribute does not have an attribute=value specification. You simply give the value, for example:

<JSB_PROPERTY NAME="myname" ISDESIGNTIMEREADONLY>

 

ISEXPERT

 

When specified, this attribute indicates that the property can be changed in Process Builder while the application is in design mode. This attribute is optional. By default, a property value can be changed any time.

This attribute does not have an attribute=value specification. You simply give the value, for example:

<JSB_PROPERTY NAME="myname" ISEXPERT>

 

Required Data Field Properties

Each data field must have the properties listed in Table 2-2:

Table 2-2    Standard Data Field Properties 

Property Name

Default Display Name

Purpose

cn

 

Name of this field

 

The common name of the data field instance. (Note this is not the name of the data field class.) The name is set when you create the data field in Process Builder.

 

description

 

Short Description

 

A description of the data field.

 

prettyname

 

Display Name

 

The field's display name which is the name that Process Builder uses for the field.

 

help

 

Help Message

 

A help message for the field.

 

fieldclassid

 

Field Class ID

 

This is the package name of the data field class. This is used to ensure that each data field type is unique. This value uses the same convention as the Java naming convention for packages. For example, if ShoppingCartField is stored in \com\netscape\pm\sample, then its fieldclassid is: com.netscape.pm.sample.ShoppingCartField

 

fieldtype

 

Data Type

 

The datatype that the field uses when it is stored in the Process Manager database. The value must be ENTITY.

 

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 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?

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.

Define a Subclass of BasicCustomField

To implement a new data field class, create a Java subclass of BasicCustomField. This class provides methods that enable iPlanet 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 pm65classes.jar file. If you have installed the Process Manager Builder, you can find this jar file in the directory builder-root\support\sdk. You may also be able to find it on the 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.
  • 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 display() method of a work item calls getData() to get the value of the data field, the load() method is invoked.

When an entrypoint or workitem form is submitted, the following happens:

  • 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.
  • If the process instance is at a workitem, the process instance already exists, so the create() method is not called.

  • 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 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.

A JavaScript script (for example, an automation script, assignment script or completion script) can use the JavaScript functions getData() and setData() to get and set the data objects of a custom field. In this case, the invocation order is as follows:

  • When getData() is called, the load() method is invoked to fetch the data unless it has already been loaded for the current activity.
  • 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 next sections discuss in detail the methods that your new data field class must implement.

Implementing Data Field Methods

Define the following methods on your subclass of BasicCustomField:

About the loadDataElementProperties Method

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 )

throws Exception

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:


<JSB_PROPERTY
   NAME="dbTableName"
   TYPE="string"
   DISPLAYNAME="Database Table Name"
   SHORTDESCRIPTION="DB table where this df stores its data"
   ISEXPERT>

<JSB_PROPERTY NAME="bgColor"
   TYPE="string"
   DISPLAYNAME="Table Background Color"
   DEFAULTVALUE="white"
   SHORTDESCRIPTION="Table Background Color">

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.


public class myCustomField
   extends BasicCustomField
{
   // Database table name
   String MY_DB_TABLE;
   // background color for table displaying this data field
   String bgColor;

public myCustomField( )
{
   super();
}

/** Get the values that were set in the Builder */
protected void loadDataElementProperties( Hashtable entry)
   throws Exception
{
   String tableBackground = (String) entry.get( "bgColor" );
   if( tableBackground == null)
         tableBackground = "white");

   // Get the database table name
   MY_DB_TABLE = (String) entry.get( "dbTableName");
   if( MY_DB_TABLE == null )
         throw new Exception( "DB Table not specified");
}

About the display Method

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:

   public void display(

      IHTMLPage html,

      int displayMode,

      String displayFormat ) throws Exception

The syntax for a workitem is:

   public void display(

      IProcessInstance pi,

      IHTMLPage html,

      int displayMode,

      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. Call the write() method on the IHTMLPage input parameter to write to the HTML page.

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


public void display( IHTMLPage htmlpage, int displayMode,
   String displayFormat ) throws Exception
{
   // Create a string buffer to hold the HTML code
   StringBuffer buffer = new StringBuffer();

   // Write the code to display the data field data in a table
   switch( displayMode)
   {
   case MODE_EDIT:
         // Write HTML text to display the field in edit mode
         buffer.append("<TABLE BORDER=1 BGCOLOR=" + bgColor + ">";
         buffer.append("<TR><TD>Enter your employee ID:</TD>");
         buffer.append("<TD><INPUT TYPE=TEXT NAME=idEntrypointFE"
            + " VALUE=your_employee_ID> </TD</TR></TABLE>");
         break;

   case MODE_VIEW:
         // In an entrypoint, this data field should not
         // be shown in view mode
         buffer.append("<P>Employee id not known.</P>");   
         break;

   case MODE_HIDDEN:
   default:
         // Do nothing
}

   // Write the contents to the HTML page
   htmlpage.write( buffer.toString() );
   // end class
   }

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:


public void display( IProcessInstance pi, IHTMLPage htmlpage,
   int displayMode, String displayFormat ) throws Exception

{
   StringBuffer buffer = new StringBuffer();

   // Get the value of this data field as an object.
   // Use getName() to get the name of this data field
   myDataObject myObject =(myDataObject) pi.getData(getName() );

   // Get the employee id, name, phone number and email
   String employeeED = myObject.employeeID;
   String name = myObject.employeeName;
   String phone = myObject.phone;
   String email = myObject.email;


   // Write the code to display the values of

   // the data field in a table
   switch( displayMode)
   {

   case MODE_EDIT:
         // Write HTML text to display the field in edit mode
         // Display a table that contains editable text fields
         buffer.append("<TABLE BORDER=1 BGCOLOR=" + bgColor + ">");

         // Display the employee ID as plain text
         // so it is not editable
         buffer.append("<TR><TD>Your employee ID:</TD>");
         buffer.append("<TD>'" + employeeID + "'> </TD</TR>");

         // Add a hidden element to represent the employee id
         // so that update() can access the employee id number
         buffer.append("<INPUT TYPE=HIDDEN NAME=idFE VALUE='" +
            + employeeID + "'>");

         // Display the name, phone, and email as text fields
         buffer.append("<TR><TD>Your name:</TD>");
         buffer.append("<TD><INPUT TYPE=TEXT NAME=nameFE" +
            " VALUE='" + name + "'> </TD</TR>");

         buffer.append("<TR><TD>Your phone:</TD>");
         buffer.append("<TD><INPUT TYPE=TEXT NAME=phoneFE" +
            " VALUE='" + phone + "'> </TD</TR>");

         buffer.append("<TR><TD>Your email:</TD>");
         buffer.append("<TD><INPUT TYPE=TEXT NAME=emailFE" +
            " VALUE='" + email + "'> </TD</TR></TABLE>");
         break;


   case MODE_VIEW:
         // Write HTML text to display the field in
         // a table in view mode
         buffer.append("<TABLE BORDER=1 BGCOLOR=" + bgColor + ">");
         buffer.append("<TR><TD>Your employee ID:</TD>");
         buffer.append("<TD>" + employeeID + "</TD</TR>");

         buffer.append("<TR><TD>Your name:</TD>");
         buffer.append("<TD>" + name + "</TD</TR>");

         buffer.append("<TR><TD>Your phone:</TD>");
         buffer.append("<TD>" + phone + "</TD</TR>");

         buffer.append("<TR><TD>Your email:</TD>");
         buffer.append("<TD>" + email + "</TD</TR></TABLE>");
         break;

   case MODE_HIDDEN:
   default:
         // Do nothing
   }
   // Write the contents to the HTML page
   htmlpage.write( buffer.toString() );
}



For more information about display(), see the discussion of display() in the Class Reference.

About the update Method

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.

The syntax is:

public void update(

   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.



Caution

Do not use variables on the data field itself to hold values that are specific to a process instance, since all process instances effectively 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.


public void update( IProcessInstance pi, IPMRequest rq )
   throws Exception
{
   // Create an instance of myDataObject and set its
   // employeeID, employeeName, phone and email variables.
   myDataObject myObject = new myDataObject();

   if (rq.isParameterDefined("idFE"))
         myObject.employeeID = rq.getParameter("idFE");

   if (rq.isParameterDefined("nameFE"))
         myObject.employeeName = rq.getParameter("nameFE");

   if (rq.isParameterDefined("phoneFE"))
         myObject.phone = rq.getParameter("phoneFE");

   if (rq.isParameterDefined("emailFE"))
         myObject.email = rq.getParameter("emailFE");

   // Put myObject into the data field on the process instance
   pi.setData( getName(), myObject);
}

About the create Method

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():


/** Initialize the data field with the default value */
public void create( IProcessInstance pi )
   throws Exception
{
   // Assign a default value for this field.
   customObject object1 = new customObject();
   object1.value1 = default_value1";
   object1.value2 = default_value2;
   pi.setData( getName(),object1);
)

About the load Method

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 database

To 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().


public void load( IProcessInstance pi ) throws Exception
{
   // Load the data from wherever it is stored
   // and put it in the PI
   // Get the entity key
   String thisID = (String) pi.getEntityKey(getName());

   // retrieveMyData() is a user-defined function ( not a system
   // one) that interrogates an external database to get the data
   myDataObject myObject =(myDataObject) retrieveMyData(thisID);

    // Put the value in the data field
   pi.setData( getName(), myObject );
}

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.


myDataObject retrieveMyData(String employeeID)throws Exception
{
   myDataObject myDataObject = new myDataObject();

   // Database-related variables
   Connection c = null;
   PreparedStatement myStatement = null;
   ResultSet myResultSet = null;

   // String MY_DB_TABLE; -- already defined
   // DataSource myDataSource; -- already defined

   try {
   // Connect to the database.
   // Database parameters are specified by myDataSource
   c = myDataSource.getConnection();

   // Create a query string to get the name, phone and email from
   // the MY_DB_TABLE database table
   // (for example EMPLOYEE_TABLE)
   String MY_QUERY_DATA = "SELECT name, phone, email " +
         " FROM MY_DB_TABLE WHERE employee_id = " + employeeID;

   // Prepare and execute the query statement
   myStatement = c.prepareStatement( MY_QUERY_DATA );
   myResultSet = myStatement.executeQuery();

   // Process the results
   while( myResultSet.next() )
   {
         String name = myResultSet.getString("name");

         String phone = myResultSet.getString("phone");
         String email = myResultSet.getString("email");
         myDataObject.employeeName = name;
         myDataObject.phone = phone;
         myDataObject.email = email;
   }
   }
   catch( Exception e ) {
   throw new Exception( "Cannot load " + getName() +
         " because: " + e );
   }
   return myDataObject;
}


For more information about load(), see the discussion of load() in the Class Reference.

About the store Method

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
  • 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

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.

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, iPlanet 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. The setEntityKey() method stores the key with 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().

Currently, iPlanet 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 iPlanet 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().


public void store( IProcessInstance pi )throws Exception
{
   myDataObject myObject =(myDataObject) pi.getData(getName());
   String myKey = myObject.employeeID;

   // Set the key so we can get it back when needed
   pi.setEntityKey(getName(), myKey);

   // Store the data. storeMyData is a user-defined function
   // not a system one
   storeMyData(myKey, myObject);
}

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.


protected void storeMyData(String myKey, myDataObject myObject)
   throws Exception
{
   Connection c = null;
   PreparedStatement myStatement = null;

   // String MY_DB_TABLE; -- previously defined
   // DataSource myDataSource; -- previously defined
try {
   // Create the SQL statement for updating the database
   String SQL_UPDATE_DATA = "UPDATE " +
         MY_DB_TABLE +
         " SET name = ? , phone = ?, email = ?" +
         " WHERE id = myKey";

   // Connect to the database.
   // Database parameters are specified by myDataSource
   c = myDataSource.getConnection();
   // Prepare and execute the SQL statement
   myStatement = c.prepareStatement( SQL_UPDATE_DATA );
   // do any other necessary preparation work
   ...
   // Update the database
   try {
         myStatement.executeUpdate();
   }
catch( Exception e ){
         throw new Exception( "Cannot save data for data field: "
            + getName() + " because: " + e );
   };
   c.commit();
   }


For more information about the store() method, see the discussion of store() in the Class Reference.

About the archive Method

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 Exception

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.

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.



Note

When you use the jar command to create an archive, a file named manifest.mf is automatically created by default. This file contains information about the other files within the archive. The manifest.mf file has no effect on the custom field.



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 this section.

The specific steps for adding a custom field are as follows:

  1. From the Insert menu, choose Data Field.
  2. In the "Create a New Data Field" dialog box click Add New Class. An example is shown in Figure 2-5:
  3. Figure 2-5    Creating a data field from a new class

  4. 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:
  5. Figure 2-6    Selecting the archive that represents a custom field class

  6. In the Name field, enter the name of the new field.
  7. 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

  8. Set the properties and close the window when you are done.

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

The AdvancedOfficeSetup sample application that ships with the Process Builder includes an example of a custom data field.

The advanced office setup application uses a custom data field called dfComputerChoice that presents a pop-up menu of computers that can be ordered for a new employee, as shown in Figure 2-8. This data field dynamically generates the list of computers every time it is displayed in edit mode. It gets the list by reading an XML file containing the choices. Whenever the company's list of approved computers changes, all the administrator needs to do is to change the list in the XML file -- there's no need to redeploy the Process Manager application.

Figure 2-8    Pop-up menu of computers

For full details of this custom data field, see Chapter 3, "Advanced Office Setup Application."

Development Hints and Tips

This section gives some hints and tips for developing and debugging custom fields:

Avoid Non-default Constructors

In classes that extend BasicCustomField, do not define non-default constructors (meaning constructors with non-zero arguments). Process Manager has no prior awareness of non-default constructors and therefore cannot call them. Thus if you define non-default constructors, your class may encounter problems during loading.

Avoid Instance Data

Custom fields, like custom activities, are stateless entities. In effect, there is only one copy of each occurrence of a custom data field per application. All the process instances in an application effectively share the same custom data field instance for each occurrence of the custom data field class in the application. Because of this, it's recommended that you avoid using instance data in a class that implements a custom data field. 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.

For example, consider a custom data field class called DynamicList that dynamically generates a list of things (such as computers), displays the list as a SELECT menu in HTML and stores the selected item in a text file.

Suppose the custom data field erroneously uses an instance variable to keep track of the file name where the value is stored, as shown in the following code:


// This is the WRONG way to track the name of the file
// where data field values are stored!!

public class UpdatableList extends BasicCustomField implements IPresentationElement ,IDataElement
{
   // The name of the file where the value is saved
   public String thisFile;
protected void loadDataElementProperties(Hashtable entry )
throws Exception
{

// Generate the file name
thisFile = generateFileName();
}
public void store(IProcessInstance pi) throws Exception
{
   // Store the value
   thisValue = pi.getData(getName());

   storeItNow (thisValue, thisFile);
   ...
}
public void display(IProcessInstance pi, IHTMLPage html,
   int displayMode, String displayFormat ) throws Exception
{
   String selectedOption = pi.getData(getName());
   // code to display the selected option
   ...
}
public void load(IProcessInstance pi) throws Exception
{
    // Get value out of the file
   thisValue = readDataFromFile (thisFile);
   return thisValue;
}

Suppose Carol starts a process instance that uses the ChooseComputer data field, which is an instance of this data field class. The loadDataElementProperties() method sets the value of the thisFile variable to CarolsValue.txt. She chooses an HP laptop computer. Her store() method stores the value in the file.

Later, Alice starts a process instance. The loadDataElementProperties() method sets the value of thisFile to AlicesValue.txt. Alice chooses a Compaq Pro computer, which is saved to the file AlicesValue.txt.

Carol's process instance reaches a work item where the ChooseComputer data field is displayed again, perhaps to confirm the choice of computer. This time, when the load() method is invoked by the display(), it reads the value from thisFile, which is now AlicesValue.txt. Thus the data field displays Alice's choice, not Carol's, which does not please Carol at all.

For an example of the correct way to record the names of files where data field values are stored, see Chapter 3 "Advanced Office Setup Application" for a discussion of the example data field class, updatableList, that is available in the AdvancedOfficeSetup application.

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 or file where the data is stored. For example, the custom data field discussed previously might use a global variable to record the file name of an XML file that contains the choices to be displayed in the SELECT list (for example, IBM ThinkPad, Apple iMac, Sun Solaris workstation and so on) . These choices do not change from employee to employee, so it is OK to store the name of the file containing the choices as an instance variable. (Note that the file name containing the selected choice does vary from process instance to process instance, whereas the one containing the choices for the menu does not vary from process instance to process instance.)

An application can contain multiple occurrences of a custom data field class. For example, an application might have custom data fields called ComputerChoices and ChairChoices, that are both instances of the DynamicList class. When the application is running, these two fields operate completely independently. If the fields use instance data, that data would not be shared between them. For example, if the data fields use an instance variable called myChoicesFileName, the ComputerChoices data field could set it to computerChoices.xml while the ChairChoices field could set it to chairChoices.xml, and there would be no confusion between the two.

Use Entity Keys

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.

To work with entity keys, use the following two methods on the IProcessInstance interface:

  • getEntityKey(fieldName)
  • This call returns the key for the custom field whose name is fieldName.

  • setEntityKey(fieldName, key)
  • Specifies key as the key for the custom field whose name is fieldName.

Deploy the Custom Field to Test It

To test and debug a custom data field, you need to import it into a Process Manager application in the Builder, deploy the application and then test it. During the development process, you will make changes to your Java source files and recompile the classes. Whether you need to redeploy your application or not depends on where Process Manager is running.

If the Process Manager engine is running on the same computer where you do your Java development work, you do not need to redeploy the application from the Builder but you do need to restart the Application Server, at least the KJS component. See the next section for more details.

If the Process Manager engine is running on a remote computer, you need to redeploy the application each time you make changes to the Java classes for the custom data field. Before redeploying, make sure the changes have been copied into the appropriate places (as discussed next) in the Applications folder hierarchy.

When you use the Builder to bring a zip or jar file for a custom data field into a Process Manager application, the Builder unzips the zip or jar file and then creates the folders needed for the package structure for the custom data field. For example, if the application name is myApp and the custom data field is in the package custom.fields.new, the Builder creates a folder called new in a folder called fields in a folder called custom in the myApp folder in the Applications directory, as illustrated in the following image:



The Builder places the unzipped files into the appropriate folders, for example, it places the JSB and Java class file for the data field in the new folder.

After making changes to the Java files, copy the compiled class files into the appropriate folder beneath the Applications directory. If you make changes to the JSB file, make sure the changes are also copied to the appropriate folder beneath the Applications directory.

To test the changes, redeploy the application.

When you're done making changes, make a new zip or jar file so that the finished data field can be imported into other Process Manager applications.

Develop and Test on a Server Where iPlanet Process Manager is Installed

If your server is on your development machine, you can develop and test your custom field without redploying after each modification.

  1. To start with, develop and compile your Java classes in your preferred Java development environment.
  2. Create the JAR file, import it into the Process Builder, and then build and deploy an application that uses the custom field.
  3. 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.
  4. If your output directory is not in the classpath for the Process Manager engine (BPMCLASSPATH) , copy the class file into the Process Manager classpath.
  5. Restart the kjs on the Application Server to update your deployed applications to use the newly compiled data field class. (For information on restarting the kjs, see the section "Print Debugging Information.")
  6. New and existing process instances will use the new definition for the custom data field.

  7. When you have completely finished debugging your custom field, create a new jar file for importing into the Process Builder.

Use One Implementation of a Java Class Per Server

When an application is deployed, the Java classes it uses are deployed to the appropriate folder in the class path on the engine. This class path is shared by all applications running on the engine. Every application that uses a particular Java class uses the same implementation of that class. For example, suppose application A and application B both use a Java class SharedClass1. When application A is deployed, its version of SharedClass1 is deployed to the class path. When application B is deployed, its version of SharedClass1 is deployed to the class path, overwriting the implementation deployed previously by application A.

Thus if multiple applications running on a Process Manager engine use the same custom data field, they should all use exactly the same implementation of the custom data field, since each time the custom data field is deployed to the engine, it overwrites the previous implementation.

If you want multiple applications to use a custom data field that is basically the same but differs slightly from application to application, make sure that the name of the data field Java class is different in each application.

Debugging Hints

Print Debugging Information

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.

Note: To run the Application Server from the command line, use the interface to stop the application server. Then open a command prompt and type kxs. When the kxs process blocks, type kjs in another command prompt to start the kjs.

Send Error Messages to the Process Manager Logs

You can also log debugging information to the iPlanet Process Manager logs by using the log() method on the IPMCluster bean or the log() method on the IPMApplication bean. Use the getPMApplication() method of BasicCustomField to get the IPMApplication.

For example:

IPMApplication myapp = getPMApplication();

myapp.log(IPMApplication.LOG_INFORMATION, "log info here", null);

You can view the logs in the Process Administrator at http://yourPMserver:port#/Administrator.apm.

Errors in store()

If the store() method has a problem, it might result in store() being invoked twice while the system tries to restore the process instance to its previous state. Thus if you have print statements that record entry into and exit from store(), and you see that store() is invoked twice or seems to be entered recursively, or you see load() being called from inside store() when getData() is called, this is an indication that there might be a problem with the definition of your store() method.

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".

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 iPlanet 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 iPlanet Process Manager. However, you need to provide a definition for the following methods in your custom data field class:

BasicCustomField also implements the getPMApplication() method that returns the IPMApplication that contains the field. Two other methods implemented by BasicCustomField are getName() and getPrettyName() which are specified by the interface IPMElement . Your custom data field can use these methods to get the name of the field itself.

archive()

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. This method is specified by the IDataElement interface.

Syntax   

public void archive (IProcessInstance pi,OutputStream os) throws Exception

Arguments   

  • pi

Object representing the process instance.

  • os

The output stream to write the data to.

Return Value    None.

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 "About the archive Method".

create()

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. This method is defined by the IDataElement interface.

Syntax   

public void create( IProcessInstance pi) throws Exception

Arguments   

  • pi

Object representing the process instance.

Return Value    None.

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 "About the create Method".

display()

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. This method is specified in the IPresentationElement interface.

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

Arguments   

  • pi

Object representing the process instance.

  • html

Object representing the HTML page to be returned to the user.

  • displayMode

Mode that the field should be displaying itself in. Possible values are MODE_EDIT, MODE_VIEW and MODE_HIDDEN.

  • displayFormat

Additional formatting information available to the field. This value is specified from the "Display Format" property of the Inspector window of the field when it is placed in the form. This value is specific to a process designer. One possible use is to distinguish between a secure viewing mode and a non-secure viewing mode, such as for credit card information. In such a case, the display format could contain either the value "secure" or "not secure."

Return Value    None.

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 "About the display Method".

For an example, see the section "Example display() method"

getName()

Summary    Returns the name of the current element. This method is defined by the IPMElement interface.

Syntax   

public String getName()

Arguments    None.

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() );
}


getPMApplication()

Summary    Returns the IPMApplication for the application containing this data field.

Syntax   

public String getPMApplication()

Arguments    None.

Return Value    An IPMApplication bean representing the application containing this data field.

Description    Use this method to get access to the application containing the custom data field. IPMApplication has many useful methods for accessing other information about the application

Example    The following code gets the pathname for the application containing this data field.


// Inside store(), get the application path
String path;
try {
    path = getPMApplication().getHomePath();
    }
catch (Exception e) {
    System.out.println("Exception getting app path" + e);
}


getPrettyName()

Summary    Returns the "pretty name" of the current element. This method is defined by the IPMElement interface.

Syntax   

public String getPrettyName()

Arguments    None.

Return Value    A String object representing the pretty name of the current element.

Description    In previous releases of iPlanet 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.

load()

Summary    Loads the data associated with the custom field. When you create a custom data field, you must implement this method. This method is specified by the IDataElement interface.

Syntax   

public void load( IProcessInstance pi) throws Exception

Arguments   

  • pi

Object representing the process instance.

Return Value    None.

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 "About the load Method"

loadDataElementProperties()

Summary    Loads the design-time properties for the field specified in Process Builder's Inspector window. Specified by BasicCustomField. Custom data fields should implement this method.

Syntax   

protected void loadDataElementProperties(Hashtable entry ) throws Exception

Arguments   

  • entry

The hashtable containing property/value pairs for the properties of this field that can be set in Process Builder .

Return Value    None.

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 "About the loadDataElementProperties Method".

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.


protected void loadDataElementProperties( Hashtable entry )
   throws Exception
{
   String dbIdentifier = (String) entry.get( "dbidentifier" );
   if( dbIdentifier == null )
         throw new Exception( "DB Identifier not specified" );
   else
         mDBIdentifier = dbIdentifier;
}


store()

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. This method is defined by the IDataElement interface.

Syntax   

public void store( IProcessInstance pi) throws Exception

Arguments   

  • pi

Object representing the process instance.

Return Value    None.

Description    It's up to the designer of the custom field to decide which external persistent data store 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 iPlanet 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, iPlanet 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 iPlanet 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 "About the store Method".

update

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. This method is specified by the IPresentationElement interface.

Syntax   

public void update( IProcessInstance pi, IPMRequest rq ) throws Exception

Arguments   

  • pi

Object representing the process instance.

  • html

Object representing the HTTP request.

Return Value    None.

Description    The update() method is called after the user has submitted a request to the iPlanet 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 "About the update Method".

IPMRequest

The IPMRequest class represents requests sent by the browser to the iPlanet 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:

getAuthenticatedUserId

Summary    Gets the ID of the authenticated user who made the request.

Syntax   

public String getAuthenticatedUserId( ) throws XInvalidRequest;

Arguments    None.

Return Value    A String of the name of the authenticated user.

Description    Gets the ID of the authenticated user who made the request

getParameter

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.

Syntax   

public String getParameter(String parameter) throws XInvalidRequest;

Arguments   

  • parameter

The name of the parameter whose value is to be retrieved.

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    The section, "About the update Method", contains an example of using the getParameter method.

isParameterDefined

Summary    Returns true if a parameter is defined in the query string sent by a form submission, otherwise returns false.

Syntax   

public boolean isParameterDefined(String parameter)

Arguments   

  • parameter

The name of a parameter whose existence is being tested

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    The section, "About the update Method", contains an example of using the isParameterDefined method.


Previous      Contents     Index      Next     
Copyright 2002 Sun Microsystems, Inc. All rights reserved.


816-6353-10