Apache Sling provides some mechanisms and support for managing events. This page describes ...
Sling distinguishes between 2 types of events:
- standard events: no garantee of processing
- job events: garantee of processing. Someone has to do something with the event (do the job).
The event mechanism is leveraging the OSGi Event Admin Specification (OSGi Compendium 113).
The OSGi API is very simple and leightweight - sending an event is just generating the event object and calling the event admin. Receiving the event is implementing a single interface and declaring through properties which topics one is interested in. For more details please refer to the following javadocs:
- package org.osgi.service.event of the OSGI API (http://www.osgi.org/javadoc/r4v42/org/osgi/service/event/package-summary.html).
- package org.apache.sling.event of the Sling API (http://sling.apache.org/apidocs/sling5/org/apache/sling/event/package-summary.html)
You can learn more in the "Eventing, Jobs and Scheduling" section (http://sling.apache.org/site/eventing-and-jobs.html).
To get started with the Sling eventing API, you will implement here a service that listens to files posted to /tmp/dropbox and moves them to the appropriate locations depending on the mime-type:
* images (.png) are moved to /dropbox/images/
* music (.mp3) are moved to /dropbox/music/
* movies (.avi) are moved to /dropbox/movies/
* otherwise the files are moved to /dropbox/other/
To do that, you will implement 2 services. The first one, called OsgiDropBoxService:
- listens to osgi event "resource added"
- sends a job event
The second one, called DropBoxEventHandler:
- listens to job event "file added to /tmp/dropbox"
- moves the file according to its extension
Listening to osgi event "resource added"
To listen to the specific osgi event "resource added":
- The service needs to implement the interface org.osgi.service.event.EventHandler and its handleEvent(Event event) method.
- The property "event.topics" needs to be set to "org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED" in the class annotations.
To get a list of possible events available in Sling (resource added, removed or changed), refer to the javadocs for org.apache.sling.api.SlingConstants.
To send a job event:
- The service needs to implement the interface org.apache.sling.event.JobProcessor and its process(org.osgi.service.event.Event job) method.
The first part of the code looks as follows:
/**
* The <code>OsgiDropBoxService</code> is listening content added to /tmp/dropbox by using OSGI events
*
* @scr.component immediate="true"
* @scr.service interface="org.osgi.service.event.EventHandler"
* @scr.property name="event.topics" valueRef="org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED"
*/
The Event Admin service is needed to send the job event:
/**
* The OSGI event admin used for sending events
* @scr.reference
*/
private EventAdmin eventAdmin;
The job topic for dropbox job events needs to be defined:
/** The job topic for dropbox job events. */
public static final String JOB_TOPIC = "com/sling/eventing/dropbox/job";
The org.osgi.service.event.EventHandler#handleEvent(Event event) method needs to be implemented:
public void handleEvent(Event event) {
if (EventUtil.isLocal(event))
Unknown macro: {
EventUtil.processJob(event, this);
}
}
The org.apache.sling.event.JobProcessor#process(Event event) method needs to be implemented.
The logic is as follows:
- the OSGI event is analyzed
- if the event is a file that has been added to "/tmp/dropbox":
- an event is created with 2 properties:
- one to flag the event as a job event
- the file path
- the job event is sent to all the listeners that subscribe to the topic of the event.
public boolean process(Event event) {
String propPath = (String) event.getProperty(SlingConstants.PROPERTY_PATH);
String propResType = (String) event.getProperty(SlingConstants.PROPERTY_RESOURCE_TYPE);
// an event is sent if a file is added to /tmp/dropbox
if (propPath.startsWith("/tmp/dropbox") && propResType.equals("nt:file")) {
final Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(EventUtil.PROPERTY_JOB_TOPIC, JOB_TOPIC);
props.put("resourcePath", propPath);
Event dropboxJobEvent = new Event(EventUtil.TOPIC_JOB, props);
eventAdmin.sendEvent(dropboxJobEvent);
log.info("the dropbox job has been sent: {}", propPath);
}
return true;
}
The whole service looks now as follows:
...
Now that you have a service that sends job events whenever a file is uploaded to "/tmp/dropbox",
you will create the service DropBoxEventHandler that listens to those job events and moves the files to a location
corresponding to their mime-types.
Listening to job events
To listen to the job events that have been defined before:
- The service also needs to implement the interface org.osgi.service.event.EventHandler and its handleEvent(Event event) method.
- The property "event.topics" needs to be set to "com.jck.sling.eventing.osgi.OsgiDropBoxService.JOB_TOPIC" in the class annotations.
To move the files:
- The service needs to implement the interface org.apache.sling.event.JobProcessor and its process(org.osgi.service.event.Event job) method.
The first part of the code looks as follows:
/**
* @scr.component immediate="true"
* @scr.service interface="org.osgi.service.event.EventHandler"
* @scr.property name="event.topics" valueRef="com.jck.sling.eventing.osgi.OsgiDropBoxService.JOB_TOPIC"
*/
public class DropBoxEventHandler implements JobProcessor, EventHandler {
Several class fields are defined:
- for logging
- to reference the SlingRepository and the JcrResourceResolverFactory services, which are used in the implementation
- to define the destination path of the files
/** Default log. */
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private Session adminSession;
/** @scr.reference */
private SlingRepository repository;
/**
* @scr.reference
*/
private JcrResourceResolverFactory resolverFactory;
private final static String IMAGES_PATH = "/dropbox/images/";
private final static String MUSIC_PATH = "/dropbox/music/";
private final static String MOVIES_PATH = "/dropbox/movies/";
private final static String OTHER_PATH = "/dropbox/other/";
The org.osgi.service.event.EventHandler#handleEvent(Event event) method needs to be implemented:
public void handleEvent(Event event) {
if (EventUtil.isLocal(event))
}
The org.apache.sling.event.JobProcessor#process(Event event) method needs to be implemented.
The logic is as follows:
- the resource path is extracted from the job event property
- the resource is obtained from the file path
- if the resource is a file, the destination path is defined based on the file mime-type
- the file is moved to the new location
public boolean process(Event event) {
try {
String resourcePath = (String) event.getProperty("resourcePath");
String resourceName = resourcePath.substring(resourcePath.lastIndexOf("/") + 1);
adminSession = repository.loginAdministrative(null);
ResourceResolver resourceResolver = resolverFactory.getResourceResolver(adminSession);
Resource res = resourceResolver.getResource(resourcePath);
if (ResourceUtil.isA(res, "nt:file")) {
String mimeType = res.getResourceMetadata().getContentType();
String destDir;
if (mimeType.equals("image/png"))
Unknown macro: {
destDir = IMAGES_PATH;
}
else if (mimeType.equals("audio/mpeg"))
Unknown macro: {
destDir = MUSIC_PATH;
}
else if (mimeType.equals("video/x-msvideo"))
Unknown macro: {
destDir = MOVIES_PATH;
}
else
Unknown macro: {
destDir = OTHER_PATH;
}
adminSession.move(resourcePath, destDir + resourceName);
adminSession.save();
log.info("The file {} has been moved to {}", resourceName, destDir);
}
return true;
} catch (RepositoryException e)
Unknown macro: {
log.error("RepositoryException}
finally {
if (adminSession != null && adminSession.isLive())
Unknown macro: {
adminSession.logout();
adminSession = null;
}
}
}
The complete code for the service looks as follows:
...
2 services
Sling sends OSGi events when resources are added, removed or changed
org.apache.sling.api.SlingConstants:
TOPIC_RESOURCE_ADDED
TOPIC_RESOURCE_REMOVED
TOPIC_RESOURCE_CHANGED
Web console: see all the events sent
http://localhost:4502/system/console/events
How to listen to osgi events:
subscribe to the event
* @scr.property name="event.topics" valueRef="org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED"
the service needs to implement JobProcessor and EventHandler (org.osgi.service.event.EventHandler, listener for events)
process(org.osgi.service.event.Event job) from JobProcessor
handleEvent(Event event) from EventHandler
Send a job event
reference the osgi EventAdmin
define the JOB_TOPIC
define the job and send it:
final Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(EventUtil.PROPERTY_JOB_TOPIC, JOB_TOPIC);
props.put("nodePath", propPath);
Event dropboxJobEvent = new Event(EventUtil.TOPIC_JOB, props);
eventAdmin.sendEvent(dropboxJobEvent);
Listen to a Job event
same as listening to an event, except:
* @scr.property name="event.topics" valueRef="com.jck.sling.eventing.osgi.OsgiDropBoxService.JOB_TOPIC"
Complete code sample
...