All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.pustike.eventbus.EventBus Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016-2018 the original author or 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
 *
 * https://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 io.github.pustike.eventbus;

import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * 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 {@link * Subscribe} annotation;
  3. Pass itself to an EventBus instance's {@link #register(Object)} method.
* *

Posting Events

To post an event, simply provide the event object to the {@link #publish(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 {@code 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. * *

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. 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 {@link 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 {@link 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 {@code * EventBus}. * @author Cliff Biffle */ public final class EventBus { // the identifier for this event bus private final String identifier; // executor to dispatch events received from the dispatcher private final Executor executor; // handler for dispatching events to subscribers private final Dispatcher dispatcher; private final SubscriberRegistry subscriberRegistry; /** * Creates a new EventBus named "default". */ public EventBus() { this("default"); } /** * Creates a new EventBus with the given {@code identifier}. * @param identifier a brief name for this bus, for logging purposes. */ public EventBus(String identifier) { this(identifier, Dispatcher.perThreadDispatchQueue(), createDirectExecutor(), null); } /** * Creates a new EventBus with the given {@code executor} and {@code dispatcher}. * @param dispatcher handler for dispatching events to subscribers * @param executor to dispatch events received from the dispatcher. */ public EventBus(Dispatcher dispatcher, Executor executor) { this("default", dispatcher, executor, null); } /** * Creates a new EventBus using external cache for subscriberMethods and eventSuperTypes. * @param identifier a brief name for this bus, for logging purposes. * @param dispatcher handler for dispatching events to subscribers * @param executor to dispatch events received from the dispatcher. * @param subscriberLoader the cache for subscriberMethods and eventTypeHierarchy */ public EventBus(String identifier, Dispatcher dispatcher, Executor executor, SubscriberLoader subscriberLoader) { this.identifier = Objects.requireNonNull(identifier); this.executor = Objects.requireNonNull(executor); this.dispatcher = Objects.requireNonNull(dispatcher); this.subscriberRegistry = new SubscriberRegistry(this, subscriberLoader); } /** * Returns the identifier for this event bus. * @return the event bus identifier */ public final String getIdentifier() { return identifier; } /** * Returns the default executor this event bus uses for dispatching events to subscribers. */ final Executor executor() { return executor; } /** * Registers the specified subscriber to the event bus. A subscribed object will be notified of any published * events on the methods annotated with the {@link Subscribe} annotation. * *

Each event handler method should take a single parameter indicating the type of event it wishes to receive. * When events are published on the bus, only subscribers who have an EventHandler method with a matching parameter * of the same type as the published event will receive the event notification from the bus. * *

Note that the EventBus maintains a {@link WeakReference} to the subscriber, but it is still advised to call * the {@link #unregister(Object)} method if the subscriber does not wish to receive events any longer. * @param object The object to subscribe to the event bus. */ public void register(Object object) { subscriberRegistry.register(object); } /** * Unregisters all subscriber methods on a registered {@code object}. * @param object object whose subscriber methods should be unregistered. */ public void unregister(Object object) { subscriberRegistry.unregister(object); } /** * 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 {@code event}'s class, and {@code event} is not already a {@link * DeadEvent}, it will be wrapped in a DeadEvent and reposted. * @param event event to post. */ public void publish(Object event) { Objects.requireNonNull(event); Iterator eventSubscribers = subscriberRegistry.getSubscribers(event); if (eventSubscribers.hasNext()) { dispatcher.dispatch(event, eventSubscribers); } else if (!(event instanceof DeadEvent) && !(event instanceof ExceptionEvent)) { // the event had no subscribers and was not itself a DeadEvent or an ExceptionEvent publish(new DeadEvent(this, event)); } } /** * Clear all subscribers from the cache. */ public void close() { subscriberRegistry.clear(); } @Override public String toString() { return getClass().getSimpleName() + "{identifier=" + identifier + "}"; } /** * Returns an {@link Executor} that runs each task in the thread that invokes {@link Executor#execute execute}, as * in {@link java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy}. */ private static Executor createDirectExecutor() { return Runnable::run; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy