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 }