Wednesday 25 February 2015

AEM Event Handling and filtering

AEM provides multiple ways of handling events to cater to changes in the repository. Some of the approaches are :

  • JCR level with observation manager.
  • At the Sling level with event handlers and jobs.
  • At the AEM level with work­flows & launchers.
  • Java classes implementing Runnable interface and scheduled using cron expressions.

These are already covered in detail on this blog http://blogs.adobe.com/experiencedelivers/experience-management/event_handling_incq
I want to add example of Event Filtering which I had to figure out as there was no example available.

Event filtering via sling event handling:

One of the major problem faced in event handling is that handler gets called for all events irrespective of the path or resource for which you want to handle the change. e.g. if you are listening to "org/apache/sling/api/resource/Resource/*" (* handles ADDED/REMOVED/CHANGED) topics your handler gets called for all the events be it your application or framework raised event.
You have to manually check if the path property matches the one you are interested into. Sling event has a property "event.filter" using which you can handle only those events which matches specific path and resourceType.

A sample component declaration with filter is listed below

 @Component(enabled = true, immediate = true)  
 @Service(value = EventHandler.class)  
 @Properties({  
     @Property(name = EventConstants.EVENT_TOPIC, value = {"org/apache/sling/api/resource/Resource/*"}),  
     @Property(name = EventConstants.EVENT_FILTER,value = "(&(path=/content/myapp/*/menu/*)(resourceType=myapp/components/page/product))") })  
Here property EventConstants.EVENT_FILTER defines the filter using path and resourceType. Event Admin checks the path to match the one defined above and then calls the event handler.
* is the wild-card character and will match any sub paths below /content/myapp/ , "&" is AND operation saying filter also needs to match the resourceType to be fit for processing by event handler.

Eventhandler black listing:

Eventadmin has a time-out (defaults to 5 secs) defined for a event to be handled, any handler taking time beyond this gets blacklisted, to handle this one should use sling Jobs which allows handling task via JobProcessor implementation. So basically you call JobUtil.processJob(..) and allow the event to be processed in the process(Event event) method.
 sample code:  
 @Component(enabled = true, immediate = true,metatype=true)  
 @Service(value = EventHandler.class)  
 @Properties({  
     @Property(name = EventConstants.EVENT_TOPIC, value = {"org/apache/sling/api/resource/Resource/*"}),  
     @Property(name = EventConstants.EVENT_FILTER,value = "(&(path=/content/myapp/*/menu/*)(resourceType=myapp/components/page/product))") })       
 public class Test implements EventHandler, JobProcessor {  
   private static final Logger LOG = LoggerFactory.getLogger(Test.class);  
   @Override  
   public void handleEvent(Event event) {  
     LOG.info("Handled event");  
     JobUtil.processJob(event, this);  
   }  
   @Override  
   public boolean process(Event job) {  
     LOG.info("Do some complex logic here");  
     return true;  
   }  
 }  
Pros and cons of previously mentioned approaches:

ApproachProsCons
Observation manager Low level and provides everything that you may need to handle update/delete/removal of nodes
You need to take care of registering/unregistering your handler.
You write more lines of code.
Maintain always on admin session to repository
If not implemented well It can cause performance nightmare.
Sling event handler Concise and easy to implement. Chances of blacklisting (configurable via Event admin in osgi console).
Can cause performance issues.
You have to figure out what Event and properties are provided.
Work-flows Well documented.
Allows Configuration and start/stop via workflow console.
Best suited for tasks which require human intervention.
Learning curve.
You need to write more code and configurations.
Scheduled java classes Allows you to do things which are not supported through previous mentioned approaches.
If you know what you want to do and can bear time gap of event occurrence vs handling.
You have to make sure that you are not creating bottlenecks.

To view the current snapshot of events you should check the events logs at http://localhost:4502/system/console/events.

3 comments:

  1. Is there a way to use the event.filter for @Property(name = EventConstants.EVENT_TOPIC, value = ReplicationAction.EVENT_TOPIC)

    ReplyDelete
  2. Hi

    I also have the same query. Could you let me know if you were able to do this.

    ReplyDelete
  3. It is possible apparently. If you are looking to filter based on path use multiple (e.g. paths instead of path)

    like so `paths=/content/geomertrixx/*` something like that.


    @Property(name = EventConstants.EVENT_TOPIC, value = ReplicationAction.EVENT_TOPIC),
    @Property(name=EventConstants.EVENT_FILTER, value = “(&(|(” + ReplicationAction.PROPERTY_TYPE + “=DEACTIVATE)(” + ReplicationAction.PROPERTY_TYPE + “=DELETE))(paths=/content/geometrixx/*))“

    ReplyDelete