Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

Multiple paths handling in EventHandler not working

Avatar

Level 3

I need to get the events while the property 'jcr:lastModified' is either added or modified. Also I only need to watch certain paths. I tried to add properties like this. I also checked the event console http://localhost:4502/system/console/events where I couldn't find my class. The debugger is also not reaching to the handleEvent method. Is this the correct way to do this.

 

@component(immediate = true, service = EventHandler.class, property = {
Constants.SERVICE_DESCRIPTION + "= This event handler listens the events on asset addition/modification",
EventConstants.EVENT_TOPIC + "=org/apache/sling/api/resource/Resource/ADDED",
EventConstants.EVENT_TOPIC + "=org/apache/sling/api/resource/Resource/CHANGED",
EventConstants.EVENT_FILTER + "(&" + "(|(path=/content/dam/custom-path1/*/jcr:content)(path=/content/dam/custom-path2/*/jcr:content)) (|("
+ SlingConstants.PROPERTY_CHANGED_ATTRIBUTES + "=*jcr:lastModified) " + "(" + ResourceChangeListener.CHANGES
+ "=*jcr:lastModified)))" })
public class PreviewS3AssetEventHandler implements EventHandler {

@Override
public void handleEvent(Event event) {
String event1 = event.getTopic();
String event2 = event1;

}

}

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @thatsmeadarsh,

Since we are looking for property change/add event on a Resource, we can make use of specific Listener named "ResourceChangeListener"https://sling.apache.org/apidocs/sling9/org/apache/sling/api/resource/observation/ResourceChangeList...

Sample snippet : (tried and works in my local - AEM 6.5.0)

Have used the PATHS as is without pattern. Glob pattern is allowed, check the description and use per your need - https://sling.apache.org/apidocs/sling9/org/apache/sling/api/resource/observation/ResourceChangeList...

package com.aem.demoproject.core.listeners;

import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

@Component(service = ResourceChangeListener.class, immediate = true, property = {
        ResourceChangeListener.PATHS + "=/content/dam/demo",
        ResourceChangeListener.PATHS + "=/content/dam/we-retail/en/features",
        ResourceChangeListener.CHANGES + "=CHANGED",
        ResourceChangeListener.CHANGES + "=ADDED",
        ResourceChangeListener.PROPERTY_NAMES_HINT + "=jcr:lastModified"
})
public class SampleResourceChangeListener implements ResourceChangeListener {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Override
    public void onChange(List<ResourceChange> list) {
        LOG.info("On add/change of jcr:lastModified");
    }
}

View solution in original post

9 Replies

Avatar

Correct answer by
Community Advisor

Hi @thatsmeadarsh,

Since we are looking for property change/add event on a Resource, we can make use of specific Listener named "ResourceChangeListener"https://sling.apache.org/apidocs/sling9/org/apache/sling/api/resource/observation/ResourceChangeList...

Sample snippet : (tried and works in my local - AEM 6.5.0)

Have used the PATHS as is without pattern. Glob pattern is allowed, check the description and use per your need - https://sling.apache.org/apidocs/sling9/org/apache/sling/api/resource/observation/ResourceChangeList...

package com.aem.demoproject.core.listeners;

import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

@Component(service = ResourceChangeListener.class, immediate = true, property = {
        ResourceChangeListener.PATHS + "=/content/dam/demo",
        ResourceChangeListener.PATHS + "=/content/dam/we-retail/en/features",
        ResourceChangeListener.CHANGES + "=CHANGED",
        ResourceChangeListener.CHANGES + "=ADDED",
        ResourceChangeListener.PROPERTY_NAMES_HINT + "=jcr:lastModified"
})
public class SampleResourceChangeListener implements ResourceChangeListener {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Override
    public void onChange(List<ResourceChange> list) {
        LOG.info("On add/change of jcr:lastModified");
    }
}

Avatar

Level 3

Thanks @Vijayalakshmi_S  and @arunpatidar  for your response. Resource change listener seems to be more appropriate option for me. However I am wondering if it is possible to inject the path properties from an OSGI configuration, cause we are looking for dynamic path listener.

