Expand my Community achievements bar.

Event handler triggered twice in AEM as Cloud Service or Not getting Triggered at all.

Avatar

Level 3

HI Team,

We have a requirement :
when Page got activated or deactivated  we need to send the page info( path, title ,uuid) to other systems.
We are writing a event handler for it. It is working as expected. But, Event handler is getting Triggered twice. Below is the code.


@Slf4j
@component( service = EventHandler.class, immediate = true,
property = { EventConstants.EVENT_TOPIC + "=" + ReplicationAction.EVENT_TOPIC, })
public class ReplicationEventHandler implements EventHandler {

If we subscribe to org/apache/sling/event/notification/job/FINISHED or org/apache/sling/distribution/agent/package/distributed
Then Event handler is not getting triggered at all. Below is the code.

@Slf4j
@component(immediate = true, service = EventHandler.class,

property = { "event.topics=org/apache/sling/event/notification/job/FINISHED",
"event.filter=(|(distribution.type=ADD)(distribution.type=DELETE))" })
public class ReplicationEventHandler implements EventHandler {

OR
@Slf4j @component(immediate = true, service = EventHandler.class, property = { "event.topics=org/apache/sling/distribution/agent/package/distributed",
"event.filter=(|(distribution.type=ADD)(distribution.type=DELETE))" })
public class ReplicationEventHandler implements EventHandler {


Could you please review the sample code and provide guidance on resolving this issue?

25 Replies

Avatar

Level 3

Hi @martin_knyazyan 

I tried using org.apache.sling.discovery.DiscoveryService#getTopology().getLocalInstance().isLeader() and
org.apache.sling.discovery.DiscoveryService#getTopology().getLocalInstance().isLocal()

I am getting same answer. So, not able to differentiate between leader and local.

I will try filter option and let you know.

Thanks & Regards,

 

Avatar

Level 3

Hi @martin_knyazyan 

One Quick Question

Which topic you got subscribed with this filter

Is it org/apache/sling/event/notification/job/FINISHED   or

org/apache/sling/distribution/agent/package/distributed

0r

com/day/cq/replication

Avatar

Level 2

Hi @Rudra-2024 .

I subscribed to the topic - org/apache/sling/distribution/agent/package/distributed.

I think your case also matches with mine. Subsription to this topic, makes sure that the content has been successfully distributed from distrbution queue.

com/day/cq/replication - If your handler is subscribed to this topic, it will be immediately notify about the replication event, without actually waiting for the distribution request to be finished with SUCCESS or ERROR.

NOTE: You can also subscribe to this topic as well -  org.apache.sling.distribution.event.DistributionEventTopics#AGENT_PACKAGE_DROPPED, which will alert you, if something goes wrong, while distributing your package.

Regards,

Avatar

Level 3

Hi Martin,

 

Thanks for the response. Do you have the sample code for first option 1) Add this in your OSGi filter !(event.application=*). 

Avatar

Level 2

Here is the simple EventHandler:

@component(
immediate = true,
service = EventHandler.class,
property = {
EventConstants.EVENT_TOPIC + "=" + DistributionEventTopics.AGENT_PACKAGE_DISTRIBUTED,
EventConstants.EVENT_TOPIC + "=" + DistributionEventTopics.AGENT_PACKAGE_DROPPED,
EventConstants.EVENT_FILTER + "=" + "(&(|(distribution.type=ADD)(distribution.type=DELETE))(!(event.application=*)))
})
public class YourEventHandler implements EventHandler {}.

Note: On clustered environment events are distributed across the cluster as we know, and !(event.application=*) - means event.application property exists in recieved event, which will contain the sling instance ID, which emits the event, hence if the value exists means the event is distributed and only the instance where this property is missing will handle the event. The event sender decides whether the event should be distributed or not, and as I understand all the events are distributed by default. There is a internal listener called Sling Distribution Event listener, which subscribes to any topics event.topics="*", and checks if the event.distrbute property is set, it adds the event.application property to the event and sends to appropriate topic. But, sometimes event senders are posting/sending events through EventAdmin without specifing this (event.distribute) property, which leads the events to be distributed across all the instances and the event filter mentioned above will not help. This is the case for distributed events, so in this case the filter will not work (means event.application property always will be null), therefore the EventHandler will be called twice. To overcome this, use one of the approaches mentioned below.

1) Use DisoveryService - DiscoveryService().getTopology().getLocalInstance().isLeader(), process only on leader.
2) Use TopologyEventListener - Simple code snippet below

@component(
immediate = true,
service = {
EventHandler.class,
TopologyEventListener.class
},
property = {
EventConstants.EVENT_TOPIC + "=" + DistributionEventTopics.AGENT_PACKAGE_DISTRIBUTED,
EventConstants.EVENT_TOPIC + "=" + DistributionEventTopics.AGENT_PACKAGE_DROPPED,
EventConstants.EVENT_FILTER + "=" + "(&(|(distribution.type=ADD)(distribution.type=DELETE))(!(event.application=*))) // you can remove the event.application
})
public class YourEventHandler implements EventHandler, TopologyEventListener {

private final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

@Override
public void handleTopologyEvent(TopologyEvent event) {
if (event.getNewView() != null) {
boolean isLeader = event.getNewView().getLocalInstance().isLeader();
if (isLeader) {
atomicBoolean.set(isLeader);
}
}
}

@Override
public void handleEvent(Event event) {
if (atomicBoolean.get()) {
// process the event only on leader
}
}

}.

After the deployment on Cloud, wait for the blue nodes to shutdown, then do your tests to avoid confusion.
You can check the running instances under Developer Console of the environment. Ideally there should be 2 author pods and 2 publish pods.

Hope this helps.