package net.redborder.cep.sources; import com.lmax.disruptor.EventHandler; import com.lmax.disruptor.dsl.Disruptor; import net.redborder.cep.sources.disruptor.EventProducer; import net.redborder.cep.sources.disruptor.MapEvent; import net.redborder.cep.sources.disruptor.MapEventFactory; import net.redborder.cep.sources.parsers.ParsersManager; import net.redborder.cep.util.ConfigData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.Map; import java.util.concurrent.Executors; /** * This abstract class serves as a parent class of the different sources implementations. * It implements some methods to send events to the ring buffer, and defines an interface * that the class children must implement in order to add a new source. * * @see net.redborder.cep.sources.kafka.KafkaSource */ public abstract class Source { private static final Logger log = LogManager.getLogger(Source.class); // The instance of disruptor that will consume the events that are produced by this source public EventProducer eventProducer; // The instance of the parsers manager that will serve parser instances in order to parse // the events that come from this source public ParsersManager parsersManager; // The list of properties associated with this instance on the config file public Map<String, Object> properties; /** * Create a new source. * <p>This method will prepare the instance with some needed variables * in order to be started later with the start method (implemented by children). * * @param parsersManager Instance of ParserManager that will serve parsers to this source instance * @param eventHandler Instance of EventHandler that will receive the events generated by this source instance * @param properties Map of properties associated with this source */ public Source(ParsersManager parsersManager, EventHandler eventHandler, Map<String, Object> properties) { // Save the references for later use this.parsersManager = parsersManager; this.properties = properties; // Create the ring buffer for this topic and start it Disruptor<MapEvent> disruptor = new Disruptor<>(new MapEventFactory(), ConfigData.getRingBufferSize(), Executors.newCachedThreadPool()); disruptor.handleEventsWith(eventHandler); disruptor.start(); // Create the event producer that will receive the events produced by // this source instance eventProducer = new EventProducer(disruptor.getRingBuffer()); prepare(); } /** * Sends a new string message to the stream. * <p>The param msg should be a string that will be parsed by the parser manager. The parser manager * stores a reference of the parser that must be used with each stream, so when a string message * is sent, the parser manager parses it to convert it into a map object. * <p>This method should be called when the source wants to generate a message to the stream. * * @param streamName The stream that will receive the message * @param msg The string message that will be sent */ public void send(String streamName, String msg) { Map<String, Object> data = parsersManager.parse(streamName, msg); eventProducer.putData(streamName, data); } /** * Sends a new map message to the producer. * * @param streamName The stream that will receive the message * @param data The message that will be sent */ public void send(String streamName, Map<String, Object> data) { eventProducer.putData(streamName, data); } /** * Gets a property from the source properties. * <p>The source properties are a series of optional properties that are specified on the config file * and associated with each stream. * * @param propertyName The property that will be returned * @return The value associated with the property name specified */ public Object getProperty(String propertyName){ return properties.get(propertyName); } /** * This method is called by the SourceManager to add one or more new streams to the source. * <p>The source implementation must process the streams and do whatever it needs to do to add * that stream to the source. For example, you could create a new consumer thread for each stream * received, open a new HTTP connection to some external third-party API, etc. * <p>The streams that will be passed to the function addStreams of the implementations are * specified on the config file by the user. * * @param streamName One or more streams that has been added in the source. * @see SourcesManager */ public abstract void addStreams(String ... streamName); /** * This method is called automatically by the Source constructor after finishing, so your * implementation can use it to prepare for the following start. For example, you could use it * to establish a connection, open some resource, etc. */ public abstract void prepare(); /** * This method will be called when the Source should be started, so after this call, the source * must start to generate messages to the producers using the send methods provided by this class. */ public abstract void start(); /** * This method will be called when the Source must stop, in order to close connections and * release resources that will no longer be useful. */ public abstract void shutdown(); }