Writing an Event Listener
XWiki supports notifications (a.k.a Observation Events) and it's possible to do some action when a document is modified, when a document's objects are modified, when a wiki is created, etc. See the full documentation in the Observation reference documentation.
There are several ways to write an Event Listener to react to some events:
- In a wiki page, as a Wiki Component
- In a wiki page, using Groovy, by writing a Component and manually registering it against the Component Manager
- In Java, as a Component
This tutorial will demonstrate all techniques on several various examples.
Adding content to pages on save
The goal is to listen to DocumentCreatingEvent and DocumentUpdatingEvent events (note that these events are fired before the page is saved and thus our code doesn't need to perform the save itself since this is done automatically).
Let's implement this example using a Wiki Component.
Follow these steps:
- Create a page, for example EventListeners.DocumentSaveListener
- Add a XWiki.ComponentClass XObject in it
- Component Role Type: org.xwiki.observation.EventListener
- Component Role Hint: mytest (you can use whatever name you want, it's the technical id of your listener that you won't be using in this tutorial)
- Component Scope: Current Wiki (this means this listener component will be registered in the current, but whatever the scope you choose, the listener will receive all events).
- Add a XWiki.ComponentMethodClass XObject for implementing the getEvents() method:
- Method Name: getEvents
- Method body code:{{groovy}}
import org.xwiki.bridge.event.*
xcontext.method.output.value = [new DocumentCreatingEvent(), new DocumentUpdatingEvent()]
{{/groovy}}
- Add another XWiki.ComponentMethodClass XObject for implementing the getName() method:
- Method Name: getName
- Method body code:{{groovy}}
xcontext.method.output.value = "mytest"
{{/groovy}}Note that mytest should be the same technical id that you've used above.
- Add another XWiki.ComponentMethodClass XObject for implementing the onEvent() method:
- Method Name: onEvent
- Method body code:{{groovy}}
def docSource = xcontext.method.input.get(1)
if (docSource.space != "EventListeners") {
docSource.setContent(docSource.content + "\n\nSome extra content...")
}
{{/groovy}}
- Save!
When you save the EventListeners.DocumentSaveListener page, the component you've defined (your Event Listener) is automatically registered and active.
You can verify it works by creating a new page or editing an existing page and you should see the text Some extra content... added at the bottom of your pages when you save them.
Log when a document is modified
In this example we want to log all document changes by adding a line in a page named Main.Logs.
We'll implement this use case using Groovy in a wiki page.
import org.xwiki.observation.*
import org.xwiki.observation.event.*
import org.xwiki.bridge.event.*
import org.xwiki.model.reference.*
import org.xwiki.model.*
import com.xpn.xwiki.web.*
import com.xpn.xwiki.*
class LoggingEventListener implements EventListener
{
def logReference = new EntityReference("Logs", EntityType.DOCUMENT,
new EntityReference("Main", EntityType.SPACE))
String getName()
{
// The unique name of this event listener
return "logging"
}
List<Event> getEvents()
{
// The list of events this listener listens to
return [new DocumentUpdatedEvent()]
}
// Called by the Observation Manager when an event matches the list of events returned
// by getEvents()
void onEvent(Event event, Object source, Object context)
{
// Prevent infinite recursion since in this example we log to wiki page which
// triggers a document change... :)
if (source.fullName != "Main.Logs") {
def xwiki = context.getWiki()
def document = xwiki.getDocument(logReference, context)
document.setContent("${document.getContent()}\n* ${source.fullName} has been modified!")
xwiki.saveDocument(document, "Logging event", true, context)
}
}
}
// Register against the Observation Manager
def observation = Utils.getComponent(ObservationManager.class)
observation.removeListener("logging")
def listener = new LoggingEventListener()
observation.addListener(listener)
{{/groovy}}
You can add other events to the getEvents returned list if you wish to listener to other events, for example:
return [new DocumentUpdateEvent(), new DocumentSaveEvent(), new DocumentDeleteEvent()]
...
Activating the listener
Contrary to the Wiki Component-based solution above, this solution (Groovy script) requires that the page be viewed/rendered for the listener to be registered against the Observation Manager. Thus if you restart your wiki for example, the page won't be rendered unless someone navigates to it.
If you need your script to always be executed in your wiki, even when it restarts, please check the tutorial to write a Script Service in Groovy which demonstrates a generic case based on Wiki Components to ensure that a script is always executed at least once in the wiki.
Send a mail whenever a comment is added
Let's implement this use case in Java this time!
Note: We're going to listen to CommentAddedEvent but in general if you wish to be notified when an XObject has been updated you should listen to XObjectUpdatedEvent (or XObjectPropertyUpdatedEvent when an XProperty has been updated).
- Let's use the XWiki Maven Archetype for creating a skeleton project, by following this tutorial.
- Make sure you have the following 2 dependencies in your pom.xml file (adjust the version to use some recent XWiki version):...
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-oldcore</artifactId>
<version>5.4.5</version>
</dependency>
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-mailsender</artifactId>
<version>5.4.5</version>
</dependency>
... - Create a CommentListener class in package org.xwiki.contrib.internal (pick the package name you wish!) with the following content:package org.xwiki.contrib.internal;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.observation.EventListener;
import org.xwiki.observation.event.Event;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.internal.event.XObjectAddedEvent;
import com.xpn.xwiki.objects.BaseObject;
import com.xpn.xwiki.plugin.mailsender.MailSenderPluginApi;
@Component
@Named("CommentEventListener")
@Singleton
public class CommentEventListener implements EventListener
{
@Inject
private Logger logger;
private EntityReference commentClassReference = new EntityReference("XWikiComments", EntityType.DOCUMENT,
new EntityReference("XWiki", EntityType.SPACE));
@Override public String getName()
{
return "CommentEventListener";
}
@Override public List<Event> getEvents()
{
return Arrays.<Event>asList(new XObjectAddedEvent());
}
@Override public void onEvent(Event event, Object source, Object data)
{
XWikiDocument document = (XWikiDocument) source;
BaseObject commentObject = document.getXObject(this.commentClassReference);
if (commentObject != null) {
try {
// Get comment
String comment = commentObject.getStringValue("comment");
// Send email
XWikiContext xcontext = (XWikiContext) data;
MailSenderPluginApi mailSender = (MailSenderPluginApi) xcontext.getWiki().getPluginApi("mailsender", xcontext);
mailSender.sendTextMessage("XWiki <xwiki@no-reply>", "john@doe.com",
"[XWiki] Comment added to " + document.toString(), comment);
} catch (Exception e) {
this.logger.error("Failure in comment listener", e);
}
}
}
} - Don't forget to register your component in the META-INF/components.txt file in your project:org.xwiki.contrib.internal.CommentEventListener
- Build the project with maven: mvn clean install and copy the JAR generated in the target directory in your XWiki's WEB-INF/lib directory, and restart XWiki.
Before trying it, go to your wiki's administration page and make sure you've configured the Mail Server settings. Try it by adding a new comment on a page. You should receive an email!
Old Notifications Tutorial
If you're using an old version of XWiki (prior to 2.0) you should check this old Notifications Tutorial.