Overview
Documentum lifecycles are used to implement business policies around the lifecycle
of an object in the repository. For example, a news article might be authored in
“Draft” state, then promoted to a “QC Review” state, and if accepted by an editor,
promoted to “Final” or “Published” state. Each state in the lifecycle can be
configured to perform certain actions as documents enter or leave it.
Some lifecycle actions, such as adding or removing version labels or assigning a new ACL,
can be achieved through configuration of the out-of-the-box lifecycle functionality.
However, more specialized actions require custom-coded lifecycle modules, which are
the subject of this article.
This article assumes that the reader knows the basics of lifecycle creation and configuration.
If you are unfamiliar with these concepts, please see the Documentum Content Server Fundamentals
Guide (Chapter 10) or this excellent article from the dm_developer website:
Creating a Simple Lifecycle
Document lifecycles (dm_policy objects) are typically constructed and administered via the Documentum
Application Builder (DAB) interface. This article focuses on lifecycle configuration performed in
the DAB client. You could also perform most of these tasks using DQL, the API, or DFC. Custom
Java code for lifecycles can be written in the editor/IDE of your choice.
The Lifecycle Implementation Type
Documentum lifecycles have an implementation type attribute which defaults to “Docbasic”.
This value sets the code language for modules that can be called from the lifecycle’s state
transitions. (A state transition is the promotion or demotion of an object to a different
lifecycle state.) Because a lifecycle can only have one implementation type, it can only
execute one type of code. When using custom code modules written in Java, set the
lifecycle’s implementation attribute to “Java” as described below.
- Using Documentum Application Builder, open the docapp that contains the lifecycle
you want to configure (or create a new DocApp and Lifecycle). - Double-click and edit the lifecycle in the DocApp.
- Set the “Implementation” drop-down value to “Java” on the “General” tab of the
lifecycle configuration form.
Figure 1:
Setting the Lifecycle’s Implementation Type
Types of Lifecycle Modules
Lifecycles can call custom code modules during state transitions.
Each lifecycle state transition provides three opportunities to call
custom code:
Pre-Entry Modules – Pre-entry modules are called prior to the state transition
and can therefore be used for such functions as validating custom entry criteria
or manipulating the transitioning object prior to the transition. Pre-entry
modules can block a state transition if the object fails custom validation
or if an exception is raised.
Action Modules – Action modules are called during the state transition only after
all entry criteria and pre-entry modules have executed successfully. Documentum ships with
several pre-built lifecycle actions for common state transition tasks (so don’t write a
custom action unless you have to!). The following actions are provided out-of-the-box:
- Add Attribute
- Add Label
- Add Link
- Change Storage
- Move Object
- Remove Attribute
- Remove Label
- Remove Link
- Rendition
- Set Attribute
Post-Entry Modules – Post entry modules are called after the transitioning
object has entered the lifecycle state. This occurs after all pre-entry and action
modules have executed.
Permissions for Lifecycle Modules
When you create a code module for your custom lifecycle code in DAB, it is
automatically assigned the “BOF_acl” ACL which has permissions of Owner(Delete)
and World(Read + Execute Procedure). These permissions allow the lifecycle to
execute modules on behalf of any user (World) who initiates a state transition.
However, if the custom code itself tries to perform activities for which the
lifecycle user does not have sufficient permssion then the following error
is raised in the WDK client:
Figure 2:
Error raised by lifecycle code executing with insufficient permission
If your custom lifecycle code needs to perform actions for which the individual lifecycle
user does not have permission, then you can configure lifecycle actions to operate on
behalf of a designated superuser (or any other user), rather than the lifecycle user.
This configuration is stored in the “a_bpaction_run_as” attribute of the docbase
configuration object, “dm_docbase_config”. These are the valid values for the
Docbase-level a_bpaction_run_as attribute:
- session_user (default) – Causes the lifecycle code to execute on behalf of
the current session user, the user who initiated the state transition. - superuser – Causes lifecycle code to execute on behalf of a superuser,
the docbase owner - lifecycle_owner – Causes lifecycle code to execute on behalf of the lifecycle owner,
the owner_name of the lifecycle’s dm_policy object. <user name>
– Causes lifecycle code to execute on behalf of any designated user.
Populate with the docbase user’s user_name.
You can configure lifecycle actions to run on behalf of the desired user by executing a DQL
command similar the one shown below:
update dm_docbase_config OBJECT set a_bpaction_run_as = ‘superuser’;
Important Note: Configuring lifecycle actions to operate on behalf of another user can have the
unintended side-effect of setting the document’s ‘Modified By’ attribute (r_modifier) to the name
of the designated user (a_bpaction_run_as), rather than the user who initiated the lifecycle
state transition.
Writing Custom Lifecycle Code
In order for custom code to be callable from lifecycle states, it must implement the correct
interface(s). A single Java class can implement multiple interfaces and thus handle multiple
events in the state transition. A single lifecycle state transition has three events that
can call out to your custom code: pre-entry, action, and post-entry. The method signatures
for each event are different, but their parameters are all the same:
- IDfSysObject – This is the repository object, usually a document, which is being
transitioned in the lifecycle. - userName – This is either the user who initiated the lifecycle state transition
or another pre-designated user that the lifecycle is configured to operate on
behalf of (See Permissions for Lifecycles in this article). - targetState – This is the descriptive name of the lifecycle state being
entered (not the current state index). For example, “Draft”.
The method signatures for the lifecycle state-transition events are described below.
Pre-Entry
A pre-entry lifecycle module must implement the IDfLifecycleUserEntryCriteria interface.
This interface has a single public method, userEntryCriteria, which returns a Boolean value.
A return value of True allows the object to enter the lifecycle state. A return value
of False or an exception will prevent the object from entering the target lifecycle state.
Interface:IDfLifecycleUserEntryCriteria
Method Signature:
public boolean userEntryCriteria ( IDfSysObject obj,
String userName,
String targetState) throws DfException {
return true;
}
Action
A lifecycle action module must implement the IDfLifecycleUserAction interface. This interface
has a single public void method, userAction, in which you can perform custom actions on the object
that is entering the lifecycle state. Errors raised in custom lifecycle action code will not
prevent an object from entering the lifecycle state.
Interface:IDfLifecycleUserAction
Method Signature:
public boolean userAction ( IDfSysObject obj,
String userName,
String targetState) throws DfException {
}
Post-Entry
A post-entry lifecycle module must implement the IDfLifecycleUserPostProcessing interface.
This interface has a single public void method, userPostProcessing, in which you can perform
custom actions on the object after it has entered the lifecycle state. Errors raised in
custom lifecycle post-entry code will not prevent an object from entering the lifecycle state.
Interface:IDfLifecycleUserPostProcessing
Method Signature:
public boolean userPostProcessing ( IDfSysObject obj,
String userName,
String targetState) throws DfException {
}
Getting a User Session
Code in lifecycle modules can retrieve a user session from the SysObject parameter.
IDfSession user Session = obj.getSession();
If the docbase is configured to run lifecycle methods as a special user/superuser,
then a session will be returned for that specific user. Otherwise, the command will
return a session for the user who initiated the lifecycle state transition (this is
the default behavior).
Creating a Lifecycle Code Module
After you have created a Java class that implements one or more of the lifecycle state
transition interfaces, add it to its own jar file and copy the jar file to a location
where it can be accessed via DAB. Note that lifecycle modules do NOT require you to
package separate interface and implementation classes in your jar file (as is the
case for TBO and SBO modules). Only the implementation class is necessary for
lifecycle modules.
Before your custom code can be called from a lifecycle it must first be added to the Docapp
(and docbase) as a custom code module. This can be done via the Insert->Module feature
of Documentum Application Builder (DAB) client.
Figure 3:
Creating a new code module in DAB
Important Note: You will have to manually type the word “Lifecycle” into the module
type drop-down box because it will not appear as an option.
When you check-in the new module in the Docapp, a new folder of type dmc_module is
created in the Lifecycle Modules area of the docbase containing the module’s jar file(s)
as dmc_jar objects.
Figure 4:
A custom lifecycle module stored in the docbase
Configuring a Lifecycle to Use Custom Modules
Now that the custom code has been packaged and added to the docapp as a lifecycle module,
the lifecycle states can be configured to call the module.
-
- Edit the state of your lifecycle from which you want to call a custom module.
- Select the tab corresponding to the transition event that should call your custom
code (entry Criteria, Actions, or PostChange) - Use the Module Name control to browse to and select your custom jar file.
Figure 5:
Configuring the lifecycle state to call a custom pre-entry module in DAB
- Don’t forget to validate and re-install the lifecycle after modifications by
pressing the “Validate” and “Install” button on the “General” tab. Note that this
button changes its label each time you press it.
Note: If your module does not include a class that implements the corresponding interface,
for the transition event then your module will not appear as a choice for the state transition in DAB.
Handling Exceptions
All of the lifecycle state transition interfaces throw DfException. If custom code within your
module throws other types of errors, it’s a good idea to roll them up to a DfException and set the
error message string to something meaningful. The custom error message will be displayed to the
transition initiator in WDK clients. The code snippet below shows how to create a DfException,
set its error message string, and throw it.
DfException ex = new DfException();
ex.setMessage("Hello this is a problem");
throw ex;
Best Practice: In pre-entry methods, when an object fails validation, it’s a good idea to
raise a DfException and set the error message string to something specific and informative,
rather than to simply return False. If you only return a value of False, then a generic
error message is returned to the user in the WDK client that gives no indication as to
what specifically caused the validation issue.
Figure 6:
A pre-entry validation error showing a custom error message
Wrap-up
Custom lifecycle modules are a very powerful tool for Documentum developers in that
they allow the addition of custom business logic to lifecycle states. Documentum’s
out-of-the-box lifecycle functionality is extensive and often covers a solution’s
business requirements solely through configuration. However, business requirements
sometimes fall outside of the capabilities of the standard Documentum lifecycle
feature set. This is where custom lifecycle modules can really save the day!