fbpx

Using Documentum Lifecycle State Extensions

Introduction

Documentum Lifecycles provide a way to change a document’s properties as it progresses
through its natural course of “life”. For example, when a document is approved, you might
want its approval_date attribute to reflect the date of the signoff. Lifecycles can automate
this type of action easily. Additionally, lifecycles can call custom code that can perform
additional actions, such as sending a notification that the document was approved.
With Documentum version 5.3, EMC introduced a feature called Lifecycle State Extensions
that makes certain types of custom lifecycle actions easier to code and support.
Prior to this version, any custom lifecycle code you wrote would be filled with
hard-coded references and configuration data. But with the addition of
Lifecycle State Extensions, Documentum now provides a place in the repository
to store that type of configuration data.

What are Lifecycle State Extensions?

A lifecycle state extension is a special type of Documentum object relationship (dm_relation)
that allows developers to associate custom attributes with a specific lifecycle state.
State extensions are useful for storing lifecycle state-specific information that is
needed by custom lifecycle state transition code. For example, consider a document
approval lifecycle for architectural plans which requires the notification of various
government agencies as the document enters different lifecycle states. The lists
of agencies to notify can be stored in state extensions that are associated with
the corresponding lifecycle states. Custom code can then access the agency lists
and generate notifications automatically, tag the documents, or perform any other
task that can be written in code.
Documentum provides an out-of-the-box state extension object type: dm_state_extension. This
object type is intended to be subtyped to add as many custom attributes as are needed to a
lifecycle state. The dm_state_extension object type is a subtype of dm_relation and can
therefore be associated with a lifecycle (dm_policy) object via its parent_id attribute.
The state extension type’s state_no attribute associates the extension with a particular
state in the lifecycle. The state extension’s child_id simply points back to itself,
creating a one-way (hanging) relation. The diagram below shows the lifecycle state
extension-related object types.

ulse-objects
Figure 1:
Lifecycle State Extension-Related Object Types and Relationships

For more information on the basics of dm_relation objects and their binding rules,
please see the excellent dm_developer article, Object Relationships.
If you’re considering using lifecycle state extensions to store Documentum users or groups then
you may want to check into Documentum alias sets which will likely provide a better solution,
especially for defining ACL accessors and workflow participants. Here’s another excellent
dm_developer article that explains how to use alias sets for these purposes: Using Documentum Alias Sets.

Creating a Custom Lifecycle State Extension Subtype

The dm_state_extension object type is provided out-of-the-box so that it can be subtyped
to add your custom state-specific attributes. You can create a custom subtype of dm_state_extension
by using the Documentum Application Builder (DAB), by executing DQL statements, or if you’re using D6 by using the new Documentum Composer administrative interface.
The first two methods are described below.
The first step is to determine the size and type of the custom attributes you want to add.
Then you can create the new object subtype using your method of choice, DAB or DQL.
This example shows the creation of a custom subtype that meets the requirements of
the fictional architectural document lifecycle described in the introduction.
The new subtype in this example, becuse it is intended to extend the QCReview lifecycle state (Quality Control Review),is named qc_lc_extension. This new object type has two custom attributes:
requires_ext_notification (Boolean), and notify_entities (String(128), repeating).

Creating a Custom State Extension Subtype Using Application Builder

  1. Using Documentum Application Builder, open the existing docapp that will contain the new lifecycle state extension object type (or create a new DocApp).
  2. Select Insert->Object Type from the main menu.
  3. Type a name for your custom state extension object type in the “Type name” box.
  4. Select the supertype, dm_state_extension.

ulse-subtype
Figure 2:
Creating a custom subtype of dm_state_extension in DAB

Perform the following steps for each custom attribute that you want to add to the new state extension subtype:

  1. Select your new custom state extension object type in the left-hand tree view and then select Insert->Attribute from the main menu.
  2. Enter a name for the new custom attribute.
  3. Select values for data type, length, and whether the attribute is repeating.

ulse-attribute
Figure 3:
Adding an attribute to the custom state extension type

4. Finally, close the object type configuration dialogs and check in the new object type.
Note: At this point you should restart the content server to refresh the data dictionary cache.

