-
Notifications
You must be signed in to change notification settings - Fork 34
Implementing Listeners
Since M3 we've implemented listeners within the API. As of M5 they're only available for the Database class, but they basically allow you to run standard code when an event is triggered from any context that uses the API. There are events like AFTER_UPDATE_DOCUMENT, AFTER_CREATE_DOCUMENT, BEFORE_REPLICATION, BEFORE_DELETE_DOCUMENT. Imagine, for example, being able to inform the user of how many documents they have created / saved during the current session. Or with a single piece of code ensure any deletions are logged automatically. Or being able to keep a track in applicationScope of any document saved via XPages, and so update the full text index when that gets up to a certain number. And there is the potential for this to be expanded beyond those options in the future.
In order to implement a listener you will need three things:
- A class that implements IDominoListener. This identifies what listeners are implemented and the code to run when those listeners are fired.
- A call that adds the listener to the database when it's loaded into memory. The easiest method is to have a Utils.getCurrentDatabase() method, which gets the database and calls Database.addListener(myClassListener) to add the relevant listener(s).
- Code that calls a method that is going to fire the listener. So for the UPDATE_DOCUMENT listeners, doc.save() or its overloaded methods. For the CREATE_DOCUMENT listeners, database.createDocument() method or its overloaded methods.
The IDominoListener interface expects code in two methods:
- getEventTypes
- eventHappened
getEventTypes does what it says on the tin: it provides a List of EnumEvents. That can be any kind of List but an ArrayList should be sufficient. The list of available event types are held in an Enum - a restricted list of options that provides typeahead and validation - called Events. It can be found in the source code in org.openntf.domino.ext.Database. (If listeners are implemented at other levels, they may be held at other levels as well).
Let's have a look at some code:
public List<EnumEvent> getEventTypes() {
ArrayList<EnumEvent> eventList = new ArrayList<EnumEvent>();
eventList.add(Events.AFTER_CREATE_DOCUMENT);
eventList.add(Events.AFTER_UPDATE_DOCUMENT);
return eventList;
}
So the code just creates the ArrayList and adds each event to be registered against for a database.
The eventHappened method is the one where you need to implement your business logic for what needs to occur when the relevant event is triggered. You get an IDominoEvent passed in which is what you will need to act upon. The IDominoEvent object contains four objects and so four methods you might want to take advantage of:
- getEvent() gets the EnumEvent currently being triggered. Because this method gets called for any EnumEvent assigned to the Listener, you'll want to check which EnumEvent is currently being triggered, to know what code to run.
- getSource() gets the source object the action is being triggered from, so for AFTER_CREATE_DOCUMENT and AFTER_UPDATE_DOCUMENT it will be the Database object the listener has been registered against.
- getTarget() gets the target object the action is being triggered against. For AFTER_CREATE_DOCUMENT, it's just called createDocument() and no Items have yet been added - not even the Form name - so there's no point in that being the target. So the target for that Event is again the Database. But for AFTER_UPDATE_DOCUMENT it's just called save(), so the Document being saved is the target. This would allow you to, for example, add an audit to an Item on the document detailing who's saving it and when.
- getPayload() gets a payload object being passed along with the the event. As of M5 no calls to eventHappened pass a payload, so this is for future-proofing.
The method returns a boolean of success or failure. If there is a failure in a BEFORE_ method, it will not trigger the subsequent Domino method, e.g. a fail in BEFORE_CREATE_DOCUMENT will not actually create the document.
So your code needs to check the event being triggered (unless your Listener only implements one EnumEvent type), then call a method that does whatever you want to occur, like this:
public boolean eventHappened(IDominoEvent event) {
try {
if (event.getEvent().equals(Events.AFTER_CREATE_DOCUMENT)) {
return incrementCreateCount();
}
if (event.getEvent().equals(Events.AFTER_UPDATE_DOCUMENT)) {
return incrementUpdateCount();
}
return false;
} catch (Exception e) {
return false;
}
}
It checks the event against the ones implemented by the Listener, in which case it calls a method to do whatever needs to be done. The method called is not important. All that's important is that it will return true if it runs successfully or false if there is an error.
When, for example, a create or save occurs, the code gets a collection of all Listeners that implement the relevant EnumEvent. It then iterates them and calls the eventHappened method, passing an IDominoEvent object that holds the EnumEvent type, the source, the target and the payload. Simple in its concept but extremely powerful in its potential scalability.