1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.watchlist.internal

File RealtimeNotificationGenerator.java

 

Coverage histogram

../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

16
38
5
1
238
121
15
0.39
7.6
5
3

Classes

Class Line # Actions
RealtimeNotificationGenerator 63 38 0% 15 13
0.77966178%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.watchlist.internal;
21   
22    import java.util.Arrays;
23    import java.util.Collection;
24    import java.util.Collections;
25    import java.util.Date;
26    import java.util.HashMap;
27    import java.util.List;
28    import java.util.Map;
29   
30    import javax.inject.Inject;
31    import javax.inject.Named;
32    import javax.inject.Singleton;
33   
34    import org.slf4j.Logger;
35    import org.xwiki.bridge.event.DocumentCreatedEvent;
36    import org.xwiki.bridge.event.DocumentDeletedEvent;
37    import org.xwiki.bridge.event.DocumentUpdatedEvent;
38    import org.xwiki.component.annotation.Component;
39    import org.xwiki.component.phase.InitializationException;
40    import org.xwiki.configuration.ConfigurationSource;
41    import org.xwiki.model.reference.DocumentReference;
42    import org.xwiki.observation.AbstractEventListener;
43    import org.xwiki.observation.ObservationContext;
44    import org.xwiki.observation.event.Event;
45    import org.xwiki.observation.remote.RemoteObservationManagerContext;
46    import org.xwiki.watchlist.internal.api.WatchListEvent;
47    import org.xwiki.watchlist.internal.api.WatchListEventType;
48    import org.xwiki.watchlist.internal.api.WatchListNotifier;
49    import org.xwiki.watchlist.internal.api.WatchListStore;
50    import org.xwiki.watchlist.internal.notification.WatchListEventMimeMessageFactory;
51   
52    import com.xpn.xwiki.XWikiContext;
53    import com.xpn.xwiki.doc.XWikiDocument;
54   
55    /**
56    * Generates notifications in real time for all the subscribers interested in the currently modified document.
57    *
58    * @version $Id: 2deb8e5a665381f03cb8af41327933e61f1c800b $
59    */
60    @Component
61    @Named(RealtimeNotificationGenerator.LISTENER_NAME)
62    @Singleton
 
