Event Listener in AEM 6.5

Event Listener in AEM 6.5

Goal

Create an event listener in AEM 6.5 listening on an Replication Event.

Use case

In this example the requirement was to trigger a replication in author to decache a JSON based on specific replicated paths.

Procedure

In order to create a Event Listener is necessary to create an OSGi service that implements the EventHandler interface.
A few remarks

As I said, the requirement was to trigger a replication of some specific paths from the author if some specific paths were published. This mapping is done in the OSGi configuration. But anyway, this is just an example and you can customize it based on your needs.

This is the class:

package com.adobe.training.core.listeners;


/**
 * Created by r.teruzzi on 19/03/2020.
 */

import com.adobe.training.core.utils.Lab2020CommonMethods;
import com.day.cq.replication.ReplicationAction;
import com.day.cq.replication.ReplicationActionType;
import com.day.cq.replication.ReplicationException;
import com.day.cq.replication.Replicator;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Session;
import java.util.LinkedHashMap;
import java.util.Map;

import static com.day.cq.commons.Externalizer.AUTHOR;

@Component(service = EventHandler.class,
        immediate = true,
        property = {
                Constants.SERVICE_DESCRIPTION + "=Lab2020 Activation Event Listener ",
                EventConstants.EVENT_TOPIC + "=" + ReplicationAction.EVENT_TOPIC
        }
)
@Designate(ocd = EventListener.EventListenerPageActivationListenerConfiguration.class)
public class EventListener implements EventHandler {

    @ObjectClassDefinition(name = "Lab2020 - Activation Event Listener")
    public @interface EventListenerPageActivationListenerConfiguration {

        @AttributeDefinition(
                name = "Enabled",
                description = "Activation Event Listener is enabled",
                type = AttributeType.BOOLEAN
        )
        boolean isEnabled() default false;

        @AttributeDefinition(name = "List of paths", description = "Configure here all the listening paths and related paths to publish " +
                "with the format <listeningPath>:<pathToPublish> e.g. /content/lab2020/en/web/test:/content/lab2020/en/mobile/test. Use ':' as separator.")
        String[] getListOfPaths();
    }

    @Reference
    private SlingSettingsService slingSettingsService;

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private Replicator replicator;

    private static final Logger LOG = LoggerFactory.getLogger(EventListener.class);
    private Map<String, String> paths = null;
    private boolean enabled = false;

    @Activate
    @Modified
    protected void activate(EventListenerPageActivationListenerConfiguration config) {
        enabled = config.isEnabled();
        paths = new LinkedHashMap<>();
        String[] listOfPaths = config.getListOfPaths();
        if (listOfPaths != null) {
            for (String siteProperty : listOfPaths) {
                String[] sitePropertyConfig = siteProperty.split(":");
                if (sitePropertyConfig.length == 2) {
                    paths.put(sitePropertyConfig[0], sitePropertyConfig[1]);
                } else {
                    LOG.error("Invalid Site property configuration {}", siteProperty);
                }
            }
        }
        LOG.info("Event Handler is enabled:" + enabled);
    }

    @Override
    public void handleEvent(Event event) {
        if (enabled && slingSettingsService.getRunModes().contains(AUTHOR)) {
            final ReplicationAction action = ReplicationAction.fromEvent(event);
            if (action != null) {
                String listeningPath = action.getPath();
                String pagePath = paths.get(listeningPath);
                LOG.info("Listened {} ", listeningPath);
                if (StringUtils.isNotBlank(pagePath)) {
                    try (ResourceResolver resourceResolver = Lab2020CommonMethods.getResourceResolver(resourceResolverFactory)) {
                        LOG.info("Path to publish: {} ", pagePath);
                        LOG.info("###########");
                        replicator.replicate(resourceResolver.adaptTo(Session.class), ReplicationActionType.ACTIVATE, pagePath);
                    } catch (ReplicationException e) {
                        LOG.error("Failed to replicate", e);
                    }
                }
            }
        }
    }
}

That’s it! This has been tested on AEM 6.5 but I’m pretty sure that can work on previous AEM versions.

If you need to use a resource listener, please have a look a this article. Here instead, you can find different articles with some OSGi examples.

Cheers! 🍜

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 )

Twitter picture

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

Facebook photo

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

Connecting to %s

%d bloggers like this: