Table of Contents
Events and event listeners have been introduced at the Nuxeo core level to allow pluggable behaviours when managing documents (or any kinds of objects of the site).
Whenever an event happens (document creation, document modification, relation creation, etc...), an event is sent to the event service that dispatches the notification to its listeners. Listeners can perform whatever action when receiving an event.
A core event has a source which is usually the document model currently being manipulated. It can also store the event identifier, that gives information about the kind of event that is happening, as well as the principal connected when performing the operation, an attached comment, the event category, etc..
Events sent to the event service have to follow the
org.nuxeo.ecm.core.api.event.CoreEvent
interface.
A core event listener has a name, an order, and may have a set of event identifiers it is supposed to react to. Its definition also contains the operations it has to execute when receiving an interesting event.
Event listeners have to follow the
org.nuxeo.ecm.core.listener.EventListener
interface.
Several event listeners exist by default in the nuxeo platform, for instance:
DublincoreListener
: it listens to
document creation/modification events and sets some dublincore
metadata accordingly (date of creation, date of last modification,
document contributors...)
DocUidGeneratorListener
: it listens to
document creation events and adds an identifier to the document if
an uid pattern has been defined for this document type.
DocVersioningListener
: it listens to
document versioning change events and changes the document version
numbers accordingly.
Event listeners can be plugged using extension points. Here are some examples of event listeners registration.
<?xml version="1.0"?> <component name="DublinCoreStorageService" version="1.0.0"> <extension target="org.nuxeo.ecm.core.listener.CoreEventListenerService" point="listener"> <listener name="dclistener" class="org.nuxeo.ecm.platform.dublincore.listener.DublinCoreListener" order="120" /> </extension> </component>
Example 8.1. DublincoreListener registration sample
<?xml version="1.0"?> <component name="org.nuxeo.ecm.platform.uidgen.service.UIDGeneratorService"> <extension target="org.nuxeo.ecm.core.listener.CoreEventListenerService" point="listener"> <listener name="uidlistener" class="org.nuxeo.ecm.platform.uidgen.corelistener.DocUIDGeneratorListener" order="10"> <eventId>documentCreated</eventId> </listener> </extension> </component>
Example 8.2. UIDGenerator listener registration sample with event filtering
The only thing needed to add an event listener is to declare
its name and its class. Sometimes the order in which listeners are called
matters so an integer order can be set to control it. A filtering on event
ids can be done when registering it too, though the notification method
could handle it too.
For instance, the UIDgenerator service will only be notified when the event service receives a document creation event.
Since release of Nuxeo EP version 5.0 M3, events involving documents send the document model as source of the event. They used to send the document itself, which was wrong and has been changed in a compatible way.
Old school event listeners should still work ok for now, but should be migrated soon as the compatibility may introduce bugs and will be removed shortly.
To migrate your event listener, make it implement the empty
interface
org.nuxeo.ecm.core.listener.DocumentModelEventListener
,
and make it deal with a DocumentModel
instead of a
Document
as event source.
If your event listener does not care about the source, or the event it deals with is not a document, you do not have to do anything.
To add an event, you have to create it and then notify listeners passing the even to the listener service. Here is a sample code on how to do it:
CoreEvent coreEvent = new CoreEventImpl(eventId, source, options, getPrincipal(), category, comment); CoreEventListenerService service = NXCore.getCoreEventListenerService(); if (service != null) { service.notifyEventListeners(coreEvent); } else { log.error("Error when notifying core event"); }
Events that are fired at the core level are forwarded to a JMS topic called NXPMessage.
This forwarding is done by a dedicated CoreEventListener (called
JMSEventListener contributed by the
nuxeo-platform-events-core
bundle).
In order to be sure that when an JMS event is received the associated DocumentModel is available, all document oriented messages that may occur at core level are forwarded to the JMS topic when the session repository is saved (ie: when data is committed).
In some cases, depending on own the Core API is used, some messages can be duplicated within the same transaction (like modifying several times the same document), the JMSEventListener marks all duplicated messages before sending them to JMS, its JMS messages receiver to choose to process or not the duplicated messages.
During the forwarding on the JMS Topic, the coreEvents are converted to EventMessage. The main difference is that the EventMessage does not contains the DocumentData (ie: all schemas and fields are unloaded), this is done in order to avoid overloading JMS.
The simplest way to add a JMS message listener is simply to define a Message Driven Bean that is bound to the NXPMessage Topic.
Here is a simple example a the definition of such a MDB :
@MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/NXPMessages"), @ActivationConfigProperty(propertyName = "providerAdapterJNDI", propertyValue = "java:/NXCoreEventsProvider"), @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") }) @TransactionManagement(TransactionManagementType.CONTAINER) public class NXAuditMessageListener implements MessageListener { private static final Log log = LogFactory.getLog(NXAuditMessageListener.class); @TransactionAttribute(TransactionAttributeType.REQUIRED) public void onMessage(Message message) { try { final Serializable obj = ((ObjectMessage) message).getObject(); if (!(obj instanceof DocumentMessage)) { log.debug("Not a DocumentMessage instance embedded ignoring."); return; } DocumentMessage docMessage = (DocumentMessage) obj; String eventId = docMessage.getEventId(); log.debug("Received a message with eventId: " + eventId); ...
The DocumentMessage
is a subclass of the
DocumentModel
.
An important point to remember is that the MDB is executed asynchronously in a dedicated thread:
there is no JAAS Session established: you can not access the repository without this
the DocumentMessage
is not bound to an
existing CoreSession
: you can not use the
DocumenMessage
to do lazy loading (ie:
DocumentMessage.getProperty()
)
So, in order to extract some document oriented properties of the document associated to the event, you must:
Establish a JAAS Session
get a connected DocumentModel
using the
DocumentRef
provided by the
DocumentMessage
Here is a code sample for this:
LoginContext lc; CoreSession session; String repositoryName = docMessage.getRepositoryName(); try { log.debug("trying to connect to ECM platform"); lc = Framework.login(); session = Framework.getService(RepositoryManager.class).getRepository(repositoryName).open(); DocumentModel connectedDoc = session.getDocument(docMessage.getRef()); ... } finally { if (session != null) CoreInstance.getInstance().close(session.getSessionId()) if (lc != null) lc.logout(); }