To avoid action handler errors or performance issues, note
the following restrictions and best practices.
Restrictions
- Handlers can create additional threads to run portions of the
work that is requested of them. Such threads run outside the transaction
context of the handler call, if any, and are prohibited from making
any Content Engine API calls.
- You cannot update the source object that is passed to a synchronous event
action handler or a lifecycle action handler. It is not supported. Updating
the source object in a synchronous event action handler results in
persistence issues.
To update a source object, use a change preprocessor
or asynchronous event action handler, as described in the next section.
- In synchronous action handlers, you cannot recursively fire more
than 50 events without encountering an EVENT_INFINITE_LOOP_DETECTED exception.
- In both synchronous and asynchronous action handlers, you cannot
create metadata and then use it for instance creation. For example,
creating an instance of a newly created document subclass, as shown
in the following code snippet, fails with a BAD_CLASSID exception.
ClassDefinition myNewDocSubclass =createDocSubclass();
Document doc = Factory.Document.createInstance(objectStore,
myNewDocSubclass.get_SymbolicName());
doc.save(RefreshMode.REFRESH);
- Do not use action handler code to display GUI elements. If you
attempt to do so, the results are unpredictable and might cause problems
as serious as hanging the Content Engine server.
- GetContentEvent is a subscribable event of type RetrievalEvent,
which does not have a source object property. Therefore, an event
action handler that receives a GetContentEvent cannot
retrieve the source object from the passed-in event. It must retrieve
the persisted source object from the object store, by using the event's SourceObjectID
property.
Best Practices
- Except for custom sweep handlers, action handlers run within a Java
Platform, Enterprise Edition (Java EE) transaction. Transactions always
have a time limit, and it is a good practice to have a transaction
open for as short a time as you can. Synchronous action handlers and
change preprocessor handlers share transaction time with all other
activity in an RPC (remote procedure call), such as other updates
and action handlers. An exception rolls back the entire transaction.
By contrast, an asynchronous handler gets its own transaction.
The
overall overhead of an asynchronous handler is higher than the overhead of
a synchronous handler (because of queuing for later execution). Therefore, use
synchronous handlers when you can, and use asynchronous handlers when
synchronous execution is not appropriate.
In either execution
mode, ensure activity in a handler is as brief as possible. 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.
- You can update a source object with an asynchronous event action
handler or a change preprocessor, which runs synchronously. Because
a change preprocessor runs before the source object is persisted (unlike
an asynchronous event action handler), it is used to alter settable-only-on-create
or settable-only-on-checkin (SOOC) properties, or to validate properties
by modifying values or rolling back the entire transaction.
A change
preprocessor is also 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 persistence of
the target object in one transaction and any changes that are 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 Java EE transaction time with the triggering action. For
updating a source object, consider using an asynchronous event action
handler in these situations:
- The change preprocessor use cases that are previously described
do not apply.
- You require that the handler is invoked only by the firing of
specific events on the source object (rather than any create, update,
or delete server request that is invoked on a class instance).
- To update a source object in an asynchronous event action handler,
you must first fetch the persisted instance of the source object before
you update it. You can call the save method on the
fetched source object.
Do not update the source object that is
passed to an asynchronous handler because the source object might
already have changed before the handler runs, and, therefore, the
object might be obsolete.
- In event action handlers, lifecycle action handlers, and document
classifiers, the source object of the action that is passed to the
handler is a shallow copy of the object, which contains only the object's
scalar (non-object) properties in the object's property cache. If you attempt
to retrieve a property value that is not contained in the shallow
copy, you get an API_PROPERTY_NOT_IN_CACHE error
message. To avoid the possibility of getting this error, check for
the existence of a value in the property cache before the property
is accessed. If the value does not exist in the property cache, fetch
the persisted object. For example:
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();
- As with other action handlers, a change preprocessor also checks
for the existence of a value in the property cache. A change preprocessor
runs regardless of the type of request type to the server (create,
update, delete). For creation requests, the property cache is sparse,
containing only client-provided and default values.