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<TestOwner> {}
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("some data"));
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 }