Expand my Community achievements bar.

SOLVED

Closing ResourceResolver and Session

Avatar

Level 4

Hi All,

In order to avoid the below error, I am creating resourceresolver and session at every place(all methods) and closing it at the end of method in finally block.. Sample piece of code for creating and closing resourceresolver and session in single method

I am doing lot of operations(remove node, add/modify/delete property under the node) in 30 different methods. Whether it will cause any performance issue if we creating and closing resourceresolver and session at 30 methods. 

Error:

01.01.2016 09:21:56.916 *WARN* [0:0:0:0:0:0:0:1 [1424679716845] GET /content/geometrixx/en/services.html HTTP/1.0] org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate Attempt to perform hasProperty while another thread is concurrently reading from session-494. Blocking until the other thread is finished using this session. Please review your code to avoid concurrent use of a session.

java.lang.Exception: Stack trace of concurrent access to session-494

at org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.perform(SessionDelegate.java:276)

at org.apache.jackrabbit.oak.jcr.session.ItemImpl.perform(ItemImpl.java:113)

at org.apache.jackrabbit.oak.jcr.session.NodeImpl.hasProperty(NodeImpl.java:812)

at org.apache.sling.jcr.resource.JcrPropertyMap.read(JcrPropertyMap.java:350)

 

Sample piece of code :

ResourceResolver resolver = null;
Session session = null;

        try {

            resolver = resolverFactory.getServiceResourceResolver(null);

            session = resolver.adaptTo(Session.class);

            Node test= session.getNode("/content/dam");

           updateproperty(test);

            session.save();

            }

        } catch (Exception e) {

            Logging exception

        } finally {

            if (session != null && session.isLive()) {

                session.logout();

            }

        if (resolver != null && resolver.isLive()) {

            resolver.close();

        }

        }

1 Accepted Solution

Avatar

Correct answer by
Community Advisor
15 Replies

Avatar

Employee

Make sure that these session and resolver variables are also declared locally.

Personally I tend to avoid to 'create' sessions, but use the one attached from the request/user.

Also using ModifiableValueMap will make things easier.

Avatar

Level 10

As @Jitendra mentioned, it really depends on how and what are you doing with session. Best practice is to create and close the session where ever necessary instead of creating it globally !

Avatar

Level 4

Thanks for your reply..

Scenario :

Copying child nodes from particular node(/content/old/node1) and replacing child nodes under path /content/new/node1.Also doing add/modify/delete property under the node /content/new/node1 and its child nodes.
Similarly (/content/old/node2) to (/content/new/node2)
(/content/old/node3) to (/content/new/node3)
(/content/old/node4) to (/content/new/node4)

I am doing the above four operation at same time(creating job for each process /content/old/node1 to /content/new/node1). Four job is running parallel.

Initial call came to job class and it is directing to job service. In job service, I created resourceresolver 
from resourceresolverfactory(org.apache.sling.api.resource) and made resourceresolver as global variable for creating session at various private lower level methods.

Example:

Private ResourceResolver resolver

Class Jobservice(){
public void mainmethod(){
resolver = resourceresolverfactory.getServiceResourceResolver(null);
String path = "/content/old/node1";
lowerMethod1(path)
}

private void lowerMethod1(String path){
Session session= resolver.adapt(Session.class);
Node destinationNode = session.getNode("/content/new/node1");
deleteAllCHildNodes(destinationNode);
updateProperty(destinationNode);
}

private void deleteAllCHildNodes(Node destNode){
Session session= resolver.adapt(Session.class);
 ---deleting all childnodes under destNode----
 session.save();
}

Likewise I am doing different operation under /content/new/node1. I was getting concurrent access error and invaliditemstateexception(item doesi not exist anymore) at the different places in my code 
because four jobs are running parallel and performing operation under the respective node. I came to know that sessions are 
opening long time by google search(https://cqdump.wordpress.com/2015/03/02/aem-scaling-patterns-avoid-shared-sessions/) abt this issue. 
Also changes done for node1 in session1(first job) will not be available in session2(opened in second job). 

In order to avoid this error, I am creating resourceresolver and session at lower level methods and closing at the end of method itself(Session is opening for short duration) as below

Class Jobservice(){
public void mainmethod(){
ResourceResolver resolver = null;
Session session = null;
try {
    resolver = resolverFactory.getServiceResourceResolver(null);
    session = resolver.adaptTo(Session.class);
    String path = "/content/old/node1";
    lowerMethod1(path);
} catch (Exception e) {
     Logging exception
} finally {

            if (session != null && session.isLive()) {
            session.logout(); 
            }
        if (resolver != null && resolver.isLive()) {
        resolver.close();
        }
        }
}

private void lowerMethod1(String path){
ResourceResolver resolver = null;
Session session = null;
try {
    resolver = resolverFactory.getServiceResourceResolver(null);
    session = resolver.adaptTo(Session.class);
    Node destinationNode = session.getNode("/content/new/node1");
    deleteAllCHildNodes(destinationNode);
    updateProperty(destinationNode);
    } catch (Exception e) {
     Logging exception
} finally {
if (session != null && session.isLive()) {
    session.logout(); 
}
if (resolver != null && resolver.isLive()) {
    resolver.close();
}
}
}

private void deleteAllCHildNodes(Node destNode){
ResourceResolver resolver = null;
Session session = null;
try {
resolver = resolverFactory.getServiceResourceResolver(null);
 session= resolver.adapt(Session.class);
 ---deleting all childnodes under destNode----
 session.save();
     } catch (Exception e) {
     Logging exception
} finally {
if (session != null && session.isLive()) {
    session.logout(); 
}
if (resolver != null && resolver.isLive()) {
    resolver.close();
}
}
}
 
Kindly share your thoughts on this one.. whether it is good practice to do create and close resourceresolver and session in single method and it will cause any performance issue because mostly maximum no of jobs(10) will run in parallel.
Please suggest any other ideas to handle the concurrent issue if my idea is not good. 

Avatar

Level 4

Thanks for your reply..

Scenario :

Copying child nodes from particular node(/content/old/node1) and replacing child nodes under path /content/new/node1.Also doing add/modify/delete property under the node /content/new/node1 and its child nodes.
Similarly (/content/old/node2) to (/content/new/node2)
(/content/old/node3) to (/content/new/node3)
(/content/old/node4) to (/content/new/node4)

I am doing the above four operation at same time(creating job for each process /content/old/node1 to /content/new/node1). Four job is running parallel.

Initial call came to job class and it is directing to job service. In job service, I created resourceresolver 
from resourceresolverfactory(org.apache.sling.api.resource) and made resourceresolver as global variable for creating session at various private lower level methods.

Example:

Private ResourceResolver resolver

Class Jobservice(){
public void mainmethod(){
resolver = resourceresolverfactory.getServiceResourceResolver(null);
String path = "/content/old/node1";
lowerMethod1(path)
}

private void lowerMethod1(String path){
Session session= resolver.adapt(Session.class);
Node destinationNode = session.getNode("/content/new/node1");
deleteAllCHildNodes(destinationNode);
updateProperty(destinationNode);
}

private void deleteAllCHildNodes(Node destNode){
Session session= resolver.adapt(Session.class);
 ---deleting all childnodes under destNode----
 session.save();
}

Likewise I am doing different operation under /content/new/node1. I was getting concurrent access error and invaliditemstateexception(item doesi not exist anymore) at the different places in my code 
because four jobs are running parallel and performing operation under the respective node. I came to know that sessions are 
opening long time by google search(https://cqdump.wordpress.com/2015/03/02/aem-scaling-patterns-avoid-shared-sessions/) abt this issue. 
Also changes done for node1 in session1(first job) will not be available in session2(opened in second job). 

In order to avoid this error, I am creating resourceresolver and session at lower level methods and closing at the end of method itself(Session is opening for short duration) as below

Class Jobservice(){
public void mainmethod(){
ResourceResolver resolver = null;
Session session = null;
try {
    resolver = resolverFactory.getServiceResourceResolver(null);
    session = resolver.adaptTo(Session.class);
    String path = "/content/old/node1";
    lowerMethod1(path);
} catch (Exception e) {
     Logging exception
} finally {

            if (session != null && session.isLive()) {
            session.logout(); 
            }
        if (resolver != null && resolver.isLive()) {
        resolver.close();
        }
        }
}

private void lowerMethod1(String path){
ResourceResolver resolver = null;
Session session = null;
try {
    resolver = resolverFactory.getServiceResourceResolver(null);
    session = resolver.adaptTo(Session.class);
    Node destinationNode = session.getNode("/content/new/node1");
    deleteAllCHildNodes(destinationNode);
    updateProperty(destinationNode);
    } catch (Exception e) {
     Logging exception
} finally {
if (session != null && session.isLive()) {
    session.logout(); 
}
if (resolver != null && resolver.isLive()) {
    resolver.close();
}
}
}

private void deleteAllCHildNodes(Node destNode){
ResourceResolver resolver = null;
Session session = null;
try {
resolver = resolverFactory.getServiceResourceResolver(null);
 session= resolver.adapt(Session.class);
 ---deleting all childnodes under destNode----
 session.save();
     } catch (Exception e) {
     Logging exception
} finally {
if (session != null && session.isLive()) {
    session.logout(); 
}
if (resolver != null && resolver.isLive()) {
    resolver.close();
}
}
}
 
Kindly share your thoughts on this one.. whether it is good practice to do create and close resourceresolver and session in single method and it will cause any performance issue because mostly maximum no of jobs(10) will run in parallel.
Please suggest any other ideas to handle the concurrent issue if my idea is not good. 

Avatar

Level 4

Thanks for your reply..

Scenario :

Copying child nodes from particular node(/content/old/node1) and replacing child nodes under path /content/new/node1.Also doing add/modify/delete property under the node /content/new/node1 and its child nodes.
Similarly (/content/old/node2) to (/content/new/node2)
(/content/old/node3) to (/content/new/node3)
(/content/old/node4) to (/content/new/node4)

I am doing the above four operation at same time(creating job for each process /content/old/node1 to /content/new/node1). Four job is running parallel.

Initial call came to job class and it is directing to job service. In job service, I created resourceresolver 
from resourceresolverfactory(org.apache.sling.api.resource) and made resourceresolver as global variable for creating session at various private lower level methods.

Example:

Private ResourceResolver resolver

Class Jobservice(){
public void mainmethod(){
resolver = resourceresolverfactory.getServiceResourceResolver(null);
String path = "/content/old/node1";
lowerMethod1(path)
}

private void lowerMethod1(String path){
Session session= resolver.adapt(Session.class);
Node destinationNode = session.getNode("/content/new/node1");
deleteAllCHildNodes(destinationNode);
updateProperty(destinationNode);
}

private void deleteAllCHildNodes(Node destNode){
Session session= resolver.adapt(Session.class);
 ---deleting all childnodes under destNode----
 session.save();
}

Likewise I am doing different operation under /content/new/node1. I was getting concurrent access error and invaliditemstateexception(item doesi not exist anymore) at the different places in my code 
because four jobs are running parallel and performing operation under the respective node. I came to know that sessions are 
opening long time by google search(https://cqdump.wordpress.com/2015/03/02/aem-scaling-patterns-avoid-shared-sessions/) abt this issue. 
Also changes done for node1 in session1(first job) will not be available in session2(opened in second job). 

In order to avoid this error, I am creating resourceresolver and session at lower level methods and closing at the end of method itself(Session is opening for short duration) as below

Class Jobservice(){
public void mainmethod(){
ResourceResolver resolver = null;
Session session = null;
try {
    resolver = resolverFactory.getServiceResourceResolver(null);
    session = resolver.adaptTo(Session.class);
    String path = "/content/old/node1";
    lowerMethod1(path);
} catch (Exception e) {
     Logging exception
} finally {

            if (session != null && session.isLive()) {
            session.logout(); 
            }
        if (resolver != null && resolver.isLive()) {
        resolver.close();
        }
        }
}

private void lowerMethod1(String path){
ResourceResolver resolver = null;
Session session = null;
try {
    resolver = resolverFactory.getServiceResourceResolver(null);
    session = resolver.adaptTo(Session.class);
    Node destinationNode = session.getNode("/content/new/node1");
    deleteAllCHildNodes(destinationNode);
    updateProperty(destinationNode);
    } catch (Exception e) {
     Logging exception
} finally {
if (session != null && session.isLive()) {
    session.logout(); 
}
if (resolver != null && resolver.isLive()) {
    resolver.close();
}
}
}

private void deleteAllCHildNodes(Node destNode){
ResourceResolver resolver = null;
Session session = null;
try {
resolver = resolverFactory.getServiceResourceResolver(null);
 session= resolver.adapt(Session.class);
 ---deleting all childnodes under destNode----
 session.save();
     } catch (Exception e) {
     Logging exception
} finally {
if (session != null && session.isLive()) {
    session.logout(); 
}
if (resolver != null && resolver.isLive()) {
    resolver.close();
}
}
}
 
Kindly share your thoughts on this one.. whether it is good practice to do create and close resourceresolver and session in single method and it will cause any performance issue because mostly maximum no of jobs(10) will run in parallel.
Please suggest any other ideas to handle the concurrent issue if my idea is not good. 

Avatar

Employee

Always use local variables in this case.

Avatar

Level 9

Hey sankarr26533925,

I don't know how are you creating jobs. But here is the solution of your problem using Java ExecuterFramework for multi-threaded application. Even if you are not using any framework and simply creating Threads then follow the same concept. Without a framework, you might have to take care of synchronization in jobService methods.

//Job Class

public class Job implements Callable{

           final ResourceResolver resourceResolver, JobService jobService;

         Job(ResourceResolver resourceResolver, JobService jobService,  /** you can pass other arguments here**/){

                this.resouceResolver = resourceResolver; this.jobService = jobService;

         }        

        @Override
    public String call() throws Exception// invoke methods for node operations from here.

            jobService.initOperations(resourceResolver); // user resourceResolver in job service get session & saving nodes property. But don't close this.

    }

 }

//This is the class where you initiate jobs.

class MainClass {

   // Get resouceResolver here and pass it to jobs. And when all your jobs are done, close resolver here in a finally block.

}

Jitendra

Avatar

Level 4

I am using JobConsumer API (org.apache.sling.event.jobs.consumer). I should use CQ5 API only..Can we pass resourceresolver through Job properties ?

Avatar

Level 4

So I can create session and resolver in each method. Will it cause any problem ?

Avatar

Level 4

Can I create resourceresolver in local method and close it ?

Avatar

Level 10

Technically you can do that but ideally, you should declare it globally for the service which can be used to complete a flow or a transaction and then make your master method to have the finally block to close the session or resourceresolver.

If you have multiple method which works independently, then you can still have the local variables

Avatar

Employee Advisor

sankarr26533925 wrote...

Can I create resourceresolver in local method and close it ?

 

That's perfectly possible.

Avatar

Level 9

@sankarr26533925,

I don't think that could solve your problem. But you can try it. I think, at the job level, Open resourceResolver & close it, but make sure it is in a synchronized block.

Jitendra

Avatar

Correct answer by
Community Advisor

Avatar

Level 1

Creating and closing ResourceResolver and Session in multiple methods can lead to potential performance issues, especially when used in high-frequency operations or with heavy resource-intensive tasks. However, it can also help in avoiding session contention issues like the one described. Below are recommendations to address your concerns and improve your implementation:

Key Recommendations

  1. Reuse ResourceResolver and Session Where Possible:

    • If your operations are scoped to a single thread and have a consistent context, consider reusing the same ResourceResolver and Session across multiple methods within the same operation.

    • Use a higher-level object to manage and share these resources, such as a service or utility class.

  2. Thread-Safe Practices:

    • Avoid sharing Session or ResourceResolver instances across threads.

    • Each thread should have its own instance to prevent concurrent access issues.

  3. Batch Operations:

    • Instead of performing operations like adding, modifying, or deleting properties across 30 methods individually, batch these operations when possible. For example, collect changes and apply them in a single session save.

    • This reduces the overhead of repeatedly opening and closing sessions.

  4. Exception Handling:

    • Ensure robust exception handling within your try-catch block to handle errors gracefully without leaving resources unclosed.

  5. Performance Profiling:

    • Profile your application using tools like JProfiler or VisualVM to monitor session creation, thread contention, and other potential bottlenecks.

  6. Centralized Resource Management:

    • Create a utility method or class to handle ResourceResolver and Session creation and closure, ensuring consistency and reducing code duplication.

Optimized Code Sample

Here’s an updated version of your code that incorporates these suggestions:

java

Copy code

public class ResourceHandler { private final ResourceResolverFactory resolverFactory; public ResourceHandler(ResourceResolverFactory resolverFactory) { this.resolverFactory = resolverFactory; } public void performOperations() { try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(null)) { Session session = resolver.adaptTo(Session.class); if (session != null) { try { Node test = session.getNode("/content/dam"); updateProperties(test); session.save(); } catch (Exception e) { // Log exception e.printStackTrace(); } } } catch (LoginException e) { // Log exception e.printStackTrace(); } } private void updateProperties(Node node) throws RepositoryException { // Perform your operations here } }

Explanation of Changes

  1. Try-with-Resources:

    • Use try-with-resources for automatic closure of the ResourceResolver. It ensures proper cleanup of resources without needing explicit finally blocks.

  2. Session Reuse:

    • The session is adapted from the ResourceResolver and is used within the same scope, ensuring no shared or stale sessions.

  3. Utility Class:

    • Encapsulate the logic in a reusable handler class to promote better organization and reuse.