CQ development patterns – Sling ResourceResolver and JCR sessions

When you work with CQ, you will find Resources and (to a lesser extent) Nodes everywhere. Many services work on the JCR repository and use the basic JCR API with its nodes and properties abstraction. Others work with Sling Resources and high level objects like Pages and DAM assets.

So, unless you are restricted to the presentation layer and component implementations, you should know the basics how to deal with JCR sessions and Sling ResourceResolver. Because that’s the basic tooling you should be able to handle. And it isn’t that hard to work with, in “Sling vs JCR” I already described how you can access data stored in the repo and work it, both on JCR and sling level.

Some words to the Sling resource resolver: Although the notion of Sling resources goes beyond the scope of a JCR repository, we have in many CQ projects a 1:1 match between a Sling resource and a JCR node. Just simple because the JcrResourceProvider is the one mounted to the root of your Sling resource tree. And in that case, whenever you open a ResourceResolver, it’s a 100% chance that you will open a JCR session as well, behind the scenes. And for the same reason you should call “close()” on every resolver resolver if you don’t need it anymore: Internally the corresponding session is logged out then.For this reasons many patterns we have for JCR sessions can also be applied to Sling ResourceResolvers.

So today I want to present some patterns or best-practices how you should work with Sling resource resolvers and JCR sessions.

1st rule: Session leaks = memory leaks
Whenever you open a JCR session, there is a reference to the JCR repository object; this link prevents the session object from being garbage collected even if you drop all references to that session object. Every session will consume a little bit of memory unless the logout() method is called explicitly. If you omit this call and create lots of sessions, you risk an out-of-memory exception by your JVM, which terminates the CQ instance. A single leaked session isn’t a problem, but if you have hundreds or thousands of leaked sessions, it might turn into a problem. Gladly you will find lot’s of warnings in CQ’s error.log, so it isn’t that hard to spot them.

A simple pattern to avoid such a situation is to limit to scope of a JCR session to the scope of a java method or OSGI service/component. At the beginning create the session and the end terminate it.

In a method scope this could like this:

@Reference
SlingRepository repo;

private void doSomething() {
  Session session = null;
  try {
    session = repo.loginAdministrative();
    ...
  } catch (RepositoryException e) {
    // log the exception
  } finally {
    if (session != null && session.isLive() {
      session.logout();
    }
  }
}

This pattern will always cleanup the session and is therefor guaranteed not leak any memory due to sessions.

In many cases you need a single session during the lifetime of a service or component. If you have that requirement, you can use this pattern:

@Component(…)
public class UsesSession {

  @Reference
  SlingRepository repo;

  Session session;

  @Activate
  protected void activate() {
    session = null;
    try {
      session = repo.loginAdministrative();
      …
    } catch (RepositoryException e) {
      // log the exception
    }
  }

  @Deactivate
  protected void deactivate() {
    if (session != null && session.isLive()) {
      session.logout();
    }
  }

}

This approach makes sure, that after the stop of the component the session is closed, and no session leaks occurs.

2nd pattern: Ownership: You open it — you close it.
The basic rule of ownership is: If you open a JCR session of a Sling ResourceResolver, you are also responsible for closing it. On the other hand: If you get passed a ResourceResolver or a Session object, do not call logout() or close() on it. This works closely together with the 1st pattern, as it clearly makes sure, that only a single instance should be responsible for the lifecycle operations of a session or a resource resolver.

3rd rule: Prefer the ResourceResolver over the JCR Session.
The sling resource resolver is a very powerful concept, and in many cases you should prefer it over the JCR session; I already wrote in “JCR vs Sling” about this topic, so let me just repeat the main advantage in short: adaptTo() to high-level business concepts and objects.

4th rule: Do not share nodes, resources and pages between threads and requests.
You might run into the case, that you need to share data between multiple threads or requests. A simple example for this is HTTP-Session or a cache which stores data you need to share between multiple or all incoming requests.
In such cases do not store nodes, pages or DAM assets or any other object based on a node or a resource in that cache. Each of these objects belongs to a certain session (or resource resolver instance), and they are not valid anymore if this session is logged out or if the resource resolver is closed; if you still try to execute methods on them, you will run into exceptions.

If you want to share such data, share objects which are not bound to any Session. So instead of putting pages in there, put the path to the page there instead. If you need the page again then, just use your own ResourceResolver, resolve it to a resource and adapt it to a page. You still can run into access control issues, if the second resources resolver does not see that node due to ACL constraints. In such a case you should think about your application design.

Update (2018-10-26): 5th rule: Adapting a ResourceResolver to a session object does not create a new session!

When you have an already open ResourceResolver, adapting it to a Session.class will just expose the internally used JCR session; it will not create a new Session for it. Therefor you must not close this session.