Apache
Home » Documentation » Tutorials & How-Tos

How to Manage Events in Sling

Apache Sling provides some mechanisms and support for managing events.

The event mechanism is leveraging the OSGi Event Admin Specification (OSGi Compendium 113). The OSGi API is very simple and lightweight. 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. Sling makes a distinction between events and job events. Unlike events, job events are garanteed to be processed. In other words: someone has to do something with the job event (do the job).

For more details please refer to the following resources:

This page drives you through the implementation of two services that rely on the Sling eventing mechanisms. The services implement the following use case: whenever a file is uploaded to a temporary location in your web application, the file is moved to a specific location according to its MIME type.

Introduction

You will now implement the logic to listen to files posted to /tmp/dropbox and to move them to the appropriate locations depending on the MIME type:

To do that, you will implement two services. The first one, called DropBoxService:

The second one, called DropBoxEventHandler:

Listening to OSGI Events

To listen to OSGi events in Sling you just need to register an org.osgi.service.event.EventHandler service with an event.topics property that describes which event topics the handler is interested in.

To listen to a Sling resource added events, for example, you'll set the event.topics property to org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED in the class annotations:

 @Property(name="event.topics",
    value=org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED)

The javadocs of the TOPIC_ constants in the org.apache.sling.api.SlingConstants class lists and explains the available event topics available in Sling.

Sending Job Events

To send an event the following code can be used:

public void sendEvent() {
    final Dictionary<String, Object> props = new Hashtable<String, Object>();
    props.put(JobUtil.PROPERTY_JOB_TOPIC, JOB_TOPIC);
    props.put("resourcePath", RESOURCE_PATH);
    final Event myEvent = new Event(JobUtil.TOPIC_JOB, props);
    eventAdmin.sendEvent(myEvent);
}

However, for our example, to send a job event the service needs to implement the org.osgi.service.event.EventHandler and org.apache.sling.event.JobProcessor interfaces:

public class DropBoxService implements JobProcessor, EventHandler {
    ...
}

To send the job event the Event Admin service needs to be referenced:

@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)) {
        EventUtil.processJob(event, this);
    }
}

The org.apache.sling.event.JobProcessor#process(Event event) method needs to be implemented:

Its logic is as follows:

For example:

public boolean process(Event event) {

    // get the resource event information
    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")) {

        // configure the job event
        final Dictionary<String, Object> props = new Hashtable<String, Object>();
        props.put(EventUtil.PROPERTY_JOB_TOPIC, JOB_TOPIC);
        props.put("resourcePath", propPath);

        // create the job event
        Event dropboxJobEvent = new Event(EventUtil.TOPIC_JOB, props);

        // deliver the job event
        eventAdmin.sendEvent(dropboxJobEvent);

        log.info("the dropbox job has been sent: {}", propPath);
    }

    // all set and done
    return true;
}

The complete code for the DropBoxService service is available here.

Listening to Job Events

Now that you have implemented a service that sends a job event when a file is uploaded to /tmp/dropbox, you will implement the service DropBoxEventHandler that listens to those job events and moves the files to a location according to their MIME types.

To listen to the job events that have been defined before the property event.topics needs to be set to mypackage.DropBoxService.JOB_TOPIC in the class annotations:

@Property(name="event.topics",
    value=mypackage.DropBoxService.JOB_TOPIC)

Handling Job Events

To move the files the service needs to implement the org.osgi.service.event.EventHandler and org.apache.sling.event.JobProcessor interfaces:

public class DropBoxEventHandler implements JobProcessor, EventHandler {

Some class fields need to be defined:

For example:

/** Default log. */
protected final Logger log = LoggerFactory.getLogger(this.getClass());

@Reference
private SlingRepository repository;

@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)) {
        EventUtil.processJob(event, this);
    }
}

The org.apache.sling.event.JobProcessor#process(Event event) method needs to be implemented.

Its logic is as follows:

or in Java Code:

public boolean process(Event event) {
    Session adminSession = null;
    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")) {
                destDir = IMAGES_PATH;
            } else if (mimeType.equals("audio/mpeg")) {
                destDir = MUSIC_PATH;
            } else if (mimeType.equals("video/x-msvideo")) {
                destDir = MOVIES_PATH;
            } else {
                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) {
        log.error("RepositoryException: " + e);
        return false;
    } finally {
        if (adminSession != null && adminSession.isLive()) {
            adminSession.logout();
            adminSession = null;
        }
    }
}

The complete code for the DropBoxEventHandler service is available here.

Rev. 1478710 by bdelacretaz on Fri, 3 May 2013 10:10:36 +0000
Apache Sling, Sling, Apache, the Apache feather logo, and the Apache Sling project logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.