Previous Next Contents Index


Chapter 18 Writing Custom Fields

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

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 Date and Textfield.

Why Use a Custom Field?
Sometimes you need to create a new class of data field, called a custom field. A custom field is a data field that provides the following additional features:

Functional View of a Custom Field
The functional model of a custom field consists of two parts: a presentation component and a data component. Figure 18.1 shows the functional view of a custom field, and it's relationship to an HTML form and an external datasource.

Figure 18.1    Functional view of a custom field

The presentation component is represented by the IPresentationElement interface. This interface defines the way a custom field will appear in an HTML form. For example, a custom field may appear as a select list, a check box, or a group of radio buttons. IPresentationElement also handles reading from and writing to the HTML form.

The data component is represented by the IDataElement interface. This interface handles the external storage of the custom field's data. By using a component-based model for custom fields, it will be possible to represent the IDataElement object as an entity bean in future releases of PAE.

Steps for Creating a Custom Field
The major steps for creating a custom field are as follows:


Defining Field Properties in a JSB File
The first step in implementing a custom field is to write a JSB file. This file defines the custom's fields properties that will be set at design time, through 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 is 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
After the <JSB> tag is a <JSB_DESCRIPTOR> tag, which 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
After the <JSB_DESCRIPTOR> tag comes a series of <JSB_PROPERTY> tags, one for each property that appears in the Inspector window. For example, here is one section of <JSB_PROPERTY> tags for the ShoppingCartField.jsb file:

/**

* Properties related to this specific format of field

*/

<JSB_PROPERTY NAME="dbtype"

               TYPE="string"

               DISPLAYNAME="DB Type"

               SHORTDESCRIPTION="DB Type"

               DEFAULTVALUE="ORACLE"

               VALUESET="ORACLE,SYBASE">

<JSB_PROPERTY NAME="dbidentifier"

               TYPE="string"

               DISPLAYNAME="DB Identifier"

               SHORTDESCRIPTION="DB Identifier">

<JSB_PROPERTY NAME="dbuser"

               TYPE="string"

               DISPLAYNAME="DB User"

               SHORTDESCRIPTION="DB User">

<JSB_PROPERTY NAME="dbpassword"

               TYPE="string"

               DISPLAYNAME="DB Password"

               SHORTDESCRIPTION="DB Password">

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 18.1:

Table 18.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 datafield 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 18.2:

Table 18.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 PAE database. The value must be ENTITY.

In addition to these required properties, each data field has 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; an applet data field has properties for a class id and parameter; 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 their field, it will have no effect.


Writing the Java Classes
To write the Java classes for a custom field, perform the following steps:

  1. Consider the Design Issues
  2. Implement IPresentationElement and IDataElement
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:

Design a Data Manager
A custom field does not represent the data that you are trying to model. Instead, the custom field acts merely as the manager of that data. This concept is shown in Figure 18.2.

Figure 18.2    A custom field acts as a data manager

The data can take the form of any Java object. This data is passed from the process instance to the custom field when it is time to store the data. The custom field knows how to store this data into the external repository. Similarly, when the custom field needs to load the data, the field will retrieve the data from the external repository and pass it to the process instance. The important idea is that the process instance is stateful, and the custom fields specify only the logic to manage the data, not the data itself.

As an example, suppose you have a "shopping cart" custom field, and the data to manage is a set of items stored in a relational database system. Let's assume that the items are stored as rows in a database table.

To display the shopping cart's contents, the custom field must retrieve the items from the database. To do so, the custom field will "wrap" the rows with a Java object, thereby providing a well-defined API. As a result, the rest of your process can access the items.

Java Code

Here is the Java code to implement the shopping cart items:

public class ItemSet

{

   Hashtable mItems = new Hashtable();

   public void addItem( Item item ) {

         mItems.put( item.getItemId(), item ); }

   public Item getItem( String itemId ) {

         return mItems.get( itemId ); }

   public Enumeration items( ) {

         return mItems.elements(); }

}

public class Item

{

   String mItemId;

   int mQuantity = 0;

