Event Stream Module

Last modified by Raphaël Jakse on 2023/03/02 15:36

cogActivity events manipulation API
Developed byUnknown
1 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard


The Event stream module offers APIs to manipulate activity events. An activity event is a recorded event that occurred at some point in the wiki.

This API replaces the older, deprecated, Activity Stream Plugin.

Note that since XWiki 9.6RC1, you can also define custom events inside wiki pages through the EventClass XObject.


Events are identified by:

  • an identifier unique for each event
  • a group identifier used for all the events caused during the same action
  • the exact date and time when the event occurred
  • an importance which allows to order events in the same group, or in the same stream

Events contain:

  • a title
  • a body
  • and a map of "parameters" (14.2+ API renamed to "custom")

Events are categorized into:

  • the application that created them, like blog, user statuses, or the general wiki
  • an eventual sub-stream inside the application, for example a space for wiki events, or a certain group for user statuses
  • the particular type of event, like adding a comment, updating an attachment, setting a new user status

Events target:

  • the wiki,
  • the space,
  • and the document affected by the event
  • and an eventual entity inside the document, for example an individual attachment for attachment events, or an individual object for comment events.

Events can store some more information about the context in which they occur:

  • the user that caused the event
  • the version of the target document at the time that the event occurred
  • the display title of the target document at the time that the event occurred
  • the requested URL that caused the event


If the new event store is enabled (the default) events can also store a map of custom properties.

The default implementation support the following types for the properties values:

  • String
  • Double
  • Float
  • Long
  • Integer
  • Boolean
  • Date
  • byte[]
  • Iterable or array of the previously listed types in input (converted to List)


Before 12.6 by default all the events are stored in the XWiki relational database. Since 12.6 a new Solr based store is now the default location where events are stored and from which they are retrieved. A migrator is available since 12.6.1 in the Distribution Wizard and in the Wiki Adminsitration to copy events from the old store to the new one.

The legacy store is currently still populated with the events and used as fallback for deprecated APIs which are not yet fully supported by the new event store API and/or Solr implementation (a good example being the HQL based search API which will most probably never be supported by the new store).

Code Samples


Adding an event to the event store (XWiki 12.5+)

  // [...]

 // Some imports we will need :

import org.xwiki.eventstream.Event;
import org.xwiki.eventstream.EventFactory;
import org.xwiki.eventstream.EventStore;

 // [...]

 // Some requirements to add events :

 private EventFactory eventFactory;

 private EventStore eventStore;

 // [...]

 // Adding an event to the stream :
 Event event = this.eventFactory.createEvent();

  event.setApplication("yowler"); // (It's like twitter, but only for cats)

  Map<String, String> params = new HashMap<String, String>();
  params.put("yowled", "meoooow !");


 // All write operation of the store are asynchonous and return a CompletableFuture
 CompletableFuture<Event> future = this.eventStore.saveEvent(event);

 // Wait for the event to be stored

Note that directly adding an event to the store is generally a bad practice and the best practice is to sent an event implementing org.xwiki.eventstream.RecordableEvent in the ObservationManager.

