/* ==================================================================== * Limited Evaluation License: * * This software is open source, but licensed. The license with this package * is an evaluation license, which may not be used for productive systems. If * you want a full license, please contact us. * * The exclusive owner of this work is the OpenRate project. * This work, including all associated documents and components * is Copyright of the OpenRate project 2006-2015. * * The following restrictions apply unless they are expressly relaxed in a * contractual agreement between the license holder or one of its officially * assigned agents and you or your organisation: * * 1) This work may not be disclosed, either in full or in part, in any form * electronic or physical, to any third party. This includes both in the * form of source code and compiled modules. * 2) This work contains trade secrets in the form of architecture, algorithms * methods and technologies. These trade secrets may not be disclosed to * third parties in any form, either directly or in summary or paraphrased * form, nor may these trade secrets be used to construct products of a * similar or competing nature either by you or third parties. * 3) This work may not be included in full or in part in any application. * 4) You may not remove or alter any proprietary legends or notices contained * in or on this work. * 5) This software may not be reverse-engineered or otherwise decompiled, if * you received this work in a compiled form. * 6) This work is licensed, not sold. Possession of this software does not * imply or grant any right to you. ** 7) You agree to disclose any changes to this work to the copyright holder * and that the copyright holder may include any such changes at its own * discretion into the work * 8) You agree not to derive other works from the trade secrets in this work, * and that any such derivation may make you liable to pay damages to the * copyright holder * 9) You agree to use this software exclusively for evaluation purposes, and * that you shall not use this software to derive commercial profit or * support your business or personal activities. * * This software is provided "as is" and any expressed or impled warranties, * including, but not limited to, the impled warranties of merchantability * and fitness for a particular purpose are disclaimed. In no event shall * The OpenRate Project or its officially assigned agents be liable to any * direct, indirect, incidental, special, exemplary, or consequential damages * (including but not limited to, procurement of substitute goods or services; * Loss of use, data, or profits; or any business interruption) however caused * and on theory of liability, whether in contract, strict liability, or tort * (including negligence or otherwise) arising in any way out of the use of * this software, even if advised of the possibility of such damage. * This software contains portions by The Apache Software Foundation, Robert * Half International. * ==================================================================== */ package OpenRate; import OpenRate.adapter.IInputAdapter; import OpenRate.adapter.IOutputAdapter; import OpenRate.adapter.realTime.IRTAdapter; import OpenRate.buffer.IBuffer; import OpenRate.configurationmanager.ClientManager; import OpenRate.configurationmanager.IEventInterface; import OpenRate.exception.ExceptionHandler; import OpenRate.exception.InitializationException; import OpenRate.exception.ProcessingException; import OpenRate.logging.ILogger; import OpenRate.logging.LogUtil; import OpenRate.process.IPlugIn; import OpenRate.transaction.ISyncPoint; import OpenRate.transaction.TransactionManager; import OpenRate.transaction.TransactionManagerFactory; import OpenRate.utils.ConversionUtils; import OpenRate.utils.PropertyUtils; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; /** * The Pipeline encapsulates the pipeline as defines as Input adapter, some * processing modules, Output adapter. This allows the easy configuration of * multiple pipelines within a single framework. Multiple pipes in a frame- work * are able to share resources and memory. * * The pipeline is constructed entirely in the ConfigurePipeline procedure, * which instantiates the adapters and creates the main pipeline thread, adding * the input, output and processing modules as threads in a dedicated thread * group, or in the main thread as necessary. * * This version now supports output adapter chaining, which means that a single * record can go to multiple outputs. Not defining an output means that all * outputs should be written to. */ public class Pipeline extends Thread implements IPipeline, IEventInterface, ISyncPoint { // Get the logs, for this and all child classes. The pipe log will be // intialised during the init, up until then, all logging will go to the // framework log, or the default logger (console). /** * Pipeline Level AstractLogger. This logger is used for logging messages * specific to this pipeline. It is instantiated during pipeline startup. */ private ILogger pipeLog = null; // The symbolic name is used in the management of the pipeline (control and // thread monitoring) and logging. private String symbolicName; // Tells the pipeline to stop when instructed private boolean stopRequested = false; private boolean stop = false; // true if an abort happened private boolean aborted = false; // Active tells the pipe if it can process records private boolean active; // Used to reset the active status after sync processing private boolean syncStatusActive; // To make sure that we do not leave transactions half finished, // The active flag is updated with the ActiveStateRequested when a transaction // finishes private boolean activeStateRequested; // The number of times that the pipeline should run, default to forever private int runCount = 0; // Concrete classes for the following member attributes are // loaded from a property file and instantiated via reflection. private final ExceptionHandler pipeExceptionHandler = new ExceptionHandler(); // And the batch input adapter for this pipe private IInputAdapter batchInputAdapter; // And the real time input adapter for this pipe private IRTAdapter rtAdapter; // tree of all threads that have been launched for processing plugins. Each // plug in has a sub-node containing a ThreadGroup for itself. private ThreadGroup pluginRoot; // tree of all threads that have been launched for output adapters. private ThreadGroup outputAdapterRoot; // tree of all threads that have been launched for output adapters. private ThreadGroup rtAdapterRoot; // list of plug ins to be run by this pipeline private ArrayList<IPlugIn> plugInList = new ArrayList<>(); // list of batch output adapters to be run by this pipeline private ArrayList<IOutputAdapter> batchOutputAdapterList = new ArrayList<>(); // list of plug ins threads to be run by this pipeline (there can be more than // one thread per logical plug in) private ArrayList<ThreadGroup> thGrpsPlugIn = new ArrayList<>(); // These are used for configuring the pipe private int sleepTime; // List of Services that this Client supports private final String SERVICE_PIPELINE_ACTIVE = CommonConfig.ACTIVE; private final String SERVICE_PIPELINE_SLEEP = "Sleep"; private final String SERVICE_RUNCOUNT = "RunCount"; private final String SERVICE_HALT_ON_EXCP = "HaltOnException"; private final String SERVICE_BUFFER_STATUS = "BufferStatus"; private final String SERVICE_PIPELINE_TYPE = "PipelineType"; // If we encounter an unhadled processing exception, this says if we stop private boolean haltOnException = true; // Does this pipe need to perform an action that needs a sync private int localSyncStatus = ISyncPoint.SYNC_STATUS_NORMAL_RUN; // reference to the transaction manager for this pipe private TransactionManager TM; // The scheduler is responsible for managing the high frequency polling when // we appear to have some work to do. This holds the date that we are due to // go back to the idle schedule. Each time the pipe receives something to do, // we push the scheduler forward. This means we stay in the "active" schedule // long enough to roll from one file to the next at high speed. private long schedulerHighSpeed = 0; // Used to map the buffers in order that we can interrogate them ArrayList<IBuffer> bufferList = new ArrayList<>(); // this tells us if this is a batch pipe - used for management private boolean batchPipeline; // Used to simplify logging and exception handling private String message; /** * Constructor */ public Pipeline() { super(); } /** * Init method will be called prior to run to allow the execution model to * initialize itself & acquire any necessary resources. * * @param Name - the name of the pipeline * @throws InitializationException */ @Override public void init(String Name) throws InitializationException { // Used to manage the transaction manager MaxTransactions String maxTransactions; // the max transactions this pipe can use as an integer int maxTransTM; // Set the name of this pipeline setSymbolicName(Name); // Create & configure the pipeline components configurePipeline(); // Register with the client manager registerClientManager(); // Get the reference to the transaction manager for this pipe TM = TransactionManagerFactory.getTransactionManager(getSymbolicName()); // set the default max transactions state of the pipeline maxTransactions = PropertyUtils.getPropertyUtils().getPropertyValueDef("PipelineList." + symbolicName + ".MaxTransactions","1"); try { maxTransTM = Integer.parseInt(maxTransactions); } catch (NumberFormatException ex) { message = "MaxTransactions must be a numeric value, but we got <" + maxTransactions + "> in pipeline <" + symbolicName + ">. Aborting."; throw new InitializationException(message, getSymbolicName()); } // Set the max transactions TM.setMaxTransactions(maxTransTM); // set up the logger setPipeLog(LogUtil.getLogUtil().getLogger(Name)); } // ----------------------------------------------------------------------------- // --------------------- Pipeline Building functions --------------------------- // ----------------------------------------------------------------------------- /** * Creates the actual pipeline implementation from the configuration. This * creates and instantiates the InputAdapter, the pipeline modules and the * output adapters, and links the modules together with the appropriate * buffers. * * @param props - the properties we have inherited * @throws InitializationException */ private void configurePipeline() throws InitializationException { // this controls the type (batch or realtime) we create String pipelineType; // used in setting up the pipe String strActiveState; String strHaltOnExcp; // Initialise the default polling sleep time sleepTime = 5000; // Get our logger setPipeLog(LogUtil.getLogUtil().getLogger(symbolicName)); try { // set the default active state of the pipeline strActiveState = PropertyUtils.getPropertyUtils().getPropertyValueDef("PipelineList." + symbolicName + "." + SERVICE_PIPELINE_ACTIVE, "true"); // set the default active state of the pipeline pipelineType = PropertyUtils.getPropertyUtils().getPropertyValueDef("PipelineList." + symbolicName + "." + SERVICE_PIPELINE_TYPE, "Batch"); // set the default active state of the pipeline strHaltOnExcp = PropertyUtils.getPropertyUtils().getPropertyValueDef("PipelineList." + symbolicName + "." + SERVICE_HALT_ON_EXCP, "True"); // Get the transaction controller configuration // Validate what we got for the pipe type if (pipelineType.equalsIgnoreCase("Batch")) { pipelineType = "Batch"; batchPipeline = true; } else if (pipelineType.equalsIgnoreCase("RealTime")) { pipelineType = "RealTime"; batchPipeline = false; } else { message = "Pipeline Type must be either Batch or RealTime, but we got <" + pipelineType + "> in pipeline <" + symbolicName + ">. Aborting."; throw new InitializationException(message, getSymbolicName()); } OpenRate.getOpenRateFrameworkLog().info("*** Constructing " + pipelineType + " pipeline <" + symbolicName + "> ***"); // Set the pipeline state with the value we have found from the // registry, otherwise it is true active = strActiveState.equalsIgnoreCase("true"); activeStateRequested = active; if (!active) { OpenRate.getOpenRateFrameworkLog().warning("Starting pipeline <" + symbolicName + "> in inactive state"); System.out.println(" Starting pipeline <" + symbolicName + "> in inactive state"); } // set the halt on exception state haltOnException = strHaltOnExcp.equalsIgnoreCase("true"); // Construct the pipeline according to the batch model if (batchPipeline) { // Get the initialised batch input adapter batchInputAdapter = getBatchInputAdapter(pipeExceptionHandler); // create and initalise the processing body of the pipe plugInList = getProcessPlugins(pipeExceptionHandler); // create the batch output adapter list batchOutputAdapterList = getBatchOutputAdapterList(pipeExceptionHandler); // Hookup the buffers through the chain hookupBuffers(getBufferClass()); } else // Construct the pipeline according to the real time model { // Get the real time input adapter rtAdapter = getRTAdapter(pipeExceptionHandler); // create and initalise the processing body of the pipe plugInList = getProcessPlugins(pipeExceptionHandler); // Set up the RT processing chain - this injects the plugin list into the // adapter so it can be used for processing rtAdapter.setProcessingList(plugInList); } } catch (InitializationException ex) { // this will already be handled as we want, just pass it up throw ex; } catch (Throwable ex) { // Unexpected exception. Wrap it and pass it up, nesting the original message message = "Unexpected exception configuring pipeline <" + getSymbolicName() + ">, message <" + ex.getMessage() + ">"; throw new InitializationException(message, getSymbolicName(), true, true, ex); } } /** * Create the FIFO buffer class for batch pipelines * * @return The buffer class * @throws InitializationException */ private Class<?> getBufferClass() throws InitializationException { // These variables are used for recovering the name of the buffer class // The buffer class we have here serves as a master for all of the rest of // the pipeline, and is cloned during pipeline setup String defaultBuffer = null; Class<?> BufferClass = null; // ------------------- Set up the FIFO buffer class ------------------------ try { // get the buffer class defaultBuffer = PropertyUtils.getPropertyUtils().getPipelinePropertyValueDef(symbolicName, "Configuration", CommonConfig.BUFFER_TYPE, CommonConfig.DEFAULT_BUFFER_TYPE); BufferClass = Class.forName(defaultBuffer); } catch (ClassNotFoundException ex) { message = "Error finding buffer class <" + defaultBuffer + "> in pipeline <" + symbolicName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } return BufferClass; } /** * Get and initialise the batch input adapter for this pipeline * * @return The initialised batch input adapter * @exception InitializationException */ private IInputAdapter getBatchInputAdapter(ExceptionHandler pipeExceptionHandler) throws InitializationException { Class<?> PluginClass = null; String PluginClassName; String PluginName; ArrayList<String> PluginNameList; // ---------------- Create the correct batch input adapter ----------------- PluginNameList = PropertyUtils.getPropertyUtils().getGenericNameList(symbolicName + ".InputAdapter"); // Check that we have the right number of input adapters (1) if (PluginNameList.size() != 1) { message = "Expecting 1 Batch Input Adapter class for pipeline <" + symbolicName + ">. Found <" + PluginNameList.size() + ">"; throw new InitializationException(message, getSymbolicName()); } // Get the name PluginName = PluginNameList.get(0); if (PluginName == null) { message = "No Batch input adapter found"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } else { OpenRate.getOpenRateFrameworkLog().debug("Batch input adapter <" + PluginName + ">"); // Get the class name PluginClassName = PropertyUtils.getPropertyUtils().getBatchInputAdapterPropertyValue(symbolicName, PluginName, "ClassName"); // we found a batch input adapter - check it and instantiate it try { PluginClass = Class.forName(PluginClassName); } catch (ClassNotFoundException | NoClassDefFoundError ex) { message = "Input adapter class <" + PluginClassName + "> not found for pipeline <" + symbolicName + ">. <" + ex.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } try { batchInputAdapter = (IInputAdapter) PluginClass.newInstance(); } catch (InstantiationException ex) { message = "Input adapter class <" + PluginClassName + "> instantiation error in pipeline <" + symbolicName + ">. <" + ex.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } catch (IllegalAccessException ex) { message = "Input adapter class <" + PluginClassName + "> access error in pipeline <" + symbolicName + ">. <" + ex.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } // Now that we have the input adapter, initialise it using the index 0 (we // have only one input adapter) batchInputAdapter.init(symbolicName, PluginName); // link the batch input adaptor to us, so it can manage the scheduler batchInputAdapter.setPipeline(this); } return batchInputAdapter; } /** * Get and initialise the adapter for real time pipelines * * @return The initialised RT adapter * @throws InitializationException */ private IRTAdapter getRTAdapter(ExceptionHandler pipeExceptionHandler) throws InitializationException { String PluginName; ArrayList<String> PluginNameList; Class<?> PluginClass = null; String PluginClassName; // ----------------- Create the correct real time adapter ------------------ PluginNameList = PropertyUtils.getPropertyUtils().getGenericNameList(symbolicName + ".RTAdapter"); // Check that we have the right number of input adapters (1) if (PluginNameList.size() != 1) { message = "Expecting 1 RT adapter class for pipeline <" + symbolicName + ">. Found <" + PluginNameList.size() + "."; throw new InitializationException(message, getSymbolicName()); } PluginName = PluginNameList.get(0); if (PluginName == null) { message = "No Real Time input adapter found"; OpenRate.getOpenRateFrameworkLog().debug(message); throw new InitializationException(message, getSymbolicName()); } else { OpenRate.getOpenRateFrameworkLog().debug("Real time input adapter <" + PluginName + ">"); // Get the class name PluginClassName = PropertyUtils.getPropertyUtils().getRTAdapterPropertyValue(symbolicName, PluginName, "ClassName"); try { PluginClass = Class.forName(PluginClassName); } catch (ClassNotFoundException ex) { message = "Input adapter class <" + PluginClassName + "> not found for pipeline <" + symbolicName + ">"; throw new InitializationException(message, getSymbolicName()); } try { rtAdapter = (IRTAdapter) PluginClass.newInstance(); } catch (InstantiationException ex) { message = "Input adapter class <" + PluginClassName + "> instantiation error in pipeline <" + symbolicName + ">. <" + ex.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } catch (IllegalAccessException ex) { message = "Input adapter class <" + PluginClassName + "> access error in pipeline <" + symbolicName + ">. <" + ex.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } // Now that we have the input adapter, initialise it using the index 0 (we // have only one input adapter) rtAdapter.init(symbolicName, PluginName); } return rtAdapter; } /** * Get and initialise the batch output adapters for this pipeline * * @param pipeExceptionHandler The Exception handler we are going to link to * @return The output adapter list * @throws InitializationException */ private ArrayList<IOutputAdapter> getBatchOutputAdapterList(ExceptionHandler pipeExceptionHandler) throws InitializationException { String PluginName; ArrayList<String> PluginNameList; Iterator<String> PluginIter; int Index; Class<?> PluginClass; String PluginClassName = null; IOutputAdapter tmpBatchOutputAdapter; // ---------------- Create the batch output adapter chain ------------------ PluginNameList = PropertyUtils.getPropertyUtils().getGenericNameList(symbolicName + ".OutputAdapter"); if (PluginNameList.isEmpty()) { message = "No Output adapter found for pipeline <" + symbolicName + ">"; throw new InitializationException(message, getSymbolicName()); } else { try { // Create the output adapters PluginIter = PluginNameList.iterator(); Index = 0; while (PluginIter.hasNext()) { PluginName = PluginIter.next(); // Now create the output adapter chain - get the adapter class PluginClassName = PropertyUtils.getPropertyUtils().getBatchOutputAdapterPropertyValue(symbolicName, PluginName, "ClassName"); OpenRate.getOpenRateFrameworkLog().debug("OutputAdapter " + Index + " = <" + PluginClassName + ">"); PluginClass = Class.forName(PluginClassName); // Find what type of output adapter tmpBatchOutputAdapter = (IOutputAdapter) PluginClass.newInstance(); // set the terminator tag on the last adapter, which has the effect of // logging records as errors if they have not been consumed by the // output adapter chain. if (Index == PluginNameList.size() - 1) { tmpBatchOutputAdapter.setTerminator(true); } // Initialise the output adapter. Note that we do not link the output to // anything, which means that until we build the buffers, all adapters are // set to sink unconsumed errors tmpBatchOutputAdapter.init(symbolicName, PluginName); batchOutputAdapterList.add(tmpBatchOutputAdapter); Index++; } } catch (ClassNotFoundException ex) { message = "Error finding plugin class <" + PluginClassName + "> in module <" + getSymbolicName() + ">"; throw new InitializationException(message, ex, getSymbolicName()); } catch (ClassCastException ex) { message = "Error creating plugin class (cast exception) <" + PluginClassName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } catch (InstantiationException ex) { message = "Error instantiating plugin class <" + PluginClassName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } catch (IllegalAccessException ex) { message = "Error accessing plugin class <" + PluginClassName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } } return batchOutputAdapterList; } /** * * Get and initialise the processing plug ins * * @param pipeExceptionHandler The Exception handler we are going to link to * @return The processing plug in list * @throws InitializationException */ private ArrayList<IPlugIn> getProcessPlugins(ExceptionHandler pipeExceptionHandler) throws InitializationException { // This is the processing plug in we are adding IPlugIn Plugin; ArrayList<String> PluginNameList; Iterator<String> PluginIter; String PluginName; Class<?> PluginClass; String pluginClassName = null; int Index; // ------------------------- Build the pipeline ---------------------------- // Add the plugins to the pipeline // Processing plug ins will be added to the pipeline in numeric order, // until a number is missing. The numbering starts at 0. try { PluginNameList = PropertyUtils.getPropertyUtils().getGenericNameList(symbolicName + ".Process"); // Now create the output adapter chain PluginIter = PluginNameList.iterator(); Index = 0; while (PluginIter.hasNext()) { PluginName = PluginIter.next(); // Get the process class for the Plugin pluginClassName = PropertyUtils.getPropertyUtils().getPluginPropertyValueDef(symbolicName, PluginName, "ClassName", "None"); if (pluginClassName.equals("None")) { message = "Could not find the ClassName definition for module <" + PluginName + "> in pipe <" + symbolicName + ">"; throw new InitializationException(message, getSymbolicName()); } OpenRate.getOpenRateFrameworkLog().debug("Process " + Index + " = " + pluginClassName); PluginClass = Class.forName(pluginClassName); Plugin = (IPlugIn) PluginClass.newInstance(); // Create the new Plugin Plugin.init(symbolicName, PluginName); Plugin.setExceptionHandler(pipeExceptionHandler); plugInList.add(Plugin); Index++; } } catch (ClassNotFoundException ex) { message = "Error finding plugin class <" + pluginClassName + "> in module <" + getSymbolicName() + ">"; throw new InitializationException(message, ex, getSymbolicName()); } catch (ClassCastException ex) { message = "Error creating plugin class (cast exception) <" + pluginClassName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } catch (InstantiationException ex) { message = "Error instantiating plugin class <" + pluginClassName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } catch (IllegalAccessException ex) { message = "Error accessing plugin class <" + pluginClassName + ">"; throw new InitializationException(message, ex, getSymbolicName()); } return plugInList; } /** * Hook up the buffers between the plug ins for batch mode * * @param BufferClass The FIFO buffer class we are using for batch pipes * @throws InitializationException */ private void hookupBuffers(Class<?> BufferClass) throws InitializationException { IBuffer tmpBuffer; IOutputAdapter tmpBatchOutputAdapter; IPlugIn tmpPlugIn; int Index; // ------------------------- Hookup buffers ---------------------------- // Now that we have the input and the output, link them with a buffer // We will insert plugins, reconnecting the buffers as necessary later, but // this method means that even empty pipelines (not that you'll ever need // one) work try { // Hookup the input buffers - there can only be one input adapter of each // type (realtime / batch) at the moment, so we can create these // statically tmpBuffer = (IBuffer) BufferClass.newInstance(); bufferList.add(tmpBuffer); batchInputAdapter.setBatchOutboundValidBuffer(tmpBuffer); tmpBuffer.setSupplier(batchInputAdapter.getSymbolicName()); // Now we hookup the output to the appropriate place (output if the // pipe is empty, otherwise the first processing class if (plugInList.isEmpty()) { // hook straight up to the output adapter chain tmpBatchOutputAdapter = batchOutputAdapterList.get(0); tmpBatchOutputAdapter.setBatchInboundValidBuffer(tmpBuffer); tmpBuffer.setConsumer(tmpBatchOutputAdapter.getSymbolicName()); } else { // Hookup to the first processing Plugin, and then build the rest of the // pipeline chain tmpPlugIn = plugInList.get(0); tmpPlugIn.setInbound(tmpBuffer); tmpBuffer.setConsumer(tmpPlugIn.getSymbolicName()); for (Index = 1; Index < plugInList.size(); Index++) { // create a new buffer tmpBuffer = (IBuffer) BufferClass.newInstance(); bufferList.add(tmpBuffer); // hook the buffer up to the next processing module for batch tmpPlugIn = plugInList.get(Index - 1); tmpPlugIn.setOutbound(tmpBuffer); tmpBuffer.setSupplier(tmpPlugIn.getSymbolicName()); tmpPlugIn = plugInList.get(Index); tmpPlugIn.setInbound(tmpBuffer); tmpBuffer.setConsumer(tmpPlugIn.getSymbolicName()); } // Last processing module, hook it up to the first output adapter tmpBuffer = (IBuffer) BufferClass.newInstance(); bufferList.add(tmpBuffer); // Last processing module, hook it up to the first output adapter tmpBuffer = (IBuffer) BufferClass.newInstance(); tmpPlugIn = plugInList.get(plugInList.size() - 1); tmpPlugIn.setOutbound(tmpBuffer); tmpBuffer.setSupplier(tmpPlugIn.getSymbolicName()); // if we have an RT adapter, hook it up onto the beginning of the output chain if (rtAdapter != null) { // if we have a batch output adapter, create a new buffer and link it if (batchOutputAdapterList.size() > 0) { message = "Output adapter defined in an RT pipe"; throw new InitializationException(message, getSymbolicName()); } } // if we only have a RT adapter, skip the batch output adpater, // otherwise hook up the first output adapter in the list to the // batch output of the pipe if (batchOutputAdapterList.size() > 0) { tmpBatchOutputAdapter = batchOutputAdapterList.get(0); tmpBatchOutputAdapter.setBatchInboundValidBuffer(tmpBuffer); tmpBuffer.setConsumer(tmpBatchOutputAdapter.getSymbolicName()); } } // Now create the output adapter chain, using the same logic - we already // know that there is at least one output adapter, we checked it, so just // do the rest of the chain. for (Index = 1; Index < batchOutputAdapterList.size(); Index++) { // create a new buffer for the valid and error records tmpBuffer = (IBuffer) BufferClass.newInstance(); bufferList.add(tmpBuffer); // hook the valid and error buffers up to the next processing module tmpBatchOutputAdapter = batchOutputAdapterList.get(Index - 1); tmpBatchOutputAdapter.setBatchOutboundValidBuffer(tmpBuffer); tmpBuffer.setSupplier(tmpBatchOutputAdapter.getSymbolicName()); tmpBatchOutputAdapter = batchOutputAdapterList.get(Index); tmpBatchOutputAdapter.setBatchInboundValidBuffer(tmpBuffer); tmpBuffer.setConsumer(tmpBatchOutputAdapter.getSymbolicName()); } } catch (InstantiationException ie) { message = "Error instantiating buffer class in pipeline <" + symbolicName + ">. <" + ie.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } catch (IllegalAccessException iae) { message = "Error accessing buffer class in pipeline <" + symbolicName + ">. <" + iae.getMessage() + ">"; throw new InitializationException(message, getSymbolicName()); } } // ----------------------------------------------------------------------------- // ---------------------- Pipeline Running functions --------------------------- // ----------------------------------------------------------------------------- /** * Run the Pipeline. This section performs the scheduling function of the * pipeline. In the case that there are records in processing, the sleep time * is ignored, and the processing loop runs at full speed. When there are no * more records to be processed, the more tranquil processing loop with the * additional sleep is performed. * * Note that to provide maximum performance, transactions are streamed * immediately one after the other, using the "streaming counter", which has * the effect of keeping the fast loop going for a defined number of cycles * even after no more records are found. The effect of holding the fast cycle * open for a few cycles means that we can close one transaction and open a * new one without ever returning to the slow cycles. * * Additionally, the active state of the pipeline is read and managed in this * section. A pipeline can only change state when we are not processing. To * enforce this, we manage the "Active" and "ActiveStateRequested" variables. */ @Override public void run() { // The sleep time for this run long tmpSleepTime; // The number of records we processed in the input adapter long recordsReceived = 0; // The number of records in the pipe long recordsInPipe; try { startPipeline(); // **** Manage the main processing loop **** while (!stop) { // perform the pipeline processing if the pipe is active if (active) { // retrieve input records, if there are any that need doing. We only // do this if there is a batch input adapter that is set if (batchInputAdapter != null) { recordsReceived = batchInputAdapter.push(batchInputAdapter.getBatchOutboundValidBuffer()); } } // perform pipeline maintenance - check for transaction completion // buffer problems etc recordsInPipe = checkPipeline(); // See if there are still records in the pipe if (getTransactionOpen((recordsReceived + recordsInPipe) > 0) | (localSyncStatus > 0)) { // This is the number of cycles we continue to use the fast scheduling // for, after there is no more real work to do. setSchedulerHigh(); } // Get the sleep time for the pipe if (getSchedulerHigh()) { tmpSleepTime = 100; } else { tmpSleepTime = sleepTime; } // **** Manage pipeline state changes **** if (TM == null) { // we are running in a non transactional pipe active = activeStateRequested; stop = stopRequested; } else { // transactional pipeline: // set the active state of the pipeline if there is a pending change if (activeStateRequested != active) { // we can set the state to false once the current transaction // has completed if ((activeStateRequested == false) & (TM.getActiveTransactionCount() == 0)) { active = activeStateRequested; OpenRate.getOpenRateFrameworkLog().info("Pipeline <" + symbolicName + "> inactive"); } // We can set the active state to true if the transaction manager // tells us that we are allowed to start new transactions if ((activeStateRequested == true) & (TM.getNewTransactionAllowed())) { active = activeStateRequested; OpenRate.getOpenRateFrameworkLog().info("Pipeline <" + symbolicName + "> active"); } } // If a stop has been requested, action it at the end of the transaction if ((stopRequested) & (TM.getActiveTransactionCount() == 0)) { stop = true; } } // This handles the sync processing - finishing the current transaction if (localSyncStatus == 2) { // if we have become inactive, move the sync status on if (!active) { localSyncStatus = 3; } } // This handles the sync processing - processing event backlog if (localSyncStatus == 4) { // currently just do it, in the future: // ToDo: buffer events that we cannot handle directly for sync // processing localSyncStatus = 5; } // **** Manage pipeline scheduling (loop timeouts) **** // This is the pipeline idle loop if (tmpSleepTime > 0) { try { OpenRate.getOpenRateFrameworkLog().debug( "Pipeline <" + symbolicName + "> will sleep for " + tmpSleepTime + " ms."); Thread.sleep(tmpSleepTime); } catch (InterruptedException e) { // ignore the exception } } // Update the runcount. We use this to stop the pipeline after a // Certain number of runs if (runCount > 0) { // If we are idle, decrement the runcount if (recordsReceived == 0) { runCount--; } if (runCount == 0) { OpenRate.getOpenRateFrameworkLog().info( "RunCount reached, setting pipe <" + symbolicName + "> inactive"); active = false; } } // if Runcount } // while !Stop // start the shutdown of the pipeline modules stopPipelineModules(); System.out.println("Pipeline <" + getSymbolicName() + "> stopped"); } catch (ProcessingException pe) { OpenRate.getOpenRateFrameworkLog().error("ProcessingException thrown.", pe); } } /** * Perform any cleanup required. This allows the IPipeline to keep resources * open after the run() method in case a multi-call model is used to execute * run() repeatedly. */ @Override public void cleanupPipeline() { // nop } /** * stop the process. This can be called to safely shutdown a process that is * either long running or runs continuously. It's not intended to be a drastic * shutdown, but rather a "find a reasonable spot to stop & do so" type * message. A few minutes of processing before reaching such a point is, while * not recommended, not unreasonable in certain cases. The process should make * an effort to shutdown as quickly as is reasonable without leaving the * application in an invalid state. */ @Override public void markForShutdown() { if (stopRequested == false) { if (OpenRate.getOpenRateFrameworkLog() != null) { OpenRate.getOpenRateFrameworkLog().warning("Pipeline <" + symbolicName + "> received Stop Command. Will exit after the current Transaction"); } // Only shut down the transaction manager if it is started if (TM != null) { // Stop new transactions being opened TM.setNewTransactionAllowed(false); } // Ask the pipeline to shut down at the first reasonable opportunity stopRequested = true; } // set the scheduler to make sure we purge out anything in progress as quickly as possible setSchedulerHigh(); } /** * This function is used to set the active state of the pipeline. An inactive * pipeline can be set active again immediately, but cannot processes any * records * * @param NewState The new state for the active flag */ public synchronized void setActive(boolean NewState) { if (TM == null) { activeStateRequested = NewState; OpenRate.getOpenRateFrameworkLog().info("Pipeline <" + symbolicName + "> active state changed"); } else { if (NewState) { activeStateRequested = true; TM.setNewTransactionAllowed(true); OpenRate.getOpenRateFrameworkLog().info("Pipeline <" + symbolicName + "> scheduled to become active"); } else { activeStateRequested = false; TM.setNewTransactionAllowed(false); OpenRate.getOpenRateFrameworkLog().info("Pipeline <" + symbolicName + "> scheduled to become inactive after transaction completion"); } } } /** * startPipeline() launches the pipeline threads, and they wait for records to * arrive pushed by the input adapter. * * @throws ProcessingException */ protected void startPipeline() throws ProcessingException { ListIterator<IPlugIn> pluginIterator; IPlugIn tmpPlugIn; ThreadGroup tmpGrpPlugIn; IOutputAdapter tmpOutputAdapter; OpenRate.getOpenRateFrameworkLog().debug("Pipeline <" + getSymbolicName() + "> starting..."); if (isBatchPipeline()) { if (plugInList.isEmpty()) { OpenRate.getOpenRateFrameworkLog().debug("no plugins, pipeline will pass records through."); } pluginIterator = plugInList.listIterator(); pluginRoot = new ThreadGroup("PlugIns"); // Don't use ThreadGroup.enumerate( ThreadGroup[] ) because there is no // guarantee that it will enforce the ordering of the thread groups. // They MUST be in creation order for the pipe to work as we expect. thGrpsPlugIn = new ArrayList<>(); // for each PlugIn, launch a set of threads. These are created in a // thread group for each plug in, hierachically subordinate to the // thread group for the processing elements of the pipeline while (pluginIterator.hasNext()) { tmpPlugIn = pluginIterator.next(); // reset IPlugIn before launching. clears shutdown flag. tmpPlugIn.reset(); // thread group name = PlugIn name tmpGrpPlugIn = new ThreadGroup(pluginRoot, tmpPlugIn.getSymbolicName()); thGrpsPlugIn.add(tmpGrpPlugIn); int thread_count = (tmpPlugIn.numThreads() > 0) ? tmpPlugIn.numThreads() : 1; for (int i = 0; i < thread_count; ++i) { Thread PlugInTh = new Thread(tmpGrpPlugIn, tmpPlugIn, tmpPlugIn.getSymbolicName() + ".Inst-" + Integer.toString(i)); // We could use this to unblock pipe bottlenecks, but at the // moment we don't seem to need it //PlugInTh.setPriority( Thread.NORM_PRIORITY ); PlugInTh.setDaemon(true); // for fatal error handling. PlugInTh.start(); } } // Create the root thread group for the output adapters outputAdapterRoot = new ThreadGroup("Output"); // Launch the batch output adapters for (int i = 0; i < batchOutputAdapterList.size(); ++i) { tmpOutputAdapter = batchOutputAdapterList.get(i); // reset Adapter before launching. clears shutdown flag. tmpOutputAdapter.reset(); Thread ThrOutAdapter = new Thread(outputAdapterRoot, tmpOutputAdapter, tmpOutputAdapter.getSymbolicName() + ".Inst-" + Integer.toString(i)); // We could use this to unblock pipe bottlenecks, but at the // moment we don't seem to need it //ThrOutAdapter.setPriority( Thread.NORM_PRIORITY ); ThrOutAdapter.setDaemon(true); // for fatal error handling. ThrOutAdapter.start(); } } else { // Create the thread group for the real time adapter rtAdapterRoot = new ThreadGroup("RT"); // Launch the real time output adapter if (rtAdapter != null) { // reset Adapter before launching. clears shutdown flag. rtAdapter.reset(); Thread ThrRTAdapter = new Thread(rtAdapterRoot, rtAdapter, rtAdapter.getSymbolicName() + ".Inst-RT"); // We could use this to unblock pipe bottlenecks, but at the // moment we don't seem to need it //ThrOutAdapter.setPriority( Thread.NORM_PRIORITY ); ThrRTAdapter.setDaemon(true); // for fatal error handling. ThrRTAdapter.start(); } } OpenRate.getOpenRateFrameworkLog().debug("Pipeline <" + getSymbolicName() + "> started."); } /** * Stop the processing in the pipeline as soon as all the records have purged * out of it. This is part of the shutdown processing: Normally a pipeline * will not stop until all of the processes in it have stopped. This method * performs the work of shutting down the threads working in the pipeline, * once the processing has been finished. * * @throws ProcessingException */ protected void stopPipelineModules() throws ProcessingException { // Shut down processing plug in threads stopPlugIns(); // and the output adapter threads stopOutputAdapters(); } /** * Perform regular maintenance on the pipeline elements. Also checks the pipe * for exceptions, and in the case one is found, report it and shut down the * pipe * * @return The approximate number of records still in the pipe */ protected int checkPipeline() { IOutputAdapter tmpOutputAdapter; IPlugIn tmpPlugIn; ListIterator<IPlugIn> pluginIterator; ListIterator<ThreadGroup> threadGroupIterator; int RecordsInPipe = 0; pluginIterator = plugInList.listIterator(); threadGroupIterator = thGrpsPlugIn.listIterator(); while (pluginIterator.hasNext() && threadGroupIterator.hasNext()) { tmpPlugIn = pluginIterator.next(); RecordsInPipe += tmpPlugIn.getOutboundRecordCount(); } for (int i = 0; i < batchOutputAdapterList.size(); ++i) { tmpOutputAdapter = batchOutputAdapterList.get(i); // anything that needs to be done 1x per batch cycle. RecordsInPipe += tmpOutputAdapter.getOutboundRecordCount(); } // check if there have been any errors in the threads, and if there // have, pass the exception up if (pipeExceptionHandler.hasError()) { // Failure occurred, propogate the error System.err.println("Exception thrown in pipeline <" + getSymbolicName() + ">, see Error Log."); OpenRate.getOpenRateFrameworkLog().error("Exception thrown in pipeline <" + getSymbolicName() + ">, see Error Log."); // report the exceptions to the ErrorLog Iterator<Exception> excList = pipeExceptionHandler.getExceptionList().iterator(); // for each of the exceptions we have collected while (excList.hasNext()) { Exception tmpException = excList.next(); OpenRate.getOpenRateErrorLog().error("Processing Exception caught.", tmpException); getPipeLog().error(tmpException.getMessage()); } // Clear down the list pipeExceptionHandler.clearExceptions(); // See if we should shutdown if (haltOnException) { // stop the pipe System.err.println("Exception thrown in pipeline <" + getSymbolicName() + ">, see Error Log."); OpenRate.getOpenRateFrameworkLog().error("Pipeline <" + getSymbolicName() + "> configured to shut down on exception. Shutting down."); aborted = true; } } return RecordsInPipe; } /** * This method orders the output adapters to close down and waits until they * respect the command. * * @throws ProcessingException */ protected void stopOutputAdapters() throws ProcessingException { IOutputAdapter tmpOutputAdapter; // Shut down the real time adapter if it is defined if (rtAdapter != null) { rtAdapter.markForClosedown(); } // Shut down the output adapters for (int i = 0; i < batchOutputAdapterList.size(); ++i) { tmpOutputAdapter = batchOutputAdapterList.get(i); // Close down the output adapters // run the output adapter so that we can clear out any records tmpOutputAdapter.markForClosedown(); } // Wait until all the output adapters have finished if (outputAdapterRoot != null) { while (outputAdapterRoot.activeCount() > 0) //&& (Handler.hasError() == false)) { OpenRate.getOpenRateFrameworkLog().debug("Waiting for output thread groups to finish."); try { // There are two possibilities here that we could use: // Yield() will just let others take over // Sleep(x) makes the PlugIn stop and wait for x ms. // I'm not really sure which has the best advantages, but I // tend towards a simple yield at the moment because we want // to avoid dead time //Thread.yield(); Thread.sleep(100); } catch (InterruptedException ie) { OpenRate.getOpenRateFrameworkLog().debug("Interrupted!"); } } // Clean up the output adapter thread groups outputAdapterRoot.destroy(); outputAdapterRoot = null; } if (batchOutputAdapterList != null) { // Shut down the output adapters for (int i = 0; i < batchOutputAdapterList.size(); ++i) { tmpOutputAdapter = batchOutputAdapterList.get(i); // anything that needs to be done 1x per batch cycle. tmpOutputAdapter.getOutboundRecordCount(); // after all the batches are processed, do any work // that must happen once & only once per interface run. tmpOutputAdapter.close(); } } } /** * Close down the plug in threads that have been launched in the pipeline * * @throws ProcessingException */ protected void stopPlugIns() throws ProcessingException { IPlugIn tmpPlugIn; ListIterator<IPlugIn> pluginIterator; ListIterator<ThreadGroup> threadGroupIterator; ThreadGroup tmpGrpPlugIn; pluginIterator = plugInList.listIterator(); threadGroupIterator = thGrpsPlugIn.listIterator(); while (pluginIterator.hasNext() && threadGroupIterator.hasNext()) { tmpPlugIn = pluginIterator.next(); tmpPlugIn.markForShutdown(); tmpGrpPlugIn = threadGroupIterator.next(); // wait for all Threads in this group to shutdown. while (tmpGrpPlugIn.activeCount() > 0) //&& (Handler.hasError() == false)) { OpenRate.getOpenRateFrameworkLog().debug( "Waiting for plugin thread group <" + tmpGrpPlugIn.getName() + "> to finish."); try { // There are two possibilities here that we could use: // Yield() will just let others take over // Sleep(x) makes the PlugIn stop and wait for x ms. // I'm not really sure which has the best advantages, but I // tend towards a simple yield at the moment because we want // to avoid dead time //Thread.yield(); Thread.sleep(100); } catch (InterruptedException ie) { OpenRate.getOpenRateFrameworkLog().debug("Interrupted!"); } } OpenRate.getOpenRateFrameworkLog().debug("ThreadGroup <" + tmpGrpPlugIn.getName() + "> dead, next... "); // Destroy the group if (tmpGrpPlugIn.isDestroyed() == false) { tmpGrpPlugIn.destroy(); } } // Clean up the Plugin thread groups if (pluginRoot != null) { pluginRoot.destroy(); pluginRoot = null; } } /** * return the symbolic name */ @Override public String getSymbolicName() { return symbolicName; } /** * set the symbolic name. Normally set internally, this method is used for * Unit tests. * * @param name The new symbolic name to use */ public void setSymbolicName(String name) { symbolicName = name; } /** * Perform Plugin level shutdown processing. This is called in order to close * any data or resources that should be closed before the pipe is destroyed */ @Override public void shutdownPipeline() { // Destroy buffers Iterator<IBuffer> bufferIter = bufferList.iterator(); while (bufferIter.hasNext()) { IBuffer tmpBuffer = bufferIter.next(); tmpBuffer = null; } bufferList.clear(); // Destroy Output Adapters } /** * Set the pipeline input scheduler to run at high speed for a period of time */ @Override public void setSchedulerHigh() { // Put the scheduled high speed time for 10 seconds schedulerHighSpeed = ConversionUtils.getConversionUtilsObject().getCurrentUTCms() + 10000; } /** * Tell us if the scheduler is still in the high speed period. * * @return true if we are in the high speed schedule */ @Override public boolean getSchedulerHigh() { return (schedulerHighSpeed > ConversionUtils.getConversionUtilsObject().getCurrentUTCms()); } /** * Abstration function for the possibility to run pipelines without a * Transaction Manager. * * @param PipeState The expected state * @return The actual state. Will equal the expected state if we are running * without a transaction manager */ public boolean getTransactionOpen(boolean PipeState) { if (TM == null) { return PipeState; } else { return (TM.getActiveTransactionCount() > 0); } } // ----------------------------------------------------------------------------- // ------------- Start of inherited IEventInterface functions ------------------ // ----------------------------------------------------------------------------- /* processControlEvent is the method that will be called when an event * is received for a module that has registered itself as a client of the * External Control Interface * * @param Command - command that is understand by the client module * @param Init - we are performing initial configuration if true * @param Parameter - parameter for the command */ @Override public String processControlEvent(String Command, boolean Init, String Parameter) { int ResultCode = -1; String logStr; if (Command.equalsIgnoreCase(SERVICE_PIPELINE_ACTIVE)) { if (Parameter.equalsIgnoreCase("false")) { // Suspend pipeline processing setActive(false); ResultCode = 0; } if (Parameter.equalsIgnoreCase("true")) { // Start pipeline processing setActive(true); ResultCode = 0; } if (Parameter.equalsIgnoreCase("")) { // Get the current status String currentActiveState; if (active != activeStateRequested) { currentActiveState = Boolean.toString(active) + "*"; } else { currentActiveState = Boolean.toString(active); } return currentActiveState; } } if (Command.equalsIgnoreCase(SERVICE_PIPELINE_SLEEP)) { if (Parameter.equals("")) { // Get the current status return Integer.toString(sleepTime); } else { try { sleepTime = Integer.parseInt(Parameter); } catch (NumberFormatException nfe) { logStr = "Sleep paramter was not numeric. Passed value = <" + Parameter + ">"; OpenRate.getOpenRateFrameworkLog().error(logStr); return logStr; } OpenRate.getOpenRateFrameworkLog().info( "Sleep time set to <" + sleepTime + "> for pipeline <" + symbolicName + ">"); ResultCode = 0; } } if (Command.equalsIgnoreCase(SERVICE_RUNCOUNT)) { if (Parameter.equals("")) { // Get the current status return Integer.toString(runCount); } else { try { runCount = Integer.parseInt(Parameter); // Also set the pipe to active if it is inactive if (!active) { setActive(true); } } catch (NumberFormatException nfe) { logStr = "RunCount paramter was not numeric. Passed value = <" + Parameter + ">"; OpenRate.getOpenRateFrameworkLog().error(logStr); return logStr; } } ResultCode = 0; } if (Command.equalsIgnoreCase(SERVICE_HALT_ON_EXCP)) { if (Parameter.equalsIgnoreCase("false")) { // Suspend pipeline processing haltOnException = false; ResultCode = 0; } if (Parameter.equalsIgnoreCase("true")) { // Start pipeline processing haltOnException = true; ResultCode = 0; } if (Parameter.equalsIgnoreCase("")) { // Get the current status return Boolean.toString(haltOnException); } } if (Command.equalsIgnoreCase(SERVICE_BUFFER_STATUS)) { Iterator<IBuffer> bufferIter = bufferList.iterator(); String responseString = ""; IBuffer tmpBuffer; if (Parameter.equals("")) { while (bufferIter.hasNext()) { tmpBuffer = bufferIter.next(); responseString = responseString + tmpBuffer.getConsumer() + "=" + tmpBuffer.getEventCount() + ", "; } // Get the current status return responseString; } } logStr = "Command " + Command + " handled by OpenRateApplication"; OpenRate.getOpenRateFrameworkLog().debug(logStr); if (ResultCode == 0) { return "OK"; } else { return "Error: Command not understood."; } } /** * registerClientManager registers the client module to the ClientManager * class which manages all the client modules available in this OpenRate * Application. * * registerClientManager registers this class as a client of the ECI listener * and publishes the commands that the plug in understands. The listener is * responsible for delivering only these commands to the plug in. * * @throws OpenRate.exception.InitializationException */ @Override public void registerClientManager() throws InitializationException { //Register this Client to this pipeline ClientManager.getClientManager().registerClient(getSymbolicName(), getSymbolicName(), this); //Register services for this Client ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_PIPELINE_ACTIVE, ClientManager.PARAM_DYNAMIC); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_PIPELINE_SLEEP, ClientManager.PARAM_DYNAMIC); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_RUNCOUNT, ClientManager.PARAM_DYNAMIC); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_HALT_ON_EXCP, ClientManager.PARAM_DYNAMIC); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_BUFFER_STATUS, ClientManager.PARAM_DYNAMIC); } // ----------------------------------------------------------------------------- // ---------------- Start of inherited ISyncPoint functions -------------------- // ----------------------------------------------------------------------------- /** * This is used for the pipeline synchronisation. See the description in the * OpenRate framework module to understand how this works. */ @Override public int getSyncStatus() { return localSyncStatus; } /** * This is used for the pipeline synchronisation. See the description in the * OpenRate framework module to understand how this works. */ @Override public void setSyncStatus(int newStatus) { if ((newStatus == ISyncPoint.SYNC_STATUS_SYNC_REQUESTED) & (localSyncStatus != ISyncPoint.SYNC_STATUS_SYNC_REQUESTED)) { // command the pipe to not accept any new transactions syncStatusActive = active; setActive(false); } if ((newStatus == ISyncPoint.SYNC_STATUS_NORMAL_RUN) & (localSyncStatus != ISyncPoint.SYNC_STATUS_NORMAL_RUN)) { // command the pipe to accept new transactions // Ticket #568446 - reset active to previous status after sync setActive(syncStatusActive); } // Update the status localSyncStatus = newStatus; } /** * Returns true if the pipe is a batch pipeline * * @return true if the pipeline is a batch pipeline, false if it is real time */ @Override public boolean isBatchPipeline() { return batchPipeline; } /** * Returns true if the pipeline aborted - used to cascade the abort to stop * the system in frameworks with multiple pipes * * @return true if the pipe aborted and the framework should stop */ @Override public boolean isAborted() { return aborted; } /** * @return the pipeLog */ @Override public ILogger getPipeLog() { return pipeLog; } /** * @param pipeLog the pipeLog to set */ public void setPipeLog(ILogger pipeLog) { this.pipeLog = pipeLog; } /** * Returns the pipeline exception handler. * * @return The exception handler for the pipeline */ @Override public ExceptionHandler getPipelineExceptionHandler() { return pipeExceptionHandler; } }