Event Listener vs Event Handler in AEM

How Can We Help?

Reading Time: 4 minutes

What are event listeners in AEM? Is this same as event handlers? What is difference in this two concepts? We will try to answer all these questions in this article.

A little searching on the web, probably you will find something like this:

The most important here is the first row: JCR level vs Sling level? What does this mean in practice? The answer of this question will be through a experiment.

The task: Please provide UUID for every new page created.

It seems that we can use either EventHandler or EventListener, and we can achieve same result, uuid for every page that is created. So, why AEM provide two different functionality for the same thing? It must be some differences between them!? Yes, indeed, there is. The tricky part here is where from we create the page. If we create from AEM , the image bellow

creation of page from AEM

then both methods are good enough. But suppose someone with admin privileges, that can access CRX. It could happen to copy-paste page, just as ordinary node.

copy-paste page from CRX

In this case EventListener will fire, but EventHandler will not. That is the key difference between them.

The Experiment:

Implementation of EventHandler

@Component(service = EventHandler.class,
immediate = true,
property = {EventConstants.EVENT_TOPIC + "=" + PageEvent.EVENT_TOPIC})
public class PageEventHandler implements EventHandler {

private ResourceResolverService resourceResolverService;

private static final Logger LOG = LoggerFactory.getLogger(PageEventHandler.class);

public void handleEvent(Event event) {
Iterator<PageModification> pageInfo = PageEvent.fromEvent(event).getModifications();
while (pageInfo.hasNext()) {
PageModification pageModification = pageInfo.next();
String pagePath = pageModification.getPath();
if (pageModification.getType().equals(PageModification.ModificationType.CREATED)) {


private void addPageIdToPage(String pagePath) {
try (ResourceResolver resolver = resourceResolverService.createWriter()) {
.map(r -> resolver.getResource(pagePath + "/jcr:content"))
.map(res -> res.adaptTo(ModifiableValueMap.class))
.map(m -> m.put("pageId", UUID.randomUUID().toString()));
LOG.info("CREATE ID FOR page {}", pagePath);
} catch (PersistenceException e) {
LOG.error("Can't save pageId on page {}", pagePath);

With this code, on every new page, we are adding pageId. When a page is created from AEM, we have pageId for the page:

pageId in CRX when page is created from AEM environment

Now, if you created page with copy-paste operation from CRX, there is no pageId.

Implementation of EventListener

@Designate(ocd = PageUUIDCreationListener.Config.class)
@Component(immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE)
public class PageCreationListener implements EventListener {

private static final Logger LOG = LoggerFactory.getLogger(PageCreationListener.class);

public void activate() {

try {
	repositorySession = repository.loginService(SYSTEM_USER_READER, null);

	JackrabbitEventFilter jackrabbitEventFilter = new JackrabbitEventFilter()
				.setAbsPath( "/content")

			Workspace workSpace = repositorySession.getWorkspace();
			if (null != workSpace) {
				observationManager = (JackrabbitObservationManager) workSpace.getObservationManager();
				observationManager.addEventListener(this, jackrabbitEventFilter);
				LOG.info("The Page Event Listener is Registered at {} for the event type {}.", "/content",

		} catch (RepositoryException e) {
			LOG.error("An error occurred while getting session", e);

// this is method from EventListener interface
   public void onEvent(final EventIterator eventIterator) {

      while (eventIterator.hasNext()) {
         Event event = eventIterator.nextEvent();
         String path;
         try {
            path = event.getPath();
            if (path.endsWith("jcr:content")) {
                  logic to add pageId here
         } >catch (RepositoryException e) {
            LOG.error("An error occurred while getting event path", e);


protected void deactivate() {
		try {
			if (null != observationManager) {
				LOG.info("The Page Event Listener is removed.");
		} catch (RepositoryException e) {
			LOG.error("An error occurred while removing event listener", e);
		} finally {
			if (null != repositorySession) {

Now, try to create page from AEM, you will notice pageId with every new page.

Try to create page with copy-paste operation in CRX. The new page also has pageId.


Always use EventListener to be on safe side. It is fired on repository level.

If you decide to use EventHandler because of simplicity, be aware that it is fired on Sling level. Changes directly in CRX would not fire the event.

Table of Contents
0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published.