Expand my Community achievements bar.

SOLVED

Session Handling

Avatar

Level 5

Hi Experts,

  I am writing a Sling Component which a Servlet can use to read and write properties from and to the repository.

Since there are many custom properties and each property updation will be mapped to a different method, is it advisable to initiate the resource resolver in the activate method of the component and then use it to adapt the session object in each method. Below code is a sample.

 

@component
public class RepoUtil{

private ResourceResolver resourceResolver;
private javax.jcr.Session session;

@Activate
@Modified
public void activate(final ComponentContext componentContext) {
    //initialize the configuration props
    resourceResolver = getResourceResolver();
}
public String getPropertyAFromNodeA(){
   session = resourceResolver.adaptTo(Session.class);
   //use session to get the node needed.
   // use node api to get the intended property
   return propertyvalue;
}
public String getPropertyBFromNodeA(){
   session = resourceResolver.adaptTo(Session.class);
   //use session to get the node needed.
   // use node api to get the intended property
   return propertyvalue;
}
public String updatePropertyAFromNodeA(){
   session = resourceResolver.adaptTo(Session.class);
   //use session to get the node needed.
   // use node api to update the intended property
  return propertyvalue;
}
}

 

Note that session is not closed at the end of each method. Since I am not creating the session object instead merely adapting it from resource resolver, I am not sure if the session object has to be closed.  Is there a pattern we can use in scenarios like this where multiple reads/writes are expected?

 

Regards,

Pal

1 Accepted Solution

Avatar

Correct answer by
Employee

100% don't initialize a resource resolver in your activate method. Keep your ResourceResolvers/JCR Sessions short-lived as you can. Also, rememeber resourceResolvers and JCR Sessions are NOT thread-safe. So if you had a servlet being hit by 2 ppl at the same time, and they both invoke the RepoUtil service using the same resourceResolver, bad things will happen (like end up w/ lost data due to unresolvable state conflicts)

 

Here's what you should do (in order of generic preference),

 

1. Pass in the slingRequest's resourceResolver to your OSGi component's methods to read/write the properties. This way you are using the actual user's security context (permissions) to write the data. This is the preferred approach.

2. If for some reason you cannot allow for the user of the sling requests's resource resolver to write the data, obtain a Service User resourceResolver with the proper permissions in your Servlet, and pass it to your methods.  

