001    /*
002     * Copyright 2013 Erik Kuefler
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005     * in compliance with the License. You may obtain a copy of the License at
006     *
007     * http://www.apache.org/licenses/LICENSE-2.0
008     *
009     * Unless required by applicable law or agreed to in writing, software distributed under the License
010     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011     * or implied. See the License for the specific language governing permissions and limitations under
012     * the License.
013     */
014    package com.ekuefler.supereventbus;
015    
016    import com.ekuefler.supereventbus.impl.EventHandlerMethod;
017    import com.ekuefler.supereventbus.multievent.MultiEvent;
018    import com.google.gwt.core.shared.GWT;
019    
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.LinkedList;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Map.Entry;
027    import java.util.Queue;
028    import java.util.SortedMap;
029    import java.util.TreeMap;
030    
031    /**
032     * An event bus implementation for GWT that is significantly more powerful than the built-in
033     * {@link com.google.web.bindery.event.shared.EventBus}. Features provided by this event bus over
034     * the built-in one include the following:
035     * <ol>
036     * <li><b>Declarative handler registration</b> - instead of using events to register individual
037     * handlers, methods can be annotated with {@link Subscribe} and are all automatically registered in
038     * a single bind step.
039     *
040     * <li><b>Flexible typing</b> - with GWT's event bus, all events must extend
041     * {@link com.google.gwt.event.shared.GwtEvent} and implement associated boilerplate. With
042     * SuperEventBus, any object can be fired on the bus, even primitive types.
043     *
044     * <li><b>Polymorphism</b> - GWT's event type are monomorphic, which means that a handler for
045     * MyEvent would never see any subclasses of MyEvent. In contrast, SuperEventBus treats events
046     * polymorphically, so a handler for MyEvent would see any subclass of MyEvent. This allows you to
047     * define hierarchies of events and handle them together, define tagging interfaces to mark groups
048     * of events that should be handled in the same way, or event define a handle for {@link Object} to
049     * handle all events fired by the system. It is also possible to handle multiple events of unrelated
050     * types with the same handler method (see below).
051     *
052     * <li><b>Better-behaved event dispatch</b> - Using the GWT event bus, firing an event from another
053     * event handler will cause that new event to be processed before processing of the original event
054     * completes. This means that other components listening for both events will see them in an
055     * undefined order. SuperEventBus uses a queue to dispatch events, so if event A is fired before
056     * event B, handlers we always receive event A before receiving event B.
057     *
058     * <li><b>Handler priorities</b> - handlers on the GWT event bus are always invoked in an undefined
059     * order. With SuperEventBus, you can use the
060     * {@link com.ekuefler.supereventbus.priority.WithPriority} annotation to force some handlers to be
061     * run before other handlers.
062     *
063     * <li><b>Filtering</b> - handlers on the GWT event bus will always be invoked whenever they are
064     * registered. With SuperEventBus, you can use the {@link com.ekuefler.supereventbus.filtering.When}
065     * annotation to conditionally disable certain handlers, based on either properties of the handler
066     * or based on the event being handled.
067     *
068     * <li><b>Handling events with unrelated types</b> - Standard polymorphism is usually powerful
069     * enough to express event handlers that should work on multiple types of events, but occasionally
070     * it is necessary to handle multiple events of unrelated types using the same method. SuperEventBus
071     * allows this via the {@link MultiEvent} class and
072     * {@link com.ekuefler.supereventbus.multievent.EventTypes} annotation, which allow events of
073     * several specified types to be wrapped and passed to a single method.
074     *
075     * <li><b>DeadEvent</b> - in order to help identify misconfiguration issues, SuperEventBus
076     * automatically posts a {@link DeadEvent} whenever an event is fired that has no handlers. The
077     * application can subscribe to this event in order to log it or report an error.
078     * </ol>
079     *
080     * To register handlers on the event bus, you must first declare an {@link EventRegistration}
081     * interface for the type of the handler like this:
082     *
083     * <pre>
084     * interface MyRegistration extends EventRegistration&lt;TestOwner&gt; {}
085     * </pre>
086     *
087     * This is necessary so that the GWT compiler can generate dispatch code specific to the handler
088     * class - you should not implement this interface yourself. Once this interface is defined, you can
089     * register an object to listen on the event bus like this:
090     *
091     * <pre>
092     * eventBus.register(this, MyRegistration.class);
093     * </pre>
094     *
095     * This will cause all {@link Subscribe}-annotated methods on the current object to be invoked
096     * whenever an appropriate event is fired. Methods annotated with {@link Subscribe} must take a
097     * single argument, which specifies the type of event to handle. An event can be any Java object,
098     * and a handler for a given type will be invoked whenever an object of that type or its subclasses
099     * is posted on the event bus. To post an object to an event bus, simply call
100     *
101     * <pre>
102     * eventBus.post(new MyEvent(&quot;some data&quot;));
103     * </pre>
104     *
105     * @author ekuefler@gmail.com (Erik Kuefler)
106     */
107    public class EventBus {
108    
109      // No-op handler method used as a sentinel when handlers are removed
110      private static final EventHandlerMethod<Object, Object> NULL_HANDLER_METHOD =
111          new EventHandlerMethod<Object, Object>() {
112            @Override
113            public void invoke(Object instance, Object arg) {}
114    
115            @Override
116            public boolean acceptsArgument(Object arg) {
117              return false;
118            }
119    
120            @Override
121            public int getDispatchOrder() {
122              return 0;
123            }
124          };
125    
126      // Map from priority numbers to a list of all event handlers registered at that priority
127      private final Map<Integer, List<EventHandler<?, ?>>> allHandlersByPriority =
128          new HashMap<Integer, List<EventHandler<?, ?>>>();
129    
130      // Cache of known event handlers for each event type. The cache for each event class keeps track
131      // of all handlers for that event and when the global handler list was last checked. When an event
132      // is fired, all new handlers added since the last time the event was fired are checked and added
133      // to the cache as need be. This should mean that all dispatches after the first for a given event
134      // type will be efficient so long as few new handlers were added.
135      private final Map<Class<?>, CacheEntry<?>> handlerCache = new HashMap<Class<?>, CacheEntry<?>>();
136    
137      // A queue of events being dispatched. When one event fires another event, it is added to the
138      // queue rather than dispatched immediately in order to preserve the order of events.
139      private final Queue<EventWithHandler<?, ?>> eventsToDispatch =
140          new LinkedList<EventWithHandler<?, ?>>();
141    
142      // Whether we are in the process of dispatching events
143      private boolean isDispatching = false;
144    
145      // List of all exception handlers registered by the user
146      private final List<ExceptionHandler> exceptionHandlers = new LinkedList<ExceptionHandler>();
147    
148      /**
149       * Creates a new event bus. In dev mode, any exceptions that occur while dispatching events will
150       * be logged with {@link GWT#log}. In prod mode, exceptions are silently ignored unless a handler
151       * is added via {@link #addExceptionHandler}.
152       */
153      public EventBus() {
154        if (!GWT.isProdMode()) {
155          addExceptionHandler(new ExceptionHandler() {
156            @Override
157            public void handleException(EventBusException e) {
158              GWT.log("Got exception when handling event \"" + e.getEvent() + "\"", e.getCause());
159            }
160          });
161        }
162      }
163    
164      /**
165       * Posts the given event to all handlers registered on this event bus. This method will return
166       * after the event has been posted to all handlers and will never throw an exception. After the
167       * event has been posted, any exceptions thrown by handlers of the event are collected and passed
168       * to each exception handler registered via {@link #addExceptionHandler(ExceptionHandler)}.
169       *
170       * @param event event object to post to all handlers
171       */
172      public <T> void post(T event) {
173        // Check argument validity
174        if (event == null) {
175          throw new NullPointerException();
176        } else if (event instanceof MultiEvent) {
177          throw new IllegalArgumentException("MultiEvents cannot be posted directly");
178        }
179    
180        // Look up the cache entry for the class of the given event, adding a new entry if this is the
181        // first time an event of the class has been fired.
182        if (!handlerCache.containsKey(event.getClass())) {
183          handlerCache.put(event.getClass(), new CacheEntry<T>());
184        }
185        @SuppressWarnings("unchecked")
186        CacheEntry<T> cacheEntry = (CacheEntry<T>) handlerCache.get(event.getClass());
187    
188        // Updates the cache for the given event class, ensuring that it contains all registered
189        // handlers for that class. This time this takes will depend on the number of new event handlers
190        // added since the last time an event of this type was fired.
191        cacheEntry.update(event);
192    
193        // Queue up all handlers for this event
194        List<EventHandler<?, T>> handlers = cacheEntry.getAllHandlers();
195        for (EventHandler<?, T> wildcardHandler : handlers) {
196          @SuppressWarnings("unchecked")
197          EventHandler<Object, T> handler = (EventHandler<Object, T>) wildcardHandler;
198          eventsToDispatch.add(new EventWithHandler<Object, T>(event, handler));
199        }
200    
201        // If this event had no handlers, post a DeadEvent for debugging purposes
202        if (handlers.isEmpty() && !(event instanceof DeadEvent)) {
203          post(new DeadEvent(event));
204        }
205    
206        // Start dispatching the queued events. If we're already dispatching, it means that the handler
207        // for one event posted another event, so we don't have to start dispatching again.
208        if (!isDispatching) {
209          dispatchQueuedEvents();
210        }
211      }
212    
213      @SuppressWarnings("unchecked")
214      private <T> void dispatchQueuedEvents() {
215        isDispatching = true;
216        try {
217          // Dispatch all events in the queue, saving any exceptions for later
218          EventWithHandler<Object, T> eventWithHandler;
219          List<EventBusException> exceptions = new LinkedList<EventBusException>();
220          while ((eventWithHandler = (EventWithHandler<Object, T>) eventsToDispatch.poll()) != null) {
221            try {
222              eventWithHandler.dispatch();
223            } catch (Exception e) {
224              exceptions.add(new EventBusException(
225                  e, eventWithHandler.handler.owner, eventWithHandler.event));
226            }
227          }
228    
229          // Notify all exception handlers of each exception
230          for (EventBusException e : exceptions) {
231            for (ExceptionHandler exceptionHandler : exceptionHandlers) {
232              try {
233                exceptionHandler.handleException(e);
234              } catch (Exception ex) {
235                GWT.log("Caught exception while handling an EventBusException, ignoring it", ex);
236              }
237            }
238          }
239        } finally {
240          isDispatching = false;
241        }
242      }
243    
244      /**
245       * Registers all {@link Subscribe}-annotated in the given object on the event bus. Any methods
246       * annotated with {@link Subscribe} must take a single argument specifying the event to handle.
247       * After an object has been registered, whenever an event is posted on the event bus via
248       * {@link #post}, all handlers on that object for that event's type or its supertypes will be
249       * invoked with that event.
250       *
251       * @param owner object to scan for {@link Subscribe}-annotated methods to register
252       * @param registrationClass the class object of a registration interface for the given owner
253       */
254      public <T> void register(T owner, Class<? extends EventRegistration<T>> registrationClass) {
255        EventRegistration<T> registration = GWT.create(registrationClass);
256    
257        // Add each handler method in the class to the global handler map according to its priority. The
258        // cache mapping event classes to handler methods will be updated when an event is fired.
259        for (EventHandlerMethod<T, ?> method : registration.getMethods()) {
260          addHandlerMethod(owner, method);
261        }
262      }
263    
264      /**
265       * Registers a single handler method on a given instance. Visible for
266       * {@link EventBusAdapter#addHandler}.
267       */
268      <T, E> void addHandlerMethod(T owner, EventHandlerMethod<T, E> method) {
269        if (!allHandlersByPriority.containsKey(method.getDispatchOrder())) {
270          allHandlersByPriority.put(method.getDispatchOrder(), new ArrayList<EventHandler<?, ?>>());
271        }
272        allHandlersByPriority.get(method.getDispatchOrder()).add(new EventHandler<T, E>(owner, method));
273      }
274    
275      /**
276       * Unregisters all event handlers on the given object. After unregistering, {@link Subscribe}-
277       * annotated methods on that object will never be invoked when an event is posted (unless the
278       * object is registered again). This given object must have already been registered on the event
279       * bus.
280       *
281       * @param owner object whose handlers should be disabled. Must already have been registered via a
282       *          call to {@link #register}.
283       * @throws IllegalArgumentException if the given object was never registered on this event bus
284       */
285      public void unregister(Object owner) {
286        // First clear entries from the global handler list. We can't actually remove entries, since
287        // this would break the indices stored in the cache. So replace removed entries with no-ops.
288        boolean removed = false;
289        for (List<EventHandler<?, ?>> handlerList : allHandlersByPriority.values()) {
290          for (EventHandler<?, ?> handler : handlerList) {
291            if (owner == handler.owner) {
292              handler.nullify();
293              removed = true;
294            }
295          }
296        }
297    
298        // Ensure that something was actually removed
299        if (!removed) {
300          throw new IllegalArgumentException("Object was never registered: " + owner);
301        }
302    
303        // Remove handlers from the cache
304        for (CacheEntry<?> entry : handlerCache.values()) {
305          entry.removeHandlersForOwner(owner);
306        }
307      }
308    
309      /**
310       * Adds an exception handler to be notified whenever an exception occurs while dispatching an
311       * event. Exception handlers are invoked only after all handlers have had a chance to process an
312       * event - at that point, every exception that occurred during dispatch is wrapped in an
313       * {@link EventBusException} and posted to every exception handler.
314       *
315       * @param exceptionHandler handler to be notified when exceptions occur
316       */
317      public void addExceptionHandler(ExceptionHandler exceptionHandler) {
318        exceptionHandlers.add(exceptionHandler);
319      }
320    
321      /** A handler method combined with a specific instance of a class declaring that method. */
322      private static class EventHandler<I, A> {
323        I owner;
324        EventHandlerMethod<I, A> method;
325    
326        EventHandler(I owner, EventHandlerMethod<I, A> method) {
327          this.owner = owner;
328          this.method = method;
329        }
330    
331        @SuppressWarnings("unchecked")
332        void nullify() {
333          owner = null;
334          method = (EventHandlerMethod<I, A>) NULL_HANDLER_METHOD;
335        }
336      }
337    
338      /** An event handler combined with a specific event to handle. */
339      private static class EventWithHandler<I, A> {
340        final A event;
341        final EventHandler<I, A> handler;
342    
343        EventWithHandler(A event, EventHandler<I, A> handler) {
344          this.event = event;
345          this.handler = handler;
346        }
347    
348        void dispatch() {
349          handler.method.invoke(handler.owner, event);
350        }
351      }
352    
353      /**
354       * An entry in the handler cache for event classes, containing a list of known handlers and the
355       * index of the last handler checked.
356       */
357      private class CacheEntry<T> {
358        // Map from priority levels to a list of known event handlers for this type at that priority.
359        // The map is updated whenever an event is fired.
360        private final SortedMap<Integer, List<EventHandler<?, T>>> knownHandlersByPriority =
361            new TreeMap<Integer, List<EventHandler<?, T>>>();
362    
363        // Map from priority levels to the last corresponding index in the global handler list that was
364        // checked at that priority. When updating the cache, we continue from this index in order to
365        // avoid having to re-scan entries that were already cached.
366        private final Map<Integer, Integer> nextHandlerToCheckByPriority =
367            new HashMap<Integer, Integer>();
368    
369        /** Updates this cache, ensuring it contains all handlers for the given event type. */
370        void update(T event) {
371          // Check each priority level in the global handler map
372          for (Entry<Integer, List<EventHandler<?, ?>>> entry : allHandlersByPriority.entrySet()) {
373            int priority = entry.getKey();
374            List<EventHandler<?, ?>> handlers = entry.getValue();
375    
376            // Ensure that we have entries for this priority level if we don't already
377            if (!knownHandlersByPriority.containsKey(priority)) {
378              knownHandlersByPriority.put(priority, new LinkedList<EventHandler<?, T>>());
379              nextHandlerToCheckByPriority.put(priority, 0);
380            }
381    
382            // Starting with the last index we checked at this priority, advance to the end of the
383            // global handler list for this priority.
384            for (; nextHandlerToCheckByPriority.get(priority) < handlers.size(); increment(priority)) {
385              int nextHandlerToCheck = nextHandlerToCheckByPriority.get(priority);
386              @SuppressWarnings("unchecked")
387              EventHandler<?, T> handler = (EventHandler<?, T>) handlers.get(nextHandlerToCheck);
388              // Add this handler to the cache only if it is appropriate for the given event
389              if (handler.method.acceptsArgument(event)) {
390                knownHandlersByPriority.get(priority).add(handler);
391              }
392            }
393          }
394        }
395    
396        /** Returns all known handlers for this entry's event type, sorted by priority. */
397        List<EventHandler<?, T>> getAllHandlers() {
398          List<EventHandler<?, T>> result = new LinkedList<EventHandler<?, T>>();
399          for (List<EventHandler<?, T>> handlerList : knownHandlersByPriority.values()) {
400            result.addAll(handlerList);
401          }
402          return result;
403        }
404    
405        /** Removes all handlers registered on the given object from this cache entry. */
406        void removeHandlersForOwner(Object owner) {
407          for (List<EventHandler<?, T>> handlerList : knownHandlersByPriority.values()) {
408            for (Iterator<EventHandler<?, T>> it = handlerList.iterator(); it.hasNext();) {
409              if (owner == it.next().owner) {
410                it.remove();
411              }
412            }
413          }
414        }
415    
416        // Increments the next handler to check index for the given priority level
417        private void increment(int priority) {
418          nextHandlerToCheckByPriority.put(priority, nextHandlerToCheckByPriority.get(priority) + 1);
419        }
420      }
421    }