Unclosed resource resolver complete guide AEM


Problem Statement:

Unclosed resource resolver issue is causing performance impact on the AEM environment

It’s hard to debug and fix the resolver issues

Best practices to close the resolver


Requirement:

Provide all the best practices to close the unclosed resource resolve and in turn improve environment stability.


Introduction:

Usually, we create/open a resource resolver:

  1. Servlets
  2. OSGi Services
  3. Workflows
  4. Schedulers

Purpose of service user based resolver.

To get access to the paths which is blocked for every one group, make changes and commit changes etc.

Creating Resource resolver instance:

This feature was released with Java 7 and the try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.

So basically, creating the resolver within the calling class like below will auto close:

try (ResourceResolver resourceResolver = resolverFactory.getServiceResourceResolver(Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "{service user name}"))){
    //business logic
}

And it is highly recommended to use the following APIs as well:

  1. FileReader
  2. ZipFile
  3. BufferedWriter

etc.

Closing the resolver for Queries:

Whenever we come across queries, we need to close the resolver manually even after using the try resource approach:

Try resource Approach:

ResourceResolver resolverResolverLeakingReference = null;
try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "{service user name}"))) {
    session = resolver.adaptTo(Session.class);
    queryBuilder = resolver.adaptTo(QueryBuilder.class);
    
    PredicateGroup predicateGroup = null;
    try {
        predicateGroup = PredicateGroup.create(map);
    } catch (VerifyError ex) {
        predicateGroup = new PredicateGroup();
    }

    Query query = queryBuilder.createQuery(predicateGroup, session);
    SearchResult result = query.getResult();
    for (final Hit hit : result.getHits()) {
        Resource resource = hit.getResource();            	
        if(resolverResolverLeakingReference == null){
            resolverResolverLeakingReference =  resource.getResourceResolver();
        }    
        //buisness logic                            
    }
} catch (Exception e) {
    log.error("Error fetching locations count results", e);
} finally {
    if (resolverResolverLeakingReference != null) {
        // Always Close the leaking QueryBuilder resourceResolver.
        resolverResolverLeakingReference.close();    
    }        
}

Requet.getResolver Approach:

ResourceResolver leakingResourceResolverReference = null;
try {
    Session session = request.getResourceResolver().adaptTo(Session.class);
    Map<String, String> map = new HashMap<>();
    //map put goes here
        Query query = queryBuilder.createQuery(PredicateGroup.create(map), session);
        SearchResult result = query.getResult();
        for (Hit hit : result.getHits()) {
            if(leakingResourceResolverReference == null) {
                leakingResourceResolverReference =  hit.getResource().getResourceResolver();
            }		         
            //buisness logic
        }    
} catch(Exception e) {
    log.info("error::{}",e.getMessage());
} finally {
    if(leakingResourceResolverReference != null){
        leakingResourceResolverReference.close();
    }
}

Or you can also follow this:

SearchResult result;
try {
    Session session = request.getResourceResolver().adaptTo(Session.class);
    Map<String, String> map = new HashMap<>();
    //map put goes here
        Query query = queryBuilder.createQuery(PredicateGroup.create(map), session);
        result = query.getResult();
        for (Hit hit : result.getHits()) {    
            //buisness logic
        }    
} catch(Exception e) {
    log.info("error::{}",e.getMessage());
} finally {
    Iterator<Resource> resources = result.getResources();
    if (resources.hasNext()) {
        resources.next().getResourceResolver().close();
    }
}

Avoid query builder and use SQL2:

try (ResourceResolver resourceResolver = resolverFactory.getServiceResourceResolver(Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "{service user name}"))){
    String querySt = "SELECT * FROM [nt:base] ";		
    Iterator<Resource> results = resourceResolver.findResources(querySt, "JCR-SQL2");
    //buisness logc
}

Use Java Streams:

You can follow the below blog to use Java stream for executing queries in AEM

AEM Query builder using Java streams

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s