Wiki source code of Local Observation Module

Last modified by Manuel Leduc on 2023/10/10 13:59

Show last authors
1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
4
5 Provides the ability to listen to internal XWiki events such as events when document change, when an action is executed, etc.
6
7 {{warning}}
8 TODO: Add architecture diagram here
9 {{/warning}}
10
11 = Features =
12
13 * Supports any kind of event, including custom events
14 * API to listen to a particular event type
15 * API to listen to any event type
16 * API to register event listeners
17 ** Automatically as component
18 ** Programmatically
19 * API to unregister event listeners programmatically
20 * API to send events to all listeners
21
22 = Examples =
23
24 See this [[Writing an Event Listener Tutorial>>xwiki:Documentation.DevGuide.Tutorials.WritingEventListenerTutorial.WebHome]].
25
26 {{toc scope="local"/}}
27
28 == Consuming and producing events ==
29
30 Observe **document save events** in the wiki, and fire a custom **user created** event when the saved document is a user document.
31
32 The first class is the event listener, the second defines the custom event itself.
33
34 An ##org.xwiki.observation.AbstractEventListener## helper is provided.
35
36 {{code language="java"}}
37 package com.example;
38
39 import java.util.Arrays;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43
44 import javax.inject.Inject;
45 import javax.inject.Named;
46
47 import org.xwiki.bridge.event.DocumentCreatedEvent;
48 import org.xwiki.bridge.event.DocumentDeletedEvent;
49 import org.xwiki.bridge.event.DocumentUpdatedEvent;
50 import org.xwiki.component.annotation.Component;
51 import org.xwiki.component.manager.ComponentLookupException;
52 import org.xwiki.component.manager.ComponentManager;
53 import org.xwiki.model.reference.DocumentReference;
54 import org.xwiki.observation.EventListener;
55 import org.xwiki.observation.ObservationManager;
56 import org.xwiki.observation.event.Event;
57
58 import com.xpn.xwiki.doc.XWikiDocument;
59
60 import com.example.event.UserCreationEvent;
61
62 // Declare that this class is a component
63 @Component
64 // The unique name of the component among the implementations of EventListener. It's not mandatory but the common practice is to use the same String for both the name of the listener and the name of the component.
65 @Named("usercreation")
66 // An optional annotation allowing, since XWiki 15.4, to indicate the priority in which a listener should be called. The lower the number, the higher the priority (the default value is 1000).
67 @Priority(1000)
68 public class UserCreationEventListener extends AbstractEventListener
69 {
70 @Inject
71 private ComponentManager componentManager;
72
73 /**
74 * The observation manager that will be use to fire user creation events. Note: We can't have the OM as a
75 * requirement, since it would create an infinite initialization loop, causing a stack overflow error (this event
76 * listener would require an initialized OM and the OM requires a list of initialized event listeners)
77 */
78 private ObservationManager observationManager;
79
80 public UserCreationEventListener()
81 {
82 super("usercreation", new DocumentCreatedEvent());
83 }
84
85 /**
86 * {@inheritDoc}
87 */
88 public void onEvent(Event event, Object source, Object data)
89 {
90 XWikiDocument document = (XWikiDocument) source;
91 String wikiName = document.getDocumentReference().getWikiReference().getName();
92 DocumentReference userClass = new DocumentReference(wikiName, "XWiki", "XWikiUsers");
93
94 if (document.getXObject(userClass) != null) {
95 // Create a map to hold our new event data
96 Map<String,String> userData = new HashMap<String,String>();
97 userData.put("firstName", document.getXObject(userClass).getStringValue("firstName"));
98 userData.put("lastName", document.getXObject(userClass).getStringValue("lastName"));
99 userData.put("email", document.getXObject(userClass).getStringValue("email"));
100 // Fire the user created event
101 UserCreatedEvent newEvent = new UserCreationEvent();
102 getObservationManager().notify(newEvent, source, userData);
103 }
104 }
105
106 private ObservationManager getObservationManager()
107 {
108 if (this.observationManager == null) {
109 try {
110 this.observationManager = componentManager.getInstance(ObservationManager.class);
111 } catch (ComponentLookupException e) {
112 throw new RuntimeException("Cound not retrieve an Observation Manager against the component manager");
113 }
114 }
115 return this.observationManager;
116 }
117
118 }
119 {{/code}}
120
121 Definition of the custom event:
122
123 {{code language="java"}}
124 package com.example.event;
125
126 import org.xwiki.bridge.event.AbstractDocumentEvent;
127 import org.xwiki.observation.event.Event;
128 import org.xwiki.observation.event.filter.EventFilter;
129
130 /**
131 * {@link Event} generated when a new user is created.
132 */
133 public class UserCreationEvent extends AbstractDocumentEvent
134 {
135 /**
136 * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
137 * changes.
138 */
139 private static final long serialVersionUID = 1L;
140
141 /**
142 * Constructor initializing the event filter with an
143 * {@link org.xwiki.observation.event.filter.AlwaysMatchingEventFilter}, meaning that this event will match any
144 * other document update event.
145 */
146 public UserCreatedEvent()
147 {
148 super();
149 }
150
151 /**
152 * Constructor initializing the event filter with a {@link org.xwiki.observation.event.filter.FixedNameEventFilter},
153 * meaning that this event will match only update events affecting the document matching the passed document name.
154 *
155 * @param documentName the name of the updated document to match
156 */
157 public UserCreatedEvent(String documentName)
158 {
159 super(documentName);
160 }
161
162 /**
163 * Constructor using a custom {@link EventFilter}.
164 *
165 * @param eventFilter the filter to use for matching events
166 */
167 public UserCreatedEvent(EventFilter eventFilter)
168 {
169 super(eventFilter);
170 }
171 }
172 {{/code}}
173
174 == Writing an Event Listener in Groovy in a Wiki page ==
175
176 See the [[tutorial>>xwiki:Documentation.DevGuide.Tutorials.WritingEventListenerTutorial.WebHome||anchor="HAddingcontenttopagesonsave"]].
177
178 == Writing an Event Listener in Velocity in a Wiki page ==
179
180 The [[documentation of the wiki components>>Extension.WikiComponent Module]] contains, as an example, the creation of an Event Listener.
181
182 == Fold events ==
183
184 An event tagged as "Fold" can be sent by a task that generates some events during its execution. Then, these generated events can be seen as children of the main task. In addition, the [[Activity Stream>>Extension.Activity Stream Plugin]] will not record these child events.
185
186 This is an example of a custom fold event:
187
188 {{code language="java"}}
189 package org.xwiki.bridge.event;
190
191 import org.xwiki.observation.event.BeginFoldEvent;
192
193 public class MyTaskBeginEvent extends AbstractDocumentEvent implements BeginFoldEvent
194 {
195 /**
196 * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
197 * changes.
198 */
199 private static final long serialVersionUID = 1L;
200
201 /**
202 * Constructor initializing the event filter with an
203 * {@link org.xwiki.observation.event.filter.AlwaysMatchingEventFilter}, meaning that this event will match any
204 * other document update event.
205 */
206 public MyTaskBeginEvent()
207 {
208 super();
209 }
210
211 /**
212 * Constructor initializing the event filter with a {@link org.xwiki.observation.event.filter.FixedNameEventFilter},
213 * meaning that this event will match only update events affecting the document matching the passed document name.
214 *
215 * @param documentName the name of the updated document to match
216 */
217 public MyTaskBeginEvent(String documentName)
218 {
219 super(documentName);
220 }
221
222 /**
223 * Constructor using a custom {@link EventFilter}.
224 *
225 * @param eventFilter the filter to use for matching events
226 */
227 public MyTaskBeginEvent(EventFilter eventFilter)
228 {
229 super(eventFilter);
230 }
231 }
232 {{/code}}
233
234 Then you need to define the corresponding {{code}}EndFoldEvent{{/code}} :
235
236 {{code language="java"}}
237 package org.xwiki.bridge.event;
238
239 import org.xwiki.observation.event.EndFoldEvent;
240
241 public class MyTaskEndEvent implements EndFoldEvent
242 {
243 /**
244 * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
245 * changes.
246 */
247 private static final long serialVersionUID = 1L;
248
249 /**
250 * Constructor initializing the event filter with an
251 * {@link org.xwiki.observation.event.filter.AlwaysMatchingEventFilter}, meaning that this event will match any
252 * other document update event.
253 */
254 public MyTaskEndEvent()
255 {
256 super();
257 }
258
259 /**
260 * Constructor initializing the event filter with a {@link org.xwiki.observation.event.filter.FixedNameEventFilter},
261 * meaning that this event will match only update events affecting the document matching the passed document name.
262 *
263 * @param documentName the name of the updated document to match
264 */
265 public MyTaskEndEvent(String documentName)
266 {
267 super(documentName);
268 }
269
270 /**
271 * Constructor using a custom {@link EventFilter}.
272 *
273 * @param eventFilter the filter to use for matching events
274 */
275 public MyTaskEndEvent(EventFilter eventFilter)
276 {
277 super(eventFilter);
278 }
279 }
280 {{/code}}

Get Connected