63    public class RealtimeNotificationGenerator extends AbstractEventListener
64    {
65    /**
66    * The name of the listener.
67    */
68    public static final String LISTENER_NAME = "RealtimeNotificationGenerator";
69   
70    /**
71    * The document containing the WatchList message template for realtime notifications.
72    */
73    public static final String REALTIME_EMAIL_TEMPLATE = "XWiki.WatchListRealtimeMessage";
74   
75    /**
76    * The events to match.
77    */
78    private static final List<Event> EVENTS = Arrays.<Event>asList(new DocumentCreatedEvent(),
79    new DocumentUpdatedEvent(), new DocumentDeletedEvent());
80   
81    /**
82    * Used to detect if certain events are not independent, i.e. executed in the context of other events, case in which
83    * they should be skipped.
84    */
85    @Inject
86    private ObservationContext observationContext;
87   
88    /**
89    * Used to obtain observation event context, i.e. if the event is remote.
90    */
91    @Inject
92    private RemoteObservationManagerContext remoteObservationManagerContext;
93   
94    /**
95    * Used to access watchlist data.
96    */
97    @Inject
98    private WatchListStore store;
99   
100    /**
101    * Used to actually deliver the notification to the user.
102    */
103    @Inject
104    private WatchListNotifier notifier;
105   
106    /**
107    * Logging framework.
108    */
109    @Inject
110    private Logger logger;
111   
112    /**
113    * Used to match {@link WatchListEvent}s with a user's watchlist.
114    */
115    @Inject
116    private WatchListEventMatcher watchlistEventMatcher;
117   
118    /**
119    * Used to access xwiki.properties.
120    */
121    @Inject
122    @Named("xwikiproperties")
123    private ConfigurationSource xwikiProperties;
124   
125    /**
126    * Allow processing of remote events.
127    */
128    private boolean allowRemote;
129   
130    /**
131    * Default constructor.
132    */
 
133  1 toggle public RealtimeNotificationGenerator()
134    {
135  1 super(LISTENER_NAME, EVENTS);
136    }
137   
138    /**
139    * Component manager initialize class. Called after all dependencies are injected.
140    *
141    * @throws InitializationException if component isn't properly initialized
142    */
 
143  0 toggle public void initialize() throws InitializationException
144    {
145  0 this.allowRemote = this.xwikiProperties.getProperty("watchlist.realtime.allowRemote", false);
146    }
147   
 
148  1 toggle @Override
149    public List<Event> getEvents()
150    {
151  1 if (this.xwikiProperties.getProperty("watchlist.realtime.enabled", false)) {
152    // If the realtime notification feature is explicitly enabled (temporarily disabled by default), then enable
153    // this event listener.
154  1 return super.getEvents();
155    } else {
156    // Otherwise disabled this event listener from being notified.
157  0 return Collections.emptyList();
158    }
159    }
160   
 
161  73 toggle @Override
162    public void onEvent(Event event, Object source, Object data)
163    {
164    // Early check if event should be processed.
165  73 if (this.remoteObservationManagerContext.isRemoteState() && !this.allowRemote) {
166    // Don't handle remote events to avoid duplicated processing.
167  0 return;
168    }
169   
170  73 XWikiDocument currentDoc = (XWikiDocument) source;
171  73 XWikiContext context = (XWikiContext) data;
172   
173    // Skip evens that are executed in the context of other event, thus not directly generated by a user.
174  73 if (observationContext.isIn(AutomaticWatchModeListener.SKIPPED_EVENTS)) {
175  12 return;
176    }
177   
178    // Prepare the notification and send it for processing in a separate thread so that the UI does not block.
179   
180  61 try {
181    // Get a corresponding watchlist event.
182  61 WatchListEvent watchListEvent = getWatchListEvent(event, currentDoc, context);
183   
184    // Early optimization since this is not related to a user but to the event itself.
185  61 if (watchlistEventMatcher.isEventSkipped(watchListEvent)) {
186    // Stop here if the event is skipped.
187  4 return;
188    }
189   
190    // Get all the realtime notification subscribers.
191  57 Collection<String> subscribers =
192    store.getSubscribers(DefaultWatchListNotificationCache.REALTIME_INTERVAL_ID);
193  57 if (subscribers.size() == 0) {
194    // Stop here if no one is interested.
195  28 return;
196    }
197   
198    // Build the notification parameters.
199  29 Map<String, Object> notificationData = new HashMap<>();
200  29 notificationData.put(WatchListEventMimeMessageFactory.TEMPLATE_PARAMETER, REALTIME_EMAIL_TEMPLATE);
201  29 notificationData.put(WatchListEventMimeMessageFactory.SKIP_CONTEXT_USER_PARAMETER, true);
202  29 notificationData.put(WatchListEventMimeMessageFactory.ATTACH_AUTHOR_AVATARS_PARAMETER, true);
203   
204    // Send the notification for processing.
205  29 notifier.sendNotification(subscribers, Arrays.asList(watchListEvent), notificationData);
206    } catch (Exception e) {
207  0 logger.error("Failed to send realtime notification to user [{}]", context.getUserReference(), e);
208    }
209    }
210   
211    /**
212    * @param event the current event
213    * @param currentDoc the affected document
214    * @param context the context of the event
215    * @return the {@link WatchListEvent} to use to notify watchers of the current document
216    */
 
217  61 toggle private WatchListEvent getWatchListEvent(Event event, XWikiDocument currentDoc, XWikiContext context)
218    {
219  61 String type = null;
220  61 DocumentReference documentReference = currentDoc.getDocumentReference();
221  61 DocumentReference userReference = context.getUserReference();
222  61 String version = currentDoc.getVersion();
223  61 Date date = currentDoc.getDate();
224   
225  61 if (event instanceof DocumentCreatedEvent) {
226  12 type = WatchListEventType.CREATE;
227  49 } else if (event instanceof DocumentUpdatedEvent) {
228  49 type = WatchListEventType.UPDATE;
229  0 } else if (event instanceof DocumentDeletedEvent) {
230  0 version = currentDoc.getOriginalDocument().getVersion();
231  0 type = WatchListEventType.DELETE;
232    }
233   
234  61 WatchListEvent watchListEvent = new WatchListEvent(documentReference, type, userReference, version, date);
235   
236  61 return watchListEvent;
237    }
238    }