Making An Event Listener

To make an event listener, create a Java class that extends the abstract class neo.xredsys.api.services.AsyncEventListenerService. This is a convenience class that extends neo.nursery.AbstractNurseryService. It provides boilerplate implementations of the methods defined in the neo.xredsys.api.IOEventListener and neo.xredsys.api.IOEventFilter interfaces.

Your class must include implementations of the following methods:

accept(event)

This method is defined in the neo.xredsys.api.services.AsyncEventListenerService class. Use it to determine which events your listener will respond to. Return true for all events you want the listener to respond to.

This method is called synchronously and blocks other event listeners from receiving events while it is executing. You should therefore only use it carry out fast initial filtering of events.

handle(event)

This method is defined in the neo.xredsys.api.services.AsyncEventListenerService class. It is called every time your accept(event) method returns true. Use it to perform whatever actions you want to be carried out for accepted events.

This method is called asynchronously and therefore does not block other event listeners. The number of events waiting to be responded to by this method can be seen in the backlog property. The execution time of this method can be recorded by the optional HitCollector.

Your class must also have a public no argument constructor. For example:

public class MyService extends AsyncEventListenerService {
  public MyService() {
    super(false);
  }
  //More code goes here...
}

This constructor creates an AsyncEventListenerService that listens to all events, both local and remote. If you want to create a service that only listens to local events, set super(true) in the constructor.

Your class may optionally implement the following methods as well:

startEventListener()

This method is defined in neo.xredsys.api.services.AsyncEventListenerService. You can use it to carry out any actions that you want to be performed when the service is started. You might, for example, use it to validate the configuration of your service.

stopEventListener()

This method is defined in neo.xredsys.api.services.AsyncEventListenerService. You can use it to carry out any actions that you want to be performed when the service is stopped. You might, for example, use it to clear lists that have been populated while the service was running.

For more detailed information about neo.xredsys.api.services.AsyncEventListenerService, see the javadoc.

Here is an example event listener called com.mycompany.events.NewArticleNotifier that:

  • Sends an e-mail to a configured e-mail address whenever a new content item is created

  • Validates its configuration on start-up

The class's constructor calls its super constructor with the parameter true:

  public NewArticleNotifier() {
    super(true);
  }

This ensures that the service only listens to local events and will therefore only send an e-mail when a content item is created on the local server. Setting this parameter to false would result in multiple mails being sent for every content item created (one from each server in the cluster).

Properties are defined to hold the both the address to which notifications are to be sent and the sender address to be included in the messages. It is considered good practice to inject parameters this way.

  public void setEmail(final String pEmail) {
    this.email = pEmail;
  }

  public String getEmail() {
    return this.email;
  }
  public void setEmailSender(final EmailSender pEmailSender) {
    this.emailSender = pEmailSender;
  }

  public EmailSender getEmailSender() {
    return this.emailSender;
  }

The accept method accepts only OBJECT_CREATED events, and only for objects of type article. It is important to ensure that this method returns fast since it blocks the execution of other event listeners' accept methods. If your accept method uses several criteria to select events, It is a good idea to place the if-tests that will reject most events first, as here:

  @Override
  protected boolean accept(final IOEvent pEvent) throws Exception {
    if (IOEvent.OBJECT_CREATED == pEvent.getType()) {
      if (IOAtom.OBJECTTYPE_ARTICLE == pEvent.getObjectKey().getObjectType()) {
        return true;
      }
    }
    return false;
  }

The handle method is only called only for events where the accept method has returned true. It is therefore safe to assume in this case that the input event is an OBJECT_CREATED event for an article. The method is executed in a separate thread so it does not need to be especially fast. If it executes too slowly to keep up with the number of events being generated then the value of the backlog property will increase accordingly.

If you define a hit collector for your event listener (see Performance monitoring in Using An Event Listener), then the execution time of this method will be recorded by the hit collector. You can view the information gathered by the hit collector on the escenic-admin web application's Performance Summary page. For information about escenic-admin see The escenic-admin Web Application.

  @Override
  protected void handle(final IOEvent pEvent) throws Exception {
    Article article = (Article) pEvent.getObject();

    EmailEvent email = new EmailEvent();
    email.setRecipients(new Address[] {new InternetAddress(getEmail())});
    email.setSubject("New article with title '" + article.getTitle() + "' was created");

    getEmailSender().sendMessage(email);
  }

The startEventListener method is called after all properties have been set, but before the EventListener is registered with the Content Engine so that it can receive events. If startEventListener throws an exception then the EventListener is not registered and will not receive any events. The startEventListener method must include a super.startEventListener() call.

The example shown here performs some simple checks on the supplied configuration values:

  @Override
  protected void startEventListener() throws IllegalStateException, IllegalArgumentException, Exception {
    super.startEventListener();

    Validate.notNull(getEmailSender(), "Email sender is not set, will not start the '" + getClass().getName() + "' service");
    Validate.notNull(getEmail(), "Email is not set, will not start the '" + getClass().getName() + "' service");
  }

Here is the example code again in full, including all the necessary boilerplate code:

package com.mycompany.events;

import javax.mail.Address;
import javax.mail.internet.InternetAddress;

import neo.xredsys.api.Article;
import neo.xredsys.api.IOAtom;
import neo.xredsys.api.IOEvent;
import neo.xredsys.api.services.AsyncEventListenerService;
import neo.xredsys.email.EmailEvent;
import neo.xredsys.email.EmailSender;

import org.apache.commons.lang.Validate; 

public class NewArticleNotifier extends AsyncEventListenerService {


  public NewArticleNotifier() {
    super(true);
  }

  private String email;

  public void setEmail(final String pEmail) {
    this.email = pEmail;
  }

  public String getEmail() {
    return this.email;
  }


  private EmailSender emailSender;

  public void setEmailSender(final EmailSender pEmailSender) {
    this.emailSender = pEmailSender;
  }

  public EmailSender getEmailSender() {
    return this.emailSender;
  }


  @Override
  protected boolean accept(final IOEvent pEvent) throws Exception {
    if (IOEvent.OBJECT_CREATED == pEvent.getType()) {
      if (IOAtom.OBJECTTYPE_ARTICLE == pEvent.getObjectKey().getObjectType()) {
        return true;
      }
    }
    return false;
  }


  @Override
  protected void handle(final IOEvent pEvent) throws Exception {
    Article article = (Article) pEvent.getObject();

    EmailEvent email = new EmailEvent();
    email.setRecipients(new Address[] {new InternetAddress(getEmail())});
    email.setSubject("New article with title '" + article.getTitle() + "' was created");

    getEmailSender().sendMessage(email);
  }


  @Override
  protected void startEventListener() throws IllegalStateException, IllegalArgumentException, Exception {
    super.startEventListener();

    Validate.notNull(getEmailSender(), "Email sender is not set, will not start the '" + getClass().getName() + "' service");
    Validate.notNull(getEmail(), "Email is not set, will not start the '" + getClass().getName() + "' service");
  }
}

Before an event listener can be used it must be:

  • Compiled

  • Added to the Content Engine's classpath

To compile the example you must add the following JAR files to the classpath:

engine-core-5.4.7.169266.jar

This is the Escenic jar that contain most of the classes needed.

common-nursery-5.4.7.169266.jar

The Content Engine's dependency injection framework.

commons-lang-2.3.jar

The Content Engine's validation framework.

mail-1.4.jar

This jar contain the dependency injection framework (Nursery) we use.