   float mPrice = 0.0f;

   public Item( String itemId, int quantity, float price )

   {

         mItemId = itemId;

         mQuantity = quantity;

         mPrice = price;

   }

   public String getItemId( ) { return mItemId; }

   public int getQuantity( ) { return mQuantity; }

   public float getPrice( ) { return mPrice; }

}

JavaScript Code

The data stored in the process instance will be an instance of ItemSet. To retrieve information about each item in the set, you can write an automation script. This script will access the shopping cart custom field by using getData(), as shown here:

function automationScript( )

{

   var pi = getProcessInstance();

   var items = pi.getData( "shopping_cart" );

   for( var e = items.elements(); e.hasMoreElements(); )

   {

         // we have a reference to an Item object

         var item = e.nextElement();

         var itemId = item.getItemId();

   }

   return true;

}

When it is time to store the set of items, the custom field will retrieve the current set from the process instance. The field will then translate the items from their Java object representation to a set of SQL rows.

Design a Thread-safe Class
Custom fields, like custom activities, are stateless entities. There can be 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, it's recommended that you avoid using instance data in the class that implements a custom field. (But it's safe to have instance variables that store read-only configuration information.) 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 relies on an entity key to identify the data it is looking for. This entity key is stored with the process instance, and the key serves as a handle to the external data. 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:

As an example, suppose you have a shopping cart custom field whose entity key is called basket_id. This ID number is a unique identier, distinguishing one basket in the shopping cart table from another basket. If you wanted to use the process instance ID as the basket ID, you would use the following code:

long basketID = pi.getInstanceId();

Implement IPresentationElement and IDataElement
IDataElement has two methods: display() and update(). IPresentationElement has four methods: create(), store(), load(), and archive(). You must implement these methods in the Java class for your custom field.

By implementing the methods on IPresentationElement and IDataElement, you define how your custom field will manage its data objects. However, there are additional methods to implement so that PAE treats your custom field just like any other data field. PAE provides these method implementations for you, through the BasicCustomField class.

This class also provides empty stubs for the IPresentationElement and IDataElement methods that you must fill in.

Therefore, to write your custom field class, use BasicCustomField as your base class. For example:

import com.netscape.pm.model.BasicCustomField;

public class myCustomField

   extends BasicCustomField

{

   ...

}

For implementation details on IDataElement, IPresentationElement, and BasicCustomField, see the section "Method Reference" on page 408.

The methods of IPresentationElement and IDataElement are invoked in a particular order, depending on which of the following actions is performed:

Displaying a Work Item
When a user displays a work item that includes a custom field, the method invocation is shown in Figure 18.3.

Figure 18.3    Methods invoked when displaying a work item

  1. The display() method (on IPresentationElement) displays the custom field in the HTML form.
  2. The load() method (on IDataElement) fetches the value that the field will display.
Initiating a Process Instance
When a user wants to initiate a process instance, the method invocation is shown in Figure 18.4.

Figure 18.4    Methods invoked when initiating a process instance

  1. The display() method (on IPresentationElement) displays the custom field at the entry point step. Optionally, the load() method (on IDataElement) fetches an initial value to display.
  2. When the user clicks the submit button, the HTTP POST or HTTP GET request is submitted to PAE. This results in a call to create(). Note that all fields have their create() method invoked, regardless of whether the field exists on the entry point form.
  3. Assuming the field is set for EDIT mode, the field's update() method is called.
  4. When the process instance is ready to store itself, the field's store() method is called. The store() method is called only if the field's data was modified by a previous use of setData().
Completing a Work Item
Completing a work item is similar to the entry point form submission, described in the previous case. However, because the process instance already exists, the create() method isn't called. The method invocation is shown in Figure 18.5.

Figure 18.5    Methods invoked when completing a work item

  1. Assuming the field is set for EDIT mode, the field's update() method is called.
  2. When the process instance is ready to store itself, the field's store() method is called. The store() method is called only if the field's data was modified by a previous use of setData().
