aboutsummaryrefslogtreecommitdiffstats
path: root/guava/src/com/google/common/eventbus/EventBus.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/eventbus/EventBus.java')
-rw-r--r--guava/src/com/google/common/eventbus/EventBus.java184
1 files changed, 103 insertions, 81 deletions
diff --git a/guava/src/com/google/common/eventbus/EventBus.java b/guava/src/com/google/common/eventbus/EventBus.java
index 94cf2e9..a962fa8 100644
--- a/guava/src/com/google/common/eventbus/EventBus.java
+++ b/guava/src/com/google/common/eventbus/EventBus.java
@@ -16,28 +16,28 @@
package com.google.common.eventbus;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
-import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
-import com.google.common.reflect.TypeToken;
-import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.common.collect.Sets;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
-import java.util.LinkedList;
+import java.util.List;
import java.util.Map.Entry;
-import java.util.Queue;
import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -101,10 +101,6 @@ import java.util.logging.Logger;
* receive any Object will never receive a DeadEvent.
*
* <p>This class is safe for concurrent use.
- *
- * <p>See the Guava User Guide article on <a href=
- * "http://code.google.com/p/guava-libraries/wiki/EventBusExplained">
- * {@code EventBus}</a>.
*
* @author Cliff Biffle
* @since 10.0
@@ -113,32 +109,18 @@ import java.util.logging.Logger;
public class EventBus {
/**
- * A thread-safe cache for flattenHierarchy(). The Class class is immutable. This cache is shared
- * across all EventBus instances, which greatly improves performance if multiple such instances
- * are created and objects of the same class are posted on all of them.
+ * All registered event handlers, indexed by event type.
*/
- private static final LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache =
- CacheBuilder.newBuilder()
- .weakKeys()
- .build(new CacheLoader<Class<?>, Set<Class<?>>>() {
- @SuppressWarnings({"unchecked", "rawtypes"}) // safe cast
+ private final SetMultimap<Class<?>, EventHandler> handlersByType =
+ Multimaps.newSetMultimap(new ConcurrentHashMap<Class<?>, Collection<EventHandler>>(),
+ new Supplier<Set<EventHandler>>() {
@Override
- public Set<Class<?>> load(Class<?> concreteClass) {
- return (Set) TypeToken.of(concreteClass).getTypes().rawTypes();
+ public Set<EventHandler> get() {
+ return new CopyOnWriteArraySet<EventHandler>();
}
});
/**
- * All registered event handlers, indexed by event type.
- *
- * <p>This SetMultimap is NOT safe for concurrent use; all access should be
- * made after acquiring a read or write lock via {@link #handlersByTypeLock}.
- */
- private final SetMultimap<Class<?>, EventHandler> handlersByType =
- HashMultimap.create();
- private final ReadWriteLock handlersByTypeLock = new ReentrantReadWriteLock();
-
- /**
* Logger for event dispatch failures. Named by the fully-qualified name of
* this class, followed by the identifier provided at construction.
*/
@@ -152,10 +134,11 @@ public class EventBus {
private final HandlerFindingStrategy finder = new AnnotatedHandlerFinder();
/** queues of events for the current thread to dispatch */
- private final ThreadLocal<Queue<EventWithHandler>> eventsToDispatch =
- new ThreadLocal<Queue<EventWithHandler>>() {
- @Override protected Queue<EventWithHandler> initialValue() {
- return new LinkedList<EventWithHandler>();
+ private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>
+ eventsToDispatch =
+ new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() {
+ @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() {
+ return new ConcurrentLinkedQueue<EventWithHandler>();
}
};
@@ -168,6 +151,38 @@ public class EventBus {
};
/**
+ * A thread-safe cache for flattenHierarch(). The Class class is immutable.
+ */
+ private LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache =
+ CacheBuilder.newBuilder()
+ .weakKeys()
+ .build(new CacheLoader<Class<?>, Set<Class<?>>>() {
+ @Override
+ public Set<Class<?>> load(Class<?> concreteClass) throws Exception {
+ List<Class<?>> parents = Lists.newLinkedList();
+ Set<Class<?>> classes = Sets.newHashSet();
+
+ parents.add(concreteClass);
+
+ while (!parents.isEmpty()) {
+ Class<?> clazz = parents.remove(0);
+ classes.add(clazz);
+
+ Class<?> parent = clazz.getSuperclass();
+ if (parent != null) {
+ parents.add(parent);
+ }
+
+ for (Class<?> iface : clazz.getInterfaces()) {
+ parents.add(iface);
+ }
+ }
+
+ return classes;
+ }
+ });
+
+ /**
* Creates a new EventBus named "default".
*/
public EventBus() {
@@ -181,7 +196,7 @@ public class EventBus {
* be a valid Java identifier.
*/
public EventBus(String identifier) {
- logger = Logger.getLogger(EventBus.class.getName() + "." + checkNotNull(identifier));
+ logger = Logger.getLogger(EventBus.class.getName() + "." + identifier);
}
/**
@@ -193,14 +208,7 @@ public class EventBus {
* @param object object whose handler methods should be registered.
*/
public void register(Object object) {
- Multimap<Class<?>, EventHandler> methodsInListener =
- finder.findAllHandlers(object);
- handlersByTypeLock.writeLock().lock();
- try {
- handlersByType.putAll(methodsInListener);
- } finally {
- handlersByTypeLock.writeLock().unlock();
- }
+ handlersByType.putAll(finder.findAllHandlers(object));
}
/**
@@ -212,20 +220,14 @@ public class EventBus {
public void unregister(Object object) {
Multimap<Class<?>, EventHandler> methodsInListener = finder.findAllHandlers(object);
for (Entry<Class<?>, Collection<EventHandler>> entry : methodsInListener.asMap().entrySet()) {
- Class<?> eventType = entry.getKey();
+ Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
Collection<EventHandler> eventMethodsInListener = entry.getValue();
-
- handlersByTypeLock.writeLock().lock();
- try {
- Set<EventHandler> currentHandlers = handlersByType.get(eventType);
- if (!currentHandlers.containsAll(eventMethodsInListener)) {
- throw new IllegalArgumentException(
- "missing event handler for an annotated method. Is " + object + " registered?");
- }
- currentHandlers.removeAll(eventMethodsInListener);
- } finally {
- handlersByTypeLock.writeLock().unlock();
+
+ if (currentHandlers == null || !currentHandlers.containsAll(entry.getValue())) {
+ throw new IllegalArgumentException(
+ "missing event handler for an annotated method. Is " + object + " registered?");
}
+ currentHandlers.removeAll(eventMethodsInListener);
}
}
@@ -245,18 +247,13 @@ public class EventBus {
boolean dispatched = false;
for (Class<?> eventType : dispatchTypes) {
- handlersByTypeLock.readLock().lock();
- try {
- Set<EventHandler> wrappers = handlersByType.get(eventType);
-
- if (!wrappers.isEmpty()) {
- dispatched = true;
- for (EventHandler wrapper : wrappers) {
- enqueueEvent(event, wrapper);
- }
+ Set<EventHandler> wrappers = getHandlersForEventType(eventType);
+
+ if (wrappers != null && !wrappers.isEmpty()) {
+ dispatched = true;
+ for (EventHandler wrapper : wrappers) {
+ enqueueEvent(event, wrapper);
}
- } finally {
- handlersByTypeLock.readLock().unlock();
}
}
@@ -272,7 +269,7 @@ public class EventBus {
* {@link #dispatchQueuedEvents()}. Events are queued in-order of occurrence
* so they can be dispatched in the same order.
*/
- void enqueueEvent(Object event, EventHandler handler) {
+ protected void enqueueEvent(Object event, EventHandler handler) {
eventsToDispatch.get().offer(new EventWithHandler(event, handler));
}
@@ -280,7 +277,7 @@ public class EventBus {
* Drain the queue of events to be dispatched. As the queue is being drained,
* new events may be posted to the end of the queue.
*/
- void dispatchQueuedEvents() {
+ protected void dispatchQueuedEvents() {
// don't dispatch if we're already dispatching, that would allow reentrancy
// and out-of-order events. Instead, leave the events to be dispatched
// after the in-progress dispatch is complete.
@@ -290,14 +287,16 @@ public class EventBus {
isDispatching.set(true);
try {
- Queue<EventWithHandler> events = eventsToDispatch.get();
- EventWithHandler eventWithHandler;
- while ((eventWithHandler = events.poll()) != null) {
+ while (true) {
+ EventWithHandler eventWithHandler = eventsToDispatch.get().poll();
+ if (eventWithHandler == null) {
+ break;
+ }
+
dispatch(eventWithHandler.event, eventWithHandler.handler);
}
} finally {
- isDispatching.remove();
- eventsToDispatch.remove();
+ isDispatching.set(false);
}
}
@@ -309,7 +308,7 @@ public class EventBus {
* @param event event to dispatch.
* @param wrapper wrapper that will call the handler.
*/
- void dispatch(Object event, EventHandler wrapper) {
+ protected void dispatch(Object event, EventHandler wrapper) {
try {
wrapper.handleEvent(event);
} catch (InvocationTargetException e) {
@@ -319,6 +318,29 @@ public class EventBus {
}
/**
+ * Retrieves a mutable set of the currently registered handlers for
+ * {@code type}. If no handlers are currently registered for {@code type},
+ * this method may either return {@code null} or an empty set.
+ *
+ * @param type type of handlers to retrieve.
+ * @return currently registered handlers, or {@code null}.
+ */
+ Set<EventHandler> getHandlersForEventType(Class<?> type) {
+ return handlersByType.get(type);
+ }
+
+ /**
+ * Creates a new Set for insertion into the handler map. This is provided
+ * as an override point for subclasses. The returned set should support
+ * concurrent access.
+ *
+ * @return a new, mutable set for handlers.
+ */
+ protected Set<EventHandler> newHandlerSet() {
+ return new CopyOnWriteArraySet<EventHandler>();
+ }
+
+ /**
* Flattens a class's type hierarchy into a set of Class objects. The set
* will include all superclasses (transitively), and all interfaces
* implemented by these superclasses.
@@ -329,8 +351,8 @@ public class EventBus {
@VisibleForTesting
Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
try {
- return flattenHierarchyCache.getUnchecked(concreteClass);
- } catch (UncheckedExecutionException e) {
+ return flattenHierarchyCache.get(concreteClass);
+ } catch (ExecutionException e) {
throw Throwables.propagate(e.getCause());
}
}
@@ -340,8 +362,8 @@ public class EventBus {
final Object event;
final EventHandler handler;
public EventWithHandler(Object event, EventHandler handler) {
- this.event = checkNotNull(event);
- this.handler = checkNotNull(handler);
+ this.event = event;
+ this.handler = handler;
}
}
}