Event han­dling can be done in a lot of dif­fer­ent ways with­in CQ, that all have their cost, their impact, and their ben­e­fits.

5 ways of doing so are detailed here :

- At the JCR lev­el with obser­va­tion

- At the Sling lev­el with event han­dlers and jobs

- At the CQ lev­el with work­flows & launch­ers

- Par­tic­u­lar case of sched­uled events

- Par­tic­u­lar case of POST to the repos­i­to­ry


JCR Observ­er

is the low­est-lev­el event han­dling in CQ. As its name indi­cates it, is at JCR lev­el and allows to lis­ten to JCR-lev­el events, gath­ered in sets (cor­re­spond­ing to per­sis­tence trans­ac­tions). javax.jcr.observation.Event lists fol­low­ing types:

·       Event.NODE_ADDED
·       Event.NODE_MOVED
·       Event.NODE_REMOVED
·       Event.PERSIST
·       Event.PROPERTY_ADDED

In order to lis­ten to such an event, you have to:

·       Man­age a live ses­sion through which you will lis­ten to the repos­i­to­ry
·       Decide how and when you lis­ten to it.
·       Imple­ment the han­dler that receive all JCR events fil­tered by your def­i­n­i­tion.

A typ­i­cal usage is

public class ExampleObservation implements EventListener {
 Logger log = LoggerFactory.getLogger(this.getClass());
 private Session adminSession;
 SlingRepository repository;
 public void activate(ComponentContext context) throws Exception {
 log.info("activating ExampleObservation");
 try {
   adminSession = repository.loginAdministrative(null);
      this, //handler
      Event.PROPERTY_ADDED|Event.NODE_ADDED, //binary combination of event types
      "/apps/example", //path
      true, //is Deep?
      null, //uuids filter
      null, //nodetypes filter
  } catch (RepositoryException e){
  log.error("unable to register session",e);
  throw new Exception(e);
public void deactivate(){
 if (adminSession != null){
public void onEvent(EventIterator eventIterator) {
  try {
    while (eventIterator.hasNext()){
      log.info("something has been added : {}", eventIterator.nextEvent().getPath());
   } catch(RepositoryException e){
   log.error("Error while treating events",e);


·       In this exam­ple we don’t care about fil­ter­ing results, but in real life you’d sure­ly do.

·       In lot of cas­es using JCR Observ­er is not clus­ter aware: If the same code is on every clus­ter node, every change to the clus­ter farm will trig­ger the treat­ment N times.

·       Please dou­ble-check the per­for­mances of your event han­dling in case it’s called –very – often.


The low lev­el aspect of the JCR observ­er allows the devel­op­er to do more or less what­ev­er he wants, but can bring lot of addi­tion­al devel­op­ment just for per­for­mances, usabil­i­ty and porta­bil­i­ty. So please use it only when need­ed.

Sling offers with­in CQ an appli­ca­tion lev­el mod­el for han­dling events, which is called “Sling event­ing sup­port”. You can find its deploy­ment in usage in Felix con­sole through web con­sole plu­g­ins, bun­dles and com­po­nents. The events processed will be those sent by appli­ca­tions, and they will bring appli­ca­tion relat­ed infor­ma­tion. An event must have a top­ic, that will con­sti­tute the “queue” for those events, to which lis­ten­ers can reg­is­ter.

Here is a sim­ple exam­ple:

Application level event sending

EventAdmin eventAdmin;
public void createExample(…) throws RepositoryException {
 final Dictionary<String,Object> properties = new Hashtable();
 properties.put(Example.PN_NAME, name);
 Event event = new Event(Example.EVENT_TOPIC, properties);

Note here that eventAdmin.postEvent is the asyn­chro­nous send­ing, you can syn­chro­nous­ly send your event by using sendE­vent API (with the per­for­mance risks it can imply).


This han­dler is lis­ten­ing to the kind of event above, plus Repli­ca­tion events

@Property(name="event.topics",value= {ReplicationAction.EVENT_TOPIC, Example.EVENT_TOPIC})
public class ExampleEventHandler implements EventHandler {
 Logger log = LoggerFactory.getLogger(this.getClass());
 public void handleEvent(Event event) {
  if (event.getTopic().equals(Example.EVENT_TOPIC)){
   log.info("Example {}, with description {} has been created...",event.getProperty(Example.PN_NAME),event.getProperty(Example.PN_DESC));
  } else if (event.getTopic().equals(ReplicationAction.EVENT_TOPIC)){
   ReplicationAction action = ReplicationAction.fromEvent(event);
   log.info("User {} has tried to replicate {}",action.getUserId(),action.getPath());

Final­ly, you can be in the sit­u­a­tion where your appli­ca­tion wants the guar­an­tee of hav­ing its event processed. This is the job use case, in which you will use JobU­til and JobProces­sor.

Note that often your lis­ten­er can be also a JobProces­sor, like fol­low­ing :

public void handleEvent(org.osgi.service.event.Event event) {
 if (EventUtil.isLocal(event)) {
  JobUtil.processJob(event, this);

CQ topics you can listen to :


·       ForumEvent.Type.TopicAdded

·       ForumEvent.Type.PostAdded


·       Page Events (MSM, CRUD, …) with PageEvent.EVENT_TOPIC and PageEvent.fromEvent API

·       Repli­ca­tion event with ReplicationAction.EVENT_TOPIC and Repli­ca­tion­Ac­tion API

·       Work­flow event (you should look fol­low­ing sec­tion)

You’ll have good sta­tis­tics, mon­i­tor­ing, and con­fig­u­ra­tion at the felix lev­el about jobs and events.


This frame­work is def­i­nite­ly a good way to go for han­dling your event, some­times, it’s even worth sur­round­ing JCR Obser­va­tion with Sling Event­ing in order to man­age it more grace­ful­ly then.

Last but not the least, you can han­dle events through work­flows, with usage of the work­flow launch­er. This is plain­lydoc­u­ment­ed already, easy and fast to write, mod­u­lar, clus­ter aware.

A few cau­tions to keep in mind though:

·       There is more pro­cess­ing than in pre­vi­ous approach­es.

·       Your treat­ment will be archived and vis­i­ble among oth­er treat­ments.

Two good rea­sons to use work­flows are :

·       if of course there’s a human step at one stage of the pro­cess­ing.

·       if the event han­dling process should be fre­quent­ly cus­tomized / para­me­ter­ized by non-admin users.

In case you want to do sched­uled treat­ment, use the sling Sched­uler by doing so:

 @Property(name="scheduler.expression" ,value="0 0/10 * * * ?"),
public class ScheduledExample implements Runnable {
 Logger log = LoggerFactory.getLogger(this.getClass());
 ExampleService exampleService;
 public void run() {
  log.info("My example : {}", exampleService.sayHello());

For doc­u­men­ta­tion about scheduler.expression for­mat, you have doc­u­men­ta­tion and sam­ples here

Han­dling a POST write to the repos­i­to­ry to do pro­cess­ing at the time the data is writ­ten is a com­mon devel­op­er task.

Often we see jcr observers han­dlers while a much sim­pler approach could be done (in case the POST is actu­al­ly han­dled by the Sling­Post­Servlet, which is very often the case): a Sling­Post­Servlet post-proces­sor should be imple­ment­ed for this.