Creating a Custom State Extension Subtype Using DQL

An alternate method for creating a new subtype of dm_state_extension is by using the
Documentum query language (DQL). Use a DQL tool like Samson or DA to create your custom
state extension object type. The DQL statement below creates a custom state extension
object type named “qc_lc_extension”.

        
CREATE TYPE qc_lc_extension (
requires_ext_notifications BOOLEAN,
notify_entites CHAR(128) REPEATING)
WITH SUPERTYPE dm_state_extension;

Note that the above DQL statement is a simplified example that does not configure many
of the available options for the object type’s attributes. For complete information about
creating objects types via DQL, please see the Documentum Content Server DQL Reference,
available from EMC.

Setting the Lifecycle’s extension_type Attribute

A lifecycle’s extension_type attribute must first be set to the name of a state extension
object type before a state extension of that type can be used. This lifecycle attribute is
not exposed via the DAB client and must be set via DQL, the API, or DFC. However, DAB is
still a useful tool for uninstalling the lifecycle, a task which must be executed prior
to setting its extension_type attribute. A lifecycle can utilize only one state extension
object type. Follow the steps below to set a lifecycle (dm_policy) object’s
extension_type attribute.

    1. Uninstall the lifecycle using DAB (click the ‘Uninstall’ button).
    2. Use a DQL statement similar to the one shown below to set the lifecycle’s extension_type attribute to the name of your custom state extension object type:
            
UPDATE dm_policy OBJECT SET extension_type = 'qc_lc_extension' WHERE object_name = 'Architectural_Doc_LC';

Note that “qc_lc_extension” is the name of the custom state extension object type created in section 2 of this article.

  1. Validate and re-install the lifecycle using DAB (click the ‘Validate’ and ‘Install’ button).

Creating Instances of a Custom State Extension

Once you’ve created a custom state extension object type, you can create instances of the object
type to extend states in a lifecycle. The section below explains how to configure a state extension’s
attribute values to extend a given lifecycle state.
These are the state extension attributes that must be populated to extend a lifecycle state:

  • relation_name – Set this value to ‘BP_STATE_EXTENSION’ for all state extensions.
  • state_no – Set this value to the index of the lifecycle state that you want to extend. The index is 0-based (first lifecycle state is index 0).
  • parent_id – Set this value to the r_object_id of the lifecycle being extended.
  • child_id – Set this value to the r_object_id of the state extension instance. The relationship points back to itself.
  • permanent_link – Set this value to TRUE if you want all future versions of the lifecycle to have the same state extensions.

The example DQL statements below show how to create an instance of a custom lifecycle state extension:

    1. First create the lifecycle state extension object, populating all of its attributes except for child_id.
            
CREATE qc_lc_extension OBJECT
	SET state_no = <index of state to extend>,
	SET parent_id = '<policy_id>',
	SET relation_name = 'BP_STATE_EXTENSION',
	SET permanent_link = TRUE,
 	SET requires_ext_notifications = TRUE,
	APPEND notify_entites = 'Travis County Commissioner',
	APPEND notify_entites = 'Travis County Tax Assessor',
	APPEND notify_entites = 'City of Austin Zoning Board';
    1. Next update the state extension’s child id to be its own object id. This is the ID that was returned from the previous CREATE OBJECT query.
            
UPDATE qc_lc_extension OBJECT
	SET child_id = '<id of this state extension>'
	WHERE r_object_id = '<id of this state extension>';

Accessing State Extension Attribute Values from Custom Lifecycle Code

State extensions are useful because they can store custom attribute values. These values
can be easily accessed in custom Java DFC code that is called from a lifecycle state transition.
This section shows how to use DFC to retrieve the correct state extension for an object entering
a particular lifecycle state.
Note: For more information on creating custom Java lifecycle modules which can be called from
lifecycle state transitions, see this excellent DM Developer article: Custom Java Lifecycle Modules.
Documentum provides a handy computed lifecycle attribute, _state_extension_obj[[index]], which is
a built-in mechanism for getting a handle to the appropriate state extension for a given lifecycle state.
Computed attributes are read-only and are evaluated by the Content Server at runtime. When you query
a lifecycle for its _state_extension_obj value for a particular state index, it will return the
object_id of the corresponding state extension object (if one exists). For example, consider
the following DFC code:

        
// Get the lifecycle's computed attribute value
// for the state's extension id
String stateExtensionIdString = lifecycle.getString(
			"_state_extension_obj[" + stateIndex + "]");

