Trigger AEM Event listener : Declaring resolver object globally to be available inside activate and onevent method to be thread safe
I have a event listener class to call a service method as below. This piece of code works well and calls the service method when the event is triggered. But,when this code went through the automatic code scanning tool sonar cube in Adobe cloud, we got a critical finding on the resolver object declaration as highlighted in bold red below. Below was the critical finding -
Usage of org.apache.sling.api.resource.ResourceResolver as a field is not thread safe.
So I removed the global declaration of resolver object and tried to create the object inside the try method as below
try (ResourceResolver resourceResolver = resolverFactory.getServiceResourceResolver(ServiceUser.REVIEW_TASK_SERVICE_USER.getAuthMap())) {
This will automatically handle the resolver.close() as well. But the issue is, in this approach, we cannot declare the resolver object globally, which will be available both in activate and onevent methods. This declaration of resolver object inside try method will not trigger the listener and my listener is no longer getting triggered.
package com.inc.aeminc.core.listeners;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.jcr.observation.ObservationManager;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inc.aeminc.core.services.CodeGeneratorService;
import com.inc.aeminc.core.sling.ServiceUser;
@8220494(service = EventListener.class, immediate = true)
public class ModelFragmentListener implements EventListener {
/**
* Logger
*/
private static final Logger log = LoggerFactory.getLogger(ModelFragmentListener.class);
/**
* Resource Resolver Factory
*/
@3214626
private ResourceResolverFactory resolverFactory;
@3214626
private SlingRepository repository;
@3214626
private CodeGeneratorService CodeGenerator;
private ObservationManager observationManager;
private ResourceResolver resourceResolver;
@580286
protected void activate(ComponentContext componentContext) {
log.info("Activating the observation");
try {
resourceResolver = resolverFactory.getServiceResourceResolver(ServiceUser.REVIEW_TASK_SERVICE_USER.getAuthMap());
/**
* Adapting the resource resolver to session object
*/
Session session = resourceResolver.adaptTo(Session.class);
if (null != session) {
observationManager = session.getWorkspace().getObservationManager();
}
log.info("Session created for ModelFragmentListener");
/**
* Adding the event listener
*/
observationManager.addEventListener(this,
Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/content/dam/testpath",
true, null, null, false);
} catch (RepositoryException | LoginException e) {
log.error("An exception Occured in code Generator ", e);
}
}
@3038739
protected void deactivate() throws RepositoryException {
if (observationManager != null) {
observationManager.removeEventListener(this);
}
}
@9944223
public void onEvent(EventIterator events) {
try {
while (events.hasNext()) {
final Event event = events.nextEvent();
String path = "";
String property = "";
try {
switch (event.getType()) {
case Event.PROPERTY_ADDED:
path = event.getPath();
property = path.substring(path.lastIndexOf("/") + 1);
if ((property.equals("lastFragmentSave")) && ((path.startsWith("/content/dam/testpath/creative-assets")) ||
(path.startsWith("/content/dam/testpath/team-assets")))) {
log.info("Property added: {} ", event.getPath());
path = path.substring(0, path.lastIndexOf("/"));
CodeGenerator.createCode(resourceResolver, path);
}
break;
case Event.PROPERTY_CHANGED:
path = event.getPath();
property = path.substring(path.lastIndexOf("/") + 1);
if ((property.equals("lastFragmentSave")) && ((path.startsWith("/content/dam/testpath/creative-assets")) ||
(path.startsWith("/content/dam/testpath/team-assets")))) {
log.info("Property added: {} ", event.getPath());
path = path.substring(0, path.lastIndexOf("/"));
CodeGenerator.createCode(resourceResolver, path);
}
break;
default:
log.info("Property noaction: {} ", event.getPath());
}
} catch (RepositoryException e) {
log.error("RepositoryException during template change listener ", e);
}
}
} catch (Exception e) {
log.error("Exception occurred", e);
}
}
}
This should be a common issue for any listener class in AEM. How do we declare the resolver object globally and make sure the same resolver object is available inside activate() and onevent() inside a listener class, also making sure it is to be thread safe?
Any leads would be much appreciated.

