View Javadoc

1   /**
2    * This file Copyright (c) 2012 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.observation.util;
35  
36  import info.magnolia.cms.core.HierarchyManager;
37  import info.magnolia.cms.util.DelayedExecutor;
38  import info.magnolia.context.LifeTimeJCRSessionUtil;
39  
40  import java.util.ArrayList;
41  import java.util.Iterator;
42  import java.util.List;
43  
44  import javax.jcr.RepositoryException;
45  import javax.jcr.observation.Event;
46  import javax.jcr.observation.EventIterator;
47  import javax.jcr.observation.EventListener;
48  import javax.jcr.observation.ObservationManager;
49  
50  import org.apache.jackrabbit.core.observation.SynchronousEventListener;
51  import org.slf4j.Logger;
52  import org.slf4j.LoggerFactory;
53  
54  /**
55   * Same class that in core magnolia, we need it for running the module with previous magnolia versions.
56   * 
57   * @author tmiyar
58   * @deprecated since 1.2 use the info.magnolia.cms.util.ObservationUtil instead
59   */
60  @Deprecated
61  public class ObservationUtil {
62      private final static Logger log = LoggerFactory
63              .getLogger(ObservationUtil.class);
64  
65      /**
66       * Registers an EventListener for any node type.
67       *
68       * @see #registerChangeListener(String,String,boolean,String[],javax.jcr.observation.EventListener)
69       */
70      public static void registerChangeListener(String repository,
71              String observationPath, EventListener listener) {
72          registerChangeListener(repository, observationPath, true, listener);
73      }
74  
75      /**
76       * Registers an EventListener for any node type.
77       *
78       * @see #registerChangeListener(String,String,boolean,String[],javax.jcr.observation.EventListener)
79       */
80      public static void registerChangeListener(String repository,
81              String observationPath, boolean includeSubnodes,
82              EventListener listener) {
83          registerChangeListener(repository, observationPath, includeSubnodes,
84                  (String[]) null, listener);
85      }
86  
87      /**
88       * Registers an EventListener for a specific node type.
89       *
90       * @see #registerChangeListener(String,String,boolean,String[],javax.jcr.observation.EventListener)
91       */
92      public static void registerChangeListener(String repository,
93              String observationPath, boolean includeSubnodes, String nodeType,
94              EventListener listener) {
95          registerChangeListener(repository, observationPath, includeSubnodes,
96                  new String[] { nodeType }, listener);
97      }
98  
99      /**
100      * Register a single event listener, bound to the given path. Be careful
101      * that if you observe "/", events are going to be generated for jcr:system,
102      * which is "shared" accross all workspaces.
103      *
104      * @param repository
105      * @param observationPath
106      *            repository path
107      * @param includeSubnodes
108      *            the isDeep parameter of ObservationManager.addEventListener()
109      * @param nodeTypes
110      *            the node types to filter events for
111      * @param listener
112      *            event listener
113      * @see ObservationManager#addEventListener
114      */
115     public static void registerChangeListener(String repository,
116             String observationPath, boolean includeSubnodes,
117             String[] nodeTypes, EventListener listener) {
118         log.debug("Registering event listener for path [{}]", observationPath); //$NON-NLS-1$
119 
120         try {
121             ObservationManager observationManager = getObservationManager(repository);
122 
123             observationManager.addEventListener(listener, Event.NODE_ADDED
124                     | Event.NODE_REMOVED | Event.PROPERTY_ADDED
125                     | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED,
126                     observationPath, includeSubnodes, null, nodeTypes, false);
127         } catch (RepositoryException e) {
128             log
129             .error(
130                     "Unable to add event listeners for " + observationPath, e); //$NON-NLS-1$
131         }
132     }
133 
134     /**
135      * @deprecated since 3.6 - typo - use registerDeferredChangeListener
136      */
137     @Deprecated
138     public static void registerDefferedChangeListener(String repository,
139             String observationPath, EventListener listener, long delay,
140             long maxDelay) {
141         registerDeferredChangeListener(repository, observationPath, listener,
142                 delay, maxDelay);
143     }
144 
145     public static void registerDeferredChangeListener(String repository,
146             String observationPath, EventListener listener, long delay,
147             long maxDelay) {
148         registerDeferredChangeListener(repository, observationPath, true,
149                 (String[]) null, listener, delay, maxDelay);
150     }
151 
152     /**
153      * @deprecated since 3.6 - typo - use registerDeferredChangeListener
154      */
155     @Deprecated
156     public static void registerDefferedChangeListener(String repository,
157             String observationPath, boolean includeSubnodes,
158             EventListener listener, long delay, long maxDelay) {
159         registerDeferredChangeListener(repository, observationPath,
160                 includeSubnodes, listener, delay, maxDelay);
161     }
162 
163     public static void registerDeferredChangeListener(String repository,
164             String observationPath, boolean includeSubnodes,
165             EventListener listener, long delay, long maxDelay) {
166         registerDeferredChangeListener(repository, observationPath,
167                 includeSubnodes, (String[]) null, listener, delay, maxDelay);
168     }
169 
170     /**
171      * @deprecated since 3.6 - typo - use registerDeferredChangeListener
172      */
173     @Deprecated
174     public static void registerDefferedChangeListener(String repository,
175             String observationPath, boolean includeSubnodes, String nodeType,
176             EventListener listener, long delay, long maxDelay) {
177         registerDeferredChangeListener(repository, observationPath,
178                 includeSubnodes, nodeType, listener, delay, maxDelay);
179     }
180 
181     public static void registerDeferredChangeListener(String repository,
182             String observationPath, boolean includeSubnodes, String nodeType,
183             EventListener listener, long delay, long maxDelay) {
184         registerDeferredChangeListener(repository, observationPath,
185                 includeSubnodes, new String[] { nodeType }, listener, delay,
186                 maxDelay);
187     }
188 
189     /**
190      * @deprecated since 3.6 - typo - use registerDeferredChangeListener
191      */
192     @Deprecated
193     public static void registerDefferedChangeListener(String repository,
194             String observationPath, boolean includeSubnodes,
195             String[] nodeTypes, EventListener listener, long delay,
196             long maxDelay) {
197         registerDeferredChangeListener(repository, observationPath,
198                 includeSubnodes, nodeTypes, listener, delay, maxDelay);
199     }
200 
201     /**
202      * The event firing is deffered in case there is a serie of fired events.
203      * 
204      * @return the wrapped EventListener so that one can unregister it.
205      */
206     public static void registerDeferredChangeListener(String repository,
207             String observationPath, boolean includeSubnodes,
208             String[] nodeTypes, EventListener listener, long delay,
209             long maxDelay) {
210         final EventListener deferedListener = instanciateDeferredEventListener(
211                 listener, delay, maxDelay);
212         registerChangeListener(repository, observationPath, includeSubnodes,
213                 nodeTypes, deferedListener);
214     }
215 
216     // //////////////////////////////////////////////
217     // Begin: added a bunch of methods to set the events the user needs
218     // //////////////////////////////////////////////////
219 
220     /**
221      * Registers an EventListener for a specific node type.
222      *
223      * @see #registerChangeListener(String,String,boolean,String[],javax.jcr.observation.EventListener)
224      */
225     public static void registerChangeListener(String repository,
226             String observationPath, boolean includeSubnodes, String nodeType,
227             int eventTypes, EventListener listener) {
228         if (nodeType == null) {
229             registerChangeListener(repository, observationPath,
230                     includeSubnodes, (String[]) null, eventTypes, listener);
231         } else {
232             registerChangeListener(repository, observationPath,
233                     includeSubnodes, new String[] { nodeType }, eventTypes,
234                     listener);
235         }
236     }
237 
238     /**
239      * Register a single event listener, bound to the given path. Be careful
240      * that if you observe "/", events are going to be generated for jcr:system,
241      * which is "shared" accross all workspaces.
242      *
243      * @param repository
244      * @param observationPath
245      *            repository path
246      * @param includeSubnodes
247      *            the isDeep parameter of ObservationManager.addEventListener()
248      * @param nodeTypes
249      *            the node types to filter events for
250      * @param listener
251      *            event listener
252      * @see ObservationManager#addEventListener
253      */
254     public static void registerChangeListener(String repository,
255             String observationPath, boolean includeSubnodes,
256             String[] nodeTypes, int eventTypes, EventListener listener) {
257         log.debug("Registering event listener for path [{}:{}]", repository, observationPath); //$NON-NLS-1$
258 
259         try {
260             ObservationManager observationManager = getObservationManager(repository);
261 
262             observationManager.addEventListener(listener, eventTypes,
263                     observationPath, includeSubnodes, null, nodeTypes, false);
264         } catch (RepositoryException e) {
265             log
266             .error(
267                     "Unable to add event listeners for " + observationPath, e); //$NON-NLS-1$
268         }
269     }
270 
271     // /////////////////////////////////////////////
272     // End: added methods to support event types definition
273     // ////////////////////////////////////////////
274 
275     /**
276      * Use this and register the returned EventListener with the
277      * registerChangeListener() methods, if you need to be able to later
278      * unregister your EventListener.
279      */
280     public static EventListener instanciateDeferredEventListener(
281             EventListener listener, long delay, long maxDelay) {
282         return new DeferringEventListener(listener, delay, maxDelay);
283     }
284 
285     public static SynchEventListener instanciateSynchEventListener(
286             EventListener listener) {
287         return new SynchEventListener(listener);
288     }
289 
290     public static void unregisterChangeListener(String repository,
291             EventListener listener) {
292         try {
293 
294             getObservationManager(repository).removeEventListener(listener);
295 
296         } catch (RepositoryException e) {
297             log.error("Unable to remove event listener [" + listener
298                     + "] from repository " + repository, e);
299         }
300     }
301 
302     private static ObservationManager getObservationManager(String repository)
303             throws RepositoryException {
304         return getHierarchyManager(repository).getWorkspace()
305                 .getObservationManager();
306     }
307 
308     private static HierarchyManager getHierarchyManager(String repository) {
309         return LifeTimeJCRSessionUtil.getHierarchyManager(repository);
310     }
311 
312     /**
313      * Synchronized Listener.
314      */
315     public static class SynchEventListener implements SynchronousEventListener {
316 
317         private final EventListener listener;
318 
319         public SynchEventListener(EventListener listener) {
320             this.listener = listener;
321 
322         }
323 
324         public void onEvent(EventIterator events) {
325             this.listener.onEvent(events);
326         }
327 
328         @Override
329         public String toString() {
330             return super.toString() + ":" + this.listener;
331         }
332     }
333 
334     /**
335      * Deferred (async) listener.
336      */
337     public static class DeferringEventListener implements EventListener {
338 
339         private final ObservationBasedDelayedExecutor executor;
340 
341         private final EventListener listener;
342 
343         public DeferringEventListener(EventListener listener, long delay,
344                 long maxDelay) {
345             this.listener = listener;
346             executor = new ObservationBasedDelayedExecutor(listener, delay,
347                     maxDelay);
348         }
349 
350         public void onEvent(EventIterator events) {
351             this.executor.consume(events);
352         }
353 
354         @Override
355         public String toString() {
356             return super.toString() + ":" + this.listener;
357         }
358     }
359 
360     /**
361      * Deferred event handling. Uses the DelayedExecutor class
362      */
363     public static class ObservationBasedDelayedExecutor {
364         private final DelayedExecutor delayedExecutor;
365         private final List eventsBuffer = new ArrayList();
366 
367         public ObservationBasedDelayedExecutor(final EventListener listener,
368                 long delay, long maxDelay) {
369             delayedExecutor = new DelayedExecutor(new Runnable() {
370                 public void run() {
371                     // during execution consume is blocked
372                     synchronized (eventsBuffer) {
373                         listener.onEvent(new ListBasedEventIterator(
374                                 eventsBuffer));
375                         eventsBuffer.clear();
376                     }
377                 }
378             }, delay, maxDelay);
379         }
380 
381         protected void consume(EventIterator events) {
382             synchronized (this.eventsBuffer) {
383                 while (events.hasNext()) {
384                     this.eventsBuffer.add(events.next());
385                 }
386                 delayedExecutor.trigger();
387             }
388         }
389     }
390 
391     /**
392      * List based event iterator. Used to collect events in a list which are
393      * later on passed to the listener.
394      */
395     public static class ListBasedEventIterator implements EventIterator {
396         private final Iterator iterator;
397         private final List events;
398         private int pos = 0;
399 
400         public ListBasedEventIterator(List events) {
401             this.events = events;
402             this.iterator = events.iterator();
403         }
404 
405         public boolean hasNext() {
406             return this.iterator.hasNext();
407         }
408 
409         public Object next() {
410             pos++;
411             return this.iterator.next();
412         }
413 
414         public void remove() {
415             this.iterator.remove();
416         }
417 
418         public Event nextEvent() {
419             return (Event) next();
420         }
421 
422         public long getPosition() {
423             return pos;
424         }
425 
426         public long getSize() {
427             return events.size();
428         }
429 
430         public void skip(long skipNum) {
431             for (int i = 0; i < skipNum; i++) {
432                 next();
433             }
434         }
435     }
436 
437 }