May 13, 2020
Estimated Post Reading Time ~

Event Listeners

Listeners are services that run because of events occurring within the system.

Event Listeners
As the name suggests, events listen for events occurring from OSGI. The main use case for this is to listen for changes in the jcr:repository. The event listeners should only run against the pre-defined event queues. 

package com.sample.core.listeners;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingConstants;
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;

@Component()
@Service(value = EventHandler.class)
@Property(name = EventConstants.EVENT_TOPIC, value = "org/apache/sling/api/resource/Resource/*")
public class SimpleResourceListener implements EventHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());

@Override
public void handleEvent(final Event event) {
if (logger.isDebugEnabled()) {
logger.debug("Resource event: {} at: {}", event.getTopic(), event.getProperty(SlingConstants.PROPERTY_PATH)); } }

This is a very simple listener that will get an event for every change in any of the queues starting org/apache/sling/api/resource/Resource
The SlingConstants class does specify the individual queues that are available for event handlers as
TOPIC_RESOURCE_ADDED
A new resource is added to the jcr:repository
TOPIC_RESOURCE_REMOVED
A resource has been removed from the jcr:repository
TOPIC_RESOURCE_CHANGED
A resource has been changed in the jcr:repository
There are other queues that are available, check-in the SlingConstants for the other main queues. 

As you can see the listener is a service of type EventHandler, so any calls to the EventHandler manager will filter down to this class. As with the filters, the event will be called because of EVERY request to the jcr:repository 

The main pieces of information that you get from the event are:
topic name
The name of the topic that the message came from.
Important if you are listening to a number of queues
property path
The path that has been added/changed/deleted
changed attributes
The attributes that have been changed
This is only available for change events
Users and groups, while visible in the jcr:explorer are not officially part of the jcr:repository, and while you may sometimes get events fired for changes in a user or group, these are not guaranteed to occur

While it is possible to filter the path the event will run on using the following code
@Property(
label = "Event Filters",
value = "(path=*/my-event-listener-samples/jcr:content)",
description = "[Optional] Event Filters used to further restrict this event handler; Uses LDAP expression against event properties.",
name = EventConstants.EVENT_FILTER,
propertyPrivate = true
)
I have found it very problematic and sometimes the events I have been expecting don't come through so I have tended to test the path as part of the event listener.

Job Listeners
While there isn't really much difference between a job listener and an event listener, the job listener runs against any queue instead of just the defined jcr:repository queues. 

package com.sample.core.jobs;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(immediate = true, label = "Job Logger", description = "Custom AEM service for logging jobs", metatype = true, enabled = true)
@Service(value={JobConsumer.class})
@Property(name=JobConsumer.PROPERTY_TOPICS, value="sample/job")
public class MailServiceJob implements JobConsumer {
private static Logger logger = LoggerFactory.getLogger(MailServiceJob.class);

@Override
public JobResult process(Job job) {
job.getPropertyNames().forEach(pn -> {
logger.debug("job sent : "+pn +"=" + job.getProperty(pn)); });
return JobResult.FAILED; }
}

As you can see the only real difference here is that the service is of type JobConsumer. The JobConsumer interface has a single method process(Job) which expects to get a JobResult back. 

There are a few responses that will be processed on completion of the job
OK
The job has completed and can be removed
FAILED
The job has failed and should handle the error handling configuration which will be explained in the ui.apps section
CANCEL
The job has been canceled and should not be retried
ASYNC
Processing of the job will be done asynchronously
Jobs won't have any access to the resource resolver or session so we will need to get them via a resource resolver factory if required.



By aem4beginner

No comments:

Post a Comment

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