Avatar

Community Advisor

The type of thePATHS property must either be String, or a String array and

The value for annotation attribute Component.property must be a constant expression.

 

So you can't pass dynamic value here.



Arun Patidar

Avatar

Level 3

Thanks @arunpatidar  for the clarification. I was thinking maybe some factory service injecting OSGi config properties as constructor would work. 

Avatar

Community Advisor

Hi @thatsmeadarsh

You can make use of ObjectClassDefinition(OCD) for providing component configuration properties dynamically from Config Admin. 

Sample snippet :

  • resource_paths() here will be evaluated as resource.paths which is the constant value for ResourceChangeListener.PATHS
    • Same goes with other two. 
  • If you would like to have the Listener working with specified default values for the very first time it is deployed/when you don't provide the config values via Config Admin(/configMgr), use the @Activate method with Config as the param. 
  • Otherwise, you can remove the method -> Once the code is deployed, provide the desired config values via /configMgr and then Listener would work with respective property and its values. 
package com.aem.demoproject.core.listeners;

import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

@Component(service = ResourceChangeListener.class, immediate = true)
@Designate(ocd = SampleResourceChangeListener.Config.class)
public class SampleResourceChangeListener implements ResourceChangeListener {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Override
    public void onChange(List<ResourceChange> list) {
        LOG.info("On add/change of jcr:lastModified, listener activated with config via OCD");
    }

    @Activate
    protected void activate(SampleResourceChangeListener.Config configValues) {
        LOG.debug("Config values={}", configValues.resource_paths());
    }

    @ObjectClassDefinition(name = "SampleResourceChangeListener", description = "Resource change Listener Registration properties")
    public @interface Config {
        @AttributeDefinition(name = "Paths", description = "ResourceChangeListener Paths property")
        String[] resource_paths() default {"/content/dam/demo"};

        @AttributeDefinition(name = "Changes", description = "ResourceChangeListener Changes property")
        String[] resource_change_types() default {"CHANGED"};

        @AttributeDefinition(name = "Properties", description = "ResourceChangeListener PropertyNamesHint property")
        String[] resource_property_names_hint() default {"jcr:lastModified"};
    }
}

 

Avatar

Level 3

How it will work the events will get triggered when we specify the following no

 ResourceChangeListener.PATHS+"=/content/aemgeeks/us/en/card",
                ResourceChangeListener.CHANGES+"=ADDED",
                ResourceChangeListener.CHANGES+"=REMOVED",
                ResourceChangeListener.CHANGES+"=CHANGED"
properties in the service. I wanted to understand how the above code works

Avatar

Community Advisor

Hi,

You can combine the path in single rule e.g.

path=/content/dam/myproj/(folder1|folder2)/*/jcr:content

EventConstants.EVENT_FILTER + "(&" + "(|(path=/content/dam/myproj/(folder1|folder2)/*/jcr:content)(|("
+ SlingConstants.PROPERTY_CHANGED_ATTRIBUTES + "=*jcr:lastModified) " + "(" + ResourceChangeListener.CHANGES
+ "=*jcr:lastModified)))" })

 



Arun Patidar

Avatar

Employee

In my case adding multiple paths worked with below syntax

 

@Component(service = EventHandler.class,
                immediate = true,
                property = {
                                EventConstants.EVENT_TOPIC + "=" + ReplicationAction.EVENT_TOPIC,
                                EventConstants.EVENT_FILTER + "=(|(paths=/content/projectname/*)(paths=/content/dam/projectname/*))"
                })
@ServiceDescription("Listener to listen on replication action in the resource tree")
@Designate(ocd = ReplicationListener.SolrSearchPropertiesConfiguration.class)
public class ReplicationListener implements EventHandler {

  Especially when we are working on Sling events like Activate or deactivate, the above code will be convenient because you have to listen to activate or deactivate events on specific paths under your project-related folders only in Assets and Sites. Something similar was even suggested by @arunpatidar  in an answer here.