The code above uses the lifecycle’s getString() method to return the computed attribute value
for _state_extension_obj[<state index>]. The return value is a string containing the r_object_id
of the corresponding state extension object instance.
The code example below is a custom implementation of the userPostProcessing() lifecycle state-transition method.
It gets the appropriate lifecycle state extension and then accesses the extension’s
attribute values and logs them out.

        
public void userPostProcessing(IDfSysObject obj, String userName, String targetState) throws DfException {

   DfLogger.info(this, "Start userPostProcessing(..)", null, null);
   DfLogger.info(this, "Lifecycle Name = " + obj.getPolicyName(), null, null);
   DfLogger.info(this, "Target State = " + targetState, null, null);

   // Get the lifecycles's object ID from the transitioning object
   IDfId policyId = obj.getPolicyId();
   DfLogger.info(this, "Lifecycle State Name = " + obj.getCurrentStateName(), null, null);

   int stateIndex = obj.getCurrentState();
   DfLogger.info(this, "Lifecycle State Index = " + stateIndex, null, null);

   IDfSession userSession = obj.getSession();
   IDfPersistentObject lifecycle = userSession.getObject(policyId);

   // Get the computed attribute value for the state's extension id
   String stateExtensionIdString = lifecycle.getString("_state_extension_obj[" + stateIndex + "]");

   DfLogger.info(this, "Lifecycle State Extension ID = " + stateExtensionIdString, null, null);
IDfPersistentObject lifecycleExtension = userSession.getObjectByQualification("qc_lc_extension where    r_object_id = '" + stateExtensionIdString + "'");

   boolean requiresExternalNotifications = lifecycleExtension.getBoolean("requires_ext_notifications");
   DfLogger.info(this, "Requires External Notifications = " + requiresExternalNotifications, null, null);

   // If this object requires external notifications then
   // log out which entities to notify
   if (requiresExternalNotifications == true) {
      int length = lifecycleExtension.getValueCount("notify_entities");
      String thisNotifyEntity;
      for (int i=0; i<length; ++i) {
         thisNotifyEntity = lifecycleExtension.getRepeatingString("notify_entities", i);
         DfLogger.info(this, "Notify Entity [" +  i + "] : " + thisNotifyEntity, null, null);
      }
   }
}

The output log for the code’s execution is shown below.

        
 Start userPostProcessing(..)
 Lifecycle Name = Test_Lifecycle
 Target State = QCReview
 Lifecycle State Name = QCReview
 Lifecycle State Index = 1
 Lifecycle State Extension ID = 3704a32080000100
 Requires External Notifications = true
 Notify Entity [0] : Travis County Commissioner
 Notify Entity [1] : Travis County Tax Assessor
 Notify Entity [2] : City of Austin Zoning Board

Notice that the method’s targetState parameter is the friendly string name for the lifecycle state,
not the state’s index which we need in order to get the lifecycle’s computed state_extension_obj[<i>]
attribute. So we instead call the transitioning object’s obj.getCurrentState() method which returns
the state’s integer index. This works fine because we are in the userPostProcessing(..) method,
however the table below shows that the object’s current state may not be what you’d expect within
other lifecycle state transitions methods.

ulse-current-state
Figure 4:
Comparison of targetState vs. getCurrentState() values for an object transitioning into the QCReview lifecycle state

Wrap-up

Lifecycle state extensions are a little-known feature in Documentum that can be very useful
when you need to maintain custom state-specific attribute values for a lifecycle.
When you use state extensions in your solutions, remember to thoroughly document
their role and maintenance as their presence won’t be immediately obvious in
Documentum’s out-of-the-box administrative tools and applications.

Like this article?

Share on facebook
Share on Facebook
Share on twitter
Share on Twitter
Share on linkedin
Share on Linkdin
Share on pinterest
Share on Pinterest

Leave a comment