CQ coding patterns: Sling vs JCR (part 2)

In the last posting I showed the benefits of Sling regarding resource access over plain JCR. But not only in resource access both frameworks offer similar functionality, but also in the important area of listening to changes in the repository. So today I want to compare JCR observation to Sling eventing.

JCR observation is a part of the JCR specification and is a very easy way to listen for changes in the repository.

@component (immediate=true, metatype=false)
@service
class Listener implements ObservationListener {

  @Reference
  SlingRepository repo;

  Session session;
  Logger log = LoggerFactory.getLogger (Listener.class);

  @activate
  protected void activate () {
    try {
      Session adminSession = repo.loginAdministrative(null);
      session = adminSession.impersonate (new SimpleCredentials("author",new char[0]));
      adminSession.logout();
      adminSession = null;
      session.getObservationManager.addEventListener( this, // listener
        NODE_CREATED|NODE_DELETED|NODE_MOVED, // eventTypes
        "/", // absPath
        true, // isDeep
        null, // uuid
        null, //nodeTypeNames
        true // noLocal
      );
    } catch (RepositoryException e) {
      log.error ("Error while registering observation", e);
    }
  }

  @deactivate
  protected void deactivate() {
    session.getObservationManager.removeListener(this);
    session.logout();
    session = null:
  }

  private handleEvents (Events events) {
    while (events.hasNext()) {
      Event e = events.next();
      … // do here your event handling
     }
  }
}

In JCR the creation of an observation listener is straight forward, also the event listening. The observation process is tied to the lifetime of the session, which is started and stopped at activation/deactivation of this sling service. This kind of implementation is a common pattern.
Note: Be aware of the implications of using an admin session!

  • You can read and write everywhere in the repository
  • You work with elevated rights you probably never need.

So try to avoid an adminSession. In th example above is use a session owned by the user “author” for this via impersonation.

A sling implementation of the same could look like this:

@Component (immediate = true)
@Service()
@Property (name = "event.topics", value = "/org/apache/sling/api/resource/Resource/*")
class Listener implements EventHandler {

  public void handleEvent (Event event) {
    // handle
  }
}

You see, that we don’t have to care about registering a listener and managing a session. Instead we just subscribe to some events emitted by the Sling Eventing framework. This framework is essentially implementing the OSGI event specification, and therefor you can also subscribing the very same way to various other event topics. You can check the “Event” tab in the Felix console, where you can see a list of events, which just happened in your CQ.

Some example for topics:

One disadvantage of Sling eventing is, that you cannot restrict the resource events to a certain path or user. Instead Sling registers with an admin session and publishes all observation events starting at the root node of the repository (/). You should filter quite early in your handleEvent routine for the events you are really interested in.

But: With these events you don’t geld hold of a resource. You always have you create them by this pattern:

if (event.getProperty(SlingConstants.PROPERTY_PATH) != null) {
  Resource resource = resourceResolver.getResource(event.getPath()); 
}

Also there you need to take care, that you’re using an appropriate ResourceResolver (try to avoid an admin resource resolver as well).

Also JCR observation has some benefits, if you want or need to limit the events you receive more granularly, for example by listening to changes of specific nodeTypes only or mixins.

So, as a conclusion: If you need to restrict the amount of repository events you want to process directly on API level, JCR observation is the right thing for you. Otherwise go for Sling Eventing, as it offers you also the possibility to receive many more types of events. For non-repository events it’s the only way to listen for them.

7 thoughts on “CQ coding patterns: Sling vs JCR (part 2)

  1. You can actually listen to Sling events for certain paths and even certain resource types by using filters.

     protected void activate(ComponentContext context) {
            final Dictionary listenEventsDictionary = new Hashtable();
            listenEventsDictionary.put(EventConstants.EVENT_TOPIC, TOPICS);
            final String filter = "(&(path=" + PATH + ")(resourceType=" + RESOURCE_TYPE + "))";
            listenEventsDictionary.put(EventConstants.EVENT_FILTER, filter);
            eventHandlerRegistration = context.getBundleContext().registerService(EventHandler.class.getName(), this, listenEventsDictionary);
            LOG.info("Registered for OSGi events: topics={}, filter={}", TOPICS, filter);
    }
    
    protected void deactivate() {
            if (eventHandlerRegistration != null) {
                eventHandlerRegistration.unregister();
                eventHandlerRegistration = null;
            }
    }
    

    where eventHandlerRegistration is a ServiceRegistration object.

  2. Hi Jörg,

    As you said “using Sling API we cannot restrict the access to paths or users”, but is it possible to restrict to only certain type of events like say “Replication” events.

    Basically, I would like to show list of all resources which have been replicated in a separate page(basically I want to create a replication log for a business user).

    1. Hi Rajesh,

      Thanks for your feedback. Yes, you can restrict the event topics. You can test the eventing behaviour on http://localhost:4502/system/console/events. Basically you’re interested in the topic ‘com/day/cq/replication/job/publish’ and the related ‘org/apache/sling/event/notification/job/*’ events, which indicate success or failures.
      I will try to come up with an example for monitoring the replication in the next weeks.

      Jörg

  3. Basic Question :
    Does ResourceResolver need the session to be live? Or can we just close the session? I dont find this explicitly mentioned anywhere?
    Regards,
    Deepak

Comments are closed.