Safe handling of ResourceResolvers

Just digging through my posts of the last years, I found that my last post to ResourceResolvers and JCR sessions is more than a year old. But unfortunately that does not mean, that this aspects seems widely understood; I still see a lot of improper use of these topics, when I review project code as part of my job.

But instead of explaining again and again, that you should never forget to close them, I want to introduce a different pattern, which can help you to avoid the “old pattern” of opening and closing completely. It’s a pattern, which encapsulates the opening and closing of a ResourceResolver, and your code is executed then as a Consumer or Function within. The ResourceResolver cannot leak, and you cannot do anything wrong. The only pre-requisite is Java 8, but that must not be a problem in 2020.

// does not return anything
public void withResourceResolver (Map<String,Object> authenticationInfo, Consumer<ResourceResolver> consumer) {
   try (ResourceResolver resolver = ResourceResolverFactory.getResourceResolver(authenticationInfo);) {
     consumer.accept (resolver);
   } catch (Exception e) {
     LOGGER.error ("Exception happend while opening ResourceResolver",e);
   }
}

Same is possible with a function to return a value

// return a value from the lambda
public <T> T withResourceResolver (Map<String,Object> authenticationInfo, Function<ResourceResolver,T> function, T defaultValue) {
   try (ResourceResolver resolver = ResourceResolverFactory.getResourceResolver(authenticationInfo);) {
     return function.apply(resolver);
   } catch (Exception e) {
     LOGGER.error ("Exception happend while opening ResourceResolver",e);
   }
   return defaultValue;
}

// convenience function
public <T> T withResourceResolver (Map<String,Object> authenticationInfo, Function<ResourceResolver,T> function) {
   return withSession(authenticationInfo,function, null;)
}

So if you are not familiar with the functional style of Java 8, some small examples how to use these methods:

Map<String,Object> authenticationInfo = …
withResourceResolver(authenticationInfo, resolver -> {
   Resource res = resolver.getResource("/");
   // do something more useful, but return nothing 
});

// return a value from the lambda 
Map<String,Object> authenticationInfo = …
String result = withResourceResolver(authenticationInfo, resolver -> {
   Resource res = resolver.getResource("/");
   return res.getPath();
});

As you can easily see, you don’t need to deal anymore with the lifecycle of ResourceResolvers anymore. And if your authenticationInfo map is always the same, you can even hardcode it within the withSession() methods, so the only parameter remains the consumer or the function.

Try-with-resource or “I will never forget to close a resource resolver”

In Java 7 the idiom “try-with-resource”  has been introduced in the java world. It helps to never forget to close a resource. And since Sling9 (roughly 2016) the ResourceResolver interface implements the AutoCloseable marker interface, so the try-with-resource idiom can be used.

That means, that you can and should use this approach:

try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(…)) {
// do something with the resolver
// no need to close it explicitly, it's closed automatically
}

With this approach you omit the otherwise obligatory finally block to close the resource resolver (which can be forgotten …).

This approach helps to reduce boilerplate code and eliminates some potential for errors. If you are developing for AEM 6.2 and newer, you should be able to use it.

ResourceResolvers and Sessions — “you open it, you close it”

I have already written about how to use resource resolvers and JCR sessions; the basic pattern to remember is always “you open it; you close it” (2nd rule).

While this stanza seems to be quite commons sense, the question is always: When is a session or a resource resolver opened/created? What API calls are responsible for it? Let me outline this today.

API calls which open a JCR resource:

API calls, which create a Sling ResourceResolver

These are the only API calls which open a JCR Session or a Sling ResourceResolver. And whenever you use one of these, you are responsible to close them as well.

And as corollary to this rule: if you have other methods or APIs which return a ResourceResolver or Session: Do not close these.

Some examples:

Session jcrSession = resourceResolver.adaptTo(Session.clase);

This just exposes the internal JCR Session of the ResourceResolver and because it’s not using one of the above APIs: Do not close this session! It’s closed automatically when you close the resource resolver.

Session adminSession = slingRepository.loginAdministrative(null);
Map authInfo = new HashMap();
authInfo.put(org.apache.sling.jcr.resource.api.JcrResourceConstants.AUTHENTICATION_INFO_SESSION, session);
ResourceResolver adminResourceResolver = resolverFactory.getResourceResolver(authInfo);

This code creates a resource resolver which wraps an already existing JCR Session. You have to close both adminSession and adminResourceResolver, because you created them both using the above mentioned API calls.