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

com.netflix.eventbus.bridge.AbstractEventBusBridge Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
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.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 DEFAULT_STATS_SUPPLIER = new Supplier() {
        @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    The subclasses's Builder.
     */
    public static abstract class Builder> {
        protected EventBus eventBus;
        protected Class eventType;
        protected Supplier statsSupplier = DEFAULT_STATS_SUPPLIER;
        protected boolean autoStart = DEFAULT_AUTO_START;
        
        /**
         * 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();
        }
        
        /**
         * Externally provided stats supplier
         * @param supplier
         */
        public T withStatsSupplier(Supplier 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 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.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
     */
    protected abstract void sendEvent(Object event) throws Exception;
    
    /**
     * 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();
            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);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy