com.ottogroup.bi.spqr.pipeline.MicroPipelineFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.ottogroup.bi.spqr.pipeline.MicroPipelineFactory.java

Source

/**
 * Copyright 2015 Otto (GmbH & Co KG)
 *
 * 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 com.ottogroup.bi.spqr.pipeline;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.ottogroup.bi.spqr.exception.ComponentInitializationFailedException;
import com.ottogroup.bi.spqr.exception.QueueInitializationFailedException;
import com.ottogroup.bi.spqr.exception.RequiredInputMissingException;
import com.ottogroup.bi.spqr.metrics.MetricsHandler;
import com.ottogroup.bi.spqr.metrics.MetricsReporterFactory;
import com.ottogroup.bi.spqr.pipeline.component.MicroPipelineComponent;
import com.ottogroup.bi.spqr.pipeline.component.MicroPipelineComponentConfiguration;
import com.ottogroup.bi.spqr.pipeline.component.MicroPipelineComponentType;
import com.ottogroup.bi.spqr.pipeline.component.emitter.Emitter;
import com.ottogroup.bi.spqr.pipeline.component.emitter.EmitterRuntimeEnvironment;
import com.ottogroup.bi.spqr.pipeline.component.operator.DelayedResponseOperator;
import com.ottogroup.bi.spqr.pipeline.component.operator.DelayedResponseOperatorRuntimeEnvironment;
import com.ottogroup.bi.spqr.pipeline.component.operator.DelayedResponseOperatorWaitStrategy;
import com.ottogroup.bi.spqr.pipeline.component.operator.DirectResponseOperator;
import com.ottogroup.bi.spqr.pipeline.component.operator.DirectResponseOperatorRuntimeEnvironment;
import com.ottogroup.bi.spqr.pipeline.component.operator.MessageCountResponseWaitStrategy;
import com.ottogroup.bi.spqr.pipeline.component.operator.OperatorTriggeredWaitStrategy;
import com.ottogroup.bi.spqr.pipeline.component.operator.TimerBasedResponseWaitStrategy;
import com.ottogroup.bi.spqr.pipeline.component.source.Source;
import com.ottogroup.bi.spqr.pipeline.component.source.SourceRuntimeEnvironment;
import com.ottogroup.bi.spqr.pipeline.exception.UnknownWaitStrategyException;
import com.ottogroup.bi.spqr.pipeline.queue.StreamingMessageQueue;
import com.ottogroup.bi.spqr.pipeline.queue.StreamingMessageQueueConfiguration;
import com.ottogroup.bi.spqr.pipeline.queue.chronicle.DefaultStreamingMessageQueue;
import com.ottogroup.bi.spqr.pipeline.queue.memory.InMemoryStreamingMessageQueue;
import com.ottogroup.bi.spqr.repository.ComponentRepository;

/**
 * Manages the instantiation of {@link MicroPipeline micro pipelines} 
 * @author mnxfst
 * @since Mar 6, 2015
 */
public class MicroPipelineFactory {

    /** our faithful logging service ... ;-) */
    private static final Logger logger = Logger.getLogger(MicroPipelineFactory.class);
    /** reference towards component repository */
    private final ComponentRepository componentRepository;
    /** identifier of processing node this factory lives on */
    private final String processingNodeId;

    /**
     * Initializes the factory using the provided input
     * @param processingNodeId
     * @param componentRepository
     */
    public MicroPipelineFactory(final String processingNodeId, final ComponentRepository componentRepository) {
        this.processingNodeId = processingNodeId;
        this.componentRepository = componentRepository;
    }

