Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2007 The Guava Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.google.common.eventbus;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 
 import java.util.Set;
Dispatches events to listeners, and provides ways for listeners to register themselves.

The EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration. It is not a general-purpose publish-subscribe system, nor is it intended for interprocess communication.

Receiving Events

To receive events, an object should:

  1. Expose a public method, known as the event subscriber, which accepts a single argument of the type of event desired;
  2. Mark it with a Subscribe annotation;
  3. Pass itself to an EventBus instance's register(java.lang.Object) method.

Posting Events

To post an event, simply provide the event object to the post(java.lang.Object) method. The EventBus instance will determine the type of event and route it to all registered listeners.

Events are routed based on their type — an event will be delivered to any subscriber for any type to which the event is assignable. This includes implemented interfaces, all superclasses, and all interfaces implemented by superclasses.

When post is called, all registered subscribers for an event are run in sequence, so subscribers should be reasonably quick. If an event may trigger an extended process (such as a database load), spawn a thread or queue it for later. (For a convenient way to do this, use an AsyncEventBus.)

Subscriber Methods

Event subscriber methods must accept only one argument: the event.

Subscribers should not, in general, throw. If they do, the EventBus will catch and log the exception. This is rarely the right solution for error handling and should not be relied upon; it is intended solely to help find problems during development.

The EventBus guarantees that it will not call a subscriber method from multiple threads simultaneously, unless the method explicitly allows it by bearing the AllowConcurrentEvents annotation. If this annotation is not present, subscriber methods need not worry about being reentrant, unless also called from outside the EventBus.

Dead Events

If an event is posted, but no registered subscribers can accept it, it is considered "dead." To give the system a second chance to handle dead events, they are wrapped in an instance of DeadEvent and reposted.

If a subscriber for a supertype of all events (such as Object) is registered, no event will ever be considered dead, and no DeadEvents will be generated. Accordingly, while DeadEvent extends java.lang.Object, a subscriber registered to receive any Object will never receive a DeadEvent.

This class is safe for concurrent use.

See the Guava User Guide article on EventBus.

Author(s):
Cliff Biffle
Since:
10.0
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.
  private static final LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache =
      CacheBuilder.newBuilder()
          .weakKeys()
          .build(new CacheLoader<Class<?>, Set<Class<?>>>() {
            @SuppressWarnings({"unchecked""rawtypes"}) // safe cast
            @Override
            public Set<Class<?>> load(Class<?> concreteClass) {
              return (Set) TypeToken.of(concreteClass).getTypes().rawTypes();
            }
          });

  
All registered event subscribers, indexed by event type.

This SetMultimap is NOT safe for concurrent use; all access should be made after acquiring a read or write lock via subscribersByTypeLock.

      HashMultimap.create();
Strategy for finding subscriber methods in registered objects. Currently, only the AnnotatedSubscriberFinder is supported, but this is encapsulated for future expansion.
queues of events for the current thread to dispatch
      new ThreadLocal<Queue<EventWithSubscriber>>() {
      return new LinkedList<EventWithSubscriber>();
    }
  };

  
true if the current thread is currently dispatching an event
  private final ThreadLocal<BooleanisDispatching =
      new ThreadLocal<Boolean>() {
    @Override protected Boolean initialValue() {
      return false;
    }
  };
Creates a new EventBus named "default".
  public EventBus() {
    this("default");
  }

  
Creates a new EventBus with the given identifier.

Parameters:
identifier a brief name for this bus, for logging purposes. Should be a valid Java identifier.
  public EventBus(String identifier) {
    this(new LoggingSubscriberExceptionHandler(identifier));
  }

  
Creates a new EventBus with the given SubscriberExceptionHandler.

Parameters:
subscriberExceptionHandler Handler for subscriber exceptions.
Since:
16.0
  public EventBus(SubscriberExceptionHandler subscriberExceptionHandler) {
    this. = checkNotNull(subscriberExceptionHandler);
  }

  
Registers all subscriber methods on object to receive events. Subscriber methods are selected and classified using this EventBus's SubscriberFindingStrategy; the default strategy is the AnnotatedSubscriberFinder.

Parameters:
object object whose subscriber methods should be registered.
  public void register(Object object) {
    Multimap<Class<?>, EventSubscribermethodsInListener =
        .findAllSubscribers(object);
    try {
      .putAll(methodsInListener);
    } finally {
    }
  }

  
Unregisters all subscriber methods on a registered object.