Accessing a Custom Field from a Script
A user's JavaScript script might use getData() to access the data objects of a custom field. The method invocation for this situation is shown in Figure 18.6.

Figure 18.6    Methods invoked when accessing a custom field from a script

  1. The load() method is called to fetch the data.
  2. The load() method typically uses setData(). Whenever a setData() is performed, the store() method is called when the process instance is ready to store itself. As a result, the store() method may be called even though the field's data has not changed.

Packaging a Custom Field
After you compile your custom field Java classes and define the JSB file, the next step is to package these files into a zip or jar archive. For example, Figure 18.7 shows an archive that contains the following 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.

Figure 18.7    Directory structure for the ShoppingCartField custom field

Note that the archive file, JSB file, and custom field class must all have the same root name. In the example shown in Figure 18.7, this name is ShoppingCartField.

As you create the archive, check that the directory structure reflects the package structure of the class. For example, the class files are in the package com.netscape.pm.sample. Therefore, the class files must be in the directory com/netscape/pm/sample, as shown in Figure 18.7. 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 "Creating a Data Field" on page 132.

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, enter a name for the new field and then click Add New Class. An example is shown in Figure 18.8:
  3. Figure 18.8    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 18.9:
  5. Figure 18.9    Selecting the archive that represents a custom field class

  6. In the "Create a New Data Field" dialog box, add the field to the Data Dictionary in either of two ways:
  7. 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.


Method Reference
This section summarizes the methods available in the following objects:

IPresentationElement Interface
The IPresentationElement interface has two methods, display() and update(). Your custom field class must implement these methods, which are summarized in the following sections.

display()
Summary
Displays the field in the HTML page.

Syntax 1
This version displays the field after a process instance has been created.

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

Syntax 2
This version displays the field when the user is viewing the entry point form.

public void display(
   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.

update()
Summary
Translates the HTTP POST or HTTP GET string parameters for this field into the usual data object associated with the field.

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

IDataElement Interface
The IDataElement interface has four methods: create(), store(), load(), and archive(). Your custom field class must implement these methods, which are summarized in the following sections.

create()
Summary
Initializes a newly created process instance with a default value for the custom field.

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.

store()
Summary
Stores the data associated with the custom field to a persistent resource.

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 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 PAE 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, PAE 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 PAE 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.

load()
Summary
Loads, from a persistent resource, the data associated with the custom field.

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.

archive()
Summary
Writes the data associated with the custom field to an output stream.

Syntax

public void update(
   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.

BasicCustomField Class
By implementing the methods on IPresentationElement and IDataElement, you define how your custom field will manage its data objects. However, there are additional methods to implement so that PAE treats your custom field just like any other data field. PAE provides these method implementations for you, through the BasicCustomField class.

This class also provides empty stubs for the IPresentationElement and IDataElement methods that you must fill in.

Therefore, to write your custom field class, use BasicCustomField as your base class. For example:

import com.netscape.pm.model.BasicCustomField;

public class myCustomField

   extends BasicCustomField

{

   ...

}

In addition, BasicCustomField provides the loadDataElementProperties() method, described next.

loadDataElementProperties()
Summary
Loads the design-time properties for the field specified in Process Builder's Inspector window.

Syntax

protected void loadDataElementProperties(
   Hashtable entry ) throws Exception

Arguments

entry. The hashtable containing field configuration properties.

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.

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 application will stop being initialized.

Example
Suppose your JSB file contains the following entry:

<JSB_PROPERTY
   NAME="dbidentifier"
   TYPE="string"
   DISPLAYNAME="External DB Identifier"
   SHORTDESCRIPTION="Local alias used to connect to external DB server"
   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;
}

IPMElement Interface
The BasicCustomField class implements the IPMElement interface, which has two methods: getName() and getPrettyName(). These methods make it easier to implement your custom field.

getName()
Summary
Returns the name of the current element.

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

getPrettyName()
Summary
Returns the "pretty name" of the current element.

Syntax

public String getPrettyName()

Arguments
None.

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

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

 

Copyright © 1999 Netscape Communications Corp. All rights reserved.