    /**
     * Instantiates the {@link MicroPipeline} according to the provided {@link MicroPipelineComponentConfiguration} 
     * @param cfg
     * @param executorService
     * @return
     * @throws RequiredInputMissingException
     * TODO validate micro pipeline for path from source to emitter
     */
    public MicroPipeline instantiatePipeline(final MicroPipelineConfiguration cfg,
            final ExecutorService executorService) throws RequiredInputMissingException,
            QueueInitializationFailedException, ComponentInitializationFailedException {

        ///////////////////////////////////////////////////////////////////////////////////
        // validate input
        if (cfg == null)
            throw new RequiredInputMissingException("Missing required configuration");
        if (StringUtils.isBlank(cfg.getId()))
            throw new RequiredInputMissingException("Missing required micro pipeline id");
        if (cfg.getComponents() == null || cfg.getComponents().isEmpty())
            throw new RequiredInputMissingException("Missing required component configurations");
        if (cfg.getQueues() == null || cfg.getQueues().isEmpty())
            throw new RequiredInputMissingException("Missing required queue configurations");
        //
        ///////////////////////////////////////////////////////////////////////////////////

        MetricRegistry.name(StringUtils.lowerCase(StringUtils.trim(this.processingNodeId)),
                StringUtils.lowerCase(StringUtils.trim(cfg.getId())), "queue", "messages");

        final MetricsHandler metricsHandler = new MetricsHandler();
        MetricsReporterFactory.attachReporters(metricsHandler, cfg.getMetricsReporter());

        ///////////////////////////////////////////////////////////////////////////////////
        // (1) initialize queues

        // keep track of all ready created queue instances and create a new one for each configuration
        // entry. if creation fails for any reason, all previously created queues are shut down and
        // a queue initialization exception is thrown
        MicroPipeline microPipeline = new MicroPipeline(StringUtils.lowerCase(StringUtils.trim(cfg.getId())), cfg);
        for (final StreamingMessageQueueConfiguration queueConfig : cfg.getQueues()) {
            String id = StringUtils.lowerCase(StringUtils.trim(queueConfig.getId()));

            // a queue for that identifier already exists: kill the pipeline and tell the caller about it
            if (microPipeline.hasQueue(id)) {
                logger.error("queue initialization failed [id=" + id + "]. Forcing shutdown of all queues.");
                microPipeline.shutdown();
                throw new QueueInitializationFailedException("Non-unique queue identifier found [id=" + id + "]");
            }

            // try to instantiate the queue, if it fails .... shutdown queues initialized so far and throw an exception
            try {
                StreamingMessageQueue queueInstance = initializeQueue(queueConfig);

                /////////////////////////////////////////////////////////////////////
                // add queue message insertion and retrieval counters
                if (queueConfig.isAttachInsertionCounter()) {
                    final Counter queueInsertionCounter = metricsHandler.counter(
                            MetricRegistry.name(StringUtils.lowerCase(StringUtils.trim(this.processingNodeId)),
                                    StringUtils.lowerCase(StringUtils.trim(cfg.getId())), "queue", id, "messages",
                                    "in"),
                            true);
                    queueInstance.setMessageInsertionCounter(queueInsertionCounter);

                    if (logger.isDebugEnabled())
                        logger.debug("queue[id=" + id + "]: message insertion counter attached");
                }

                if (queueConfig.isAttachRetrievalCounter()) {
                    final Counter queueRetrievalCounter = metricsHandler.counter(
                            MetricRegistry.name(StringUtils.lowerCase(StringUtils.trim(this.processingNodeId)),
                                    StringUtils.lowerCase(StringUtils.trim(cfg.getId())), "queue", id, "messages",
                                    "out"),
                            true);
                    queueInstance.setMessageRetrievalCounter(queueRetrievalCounter);

                    if (logger.isDebugEnabled())
                        logger.debug("queue[id=" + id + "]: message retrieval counter attached");
                }
                /////////////////////////////////////////////////////////////////////

                microPipeline.addQueue(id, queueInstance);
                logger.info("queue initialized[id=" + id + "]");
            } catch (Exception e) {
                logger.error("queue initialization failed [id=" + id + "]. Forcing shutdown of all queues.");
                microPipeline.shutdown();
                throw new QueueInitializationFailedException(
                        "Failed to initialize queue [id=" + id + "]. Reason: " + e.getMessage(), e);
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////

        ///////////////////////////////////////////////////////////////////////////////////
        // (2) initialize components
        final Map<String, MicroPipelineComponent> components = new HashMap<>();
        boolean sourceComponentFound = false;
        boolean emitterComponentFound = false;
        for (final MicroPipelineComponentConfiguration componentCfg : cfg.getComponents()) {
            String id = StringUtils.lowerCase(StringUtils.trim(componentCfg.getId()));

            // a component for that identifier already exists: kill the pipeline and tell the caller about it
            if (microPipeline.hasComponent(id)) {
                logger.error("component initialization failed [id=" + id + ", class=" + componentCfg.getName()
                        + ", version=" + componentCfg.getVersion()
                        + "]. Forcing shutdown of all queues and components.");
                microPipeline.shutdown();
                throw new ComponentInitializationFailedException(
                        "Non-unique component identifier found [id=" + id + "]");
            }

            // try to instantiate component, if it fails .... shutdown queues and components initialized so far and throw an exception
            try {
                MicroPipelineComponent component = initializeComponent(componentCfg, microPipeline.getQueues());
                if (component.getType() == null) {
                    logger.error("component initialization failed [id=" + id + ", class=" + componentCfg.getName()
                            + ", version=" + componentCfg.getVersion()
                            + "]. Type missing. Forcing shutdown of all queues and components.");
                    microPipeline.shutdown();
                    throw new ComponentInitializationFailedException(
                            "Failed to initialize component [id=" + id + ", class=" + componentCfg.getName()
                                    + ", version=" + componentCfg.getVersion() + "]. Reason: type missing");
                }

                final StreamingMessageQueue fromQueue = microPipeline
                        .getQueue(StringUtils.lowerCase(StringUtils.trim(componentCfg.getFromQueue())));
                final StreamingMessageQueue toQueue = microPipeline
                        .getQueue(StringUtils.lowerCase(StringUtils.trim(componentCfg.getToQueue())));

                Counter messageCounter = null;
                if (componentCfg.isAttachMessageCounter()) {
                    messageCounter = metricsHandler.counter(
                            MetricRegistry.name(StringUtils.lowerCase(StringUtils.trim(this.processingNodeId)),
                                    StringUtils.lowerCase(StringUtils.trim(cfg.getId())), "component", id,
                                    "messages", "count"),
                            true);
                }

                switch (component.getType()) {
                case SOURCE: {
                    SourceRuntimeEnvironment srcEnv = new SourceRuntimeEnvironment(this.processingNodeId,
                            cfg.getId(), (Source) component, toQueue.getProducer());

                    ///////////////////////////////////////////////
                    // attach monitoring components
                    if (messageCounter != null)
                        srcEnv.setMessageCounter(messageCounter);
                    ///////////////////////////////////////////////

                    microPipeline.addSource(id, srcEnv);
                    sourceComponentFound = true;
                    break;
                }
                case DIRECT_RESPONSE_OPERATOR: {

                    DirectResponseOperatorRuntimeEnvironment directResponseEnv = new DirectResponseOperatorRuntimeEnvironment(
                            this.processingNodeId, cfg.getId(), (DirectResponseOperator) component,
                            fromQueue.getConsumer(), toQueue.getProducer());

                    ///////////////////////////////////////////////
                    // attach monitoring components
                    if (componentCfg.isAttachProcessingTimer()) {
                        final Timer messageProcessingTimer = metricsHandler.timer(
                                MetricRegistry.name(StringUtils.lowerCase(StringUtils.trim(this.processingNodeId)),
                                        StringUtils.lowerCase(StringUtils.trim(cfg.getId())), "component", id,
                                        "messages", "timer"));
                        directResponseEnv.setMessageProcessingTimer(messageProcessingTimer);
                    }

                    if (messageCounter != null)
                        directResponseEnv.setMessageCounter(messageCounter);
                    ///////////////////////////////////////////////

                    microPipeline.addOperator(id, directResponseEnv);
                    break;
                }
                case DELAYED_RESPONSE_OPERATOR: {
                    DelayedResponseOperatorRuntimeEnvironment delayedResponseEnv = new DelayedResponseOperatorRuntimeEnvironment(
                            this.processingNodeId, cfg.getId(), (DelayedResponseOperator) component,
                            getResponseWaitStrategy(componentCfg), fromQueue.getConsumer(), toQueue.getProducer(),
                            executorService);

                    ///////////////////////////////////////////////
                    // attach monitoring components
                    if (messageCounter != null)
                        delayedResponseEnv.setMessageCounter(messageCounter);
                    ///////////////////////////////////////////////

                    microPipeline.addOperator(id, delayedResponseEnv);
                    break;
                }
                case EMITTER: {
                    EmitterRuntimeEnvironment emitterEnv = new EmitterRuntimeEnvironment(this.processingNodeId,
                            cfg.getId(), (Emitter) component, fromQueue.getConsumer());

                    ///////////////////////////////////////////////
                    // attach monitoring components
                    if (componentCfg.isAttachProcessingTimer()) {
                        final Timer messageEmitDurationTimer = metricsHandler.timer(
                                MetricRegistry.name(StringUtils.lowerCase(StringUtils.trim(this.processingNodeId)),
                                        StringUtils.lowerCase(StringUtils.trim(cfg.getId())), "component", id,
                                        "messages", "emit", "duration"));
                        emitterEnv.setMessageEmitDurationTimer(messageEmitDurationTimer);
                    }

                    if (messageCounter != null)
                        emitterEnv.setMessageCounter(messageCounter);
                    ///////////////////////////////////////////////

                    microPipeline.addEmitter(id, emitterEnv);
                    emitterComponentFound = true;
                    break;
                }
                }

                components.put(id, component);
            } catch (Exception e) {
                logger.error("component initialization failed [id=" + id + ", class=" + componentCfg.getName()
                        + ", version=" + componentCfg.getVersion()
                        + "]. Forcing shutdown of all queues and components. Reason: " + e.getMessage(), e);
                microPipeline.shutdown();
                throw new ComponentInitializationFailedException(
                        "Failed to initialize component [id=" + id + ", class=" + componentCfg.getName()
                                + ", version=" + componentCfg.getVersion() + "]. Reason: " + e.getMessage(),
                        e);
            }
        }

        if (!sourceComponentFound) {
            microPipeline.shutdown();
            throw new RequiredInputMissingException("Missing required source component");
        }

        if (!emitterComponentFound) {
            microPipeline.shutdown();
            throw new RequiredInputMissingException("Missing required emitter component");
        }

        ///////////////////////////////////////////////////////////////////////////////////

        microPipeline.attachComponentMetricsHandler(metricsHandler);

        ///////////////////////////////////////////////////////////////////////////////////
        // (3) start components --> ramp up their runtime environments 
        for (String sourceId : microPipeline.getSources().keySet()) {
            executorService.submit(microPipeline.getSources().get(sourceId));
            if (logger.isDebugEnabled())
                logger.debug("Started runtime environment for source [id=" + sourceId + "]");
        }
        for (String directResponseOperatorId : microPipeline.getDirectResponseOperators().keySet()) {
            executorService.submit(microPipeline.getDirectResponseOperators().get(directResponseOperatorId));
            if (logger.isDebugEnabled())
                logger.debug("Started runtime environment for direct response operator [id="
                        + directResponseOperatorId + "]");
        }
        for (String delayedResponseOperatorId : microPipeline.getDelayedResponseOperators().keySet()) {
            executorService.submit(microPipeline.getDelayedResponseOperators().get(delayedResponseOperatorId));
            if (logger.isDebugEnabled())
                logger.debug("Started runtime environment for delayed response operator [id="
                        + delayedResponseOperatorId + "]");
        }
        for (String emitterId : microPipeline.getEmitters().keySet()) {
            executorService.submit(microPipeline.getEmitters().get(emitterId));
            if (logger.isDebugEnabled())
                logger.debug("Started runtime environment for emitter [id=" + emitterId + "]");
        }

        if (logger.isDebugEnabled())
            logger.debug("Started stats collector");
        //
        ///////////////////////////////////////////////////////////////////////////////////

        return microPipeline;
    }

    /**
     * Initializes a {@link StreamingMessageQueue} instance according to provided information.
     * @param queueConfiguration+
     * @return
     * @throws RequiredInputMissingException
     * @throws QueueInitializationFailedException
     */
    protected StreamingMessageQueue initializeQueue(final StreamingMessageQueueConfiguration queueConfiguration)
            throws RequiredInputMissingException, QueueInitializationFailedException {

        ///////////////////////////////////////////////////////////////////////////////////
        // validate input
        if (queueConfiguration == null)
            throw new RequiredInputMissingException("Missing required queue configuration");
        if (StringUtils.isBlank(queueConfiguration.getId()))
            throw new RequiredInputMissingException("Missing required queue identifier");
        //
        ///////////////////////////////////////////////////////////////////////////////////

        ///////////////////////////////////////////////////////////////////////////////////
        // check properties for optional settings
        boolean inMemoryQueue = false;
        if (queueConfiguration.getProperties() != null && !queueConfiguration.getProperties().isEmpty()) {
            String queueType = StringUtils.lowerCase(StringUtils
                    .trim(queueConfiguration.getProperties().getProperty(StreamingMessageQueue.CFG_QUEUE_TYPE)));
            inMemoryQueue = StringUtils.equalsIgnoreCase(queueType, InMemoryStreamingMessageQueue.CFG_QUEUE_TYPE);
        }
        ///////////////////////////////////////////////////////////////////////////////////

        if (inMemoryQueue) {
            try {
                StreamingMessageQueue queue = new InMemoryStreamingMessageQueue();
                queue.setId(StringUtils.lowerCase(StringUtils.trim(queueConfiguration.getId())));
                queue.initialize((queueConfiguration.getProperties() != null ? queueConfiguration.getProperties()
                        : new Properties()));
                return queue;
            } catch (Exception e) {
                throw new QueueInitializationFailedException("Failed to initialize streaming message queue '"
                        + queueConfiguration.getId() + "'. Error: " + e.getMessage());
            }
        }

        try {
            StreamingMessageQueue queue = new DefaultStreamingMessageQueue();
            queue.setId(StringUtils.lowerCase(StringUtils.trim(queueConfiguration.getId())));
            queue.initialize((queueConfiguration.getProperties() != null ? queueConfiguration.getProperties()
                    : new Properties()));
            return queue;
        } catch (Exception e) {
            throw new QueueInitializationFailedException("Failed to initialize streaming message queue '"
                    + queueConfiguration.getId() + "'. Error: " + e.getMessage());
        }
    }

    /**
     * Initializes a {@link MicroPipelineComponent} instance according to provided information
     * @param componentConfiguration
     * @return
     * @throws RequiredInputMissingException
     * @throws ComponentInitializationFailedException
     * TODO test for settings that must be provided for type SOURCE
     * TODO test for settings that must be provided for type EMITTER
     * TODO test for settings that must be provided for type OPERATOR
     * TODO test queue references in toQueues and fromQueues
     * TODO test component instantiation
     */
    protected MicroPipelineComponent initializeComponent(
            final MicroPipelineComponentConfiguration componentConfiguration,
            final Map<String, StreamingMessageQueue> queues)
            throws RequiredInputMissingException, ComponentInitializationFailedException {

        ///////////////////////////////////////////////////////////////////////////////////
        // validate input
        if (componentConfiguration == null)
            throw new RequiredInputMissingException("Missing required component configuration");
        if (StringUtils.isBlank(componentConfiguration.getId()))
            throw new RequiredInputMissingException("Missing required component identifier");
        if (componentConfiguration.getType() == null)
            throw new RequiredInputMissingException("Missing required component type");
        if (StringUtils.isBlank(componentConfiguration.getName()))
            throw new RequiredInputMissingException("Missing required component name");
        if (StringUtils.isBlank(componentConfiguration.getVersion()))
            throw new RequiredInputMissingException("Missing required component version");
        if (componentConfiguration.getSettings() == null)
            throw new RequiredInputMissingException("Missing required component settings");
        //
        ////////////////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////////////////
        // validate settings for components of type: SOURCE
        if (componentConfiguration.getType() == MicroPipelineComponentType.SOURCE) {

            if (StringUtils.isBlank(componentConfiguration.getToQueue()))
                throw new RequiredInputMissingException("Missing required queues to write content to");
            if (!queues.containsKey(StringUtils.lowerCase(StringUtils.trim(componentConfiguration.getToQueue()))))
                throw new RequiredInputMissingException(
                        "Unknown destination queue '" + componentConfiguration.getToQueue() + "'");

            ////////////////////////////////////////////////////////////////////////////////////
            // validate settings for components of type: DIRECT_RESPONSE_OPERATOR
        } else if (componentConfiguration.getType() == MicroPipelineComponentType.DIRECT_RESPONSE_OPERATOR) {

            if (StringUtils.isBlank(componentConfiguration.getToQueue()))
                throw new RequiredInputMissingException("Missing required queues to write content to");
            if (!queues.containsKey(StringUtils.lowerCase(StringUtils.trim(componentConfiguration.getToQueue()))))
                throw new RequiredInputMissingException(
                        "Unknown destination queue '" + componentConfiguration.getToQueue() + "'");

            if (StringUtils.isBlank(componentConfiguration.getFromQueue()))
                throw new RequiredInputMissingException("Missing required queues to retrieve content from");
            if (!queues.containsKey(StringUtils.lowerCase(StringUtils.trim(componentConfiguration.getFromQueue()))))
                throw new RequiredInputMissingException(
                        "Unknown source queue '" + componentConfiguration.getFromQueue() + "'");

            ////////////////////////////////////////////////////////////////////////////////////
            // validate settings for components of type: DELAYED_RESPONSE_OPERATOR
        } else if (componentConfiguration.getType() == MicroPipelineComponentType.DELAYED_RESPONSE_OPERATOR) {

            if (StringUtils.isBlank(componentConfiguration.getToQueue()))
                throw new RequiredInputMissingException("Missing required queues to write content to");
            if (!queues.containsKey(StringUtils.lowerCase(StringUtils.trim(componentConfiguration.getToQueue()))))
                throw new RequiredInputMissingException(
                        "Unknown destination queue '" + componentConfiguration.getToQueue() + "'");

            if (StringUtils.isBlank(componentConfiguration.getFromQueue()))
                throw new RequiredInputMissingException("Missing required queues to retrieve content from");
            if (!queues.containsKey(StringUtils.lowerCase(StringUtils.trim(componentConfiguration.getFromQueue()))))
                throw new RequiredInputMissingException(
                        "Unknown source queue '" + componentConfiguration.getFromQueue() + "'");

            if (StringUtils.isBlank(componentConfiguration.getSettings()
                    .getProperty(DelayedResponseOperator.CFG_WAIT_STRATEGY_NAME)))
                throw new RequiredInputMissingException(
                        "Missing required settings for wait strategy applied to delayed response operator");

            ////////////////////////////////////////////////////////////////////////////////////
            // validate settings for components of type: EMITTER
        } else if (componentConfiguration.getType() == MicroPipelineComponentType.EMITTER) {

            if (StringUtils.isBlank(componentConfiguration.getFromQueue()))
                throw new RequiredInputMissingException("Missing required queues to retrieve content from");
            if (!queues.containsKey(StringUtils.lowerCase(StringUtils.trim(componentConfiguration.getFromQueue()))))
                throw new RequiredInputMissingException(
                        "Unknown source queue '" + componentConfiguration.getFromQueue() + "'");
        }
        //
        ////////////////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////////////////
        // instantiate component class
        try {
            return this.componentRepository.newInstance(componentConfiguration.getId(),
                    componentConfiguration.getName(), componentConfiguration.getVersion(),
                    componentConfiguration.getSettings());
        } catch (Exception e) {
            throw new ComponentInitializationFailedException("Failed to initialize component '"
                    + componentConfiguration.getId() + "'. Error: " + e.getMessage(), e);
        }
        //
        ////////////////////////////////////////////////////////////////////////////////////

    }

    /**
     * Instantiates, initializes and returns the {@link DelayedResponseOperatorWaitStrategy} configured for the {@link DelayedResponseOperator}
     * whose {@link MicroPipelineComponentConfiguration configuration} is provided when calling this method. 
     * @param delayedResponseOperatorCfg
     * @return
     */
    protected DelayedResponseOperatorWaitStrategy getResponseWaitStrategy(
            final MicroPipelineComponentConfiguration delayedResponseOperatorCfg)
            throws RequiredInputMissingException, UnknownWaitStrategyException {

        /////////////////////////////////////////////////////////////////////////////////////
        // validate input
        if (delayedResponseOperatorCfg == null)
            throw new RequiredInputMissingException("Missing required delayed response operator configuration");
        if (delayedResponseOperatorCfg.getSettings() == null)
            throw new RequiredInputMissingException("Missing required delayed response operator settings");
        String strategyName = StringUtils.lowerCase(StringUtils.trim(delayedResponseOperatorCfg.getSettings()
                .getProperty(DelayedResponseOperator.CFG_WAIT_STRATEGY_NAME)));
        if (StringUtils.isBlank(strategyName))
            throw new RequiredInputMissingException(
                    "Missing required strategy name expected as part of operator settings ('"
                            + DelayedResponseOperator.CFG_WAIT_STRATEGY_NAME + "')");
        //
        /////////////////////////////////////////////////////////////////////////////////////

        if (logger.isDebugEnabled())
            logger.debug("Settings provided for strategy '" + strategyName + "'");
        Properties strategyProperties = new Properties();
        for (Enumeration<Object> keyEnumerator = delayedResponseOperatorCfg.getSettings().keys(); keyEnumerator
                .hasMoreElements();) {
            String key = (String) keyEnumerator.nextElement();
            if (StringUtils.startsWith(key, DelayedResponseOperator.CFG_WAIT_STRATEGY_SETTINGS_PREFIX)) {
                String waitStrategyCfgKey = StringUtils.substring(key, StringUtils.lastIndexOf(key, ".") + 1);
                if (StringUtils.isNoneBlank(waitStrategyCfgKey)) {
                    String waitStrategyCfgValue = delayedResponseOperatorCfg.getSettings().getProperty(key);
                    strategyProperties.put(waitStrategyCfgKey, waitStrategyCfgValue);

                    if (logger.isDebugEnabled())
                        logger.debug("\t" + waitStrategyCfgKey + ": " + waitStrategyCfgValue);

                }
            }
        }

        if (StringUtils.equalsIgnoreCase(strategyName, MessageCountResponseWaitStrategy.WAIT_STRATEGY_NAME)) {
            MessageCountResponseWaitStrategy strategy = new MessageCountResponseWaitStrategy();
            strategy.initialize(strategyProperties);
            return strategy;
        } else if (StringUtils.equalsIgnoreCase(strategyName, TimerBasedResponseWaitStrategy.WAIT_STRATEGY_NAME)) {
            TimerBasedResponseWaitStrategy strategy = new TimerBasedResponseWaitStrategy();
            strategy.initialize(strategyProperties);
            return strategy;
        } else if (StringUtils.equalsIgnoreCase(strategyName, OperatorTriggeredWaitStrategy.WAIT_STRATEGY_NAME)) {
            OperatorTriggeredWaitStrategy strategy = new OperatorTriggeredWaitStrategy();
            strategy.initialize(strategyProperties);
            return strategy;
        }

        throw new UnknownWaitStrategyException("Unknown wait strategy '" + strategyName + "'");

    }

}