Event Listener vs Event Handler in AEM

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 {

@Reference
private ResourceResolverService resourceResolverService;

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

@Override
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)) {

addPageIdToPage(pagePath);
}
}
}

private void addPageIdToPage(String pagePath) {
try (ResourceResolver resolver = resourceResolverService.createWriter()) {
Optional.of(resolver)
.map(r -> resolver.getResource(pagePath + "/jcr:content"))
.map(res -> res.adaptTo(ModifiableValueMap.class))
.map(m -> m.put("pageId", UUID.randomUUID().toString()));
resolver.commit();
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);
   

@Activate
@Modified
public void activate() {

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

	JackrabbitEventFilter jackrabbitEventFilter = new JackrabbitEventFilter()
				.setAbsPath( "/content")
				.setNodeTypes("cq:Page")
				.setEventTypes(Event.NODE_ADDED)
				.setIsDeep(true)
				.setNoExternal(true)
				.setNoLocal(false);

			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",
					Event.NODE_ADDED);

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

// this is method from EventListener interface
   @Override
   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);
         }

      }
   }

@Deactivate
protected void deactivate() {
		try {
			if (null != observationManager) {
				observationManager.removeEventListener(this);
				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) {
				repositorySession.logout();
			}
		}
	}
}

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.

Conclusion

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.

Vuejs Amsterdam 2022

Reading Time: 5 minutes

Amazing speakers, interesting topics, great venue and everything nice – this were the ingredients chosen to create the perfect conference – Vuejs Amsterdam.

Last month me and my coworkers were sent on a mission, to improve our knowledge, become better developers and expand our views. So we decided to visit the best known Vue conference in the world.

The only constant in the technology industry is change

Marc Benioff

So what better way to keep up with that change rather than visiting a conference where the best speakers are gathered with the latest updates and you get to be one of the first people to hear them?

VUEJS AMSTERDAM

Vuejs Amsterdam is an event which took place on 2nd and 3rd of June 2022. According to their website, these were the numbers for those 2 days: 

Number of speakers – 20+ Vue core members
Number of attendees – 1000+ daily
Global audience from 20+ countries

The conference was held in Amsterdam theatre, in one of the most beautiful venues I’ve ever seen. One thing that I liked the most was that there was only one main stage, so we could listen to all the presentations, without having to choose from multiple presentations and multiple stages at the same time. The atmosphere in the venue was amazing. Everybody seemed like they had a great time, participating in interesting trivia games and quizzes and enjoying the wonderful food!

Also, I must say, visiting Amsterdam in such a nice weather was a big plus.

Conference recap

Here are some notes from my favourite speakers and topics:

State of the Vuenion 2022 – Evan You

  • Evan You is the creator of Vue, so I think that this was the best way to open the conference, even though his presentation was remote. The topic that he presented was about the current state of Vue.js, latest news and upcoming changes.
  • So the current state is the following:
    • Since 07.02.2022 Vue 3 is the default version
    • There is a brand new documentation 
    • Vue 3 +70% since default version launch, 4x downloads more than last year, 25% of all Vue downloads
    • Vuetify beta 3 release – May 19th 2022
    • Vue 2.7 – built in composition api (without plugin), moved Vue 2 codebase to Typescript, final minor release for Vue 2, EOL end of 2023

Getting more out of your Pinia stores – Eduardo San Martin Morote (creator of vue-router)

  • Pinia is a Vue store, successor of Vuex 
  • Has great documentation https://pinia.vuejs.org/
  • Vue 2 and Vue 3 compatible
  • Lighter than Vuex
  • Based on the Composition API 
  • Has a huge potential to replace Vuex
  • Here is an example usage:
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  actions: {
    increment() {
      this.count++
    },
  },
})
import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const counter = useCounterStore()

    counter.count++
    // or using an action instead
    counter.increment()
  },
}

Fast stories powered by Vite: Histoire – Guillaume Thru

  • Histoire is an interactive component playground powered by Vite
  • One of the most memorable moments in the conference was when the speaker changed the repository from private to public, and it was an honour to be present at that moment
  • https://histoire.dev/

Local state and server cache: How to balance them with vue-query – Natalia Tepluhina

  • One of my favourite speakers
  • There were three main questions at the beginning of the presentation
    • How to fetch only when data is not fetched already?
    • How to keep my data up to date?
    • What if two components start fetching simultaneously?
  • The answer to these questions is using vue-query – package which provides hooks for fetching, caching and updating data in Vue
  • The vue-query setup has some really interesting props which can be set for the configuration: 
    • retry – number of times it should refetch if there is an BE error
    • refetchOnWindowFocus – decides if it should refetch the data every time we do a tab switch
    • placeholderData – initial data before fetching
    • staleTime – interval of time while client considers your data being okay and there is no need to fetch it again

Vue 2 to 3 migration – a real life experience – An Phan

  • I think this is the topic that everyone was most interested in, because as Vue developers this is a problem which we are facing or will be facing in the near future.

Conclusion

I think that these kind of events are super important for learning new things, networking, getting inspired and keep up with the technology. On top of that, listening to the Vue core team members was definitely something that I will never forget!

Our conference roadmap 2019

Reading Time: 2 minutes

As we have already mentioned on our culture page we have planned a budget 💰 this year for all of our employees for further education purposes. Therefore everybody can go to a conference, workshop or the like. On our blog you will find posts from our employees about the individual events, what the expectations are, what the reality on the event is, maybe some surrounding information why it is worth to visit a conference at a specific city, maybe what is not recommendable and at the end what was the key learning.

So let’s conquer Europe … 9 metropolises, 12 different events:

The opening event started this week – Jeremy and Shady are in Bucharest 🇷🇴 to visit the Voxxed Days. Maybe you have already read the corresponding blog post and their expectations about the trip or you have already seen some pics on twitter. If not – you can find the post here. We are curious about their updates during/after their visit, especially about their culinary impressions 🥙 about Romania 😉.

To see all other scheduled events you can have a look at our conference plan 📆:

Date Event City / Country Who joins
March, 20-22 VOXXED DAYS Bucharest, Romania Jeremy, Shady
April, 3-5 FRONTCON Riga, Latvia Stephan
April, 5-6 JPoint 2019 Moscow, Russia Miodrag, Boris
May, 5 SAYGIN YALCIN Frankfurt, Germany Fatih
May, 16-17 SPRING I/O Barcelona, Spain Antonie, Amit
May, 26-29 UIKonf Berlin, Germany Dimitar
June, 1-2 JSConf EU Berlin, Germany Marijan
June, 6-7 WAD World Congress Berlin, Germany Lukasz
June, 24-26 DEVOXX POLAND Krakow, Poland Stefan
Sept, 2-4 adaptTo() Berlin, Germany Amit
Nov, 1-2 DEVOXX UKRAINE Kiev, Ukraine Jeremy, Shady
Dec, 5-6dotJSParis, FranceYoussef

Now you know where to meet us and maybe you got some ideas if you are also looking for an interesting event this year. If you have questions to an event, want to meet us somewhere or you have some insights for a specific conference or city please use the comment box below or get in touch with us directly – we are looking forward to your feedback – or to meet you at a conference ✌️!