3. You could alternatively obtain the Service User resource resolver in your RepoUtil, and get a new resourceResolver in each method, but obviously thats a bit wasteful (if the servlet calls 3 methods, then you'd be obtaining the same resourceResolver 3x)

 

Also, try to avoid using JCR APIs when you can use Sling APIs, I'm specifically saying here you should use SLing Resources and ValueMaps (and ModifiableValueMaps) over JCR Nodes and Properties. If all your RepoUtil does is a "dumb" read/write of JCR properties, it seems unnucessary. If you had a domain-specific OSGi Service, such as "ContactUsService" and you pass in a bunch of data to something like contantUsService.submit(email, name, message) .. and that service puts those 3 params in the "right place" in the JCR, that makes more sense. Just having a OSGI component that generically reads/writes properties doesn't strike me as useful and redundant of Sling's APIs.

 

https://experienceleague.adobe.com/docs/experience-manager-learn/foundation/development/understand-j...

View solution in original post

7 Replies

Avatar

Correct answer by
Employee

100% don't initialize a resource resolver in your activate method. Keep your ResourceResolvers/JCR Sessions short-lived as you can. Also, rememeber resourceResolvers and JCR Sessions are NOT thread-safe. So if you had a servlet being hit by 2 ppl at the same time, and they both invoke the RepoUtil service using the same resourceResolver, bad things will happen (like end up w/ lost data due to unresolvable state conflicts)

 

Here's what you should do (in order of generic preference),

 

1. Pass in the slingRequest's resourceResolver to your OSGi component's methods to read/write the properties. This way you are using the actual user's security context (permissions) to write the data. This is the preferred approach.

2. If for some reason you cannot allow for the user of the sling requests's resource resolver to write the data, obtain a Service User resourceResolver with the proper permissions in your Servlet, and pass it to your methods.  

3. You could alternatively obtain the Service User resource resolver in your RepoUtil, and get a new resourceResolver in each method, but obviously thats a bit wasteful (if the servlet calls 3 methods, then you'd be obtaining the same resourceResolver 3x)

 

Also, try to avoid using JCR APIs when you can use Sling APIs, I'm specifically saying here you should use SLing Resources and ValueMaps (and ModifiableValueMaps) over JCR Nodes and Properties. If all your RepoUtil does is a "dumb" read/write of JCR properties, it seems unnucessary. If you had a domain-specific OSGi Service, such as "ContactUsService" and you pass in a bunch of data to something like contantUsService.submit(email, name, message) .. and that service puts those 3 params in the "right place" in the JCR, that makes more sense. Just having a OSGI component that generically reads/writes properties doesn't strike me as useful and redundant of Sling's APIs.

 

https://experienceleague.adobe.com/docs/experience-manager-learn/foundation/development/understand-j...

Avatar

Level 5
Thanks for your response. One more question, what about session. Should it be closed every time I get it?
Yes, but only if you create that new JCR session/resourceResolver (aka get a service user resourceresolver) you should close it as quickly as possible, ie. when you're done with it. Don't leave them hanging around for any longer than you need them. As Ideally you re-use the resource resolver from with sling request, and since you dont make the resource resolver from the sling request, you wouldnt close that (youd let Sling close that one when its done processing the request)

Avatar

Employee Advisor

There are a few things to note here:

* An OSGI component is a singleton, thus your Resource Resolver is used on every invocation. And as Resource Resolvers are not thread-safe, you are likely to run into problems. To fix that you can either require that on every invocation of your service's method a ResourceResolver is provided, which is then used. The other way to solve is to create a dedicated ResourceResolver for every of these invocations. The same applies for JCR Sessions.

* Why do you use the JCR API? In 99% of all cases you can achieve the same with the Sling Resource API.

 

To answer your question: if you use resourceResolver.adaptTo(Session.class), you don't open the session. I have answered that question more deeply here: https://cqdump.wordpress.com/2018/11/12/resourceresolvers-and-sessions-you-open-it-you-close-it/

 

HTH,

Jörg

 

 

 

Avatar

Level 5
Hi Jörg_Hoh, Thanks for your response. Couple more questions. 1) I am working on a custom authentication handler, if i do a repository.login(credentials), should i close the resulting session? eventhough i am creating a token if the user is valid, i am not sure if i should close the session. 2) I will be creating resource resolver for every request made to authentication handler and will pass it to the component methods which inturn will be used obtain the session in component. In this case should i close the resource resolver? If so I will be closing it in the finally block of the authentication handler. Is my understanding correct?

Avatar

Employee Advisor
Hi, if you work on a custom authentication handler, you can check the 2 authentication provider implementations which already ship with Sling (see https://sling.apache.org/documentation/the-sling-engine/authentication/authentication-authentication...). I haven't implemented a custom auth handler yet, so my practical advice is limited.

Avatar

Employee

@Jai1122-

1) if you are creating the JCR Session in your code (ie. calling repository.login(..)) then you are responsible for closing that JCR Session (preferably in a finally block). Your JCR Session used to create a cq:User and/or its Token has nothing to do with the security context the user gets when they login with the token.

 

2) Yes, again, since your code is creating the resource resolver that calls your RepoUtil, you need to also close that resourceResolver when you are finished with it (in a finally block, preferably).

 

Also, you keep suggesting your dropping down to JCR Session as the API layer of choice, as both @Jörg_Hoh  and I noted, you should really look at leveraging Sling APIs - they provide a much better abstraction.


Also, make sure you are leveraging Sling Service Users anytime you need to create your own "special" security context (ie. you can't use the user's slingRequest.getResourceResolver()) .. you can see how this can be done here: https://github.com/Adobe-Consulting-Services/acs-aem-samples/blob/master/core/src/main/java/com/adob...