Wiki source code of Local Observation Module

Last modified by Clemens Robbenhaar on 2015/06/19 18:07

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>>platform:DevGuide.WritingEventListenerTutorial]].
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 Since 5.4 an ##org.xwiki.observation.AbstractEventListener## 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 @Component
63 @Named("usercreation")
64 public class UserCreationEventListener extends AbstractEventListener
65 {
66 @Inject
67 private ComponentManager componentManager;
68
69 /**
70 * The observation manager that will be use to fire user creation events. Note: We can't have the OM as a
71 * requirement, since it would create an infinite initialization loop, causing a stack overflow error (this event
72 * listener would require an initialized OM and the OM requires a list of initialized event listeners)
73 */
74 private ObservationManager observationManager;
75
76 public UserCreationEventListener()
77 {
78 super("usercreation", new DocumentCreatedEvent());
79 }
80
81 /**
82 * {@inheritDoc}
83 */
84 public void onEvent(Event event, Object source, Object data)
85 {
86 XWikiDocument document = (XWikiDocument) source;
87 String wikiName = document.getDocumentReference().getWikiReference().getName();
88 DocumentReference userClass = new DocumentReference(wikiName, "XWiki", "XWikiUsers");
89
90 if (document.getXObject(userClass) != null) {
91 // Create a map to hold our new event data
92 Map<String,String> userData = new HashMap<String,String>();
93 userData.put("firstName", document.getXObject(userClass).getStringValue("firstName"));
94 userData.put("lastName", document.getXObject(userClass).getStringValue("lastName"));
95 userData.put("email", document.getXObject(userClass).getStringValue("email"));
96 // Fire the user created event
97 UserCreatedEvent newEvent = new UserCreationEvent();
98 getObservationManager().notify(newEvent, source, userData);
99 }
100 }
101
102 private ObservationManager getObservationManager()
103 {
104 if (this.observationManager == null) {
105 try {
106 this.observationManager = componentManager.getInstance(ObservationManager.class);
107 } catch (ComponentLookupException e) {
108 throw new RuntimeException("Cound not retrieve an Observation Manager against the component manager");
109 }
110 }
111 return this.observationManager;
112 }
113
114 }
115 {{/code}}
116
117 Here is an example before 5.4:
118
119 {{code language="java"}}
120 package com.example;
121
122 import java.util.Arrays;
123 import java.util.HashMap;
124 import java.util.List;
125 import java.util.Map;
126
127 import javax.inject.Inject;
128 import javax.inject.Named;
129
130 import org.xwiki.bridge.event.DocumentCreatedEvent;
131 import org.xwiki.bridge.event.DocumentDeletedEvent;
132 import org.xwiki.bridge.event.DocumentUpdatedEvent;
133 import org.xwiki.component.annotation.Component;
134 import org.xwiki.component.manager.ComponentLookupException;
135 import org.xwiki.component.manager.ComponentManager;
136 import org.xwiki.model.reference.DocumentReference;
137 import org.xwiki.observation.EventListener;
138 import org.xwiki.observation.ObservationManager;
139 import org.xwiki.observation.event.Event;
140
141 import com.xpn.xwiki.doc.XWikiDocument;
142
143 import com.example.event.UserCreationEvent;
144
145 @Component
146 @Named("usercreation")
147 public class UserCreationEventListener implements EventListener
148 {
149 @Inject
150 private ComponentManager componentManager;
151
152 /**
153 * The observation manager that will be use to fire user creation events. Note: We can't have the OM as a
154 * requirement, since it would create an infinite initialization loop, causing a stack overflow error (this event
155 * listener would require an initialized OM and the OM requires a list of initialized event listeners)
156 */
157 private ObservationManager observationManager;
158
159 /**
160 * {@inheritDoc}
161 */
162 public List<Event> getEvents()
163 {
164 return Arrays.<Event>asList(new DocumentCreatedEvent());
165 }
166
167 /**
168 * {@inheritDoc}
169 */
170 public String getName()
171 {
172 return "usercreation";
173 }
174
175 /**
176 * {@inheritDoc}
177 */
178 public void onEvent(Event event, Object source, Object data)
179 {
180 XWikiDocument document = (XWikiDocument) source;
181 String wikiName = document.getDocumentReference().getWikiReference().getName();
182 DocumentReference userClass = new DocumentReference(wikiName, "XWiki", "XWikiUsers");
183
184 if (document.getXObject(userClass) != null) {
185 // Create a map to hold our new event data
186 Map<String,String> userData = new HashMap<String,String>();
187 userData.put("firstName", document.getXObject(userClass).getStringValue("firstName"));
188 userData.put("lastName", document.getXObject(userClass).getStringValue("lastName"));
189 userData.put("email", document.getXObject(userClass).getStringValue("email"));
190 // Fire the user created event
191 UserCreatedEvent newEvent = new UserCreationEvent();
192 getObservationManager().notify(newEvent, source, userData);
193 }
194 }
195
196 private ObservationManager getObservationManager()
197 {
198 if (this.observationManager == null) {
199 try {
200 this.observationManager = componentManager.getInstance(ObservationManager.class);
201
202 } catch (ComponentLookupException e) {
203 throw new RuntimeException("Cound not retrieve an Observation Manager against the component manager");
204 }
205 }
206 return this.observationManager;
207 }
208
209 }
210 {{/code}}
211
212 Definition of the custom event:
213
214 {{code language="java"}}
215 package com.example.event;
216
217 import org.xwiki.bridge.event.AbstractDocumentEvent;
218 import org.xwiki.observation.event.Event;
219 import org.xwiki.observation.event.filter.EventFilter;
220
221 /**
222 * {@link Event} generated when a new user is created.
223 */
224 public class UserCreationEvent extends AbstractDocumentEvent
225 {
226 /**
227 * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
228 * changes.
229 */
230 private static final long serialVersionUID = 1L;
231
232 /**
233 * Constructor initializing the event filter with an
234 * {@link org.xwiki.observation.event.filter.AlwaysMatchingEventFilter}, meaning that this event will match any
235 * other document update event.
236 */
237 public UserCreatedEvent()
238 {
239 super();
240 }
241
242 /**
243 * Constructor initializing the event filter with a {@link org.xwiki.observation.event.filter.FixedNameEventFilter},
244 * meaning that this event will match only update events affecting the document matching the passed document name.
245 *
246 * @param documentName the name of the updated document to match
247 */
248 public UserCreatedEvent(String documentName)
249 {
250 super(documentName);
251 }
252
253 /**
254 * Constructor using a custom {@link EventFilter}.
255 *
256 * @param eventFilter the filter to use for matching events
257 */
258 public UserCreatedEvent(EventFilter eventFilter)
259 {
260 super(eventFilter);
261 }
262 }
263 {{/code}}
264
265 == Writing an Event Listener in Groovy in a Wiki page ==
266
267 See the [[tutorial>>platform:DevGuide.WritingEventListenerTutorial||anchor="HAddingcontenttopagesonsave"]].
268
269 == Writing an Event Listener in Velocity in a Wiki page ==
270
271 The [[documentation of the wiki components>>Extension.WikiComponent Module]] contains, as an example, the creation of an Event Listener.
272
273 == Fold events ==
274
275 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.
276
277 This is an example of a custom fold event:
278
279 {{code language="java"}}
280 package org.xwiki.bridge.event;
281
282 import org.xwiki.observation.event.BeginFoldEvent;
283
284 public class MyTaskBeginEvent extends AbstractDocumentEvent implements BeginFoldEvent
285 {
286 /**
287 * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
288 * changes.
289 */
290 private static final long serialVersionUID = 1L;
291
292 /**
293 * Constructor initializing the event filter with an
294 * {@link org.xwiki.observation.event.filter.AlwaysMatchingEventFilter}, meaning that this event will match any
295 * other document update event.
296 */
297 public MyTaskBeginEvent()
298 {
299 super();
300 }
301
302 /**
303 * Constructor initializing the event filter with a {@link org.xwiki.observation.event.filter.FixedNameEventFilter},
304 * meaning that this event will match only update events affecting the document matching the passed document name.
305 *
306 * @param documentName the name of the updated document to match
307 */
308 public MyTaskBeginEvent(String documentName)
309 {
310 super(documentName);
311 }
312
313 /**
314 * Constructor using a custom {@link EventFilter}.
315 *
316 * @param eventFilter the filter to use for matching events
317 */
318 public MyTaskBeginEvent(EventFilter eventFilter)
319 {
320 super(eventFilter);
321 }
322 }
323 {{/code}}
324
325 Then you need to define the corresponding {{code}}EndFoldEvent{{/code}} :
326
327 {{code language="java"}}
328 package org.xwiki.bridge.event;
329
330 import org.xwiki.observation.event.EndFoldEvent;
331
332 public class MyTaskEndEvent implements EndFoldEvent
333 {
334 /**
335 * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
336 * changes.
337 */
338 private static final long serialVersionUID = 1L;
339
340 /**
341 * Constructor initializing the event filter with an
342 * {@link org.xwiki.observation.event.filter.AlwaysMatchingEventFilter}, meaning that this event will match any
343 * other document update event.
344 */
345 public MyTaskEndEvent()
346 {
347 super();
348 }
349
350 /**
351 * Constructor initializing the event filter with a {@link org.xwiki.observation.event.filter.FixedNameEventFilter},
352 * meaning that this event will match only update events affecting the document matching the passed document name.
353 *
354 * @param documentName the name of the updated document to match
355 */
356 public MyTaskEndEvent(String documentName)
357 {
358 super(documentName);
359 }
360
361 /**
362 * Constructor using a custom {@link EventFilter}.
363 *
364 * @param eventFilter the filter to use for matching events
365 */
366 public MyTaskEndEvent(EventFilter eventFilter)
367 {
368 super(eventFilter);
369 }
370 }
371 {{/code}}

Get Connected