001 /* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package com.google.common.eventbus; 018 019 import com.google.common.annotations.Beta; 020 import com.google.common.annotations.VisibleForTesting; 021 import com.google.common.base.Supplier; 022 import com.google.common.base.Throwables; 023 import com.google.common.cache.CacheBuilder; 024 import com.google.common.cache.CacheLoader; 025 import com.google.common.cache.LoadingCache; 026 import com.google.common.collect.Lists; 027 import com.google.common.collect.Multimap; 028 import com.google.common.collect.Multimaps; 029 import com.google.common.collect.SetMultimap; 030 import com.google.common.collect.Sets; 031 032 import java.lang.reflect.InvocationTargetException; 033 import java.util.Collection; 034 import java.util.List; 035 import java.util.Map.Entry; 036 import java.util.Set; 037 import java.util.concurrent.ConcurrentHashMap; 038 import java.util.concurrent.ConcurrentLinkedQueue; 039 import java.util.concurrent.CopyOnWriteArraySet; 040 import java.util.concurrent.ExecutionException; 041 import java.util.logging.Level; 042 import java.util.logging.Logger; 043 044 /** 045 * Dispatches events to listeners, and provides ways for listeners to register 046 * themselves. 047 * 048 * <p>The EventBus allows publish-subscribe-style communication between 049 * components without requiring the components to explicitly register with one 050 * another (and thus be aware of each other). It is designed exclusively to 051 * replace traditional Java in-process event distribution using explicit 052 * registration. It is <em>not</em> a general-purpose publish-subscribe system, 053 * nor is it intended for interprocess communication. 054 * 055 * <h2>Receiving Events</h2> 056 * To receive events, an object should:<ol> 057 * <li>Expose a public method, known as the <i>event handler</i>, which accepts 058 * a single argument of the type of event desired;</li> 059 * <li>Mark it with a {@link Subscribe} annotation;</li> 060 * <li>Pass itself to an EventBus instance's {@link #register(Object)} method. 061 * </li> 062 * </ol> 063 * 064 * <h2>Posting Events</h2> 065 * To post an event, simply provide the event object to the 066 * {@link #post(Object)} method. The EventBus instance will determine the type 067 * of event and route it to all registered listeners. 068 * 069 * <p>Events are routed based on their type — an event will be delivered 070 * to any handler for any type to which the event is <em>assignable.</em> This 071 * includes implemented interfaces, all superclasses, and all interfaces 072 * implemented by superclasses. 073 * 074 * <p>When {@code post} is called, all registered handlers for an event are run 075 * in sequence, so handlers should be reasonably quick. If an event may trigger 076 * an extended process (such as a database load), spawn a thread or queue it for 077 * later. (For a convenient way to do this, use an {@link AsyncEventBus}.) 078 * 079 * <h2>Handler Methods</h2> 080 * Event handler methods must accept only one argument: the event. 081 * 082 * <p>Handlers should not, in general, throw. If they do, the EventBus will 083 * catch and log the exception. This is rarely the right solution for error 084 * handling and should not be relied upon; it is intended solely to help find 085 * problems during development. 086 * 087 * <p>The EventBus guarantees that it will not call a handler method from 088 * multiple threads simultaneously, unless the method explicitly allows it by 089 * bearing the {@link AllowConcurrentEvents} annotation. If this annotation is 090 * not present, handler methods need not worry about being reentrant, unless 091 * also called from outside the EventBus. 092 * 093 * <h2>Dead Events</h2> 094 * If an event is posted, but no registered handlers can accept it, it is 095 * considered "dead." To give the system a second chance to handle dead events, 096 * they are wrapped in an instance of {@link DeadEvent} and reposted. 097 * 098 * <p>If a handler for a supertype of all events (such as Object) is registered, 099 * no event will ever be considered dead, and no DeadEvents will be generated. 100 * Accordingly, while DeadEvent extends {@link Object}, a handler registered to 101 * receive any Object will never receive a DeadEvent. 102 * 103 * <p>This class is safe for concurrent use. 104 * 105 * <p>See the Guava User Guide article on <a href= 106 * "http://code.google.com/p/guava-libraries/wiki/EventBusExplained"> 107 * {@code EventBus}</a>. 108 * 109 * @author Cliff Biffle 110 * @since 10.0 111 */ 112 @Beta 113 public class EventBus { 114 115 /** 116 * All registered event handlers, indexed by event type. 117 */ 118 private final SetMultimap<Class<?>, EventHandler> handlersByType = 119 Multimaps.newSetMultimap(new ConcurrentHashMap<Class<?>, Collection<EventHandler>>(), 120 new Supplier<Set<EventHandler>>() { 121 @Override 122 public Set<EventHandler> get() { 123 return new CopyOnWriteArraySet<EventHandler>(); 124 } 125 }); 126 127 /** 128 * Logger for event dispatch failures. Named by the fully-qualified name of 129 * this class, followed by the identifier provided at construction. 130 */ 131 private final Logger logger; 132 133 /** 134 * Strategy for finding handler methods in registered objects. Currently, 135 * only the {@link AnnotatedHandlerFinder} is supported, but this is 136 * encapsulated for future expansion. 137 */ 138 private final HandlerFindingStrategy finder = new AnnotatedHandlerFinder(); 139 140 /** queues of events for the current thread to dispatch */ 141 private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> 142 eventsToDispatch = 143 new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() { 144 @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() { 145 return new ConcurrentLinkedQueue<EventWithHandler>(); 146 } 147 }; 148 149 /** true if the current thread is currently dispatching an event */ 150 private final ThreadLocal<Boolean> isDispatching = 151 new ThreadLocal<Boolean>() { 152 @Override protected Boolean initialValue() { 153 return false; 154 } 155 }; 156 157 /** 158 * A thread-safe cache for flattenHierarch(). The Class class is immutable. 159 */ 160 private LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache = 161 CacheBuilder.newBuilder() 162 .weakKeys() 163 .build(new CacheLoader<Class<?>, Set<Class<?>>>() { 164 @Override 165 public Set<Class<?>> load(Class<?> concreteClass) throws Exception { 166 List<Class<?>> parents = Lists.newLinkedList(); 167 Set<Class<?>> classes = Sets.newHashSet(); 168 169 parents.add(concreteClass); 170 171 while (!parents.isEmpty()) { 172 Class<?> clazz = parents.remove(0); 173 classes.add(clazz); 174 175 Class<?> parent = clazz.getSuperclass(); 176 if (parent != null) { 177 parents.add(parent); 178 } 179 180 for (Class<?> iface : clazz.getInterfaces()) { 181 parents.add(iface); 182 } 183 } 184 185 return classes; 186 } 187 }); 188 189 /** 190 * Creates a new EventBus named "default". 191 */ 192 public EventBus() { 193 this("default"); 194 } 195 196 /** 197 * Creates a new EventBus with the given {@code identifier}. 198 * 199 * @param identifier a brief name for this bus, for logging purposes. Should 200 * be a valid Java identifier. 201 */ 202 public EventBus(String identifier) { 203 logger = Logger.getLogger(EventBus.class.getName() + "." + identifier); 204 } 205 206 /** 207 * Registers all handler methods on {@code object} to receive events. 208 * Handler methods are selected and classified using this EventBus's 209 * {@link HandlerFindingStrategy}; the default strategy is the 210 * {@link AnnotatedHandlerFinder}. 211 * 212 * @param object object whose handler methods should be registered. 213 */ 214 public void register(Object object) { 215 handlersByType.putAll(finder.findAllHandlers(object)); 216 } 217 218 /** 219 * Unregisters all handler methods on a registered {@code object}. 220 * 221 * @param object object whose handler methods should be unregistered. 222 * @throws IllegalArgumentException if the object was not previously registered. 223 */ 224 public void unregister(Object object) { 225 Multimap<Class<?>, EventHandler> methodsInListener = finder.findAllHandlers(object); 226 for (Entry<Class<?>, Collection<EventHandler>> entry : methodsInListener.asMap().entrySet()) { 227 Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey()); 228 Collection<EventHandler> eventMethodsInListener = entry.getValue(); 229 230 if (currentHandlers == null || !currentHandlers.containsAll(entry.getValue())) { 231 throw new IllegalArgumentException( 232 "missing event handler for an annotated method. Is " + object + " registered?"); 233 } 234 currentHandlers.removeAll(eventMethodsInListener); 235 } 236 } 237 238 /** 239 * Posts an event to all registered handlers. This method will return 240 * successfully after the event has been posted to all handlers, and 241 * regardless of any exceptions thrown by handlers. 242 * 243 * <p>If no handlers have been subscribed for {@code event}'s class, and 244 * {@code event} is not already a {@link DeadEvent}, it will be wrapped in a 245 * DeadEvent and reposted. 246 * 247 * @param event event to post. 248 */ 249 public void post(Object event) { 250 Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass()); 251 252 boolean dispatched = false; 253 for (Class<?> eventType : dispatchTypes) { 254 Set<EventHandler> wrappers = getHandlersForEventType(eventType); 255 256 if (wrappers != null && !wrappers.isEmpty()) { 257 dispatched = true; 258 for (EventHandler wrapper : wrappers) { 259 enqueueEvent(event, wrapper); 260 } 261 } 262 } 263 264 if (!dispatched && !(event instanceof DeadEvent)) { 265 post(new DeadEvent(this, event)); 266 } 267 268 dispatchQueuedEvents(); 269 } 270 271 /** 272 * Queue the {@code event} for dispatch during 273 * {@link #dispatchQueuedEvents()}. Events are queued in-order of occurrence 274 * so they can be dispatched in the same order. 275 */ 276 protected void enqueueEvent(Object event, EventHandler handler) { 277 eventsToDispatch.get().offer(new EventWithHandler(event, handler)); 278 } 279 280 /** 281 * Drain the queue of events to be dispatched. As the queue is being drained, 282 * new events may be posted to the end of the queue. 283 */ 284 protected void dispatchQueuedEvents() { 285 // don't dispatch if we're already dispatching, that would allow reentrancy 286 // and out-of-order events. Instead, leave the events to be dispatched 287 // after the in-progress dispatch is complete. 288 if (isDispatching.get()) { 289 return; 290 } 291 292 isDispatching.set(true); 293 try { 294 while (true) { 295 EventWithHandler eventWithHandler = eventsToDispatch.get().poll(); 296 if (eventWithHandler == null) { 297 break; 298 } 299 300 dispatch(eventWithHandler.event, eventWithHandler.handler); 301 } 302 } finally { 303 isDispatching.set(false); 304 } 305 } 306 307 /** 308 * Dispatches {@code event} to the handler in {@code wrapper}. This method 309 * is an appropriate override point for subclasses that wish to make 310 * event delivery asynchronous. 311 * 312 * @param event event to dispatch. 313 * @param wrapper wrapper that will call the handler. 314 */ 315 protected void dispatch(Object event, EventHandler wrapper) { 316 try { 317 wrapper.handleEvent(event); 318 } catch (InvocationTargetException e) { 319 logger.log(Level.SEVERE, 320 "Could not dispatch event: " + event + " to handler " + wrapper, e); 321 } 322 } 323 324 /** 325 * Retrieves a mutable set of the currently registered handlers for 326 * {@code type}. If no handlers are currently registered for {@code type}, 327 * this method may either return {@code null} or an empty set. 328 * 329 * @param type type of handlers to retrieve. 330 * @return currently registered handlers, or {@code null}. 331 */ 332 Set<EventHandler> getHandlersForEventType(Class<?> type) { 333 return handlersByType.get(type); 334 } 335 336 /** 337 * Creates a new Set for insertion into the handler map. This is provided 338 * as an override point for subclasses. The returned set should support 339 * concurrent access. 340 * 341 * @return a new, mutable set for handlers. 342 */ 343 protected Set<EventHandler> newHandlerSet() { 344 return new CopyOnWriteArraySet<EventHandler>(); 345 } 346 347 /** 348 * Flattens a class's type hierarchy into a set of Class objects. The set 349 * will include all superclasses (transitively), and all interfaces 350 * implemented by these superclasses. 351 * 352 * @param concreteClass class whose type hierarchy will be retrieved. 353 * @return {@code clazz}'s complete type hierarchy, flattened and uniqued. 354 */ 355 @VisibleForTesting 356 Set<Class<?>> flattenHierarchy(Class<?> concreteClass) { 357 try { 358 return flattenHierarchyCache.get(concreteClass); 359 } catch (ExecutionException e) { 360 throw Throwables.propagate(e.getCause()); 361 } 362 } 363 364 /** simple struct representing an event and it's handler */ 365 static class EventWithHandler { 366 final Object event; 367 final EventHandler handler; 368 public EventWithHandler(Object event, EventHandler handler) { 369 this.event = event; 370 this.handler = handler; 371 } 372 } 373 }