Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
reactor.bus.EventBus Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2014 Pivotal Software, Inc.
*
* 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 reactor.bus;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.Environment;
import reactor.bus.filter.PassThroughFilter;
import reactor.bus.publisher.BusPublisher;
import reactor.bus.registry.CachingRegistry;
import reactor.bus.registry.Registration;
import reactor.bus.registry.Registries;
import reactor.bus.registry.Registry;
import reactor.bus.routing.ConsumerFilteringRouter;
import reactor.bus.routing.Router;
import reactor.bus.selector.ClassSelector;
import reactor.bus.selector.Selector;
import reactor.bus.selector.Selectors;
import reactor.bus.spec.EventBusSpec;
import reactor.core.Dispatcher;
import reactor.core.dispatch.SynchronousDispatcher;
import reactor.core.support.Assert;
import reactor.core.support.UUIDUtils;
import reactor.fn.Consumer;
import reactor.fn.Function;
import reactor.fn.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.UUID;
/**
* A reactor is an event gateway that allows other components to register {@link Event} {@link Consumer}s that can
* subsequently be notified of events. A consumer is typically registered with a {@link Selector} which, by matching on
* the notification key, governs which events the consumer will receive. When a {@literal Reactor} is notified of
* an {@link Event}, a task is dispatched using the reactor's {@link Dispatcher} which causes it to be executed on a
* thread based on the implementation of the {@link Dispatcher} being used.
*
* @author Jon Brisbin
* @author Stephane Maldini
* @author Andy Wilkinson
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class EventBus implements Bus>, Consumer> {
private static final Router DEFAULT_EVENT_ROUTER = new ConsumerFilteringRouter(
new PassThroughFilter()
);
private final Dispatcher dispatcher;
private final Registry>> consumerRegistry;
private final Router router;
private final Consumer dispatchErrorHandler;
private final Consumer uncaughtErrorHandler;
private volatile UUID id;
/**
* Create a new {@link reactor.bus.spec.EventBusSpec} to configure a Reactor.
*
* @return The Reactor spec
*/
public static EventBusSpec config() {
return new EventBusSpec();
}
/**
* Create a new synchronous {@link EventBus}
*
* @return A new {@link EventBus}
*/
public static EventBus create() {
return new EventBus(SynchronousDispatcher.INSTANCE);
}
/**
* Create a new {@link EventBus} using the given {@link reactor.Environment}.
*
* @param env The {@link reactor.Environment} to use.
* @return A new {@link EventBus}
*/
public static EventBus create(Environment env) {
return new EventBusSpec().env(env).dispatcher(env.getDefaultDispatcher()).get();
}
/**
* Create a new {@link EventBus} using the given {@link reactor.core.Dispatcher}.
*
* @param dispatcher The name of the {@link reactor.core.Dispatcher} to use.
* @return A new {@link EventBus}
*/
public static EventBus create(Dispatcher dispatcher) {
return new EventBusSpec().dispatcher(dispatcher).get();
}
/**
* Create a new {@link EventBus} using the given {@link reactor.Environment} and dispatcher name.
*
* @param env The {@link reactor.Environment} to use.
* @param dispatcher The name of the {@link reactor.core.Dispatcher} to use.
* @return A new {@link EventBus}
*/
public static EventBus create(Environment env, String dispatcher) {
return new EventBusSpec().env(env).dispatcher(dispatcher).get();
}
/**
* Create a new {@link EventBus} using the given {@link reactor.Environment} and {@link
* reactor.core.Dispatcher}.
*
* @param env The {@link reactor.Environment} to use.
* @param dispatcher The {@link reactor.core.Dispatcher} to use.
* @return A new {@link EventBus}
*/
public static EventBus create(Environment env, Dispatcher dispatcher) {
return new EventBusSpec().env(env).dispatcher(dispatcher).get();
}
/**
* Create a new {@literal Reactor} that uses the given {@link Dispatcher}. The reactor will use a default {@link
* reactor.bus.routing.Router} that broadcast events to all of the registered consumers that {@link
* Selector#matches(Object) match}
* the notification key and does not perform any type conversion.
*
* @param dispatcher The {@link Dispatcher} to use. May be {@code null} in which case a new {@link
* SynchronousDispatcher} is used
*/
public EventBus(@Nullable Dispatcher dispatcher) {
this(dispatcher, null);
}
/**
* Create a new {@literal Reactor} that uses the given {@link Dispatcher}. The reactor will use a default {@link
* CachingRegistry}.
*
* @param dispatcher The {@link Dispatcher} to use. May be {@code null} in which case a new synchronous dispatcher
* is used.
* @param router The {@link Router} used to route events to {@link Consumer Consumers}. May be {@code null} in
* which case the
* default event router that broadcasts events to all of the registered consumers that {@link
* Selector#matches(Object) match} the notification key and does not perform any type conversion
* will be used.
*/
public EventBus(@Nullable Dispatcher dispatcher,
@Nullable Router router) {
this(dispatcher, router, null, null);
}
public EventBus(@Nullable Dispatcher dispatcher,
@Nullable Router router,
@Nullable Consumer dispatchErrorHandler,
@Nullable final Consumer uncaughtErrorHandler) {
this(Registries.>>create(),
dispatcher,
router,
dispatchErrorHandler,
uncaughtErrorHandler);
}
/**
* Create a new {@literal Reactor} that uses the given {@code dispatacher} and {@code eventRouter}.
*
* @param dispatcher The {@link Dispatcher} to use. May be {@code null} in which case a new synchronous
* dispatcher is used.
* @param router The {@link Router} used to route events to {@link Consumer Consumers}. May be {@code
* null} in which case the
* default event router that broadcasts events to all of the registered consumers that {@link
* Selector#matches(Object) match} the notification key and does not perform any type
* conversion will be used.
* @param consumerRegistry The {@link Registry} to be used to match {@link Selector} and dispatch to {@link
* Consumer}.
*/
public EventBus(@Nonnull Registry>> consumerRegistry,
@Nullable Dispatcher dispatcher,
@Nullable Router router,
@Nullable Consumer dispatchErrorHandler,
@Nullable final Consumer uncaughtErrorHandler) {
Assert.notNull(consumerRegistry, "Consumer Registry cannot be null.");
this.consumerRegistry = consumerRegistry;
this.dispatcher = (null == dispatcher ? SynchronousDispatcher.INSTANCE : dispatcher);
this.router = (null == router ? DEFAULT_EVENT_ROUTER : router);
if (null == dispatchErrorHandler) {
this.dispatchErrorHandler = new Consumer() {
@Override
public void accept(Throwable t) {
Class extends Throwable> type = t.getClass();
EventBus.this.router.route(type,
Event.wrap(t),
EventBus.this.consumerRegistry.select(type),
null,
null);
}
};
} else {
this.dispatchErrorHandler = dispatchErrorHandler;
}
this.uncaughtErrorHandler = uncaughtErrorHandler;
this.on(new ClassSelector(Throwable.class), new Consumer>() {
Logger log;
@Override
public void accept(Event ev) {
if (null == uncaughtErrorHandler) {
if (null == log) {
log = LoggerFactory.getLogger(EventBus.class);
}
log.error(ev.getData().getMessage(), ev.getData());
} else {
uncaughtErrorHandler.accept(ev.getData());
}
}
});
}
/**
* Get the unique, time-used {@link UUID} of this {@literal Reactor}.
*
* @return The {@link UUID} of this {@literal Reactor}.
*/
public synchronized UUID getId() {
if (null == id) {
id = UUIDUtils.create();
}
return id;
}
/**
* Get the {@link Registry} is use to maintain the {@link Consumer}s currently listening for events on this {@literal
* Reactor}.
*
* @return The {@link Registry} in use.
*/
public Registry>> getConsumerRegistry() {
return consumerRegistry;
}
/**
* Get the {@link Dispatcher} currently in use.
*
* @return The {@link Dispatcher}.
*/
public Dispatcher getDispatcher() {
return dispatcher;
}
/**
* Get the {@link reactor.bus.routing.Router} used to route events to {@link Consumer Consumers}.
*
* @return The {@link reactor.bus.routing.Router}.
*/
public Router getRouter() {
return router;
}
public Consumer getDispatchErrorHandler() {
return dispatchErrorHandler;
}
public Consumer getUncaughtErrorHandler() {
return uncaughtErrorHandler;
}
@Override
public boolean respondsToKey(Object key) {
List>>> registrations = consumerRegistry.select(key);
if (registrations.isEmpty()) return false;
for (Registration, ?> reg : registrations) {
if (!reg.isCancelled()) {
return true;
}
}
return false;
}
@Override
public > Registration>> on(final Selector selector,
final Consumer consumer) {
Assert.notNull(selector, "Selector cannot be null.");
Assert.notNull(consumer, "Consumer cannot be null.");
final Class> tClass = extractGeneric(consumer);
Consumer proxyConsumer = new Consumer() {
@Override
public void accept(T e) {
if (null != selector.getHeaderResolver()) {
e.getHeaders().setAll(selector.getHeaderResolver().resolve(e.getKey()));
}
if (tClass == null || e.getData() == null || tClass.isAssignableFrom(e.getData().getClass())) {
consumer.accept(e);
}
}
};
return consumerRegistry.register(selector, proxyConsumer);
}
private Class> extractGeneric(Consumer extends Event>> consumer) {
if(consumer.getClass().getGenericInterfaces().length == 0) return null;
Type t = consumer.getClass().getGenericInterfaces()[0];
if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
ParameterizedType pt = (ParameterizedType) t;
if(pt.getActualTypeArguments().length == 0) return null;
t = pt.getActualTypeArguments()[0];
if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
pt = (ParameterizedType) t;
if(pt.getActualTypeArguments().length == 0) return null;
Type t1 = pt.getActualTypeArguments()[0];
if (t1 instanceof ParameterizedType) {
return (Class>) ((ParameterizedType) t1).getRawType();
} else if (t1 instanceof Class) {
return (Class>) t1;
}
}
}
return null;
}
/**
* Attach a Publisher to the {@link Bus} with the specified {@link Selector}.
*
* @param broadcastSelector the {@link Selector}/{@literal Object} tuple to listen to
* @return a new {@link Publisher}
* @since 2.0
*/
public Publisher extends Event>> on(Selector broadcastSelector) {
return new BusPublisher<>(this, broadcastSelector);
}
@Override
public EventBus notify(Object key, Event> ev) {
Assert.notNull(key, "Key cannot be null.");
Assert.notNull(ev, "Event cannot be null.");
ev.setKey(key);
dispatcher.dispatch(ev, this, dispatchErrorHandler);
return this;
}
/**
* Pass values accepted by this {@code Stream} into the given {@link Bus}, notifying with the given key.
*
* @param key the key to notify on
* @param source the {@link Publisher} to consume
* @return {@literal new Stream}
* @since 1.1, 2.0
*/
public final EventBus notify(@Nonnull final Publisher> source, @Nonnull final Object key) {
return notify(source, new Function(){
@Override
public Object apply(Object o) {
return key;
}
});
}
/**
* Pass values accepted by this {@code Stream} into the given {@link Bus}, notifying with the given key.
*
* @param source the {@link Publisher} to consume
* @param keyMapper the key function mapping each incoming data to a key to notify on
* @return {@literal new Stream}
* @since 2.0
*/
public final EventBus notify(@Nonnull final Publisher extends T> source, @Nonnull final Function super T, ?> keyMapper) {
source.subscribe(new Subscriber() {
Subscription s;
@Override
public void onSubscribe(Subscription s) {
this.s = s;
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(T t) {
EventBus.this.notify(keyMapper.apply(t), Event.wrap(t));
}
@Override
public void onError(Throwable t) {
if(s != null) s.cancel();
}
@Override
public void onComplete() {
if(s != null) s.cancel();
}
});
return this;
}
/**
* Assign a {@link reactor.fn.Function} to receive an {@link Event} and produce a reply of the given type.
*
* @param sel The {@link Selector} to be used for matching
* @param fn The transformative {@link reactor.fn.Function} to call to receive an {@link Event}
* @return A {@link Registration} object that allows the caller to interact with the given mapping
*/
public , V> Registration, Consumer extends Event>>> receive(Selector sel, Function fn) {
return on(sel, new ReplyToConsumer<>(fn));
}
/**
* Notify this component that the given {@link reactor.fn.Supplier} can provide an event that's ready to be
* processed.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param supplier The {@link reactor.fn.Supplier} that will provide the actual {@link Event}
* @return {@literal this}
*/
public EventBus notify(Object key, Supplier extends Event>> supplier) {
return notify(key, supplier.get());
}
/**
* Notify this component that the consumers registered with a {@link Selector} that matches the {@code key} should be
* triggered with a {@literal null} input argument.
*
* @param key The key to be matched by {@link Selector Selectors}
* @return {@literal this}
*/
public EventBus notify(Object key) {
return notify(key, new Event<>(Void.class));
}
/**
* Notify this component of the given {@link Event} and register an internal {@link Consumer} that will take the
* output of a previously-registered {@link Function} and respond using the key set on the {@link Event}'s {@literal
* replyTo} property.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param ev The {@literal Event}
* @return {@literal this}
*/
public EventBus send(Object key, Event> ev) {
return notify(key, new ReplyToEvent(ev, this));
}
/**
* Notify this component that the given {@link Supplier} will provide an {@link Event} and register an internal
* {@link
* Consumer} that will take the output of a previously-registered {@link Function} and respond using the key set on
* the {@link Event}'s {@literal replyTo} property.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param supplier The {@link Supplier} that will provide the actual {@link Event} instance
* @return {@literal this}
*/
public EventBus send(Object key, Supplier extends Event>> supplier) {
return notify(key, new ReplyToEvent(supplier.get(), this));
}
/**
* Notify this component of the given {@link Event} and register an internal {@link Consumer} that will take the
* output of a previously-registered {@link Function} and respond to the key set on the {@link Event}'s {@literal
* replyTo} property and will call the {@code notify} method on the given {@link Bus}.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param ev The {@literal Event}
* @param replyTo The {@link Bus} on which to invoke the notify method
* @return {@literal this}
*/
public EventBus send(Object key, Event> ev, Bus replyTo) {
return notify(key, new ReplyToEvent(ev, replyTo));
}
/**
* Notify this component that the given {@link Supplier} will provide an {@link Event} and register an internal
* {@link
* Consumer} that will take the output of a previously-registered {@link Function} and respond to the key set on the
* {@link Event}'s {@literal replyTo} property and will call the {@code notify} method on the given {@link
* Bus}.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param supplier The {@link Supplier} that will provide the actual {@link Event} instance
* @param replyTo The {@link Bus} on which to invoke the notify method
* @return {@literal this}
*/
public EventBus send(Object key, Supplier extends Event>> supplier, Bus replyTo) {
return notify(key, new ReplyToEvent(supplier.get(), replyTo));
}
/**
* Register the given {@link reactor.fn.Consumer} on an anonymous {@link reactor.bus.selector.Selector} and
* set the given event's {@code replyTo} property to the corresponding anonymous key, then register the consumer to
* receive replies from the {@link reactor.fn.Function} assigned to handle the given key.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param event The event to notify.
* @param reply The consumer to register as a reply handler.
* @return {@literal this}
*/
public > EventBus sendAndReceive(Object key, Event> event, Consumer reply) {
Selector sel = Selectors.anonymous();
on(sel, reply).cancelAfterUse();
notify(key, event.setReplyTo(sel.getObject()));
return this;
}
/**
* Register the given {@link reactor.fn.Consumer} on an anonymous {@link reactor.bus.selector.Selector} and
* set the event's {@code replyTo} property to the corresponding anonymous key, then register the consumer to receive
* replies from the {@link reactor.fn.Function} assigned to handle the given key.
*
* @param key The key to be matched by {@link Selector Selectors}
* @param supplier The supplier to supply the event.
* @param reply The consumer to register as a reply handler.
* @return {@literal this}
*/
public > EventBus sendAndReceive(Object key, Supplier extends Event>> supplier, Consumer reply) {
return sendAndReceive(key, supplier.get(), reply);
}
/**
* Create an optimized path for publishing notifications to the given key.
*
* @param key The key to be matched by {@link Selector Selectors}
* @return a {@link Consumer} to invoke with the {@link Event Events} to publish
*/
public Consumer> prepare(final Object key) {
return new Consumer>() {
final List>>> regs = consumerRegistry.select(key);
final int size = regs.size();
@Override
public void accept(Event ev) {
for (int i = 0; i < size; i++) {
Registration>> reg =
(Registration>>) regs.get(i);
ev.setKey(key);
dispatcher.dispatch(ev, reg.getObject(), dispatchErrorHandler);
}
}
};
}
/**
* Schedule an arbitrary {@link reactor.fn.Consumer} to be executed on the current Reactor {@link
* reactor.core.Dispatcher}, passing the given {@param data}.
*
* @param consumer The {@link reactor.fn.Consumer} to invoke.
* @param data The data to pass to the consumer.
* @param The type of the data.
*/
public void schedule(final Consumer consumer, final T data) {
dispatcher.dispatch(null, new Consumer>() {
@Override
public void accept(Event> event) {
consumer.accept(data);
}
}, dispatchErrorHandler);
}
@Override
public void accept(Event> event) {
router.route(event.getKey(), event, consumerRegistry.select(event.getKey()), null, dispatchErrorHandler);
}
public static class ReplyToEvent extends Event {
private static final long serialVersionUID = 1937884784799135647L;
private final Bus replyToObservable;
private ReplyToEvent(Headers headers, T data, Object replyTo,
Bus replyToObservable,
Consumer errorConsumer) {
super(headers, data, errorConsumer);
setReplyTo(replyTo);
this.replyToObservable = replyToObservable;
}
private ReplyToEvent(Event delegate, Bus replyToObservable) {
this(delegate.getHeaders(), delegate.getData(), delegate.getReplyTo(), replyToObservable,
delegate.getErrorConsumer());
}
@Override
public Event copy(X data) {
return new ReplyToEvent(getHeaders(), data, getReplyTo(), replyToObservable, getErrorConsumer());
}
public Bus getReplyToObservable() {
return replyToObservable;
}
}
public class ReplyToConsumer, V> implements Consumer {
private final Function fn;
private ReplyToConsumer(Function fn) {
this.fn = fn;
}
@Override
public void accept(E ev) {
Bus replyToObservable = EventBus.this;
if (ReplyToEvent.class.isAssignableFrom(ev.getClass())) {
Bus o = ((ReplyToEvent>) ev).getReplyToObservable();
if (null != o) {
replyToObservable = o;
}
}
try {
V reply = fn.apply(ev);
Event> replyEv;
if (null == reply) {
replyEv = new Event(Void.class);
} else {
replyEv = (Event.class.isAssignableFrom(reply.getClass()) ? (Event>) reply : Event.wrap(reply));
}
replyToObservable.notify(ev.getReplyTo(), replyEv);
} catch (Throwable x) {
replyToObservable.notify(x.getClass(), Event.wrap(x));
}
}
public Function getDelegate() {
return fn;
}
}
}