package com.netflix.eventbus.bridge; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.inject.Inject; import com.netflix.eventbus.spi.DynamicSubscriber; import com.netflix.eventbus.spi.EventBus; import com.netflix.eventbus.spi.EventFilter; import com.netflix.eventbus.spi.Subscribe; /** * Implements basic functionality of registering for the event bus and * forwarding events to another messaging API. * * @author elandau * */ public abstract class AbstractEventBusBridge implements EventBusBridge { public static final Boolean DEFAULT_AUTO_START = true; public static final Supplier<EventBusBridgeStats> DEFAULT_STATS_SUPPLIER = new Supplier<EventBusBridgeStats>() { @Override public EventBusBridgeStats get() { return new SimpleEventBusBridgeStats(); } }; /** * Base builder with support for fluent subsclasses * * Note that the subclass's Build method MUST call validate(). * * @param <T> The subclasses's Builder. */ public static abstract class Builder<T extends Builder<T>> { protected EventBus eventBus; protected Class<?> eventType; protected Supplier<EventBusBridgeStats> statsSupplier = DEFAULT_STATS_SUPPLIER; protected boolean autoStart = DEFAULT_AUTO_START; protected EventFilter filter; /** * The event bus to use * @param eventBus */ @Inject public T withEventBus(EventBus eventBus) { this.eventBus = eventBus; return self(); } /** * Specify the event type to listen to. This can be a primitive, Pojo, or * Annotatable. * @param eventType */ public T withEventType(Class<?> eventType) { this.eventType = eventType; return self(); } /** * Predicate used to filter which events may be sent to the sink * @param filter */ public T withFilter(EventFilter filter) { this.filter = filter; return self(); } /** * Externally provided stats supplier * @param supplier */ public T withStatsSupplier(Supplier<EventBusBridgeStats> supplier) { if (supplier != null) this.statsSupplier = supplier; return self(); } /** * Indicate whether the bridge should begin consuming messages immediately * after being constructed, otherwise events will not be consumed until * resume() is called. * @param autoStart * @return */ public T withAutoStart(Boolean autoStart) { this.autoStart = autoStart; return self(); } /** * Externally provided specific instance of stats object * @param stats */ public T withStats(EventBusBridgeStats stats) { if (stats != null) this.statsSupplier = Suppliers.ofInstance(stats); return self(); } protected void validate() throws Exception { Preconditions.checkNotNull(eventType, "Must specify an event type"); Preconditions.checkNotNull(eventBus, "Must specify an event bus"); } /** * Trick to allow for fluent API using the base Builder and the subclass's * builder. The subclass must implement self() as 'return this;'. * @return */ protected abstract T self(); } protected final EventBus eventBus; protected final EventBusBridgeStats stats; protected final Class<?> eventType; protected final Object subscriber; protected final EventFilter filter; protected volatile Boolean paused = false; protected AbstractEventBusBridge(Builder<?> init) throws Exception { this.eventBus = init.eventBus; this.stats = init.statsSupplier.get(); this.eventType = init.eventType; this.paused = !init.autoStart; this.filter = init.filter; this.subscriber = new DynamicSubscriber() { @Override public Class<?> getEventType() { return eventType; } @Subscribe public void consume(Object obj) { try { if (!paused) { sendEvent(obj); stats.incConsumeCount(); } } catch (Exception e) { stats.incConsumeErrorCount(e); } } }; } @PostConstruct final public void init() throws Exception { preInit(); if (!this.isPaused()) { paused = true; resume(); } } @PreDestroy final public void shutdown() throws Exception { pause(); postShutdown(); } /** * Template method for sending an event * @param event * @throws Exception * @deprecated Use {@link onNextEvent} instead */ @Deprecated protected abstract void sendEvent(Object event) throws Exception; protected void onNextEvent(Object event) throws Exception { sendEvent(event); } /** * Template method giving the subclass a chance to initialize any connection * as init time. * @throws Exception */ protected void preInit() throws Exception { } /** * Template method giving the subclass a chance to do final cleanup after * the bridge is terminated * @throws Exception */ protected void postShutdown() throws Exception { } /** * Template method giving the subclass a chance to perform any necessary * steps prior to resuming consumption of messages. Note that preResume() * is called immediately after preInit() during the first initialization * phase. This is a good place to resume any threads previous * * This call is thread safe. * * @throws Exception */ protected void preResume() throws Exception { } /** * Template method giving the subclass a chance to perform any necessary * steps immediately after pausing consumption of messages. This is a * good place to temporarily suspend any threads specific to the * bridge implementation. * * This call is thread safe. * * @throws Exception */ protected void postPause() throws Exception { } @Override final public synchronized void pause() throws Exception { if (paused == false) { paused = true; this.eventBus.unregisterSubscriber(subscriber); postPause(); } } @Override final public synchronized void resume() throws Exception { if (paused == true) { preResume(); if (filter != null) this.eventBus.registerSubscriber(filter, subscriber); else this.eventBus.registerSubscriber(subscriber); paused = false; } } public boolean isPaused() { return paused; } public long getConsumeErrorCount() { return stats.getConsumeErrorCount(); } public long getConsumeCount() { return stats.getConsumeCount(); } public Exception getLastConsumeException() { return stats.getLastConsumeException(); } public EventBusBridgeStats getStats() { return new ImmutableEventBusBridgeStats(stats); } }