Retrieving events from the store (XWiki 12.5+)

  import org.xwiki.eventstream.EventSearchResult;
 import org.xwiki.eventstream.query.SimpleEventQuery;
 import org.xwiki.eventstream.query.SortableEventQuery.SortClause.Order;

  SimpleEventQuery query = new SimpleEventQuery();
 // Only return events for which the application is "yowler"
 query.eq(Event.FIELD_APPLICATION, "yowler");
 // Since 13.9 it's possible to manipulate custom event parameters
 query.parameter().eq("mycustomperameter", "myvalue");
 // Since 14.2 the custom parameters are deprecated and replaced with more generic custom properties with support for various other types than String
 query.custom().eq("mycustomnumber", 42);
 // Search for events in which the custom list property "mycustomlist" has one of its elements equal to "element1"
 query.custom(List.class).eq("mycustomlist", "element1");
 // Since 14.4RC1 it's possible to search for text contained in a field
 query.contains(Event.FIELD_DOCUMENTTITLE, "Hello");
 // Filter out all events triggered by superadmin
 query.not().eq(Event.FIELD_USER, "xwiki:XWiki.superadmin");
 // Sort events by date in descending order
 query.addSort(Event.FIELD_DATE, Order.DESC);
 // Limit the number of result to return

  EventSearchResult result = this.eventStore.search(query)

  result.stream().forEach((event) -> {
   // Do anything with those events

Define custom events in wiki pages

Since XWiki 9.6RC1, the EventClass XObject allows anyone with the admin rights on the current wiki to define a custom event. In order to demonstrate how a custom event definition can be useful, we will use one event defined in the Blog application that should be triggered when a blog post is published, and "translate" it into its equivalent XObject definition.

First of all, we have to create an XObject of type EventClass in any wiki page (a hidden page with restricted access rights is still preferred). This XObject has several parameters : 

  • Application: The name of the application related to the event;
  • Event Type: The type (name) of the event;
  • Event Icon: The name of the icon associated to the event;
  • Event description: A short description of the event that can be displayed to the user;
  • Listen to …: A list of events that can be used to trigger this custom event. Only one event in this list is needed to trigger the custom event.
  • Object Type: A list of XObject types that should be linked to the document that is the source of the event, note that you can leave this field blank in case where one of the triggering events defined in the Listen to … field doesn’t have a particular document attached to it;
  • Validation Template: In this template, you can define specific conditions in order to allow (or not) the triggering of the custom event :
    • The template comes with two additional context variables :
      • event: the event, which is an instance of one of the classes defined in the Listen to … field;
      • source: the source of the event;
    • In order to validate the triggering of the custom event, the template should affect the value "true" to the variable "xreturn". Example: #set ($xreturn = true);

      • Before XWiki 9.11.2, the template needed to write "true" once rendered. If you need to support older wikis, the "xreturn" binding cannot be used
    • If the Validation Template field is left blank, the template rendering will be ignored.
  • Target (Since 9.11.2): In this template, you can define a list of users to which the event will be sent. Other users will not see this event.
    • The template comes with two additional context variables :
      • event: the event, which is an instance of one of the classes defined in the Listen to … field;
      • source: the source of the event;
    • In order to generate the target list, the template should affect a list of user id (serialized references of the users) to the variable "xreturn". Examples: #set ($xreturn = ['xwiki:XWiki.UserA', 'xwiki:XWiki.UserB']) or #set ($xreturn = [$services.model.serialize($event.user, 'default')]).

The Application and the Event description parameters can both be set with translation keys in order to be displayed in multiple languages.

Here is one example of an XObject that copy the same comportment of the BlogPostPublishedEvent in the Blog application :


Once your XObject is defined … everything is done ! The event is now registered. If you use the notifications application provided by default with XWiki Platform, every user should now see the custom event in their notification center, and should be able to subscribe to it : 


User notifications preferences are not deleted when you remove a custom event from your wiki. If you create a new custom event with the same Event Type, the user preferences will be restored.

Example of an event (notification) sent to a particular user only if the wiki handle the TargetableEvents

  • field: Validation Expression:
      ## The "xreturn" binding is supported since 9.11.2, which mean older xwiki versions won't send this event
     #set ($xreturn = true)
  • field: Target:
      ## Example: send the event to the creator of the document only
     #set ($document = $xwiki.getDocument($event.document))
     #set ($xreturn = [$services.model.serialize($document.creatorReference, 'default')])

When to use Untyped Event instead of creating your own RecordableEvent

If you are a Java developer, you may prefer to create your own class that implement the RecordableEvent interface (as explained in Tutorial: How to send notifications).

But if you don't want to add a Java module in your extension, or if you don't want to depend to a recent version of XWiki and still support XWiki versions older than 9.11.2, you should used this Custom Event mechanism. Old XWiki versions will only ignore this XObject.

Events sent by the Event Stream

Since XWiki 9.6RC1, the Event Stream can send two new events :

In order to avoid event loops (an event from the observation manager that adds a new event to the event stream that triggers another event from the observation manager that adds new events to the event stream and so on …), the default implementation of the Event Stream will not allow more than one EventStreamEvent to be added to the Event Stream in the same execution context.

You can find out more checking out this module source code :


Created by Jerome on 2011/08/15 17:50

Get Connected