April 10, 2020
Estimated Post Reading Time ~

Eventing in AEM

There are various ways by which event handling can be done in AEM
  • Event Listener - JCR level events with the observation
  • Event Handler - Sling level events
  • Workflow and Launchers
  • Schedulers with cron expressions
In this post, we will be discussing Event Handlers and Event Listeners.
All the events in AEM can be viewed at - http://<host>:<port>/system/console/events.

Event Handlers
We can create an Event Handler by following below steps 
  • Write a service class that implements the EventHandler interface.
  • Register the service with property EventConstants.EVENT_TOPIC.
  • Implement the handleEvent(Event event) method to trigger the job.
Let us create a custom event handler that will be triggered when a page is activated and logs the topic for which the handler was registered.

Create a class CustomEventHandler and paste the following code in it.

package org.redquark.demo.core.listeners;

import org.apache.sling.api.SlingConstants;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Anirudh Sharma
*
* Event Handler that listens to the Sling events
*/
@Component(immediate = true, service = EventHandler.class, property = {
Constants.SERVICE_DESCRIPTION + "= This event handler listens the events on page activation",
EventConstants.EVENT_TOPIC + "=org/apache/sling/api/resource/Resource/ADDED",
EventConstants.EVENT_TOPIC + "=org/apache/sling/api/resource/Resource/CHANGED",
EventConstants.EVENT_FILTER + "(&" + "(path=/content/we-retail/us/en/*/jcr:content) (|(" +
SlingConstants.PROPERTY_CHANGED_ATTRIBUTES + "=*jcr:title) " + "(" + ResourceChangeListener.CHANGES +
"=*jcr:title)))"
})
public class CustomEventHandler implements EventHandler {

/**
* Logger
*/
private static final Logger log = LoggerFactory.getLogger(CustomEventHandler.class);

@Override
public void handleEvent(Event event) {

log.info("Event is: {}", event.getTopic());
}
}

As you can see that we have service property service=EventHandler.class and EventConstants.EVENT_TOPIC registered to resource added and changed which normally happens in AEM Replication
We have implemented the handleEvent() method and it is logging the topic for which this event handler is registered.
Deploy the code and activate (publish) any page. You will see the following traces in the logs

org.redquark.demo.core.listeners.CustomEventHandler Event is: org/apache/sling/api/resource/Resource/ADDED
org.redquark.demo.core.listeners.CustomEventHandler Event is: org/apache/sling/api/resource/Resource/CHANGED


Event Listeners
We can achieve event handling at the JCR level by implementing the EventListener interface in a class. JCR Observer is the lowest-level event handling in AEM. As its name indicates, it is at the JCR level and allows us to listen to JCR-level events, gathered in sets (corresponding to persistence transactions). javax.jcr.observation.Event lists the following types:
  1. Event.NODE_ADDED
  2. Event.NODE_MOVED
  3. Event.NODE_REMOVED
  4. Event.PERSIST
  5. Event.PROPERTY_ADDED
  6. Event.PROPERTY_CHANGED
  7. Event.PROPERTY_REMOVED
Let us create an event listener that listens to the event when a new node is added at a specified path.

Create a new class CustomEventListener and paste the following code in it

package org.redquark.demo.core.listeners;

import java.util.HashMap;
import java.util.Map;

import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;

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;

/**
* @author Anirudh Sharma
*
* Event Listener that listens to JCR events
*/
@Component(service = EventListener.class, immediate = true)
public class CustomEventListener implements EventListener {

/**
* Logger
*/
private static final Logger log = LoggerFactory.getLogger(CustomEventListener.class);

/**
* Resource Resolver Factory
*/
@Reference
private ResourceResolverFactory resolverFactory;

/**
* Resource Resolver
*/
private ResourceResolver resolver;

@Reference
private SlingRepository repository;

/**
* Session object
*/
private Session session;

/**
* Activate method to initialize stuff
*/
@Activate
protected void activate(ComponentContext componentContext) {

log.info("Activating the observation");

try {

/**
* This map will be used to get session via getServiceResourceResolver() method
*/
Map < String, Object > params = new HashMap < > ();

/**
* Adding the subservice name in the param map
*/
params.put(ResourceResolverFactory.SUBSERVICE, "eventingService");

/**
* Getting resource resolver from the service factory
*/
resolver = resolverFactory.getServiceResourceResolver(params);

/**
* Adapting the resource resolver to session object
*/
session = resolver.adaptTo(Session.class);

log.info("Session created");

/**
* Adding the event listener
*/
session.getWorkspace().getObservationManager().addEventListener(this,
Event.PROPERTY_ADDED | Event.NODE_ADDED, "/apps/demoproject", true, null, null, false);

} catch (Exception e) {
    log.error(e.getMessage(), e);
}
}

@Deactivate
protected void deactivate() {

if (session != null) {

session.logout();
}
}

@Override
public void onEvent(EventIterator events) {

try {
while (events.hasNext()) {
    log.info("Something has been added: {} ", events.nextEvent().getPath());
}
} catch (Exception e) {

log.error("Exception occurred", e);
}
}

}
  • First, we need to get the javax.jcr.Session object to add the event listener. Here we are using the concept of service/system user in AEM.
  • After getting the session, we are adding the event listener and registering them for PROPERTY_ADDED and NODE_ADDED events on the path /apps/demoproject.
  • In the implemented onEvent(EventIterator events), we are logging the paths where a new node is added
  • Now go to the CRX Explorer and create a system user "demouser" and give all permissions on the root.
  • Go to ./system/console/configMgr and search for Apache Sling Mapper User Mapper Service and configure as per the below screenshot

User Mapper Service
Deploy the code and create new folder at location /apps/demoproject, you will see the following traces in the logs

org.redquark.demo.core.listeners.CustomEventListener Something has been added: /apps/demoproject/eventTest
org.redquark.demo.core.listeners.CustomEventListener Something has been added: /apps/demoproject/eventTest/jcr:primaryType
org.redquark.demo.core.listeners.CustomEventListener Something has been added: /apps/demoproject/eventTest/jcr:createdBy
org.redquark.demo.core.listeners.CustomEventListener Something has been added: /apps/demoproject/eventTest/jcr:created


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.