You can extend Content Engine functionality by implementing one or more Java™ interfaces in the com.filenet.api.engine package. As a guide for making implementation choices, this topic provides restrictions, best practices, and feature comparisons.
For some server extensibility use cases, knowing whether to use a change preprocessor or an event action handler is not always obvious, especially if you want to modify the source object. The following table compares features of change preprocessors, which aways execute synchronously, with features of event action handlers, which you can configure to run either synchronously or asynchronously.
Change Preprocessor - synchronous | Event Action - synchronous | Event Action - asynchronous | |
---|---|---|---|
When does action handler run? (Any request to the Content Engine can trigger one or more of each server extension type.) | In transaction, before database updates have occurred. Runs first. | In transaction, after database updates have occurred. Runs second. | After transaction has committed. Runs third in a separate transaction. |
Executes before source object is saved? | yes | yes | no |
Server disables security access checks during execution? | no | yes | yes |
Gap in time between client saving source object and handler executing? | no | no | yes |
Can update source object? | yes | no | yes |
Can update SettableOnlyonCreate (SOOC) properties? | yes | no | no |
Can update other objects referenced by OVPs (object-valued properties)? | yes | yes | yes |
Can subscribe to events? | no | yes | yes |
Execution can be filtered by event and property values? | no 1 | yes | yes |
Unhandled exceptions rollback the user's transaction? | yes | yes | no |
1 Change preprocessors are invoked unconditionally. However, the source object passed to a change preprocessor action handler includes a properties collection and a set of pending actions, allowing the action handler to filter by event and property values.
To update a source object, use a change preprocessor or asynchronous event action handler, as described in "Best Practices" below.
ClassDefinition myNewDocSubclass =createDocSubclass();
Document doc = Factory.Document.createInstance(objectStore,
myNewDocSubclass.get_SymbolicName());
doc.save(RefreshMode.REFRESH);
The overall overhead of asynchronous is higher than the overhead of synchronous (because of queuing for later execution). Therefore, you should use synchronous when you can, and use asynchronous where synchronous execution is not appropriate.
In either execution mode, activity in a handler should be as brief as it can be. For long-running activity (for example, a callout to another system), consider adding information to some external queuing mechanism so that your own handler code can complete quickly.
A change preprocessor should also be used in cases when the handler must complete in time to satisfy dependencies of other applications, such as a subsequent action of a client application that relies on the successful completion of the handler. With asynchronous event handlers, there is a brief window of time (usually seconds or minutes) between persistance of the target object in one transaction and any changes applied by the asynchronous event handler in a separate transaction. Therefore, avoid an asynchronous event handler if a dependent client cannot tolerate a time period when required handler changes have not been applied.
The advantage of an asynchronous event handler is that it does not have to share JEE transaction time with the triggering action. For updating a source object, consider using an asynchronous event action handler in these situations:
Do not update the source object passed to an asynchronous handler because the source object might already have changed before the handler executes, and, therefore, the object might be obsolete.
IndependentObject sourceObj = event.get_SourceObject();
if (!sourceObj.getProperties().isPropertyPresent(PropertyNames.FOLDERS_FILED_IN))
{
Id id = event.get_SourceObjectId();
ObjectStore os = event.getObjectStore();
// Fetch the source object. By specifying null instead of a property filter,
// all properties are retrieved.
sourceObj = Factory.Document.fetchInstance(os, id, null);
}
Property prop = sourceObj.getProperties().find(PropertyNames.FOLDERS_FILED_IN);
FolderSet folders = (FolderSet) prop.getIndependentObjectSetValue();