Parameters:
object object whose subscriber methods should be unregistered.
Throws:
java.lang.IllegalArgumentException if the object was not previously registered.
  public void unregister(Object object) {
    Multimap<Class<?>, EventSubscribermethodsInListener = .findAllSubscribers(object);
    for (Entry<Class<?>, Collection<EventSubscriber>> entry :
          methodsInListener.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<EventSubscribereventMethodsInListener = entry.getValue();
      try {
        Set<EventSubscribercurrentSubscribers = .get(eventType);
        if (!currentSubscribers.containsAll(eventMethodsInListener)) {
          throw new IllegalArgumentException(
              "missing event subscriber for an annotated method. Is " + object + " registered?");
        }
        currentSubscribers.removeAll(eventMethodsInListener);
      } finally {
      }
    }
  }

  
Posts an event to all registered subscribers. This method will return successfully after the event has been posted to all subscribers, and regardless of any exceptions thrown by subscribers.

If no subscribers have been subscribed for event's class, and event is not already a DeadEvent, it will be wrapped in a DeadEvent and reposted.

Parameters:
event event to post.
  public void post(Object event) {
    Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
    boolean dispatched = false;
    for (Class<?> eventType : dispatchTypes) {
      try {
        Set<EventSubscriberwrappers = .get(eventType);
        if (!wrappers.isEmpty()) {
          dispatched = true;
          for (EventSubscriber wrapper : wrappers) {
            enqueueEvent(eventwrapper);
          }
        }
      } finally {
      }
    }
    if (!dispatched && !(event instanceof DeadEvent)) {
      post(new DeadEvent(thisevent));
    }
  }

  
Queue the event for dispatch during dispatchQueuedEvents(). Events are queued in-order of occurrence so they can be dispatched in the same order.
  void enqueueEvent(Object eventEventSubscriber subscriber) {
    .get().offer(new EventWithSubscriber(eventsubscriber));
  }

  
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.
    // 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.
    if (.get()) {
      return;
    }
    .set(true);
    try {
      Queue<EventWithSubscriberevents = .get();
      EventWithSubscriber eventWithSubscriber;
      while ((eventWithSubscriber = events.poll()) != null) {
        dispatch(eventWithSubscriber.eventeventWithSubscriber.subscriber);
      }
    } finally {
      .remove();
    }
  }

  
Dispatches event to the subscriber in wrapper. This method is an appropriate override point for subclasses that wish to make event delivery asynchronous.

Parameters:
event event to dispatch.
wrapper wrapper that will call the subscriber.
  void dispatch(Object eventEventSubscriber wrapper) {
    try {
      wrapper.handleEvent(event);
    } catch (InvocationTargetException e) {
      try {
            e.getCause(),
            new SubscriberExceptionContext(
                this,
                event,
                wrapper.getSubscriber(),
                wrapper.getMethod()));
      } catch (Throwable t) {
        // If the exception handler throws, log it. There isn't much else to do!
        Logger.getLogger(EventBus.class.getName()).log(.,
             String.format(
            "Exception %s thrown while handling exception: %s"t,
            e.getCause()),
            t);
      }
    }
  }

  
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.

Parameters:
concreteClass class whose type hierarchy will be retrieved.
Returns:
clazz's complete type hierarchy, flattened and uniqued.
  Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
    try {
      return .getUnchecked(concreteClass);
    } catch (UncheckedExecutionException e) {
      throw Throwables.propagate(e.getCause());
    }
  }

  
Simple logging handler for subscriber exceptions.
  private static final class LoggingSubscriberExceptionHandler
      implements SubscriberExceptionHandler {

    
Logger for event dispatch failures. Named by the fully-qualified name of this class, followed by the identifier provided at construction.
    private final Logger logger;

    

Parameters:
identifier a brief name for this bus, for logging purposes. Should be a valid Java identifier.
    public LoggingSubscriberExceptionHandler(String identifier) {
       = Logger.getLogger(
          EventBus.class.getName() + "." + checkNotNull(identifier));
    }
    @Override
    public void handleException(Throwable exception,
        SubscriberExceptionContext context) {
      .log(."Could not dispatch event: " 
          + context.getSubscriber() + " to " + context.getSubscriberMethod(),
          exception.getCause());
    }
  }

  
simple struct representing an event and it's subscriber
  static class EventWithSubscriber {
    final Object event;
    public EventWithSubscriber(Object eventEventSubscriber subscriber) {
      this. = checkNotNull(event);
      this. = checkNotNull(subscriber);
    }
  }
New to GrepCode? Check out our FAQ X