11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/* 21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2007 The Guava Authors 31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); 51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License. 61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at 71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0 91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software 111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, 121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and 141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License. 151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.eventbus; 181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull; 207dd252788645e940eada959bdde927426e2531c9Paul Duffin 211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.Beta; 221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.VisibleForTesting; 231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.base.Throwables; 241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.CacheBuilder; 251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.CacheLoader; 261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.LoadingCache; 277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.HashMultimap; 281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.Multimap; 291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.SetMultimap; 307dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.reflect.TypeToken; 317dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.util.concurrent.UncheckedExecutionException; 321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.InvocationTargetException; 341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Collection; 357dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.LinkedList; 361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Map.Entry; 377dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Queue; 381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Set; 397dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.locks.ReadWriteLock; 407dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.locks.ReentrantReadWriteLock; 411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.logging.Level; 421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.logging.Logger; 431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/** 451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Dispatches events to listeners, and provides ways for listeners to register 461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * themselves. 471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>The EventBus allows publish-subscribe-style communication between 491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * components without requiring the components to explicitly register with one 501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * another (and thus be aware of each other). It is designed exclusively to 511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * replace traditional Java in-process event distribution using explicit 521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * registration. It is <em>not</em> a general-purpose publish-subscribe system, 531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * nor is it intended for interprocess communication. 541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <h2>Receiving Events</h2> 560888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>To receive events, an object should: 570888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <ol> 580888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <li>Expose a public method, known as the <i>event subscriber</i>, which accepts 591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * a single argument of the type of event desired;</li> 601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <li>Mark it with a {@link Subscribe} annotation;</li> 611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <li>Pass itself to an EventBus instance's {@link #register(Object)} method. 621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * </li> 631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * </ol> 641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <h2>Posting Events</h2> 660888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>To post an event, simply provide the event object to the 671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@link #post(Object)} method. The EventBus instance will determine the type 681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * of event and route it to all registered listeners. 691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>Events are routed based on their type — an event will be delivered 710888a09821a98ac0680fad765217302858e70fa4Paul Duffin * to any subscriber for any type to which the event is <em>assignable.</em> This 721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * includes implemented interfaces, all superclasses, and all interfaces 731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * implemented by superclasses. 741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 750888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>When {@code post} is called, all registered subscribers for an event are run 760888a09821a98ac0680fad765217302858e70fa4Paul Duffin * in sequence, so subscribers should be reasonably quick. If an event may trigger 771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * an extended process (such as a database load), spawn a thread or queue it for 781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * later. (For a convenient way to do this, use an {@link AsyncEventBus}.) 791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 800888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <h2>Subscriber Methods</h2> 810888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>Event subscriber methods must accept only one argument: the event. 821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 830888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>Subscribers should not, in general, throw. If they do, the EventBus will 841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * catch and log the exception. This is rarely the right solution for error 851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * handling and should not be relied upon; it is intended solely to help find 861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * problems during development. 871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 880888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>The EventBus guarantees that it will not call a subscriber method from 891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * multiple threads simultaneously, unless the method explicitly allows it by 901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * bearing the {@link AllowConcurrentEvents} annotation. If this annotation is 910888a09821a98ac0680fad765217302858e70fa4Paul Duffin * not present, subscriber methods need not worry about being reentrant, unless 921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * also called from outside the EventBus. 931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <h2>Dead Events</h2> 950888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>If an event is posted, but no registered subscribers can accept it, it is 961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * considered "dead." To give the system a second chance to handle dead events, 971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * they are wrapped in an instance of {@link DeadEvent} and reposted. 981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 990888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>If a subscriber for a supertype of all events (such as Object) is registered, 1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * no event will ever be considered dead, and no DeadEvents will be generated. 1010888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Accordingly, while DeadEvent extends {@link Object}, a subscriber registered to 1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * receive any Object will never receive a DeadEvent. 1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>This class is safe for concurrent use. 1057dd252788645e940eada959bdde927426e2531c9Paul Duffin * 1067dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>See the Guava User Guide article on <a href= 1077dd252788645e940eada959bdde927426e2531c9Paul Duffin * "http://code.google.com/p/guava-libraries/wiki/EventBusExplained"> 1087dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@code EventBus}</a>. 1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Cliff Biffle 1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 10.0 1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@Beta 1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class EventBus { 1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1177dd252788645e940eada959bdde927426e2531c9Paul Duffin * A thread-safe cache for flattenHierarchy(). The Class class is immutable. This cache is shared 1187dd252788645e940eada959bdde927426e2531c9Paul Duffin * across all EventBus instances, which greatly improves performance if multiple such instances 1197dd252788645e940eada959bdde927426e2531c9Paul Duffin * are created and objects of the same class are posted on all of them. 1207dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 1210888a09821a98ac0680fad765217302858e70fa4Paul Duffin private static final LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache = 1220888a09821a98ac0680fad765217302858e70fa4Paul Duffin CacheBuilder.newBuilder() 1230888a09821a98ac0680fad765217302858e70fa4Paul Duffin .weakKeys() 1240888a09821a98ac0680fad765217302858e70fa4Paul Duffin .build(new CacheLoader<Class<?>, Set<Class<?>>>() { 1250888a09821a98ac0680fad765217302858e70fa4Paul Duffin @SuppressWarnings({"unchecked", "rawtypes"}) // safe cast 1260888a09821a98ac0680fad765217302858e70fa4Paul Duffin @Override 1270888a09821a98ac0680fad765217302858e70fa4Paul Duffin public Set<Class<?>> load(Class<?> concreteClass) { 1280888a09821a98ac0680fad765217302858e70fa4Paul Duffin return (Set) TypeToken.of(concreteClass).getTypes().rawTypes(); 1290888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 1300888a09821a98ac0680fad765217302858e70fa4Paul Duffin }); 1317dd252788645e940eada959bdde927426e2531c9Paul Duffin 1327dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 1330888a09821a98ac0680fad765217302858e70fa4Paul Duffin * All registered event subscribers, indexed by event type. 1347dd252788645e940eada959bdde927426e2531c9Paul Duffin * 1357dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>This SetMultimap is NOT safe for concurrent use; all access should be 1360888a09821a98ac0680fad765217302858e70fa4Paul Duffin * made after acquiring a read or write lock via {@link #subscribersByTypeLock}. 1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1380888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final SetMultimap<Class<?>, EventSubscriber> subscribersByType = 1390888a09821a98ac0680fad765217302858e70fa4Paul Duffin HashMultimap.create(); 1400888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final ReadWriteLock subscribersByTypeLock = new ReentrantReadWriteLock(); 1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1430888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Strategy for finding subscriber methods in registered objects. Currently, 1440888a09821a98ac0680fad765217302858e70fa4Paul Duffin * only the {@link AnnotatedSubscriberFinder} is supported, but this is 1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * encapsulated for future expansion. 1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1470888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final SubscriberFindingStrategy finder = new AnnotatedSubscriberFinder(); 1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** queues of events for the current thread to dispatch */ 1500888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final ThreadLocal<Queue<EventWithSubscriber>> eventsToDispatch = 1510888a09821a98ac0680fad765217302858e70fa4Paul Duffin new ThreadLocal<Queue<EventWithSubscriber>>() { 1520888a09821a98ac0680fad765217302858e70fa4Paul Duffin @Override protected Queue<EventWithSubscriber> initialValue() { 1530888a09821a98ac0680fad765217302858e70fa4Paul Duffin return new LinkedList<EventWithSubscriber>(); 1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert }; 1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** true if the current thread is currently dispatching an event */ 1580888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final ThreadLocal<Boolean> isDispatching = 1590888a09821a98ac0680fad765217302858e70fa4Paul Duffin new ThreadLocal<Boolean>() { 1600888a09821a98ac0680fad765217302858e70fa4Paul Duffin @Override protected Boolean initialValue() { 1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return false; 1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert }; 1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1650888a09821a98ac0680fad765217302858e70fa4Paul Duffin private SubscriberExceptionHandler subscriberExceptionHandler; 1660888a09821a98ac0680fad765217302858e70fa4Paul Duffin 1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Creates a new EventBus named "default". 1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public EventBus() { 1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this("default"); 1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Creates a new EventBus with the given {@code identifier}. 1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param identifier a brief name for this bus, for logging purposes. Should 1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * be a valid Java identifier. 1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public EventBus(String identifier) { 1810888a09821a98ac0680fad765217302858e70fa4Paul Duffin this(new LoggingSubscriberExceptionHandler(identifier)); 1820888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 1830888a09821a98ac0680fad765217302858e70fa4Paul Duffin 1840888a09821a98ac0680fad765217302858e70fa4Paul Duffin /** 1850888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Creates a new EventBus with the given {@link SubscriberExceptionHandler}. 1860888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 1870888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @param subscriberExceptionHandler Handler for subscriber exceptions. 1880888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @since 16.0 1890888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 1900888a09821a98ac0680fad765217302858e70fa4Paul Duffin public EventBus(SubscriberExceptionHandler subscriberExceptionHandler) { 1910888a09821a98ac0680fad765217302858e70fa4Paul Duffin this.subscriberExceptionHandler = checkNotNull(subscriberExceptionHandler); 1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1950888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Registers all subscriber methods on {@code object} to receive events. 1960888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Subscriber methods are selected and classified using this EventBus's 1970888a09821a98ac0680fad765217302858e70fa4Paul Duffin * {@link SubscriberFindingStrategy}; the default strategy is the 1980888a09821a98ac0680fad765217302858e70fa4Paul Duffin * {@link AnnotatedSubscriberFinder}. 1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 2000888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @param object object whose subscriber methods should be registered. 2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void register(Object object) { 2030888a09821a98ac0680fad765217302858e70fa4Paul Duffin Multimap<Class<?>, EventSubscriber> methodsInListener = 2040888a09821a98ac0680fad765217302858e70fa4Paul Duffin finder.findAllSubscribers(object); 2050888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByTypeLock.writeLock().lock(); 2067dd252788645e940eada959bdde927426e2531c9Paul Duffin try { 2070888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByType.putAll(methodsInListener); 2087dd252788645e940eada959bdde927426e2531c9Paul Duffin } finally { 2090888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByTypeLock.writeLock().unlock(); 2107dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 2140888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Unregisters all subscriber methods on a registered {@code object}. 2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 2160888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @param object object whose subscriber methods should be unregistered. 2171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @throws IllegalArgumentException if the object was not previously registered. 2181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void unregister(Object object) { 2200888a09821a98ac0680fad765217302858e70fa4Paul Duffin Multimap<Class<?>, EventSubscriber> methodsInListener = finder.findAllSubscribers(object); 2210888a09821a98ac0680fad765217302858e70fa4Paul Duffin for (Entry<Class<?>, Collection<EventSubscriber>> entry : 2220888a09821a98ac0680fad765217302858e70fa4Paul Duffin methodsInListener.asMap().entrySet()) { 2237dd252788645e940eada959bdde927426e2531c9Paul Duffin Class<?> eventType = entry.getKey(); 2240888a09821a98ac0680fad765217302858e70fa4Paul Duffin Collection<EventSubscriber> eventMethodsInListener = entry.getValue(); 2257dd252788645e940eada959bdde927426e2531c9Paul Duffin 2260888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByTypeLock.writeLock().lock(); 2277dd252788645e940eada959bdde927426e2531c9Paul Duffin try { 2280888a09821a98ac0680fad765217302858e70fa4Paul Duffin Set<EventSubscriber> currentSubscribers = subscribersByType.get(eventType); 2290888a09821a98ac0680fad765217302858e70fa4Paul Duffin if (!currentSubscribers.containsAll(eventMethodsInListener)) { 2300888a09821a98ac0680fad765217302858e70fa4Paul Duffin throw new IllegalArgumentException( 2310888a09821a98ac0680fad765217302858e70fa4Paul Duffin "missing event subscriber for an annotated method. Is " + object + " registered?"); 2327dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2330888a09821a98ac0680fad765217302858e70fa4Paul Duffin currentSubscribers.removeAll(eventMethodsInListener); 2347dd252788645e940eada959bdde927426e2531c9Paul Duffin } finally { 2350888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByTypeLock.writeLock().unlock(); 2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 2410888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Posts an event to all registered subscribers. This method will return 2420888a09821a98ac0680fad765217302858e70fa4Paul Duffin * successfully after the event has been posted to all subscribers, and 2430888a09821a98ac0680fad765217302858e70fa4Paul Duffin * regardless of any exceptions thrown by subscribers. 2441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 2450888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>If no subscribers have been subscribed for {@code event}'s class, and 2461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@code event} is not already a {@link DeadEvent}, it will be wrapped in a 2471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * DeadEvent and reposted. 2481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 2491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param event event to post. 2501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void post(Object event) { 2521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass()); 2531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert boolean dispatched = false; 2551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert for (Class<?> eventType : dispatchTypes) { 2560888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByTypeLock.readLock().lock(); 2577dd252788645e940eada959bdde927426e2531c9Paul Duffin try { 2580888a09821a98ac0680fad765217302858e70fa4Paul Duffin Set<EventSubscriber> wrappers = subscribersByType.get(eventType); 2597dd252788645e940eada959bdde927426e2531c9Paul Duffin 2607dd252788645e940eada959bdde927426e2531c9Paul Duffin if (!wrappers.isEmpty()) { 2617dd252788645e940eada959bdde927426e2531c9Paul Duffin dispatched = true; 2620888a09821a98ac0680fad765217302858e70fa4Paul Duffin for (EventSubscriber wrapper : wrappers) { 2637dd252788645e940eada959bdde927426e2531c9Paul Duffin enqueueEvent(event, wrapper); 2647dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2667dd252788645e940eada959bdde927426e2531c9Paul Duffin } finally { 2670888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscribersByTypeLock.readLock().unlock(); 2681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (!dispatched && !(event instanceof DeadEvent)) { 2721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert post(new DeadEvent(this, event)); 2731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert dispatchQueuedEvents(); 2761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 2791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Queue the {@code event} for dispatch during 2801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@link #dispatchQueuedEvents()}. Events are queued in-order of occurrence 2811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * so they can be dispatched in the same order. 2821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2830888a09821a98ac0680fad765217302858e70fa4Paul Duffin void enqueueEvent(Object event, EventSubscriber subscriber) { 2840888a09821a98ac0680fad765217302858e70fa4Paul Duffin eventsToDispatch.get().offer(new EventWithSubscriber(event, subscriber)); 2851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 2881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Drain the queue of events to be dispatched. As the queue is being drained, 2891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * new events may be posted to the end of the queue. 2901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2917dd252788645e940eada959bdde927426e2531c9Paul Duffin void dispatchQueuedEvents() { 2921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // don't dispatch if we're already dispatching, that would allow reentrancy 2931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // and out-of-order events. Instead, leave the events to be dispatched 2941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // after the in-progress dispatch is complete. 2951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (isDispatching.get()) { 2961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return; 2971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert isDispatching.set(true); 3001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert try { 3010888a09821a98ac0680fad765217302858e70fa4Paul Duffin Queue<EventWithSubscriber> events = eventsToDispatch.get(); 3020888a09821a98ac0680fad765217302858e70fa4Paul Duffin EventWithSubscriber eventWithSubscriber; 3030888a09821a98ac0680fad765217302858e70fa4Paul Duffin while ((eventWithSubscriber = events.poll()) != null) { 3040888a09821a98ac0680fad765217302858e70fa4Paul Duffin dispatch(eventWithSubscriber.event, eventWithSubscriber.subscriber); 3051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } finally { 3077dd252788645e940eada959bdde927426e2531c9Paul Duffin isDispatching.remove(); 3087dd252788645e940eada959bdde927426e2531c9Paul Duffin eventsToDispatch.remove(); 3091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 3121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 3130888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Dispatches {@code event} to the subscriber in {@code wrapper}. This method 3141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * is an appropriate override point for subclasses that wish to make 3151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * event delivery asynchronous. 3161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 3171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param event event to dispatch. 3180888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @param wrapper wrapper that will call the subscriber. 3191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 3200888a09821a98ac0680fad765217302858e70fa4Paul Duffin void dispatch(Object event, EventSubscriber wrapper) { 3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert try { 3221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert wrapper.handleEvent(event); 3231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } catch (InvocationTargetException e) { 3240888a09821a98ac0680fad765217302858e70fa4Paul Duffin try { 3250888a09821a98ac0680fad765217302858e70fa4Paul Duffin subscriberExceptionHandler.handleException( 3260888a09821a98ac0680fad765217302858e70fa4Paul Duffin e.getCause(), 3270888a09821a98ac0680fad765217302858e70fa4Paul Duffin new SubscriberExceptionContext( 3280888a09821a98ac0680fad765217302858e70fa4Paul Duffin this, 3290888a09821a98ac0680fad765217302858e70fa4Paul Duffin event, 3300888a09821a98ac0680fad765217302858e70fa4Paul Duffin wrapper.getSubscriber(), 3310888a09821a98ac0680fad765217302858e70fa4Paul Duffin wrapper.getMethod())); 3320888a09821a98ac0680fad765217302858e70fa4Paul Duffin } catch (Throwable t) { 3330888a09821a98ac0680fad765217302858e70fa4Paul Duffin // If the exception handler throws, log it. There isn't much else to do! 3340888a09821a98ac0680fad765217302858e70fa4Paul Duffin Logger.getLogger(EventBus.class.getName()).log(Level.SEVERE, 3350888a09821a98ac0680fad765217302858e70fa4Paul Duffin String.format( 3360888a09821a98ac0680fad765217302858e70fa4Paul Duffin "Exception %s thrown while handling exception: %s", t, 3370888a09821a98ac0680fad765217302858e70fa4Paul Duffin e.getCause()), 3380888a09821a98ac0680fad765217302858e70fa4Paul Duffin t); 3390888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 3401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 3431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 3441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Flattens a class's type hierarchy into a set of Class objects. The set 3451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * will include all superclasses (transitively), and all interfaces 3461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * implemented by these superclasses. 3471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 3481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param concreteClass class whose type hierarchy will be retrieved. 3491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return {@code clazz}'s complete type hierarchy, flattened and uniqued. 3501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 3511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @VisibleForTesting 3521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert Set<Class<?>> flattenHierarchy(Class<?> concreteClass) { 3531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert try { 3547dd252788645e940eada959bdde927426e2531c9Paul Duffin return flattenHierarchyCache.getUnchecked(concreteClass); 3557dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (UncheckedExecutionException e) { 3561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw Throwables.propagate(e.getCause()); 3571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 3600888a09821a98ac0680fad765217302858e70fa4Paul Duffin /** 3610888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Simple logging handler for subscriber exceptions. 3620888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 3630888a09821a98ac0680fad765217302858e70fa4Paul Duffin private static final class LoggingSubscriberExceptionHandler 3640888a09821a98ac0680fad765217302858e70fa4Paul Duffin implements SubscriberExceptionHandler { 3650888a09821a98ac0680fad765217302858e70fa4Paul Duffin 3660888a09821a98ac0680fad765217302858e70fa4Paul Duffin /** 3670888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Logger for event dispatch failures. Named by the fully-qualified name of 3680888a09821a98ac0680fad765217302858e70fa4Paul Duffin * this class, followed by the identifier provided at construction. 3690888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 3700888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final Logger logger; 3710888a09821a98ac0680fad765217302858e70fa4Paul Duffin 3720888a09821a98ac0680fad765217302858e70fa4Paul Duffin /** 3730888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @param identifier a brief name for this bus, for logging purposes. Should 3740888a09821a98ac0680fad765217302858e70fa4Paul Duffin * be a valid Java identifier. 3750888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 3760888a09821a98ac0680fad765217302858e70fa4Paul Duffin public LoggingSubscriberExceptionHandler(String identifier) { 3770888a09821a98ac0680fad765217302858e70fa4Paul Duffin logger = Logger.getLogger( 3780888a09821a98ac0680fad765217302858e70fa4Paul Duffin EventBus.class.getName() + "." + checkNotNull(identifier)); 3790888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 3800888a09821a98ac0680fad765217302858e70fa4Paul Duffin 3810888a09821a98ac0680fad765217302858e70fa4Paul Duffin @Override 3820888a09821a98ac0680fad765217302858e70fa4Paul Duffin public void handleException(Throwable exception, 3830888a09821a98ac0680fad765217302858e70fa4Paul Duffin SubscriberExceptionContext context) { 3840888a09821a98ac0680fad765217302858e70fa4Paul Duffin logger.log(Level.SEVERE, "Could not dispatch event: " 3850888a09821a98ac0680fad765217302858e70fa4Paul Duffin + context.getSubscriber() + " to " + context.getSubscriberMethod(), 3860888a09821a98ac0680fad765217302858e70fa4Paul Duffin exception.getCause()); 3870888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 3880888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 3897dd252788645e940eada959bdde927426e2531c9Paul Duffin 3900888a09821a98ac0680fad765217302858e70fa4Paul Duffin /** simple struct representing an event and it's subscriber */ 3910888a09821a98ac0680fad765217302858e70fa4Paul Duffin static class EventWithSubscriber { 3920888a09821a98ac0680fad765217302858e70fa4Paul Duffin final Object event; 3930888a09821a98ac0680fad765217302858e70fa4Paul Duffin final EventSubscriber subscriber; 3940888a09821a98ac0680fad765217302858e70fa4Paul Duffin public EventWithSubscriber(Object event, EventSubscriber subscriber) { 3957dd252788645e940eada959bdde927426e2531c9Paul Duffin this.event = checkNotNull(event); 3960888a09821a98ac0680fad765217302858e70fa4Paul Duffin this.subscriber = checkNotNull(subscriber); 3971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert} 400