com.ottogroup.bi.spqr.pipeline.component.operator.DirectResponseOperatorRuntimeEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for com.ottogroup.bi.spqr.pipeline.component.operator.DirectResponseOperatorRuntimeEnvironment.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.component.operator;

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

import com.codahale.metrics.Counter;
import com.codahale.metrics.Timer;
import com.ottogroup.bi.spqr.exception.RequiredInputMissingException;
import com.ottogroup.bi.spqr.pipeline.message.StreamingDataMessage;
import com.ottogroup.bi.spqr.pipeline.queue.StreamingMessageQueueConsumer;
import com.ottogroup.bi.spqr.pipeline.queue.StreamingMessageQueueProducer;
import com.ottogroup.bi.spqr.pipeline.queue.strategy.StreamingMessageQueueWaitStrategy;

/**
 * Provides a runtime environment for {@link DirectResponseOperator} instances. The environment polls
 * messages from the assigned {@link StreamingMessageQueueConsumer}, forwards them for further processing
 * to the {@link DirectResponseOperator} and inserts all generated {@link StreamingDataMessage response messages}
 * into the {@link StreamingMessageQueueProducer}. The message order as received from the operator is 
 * preserved when handing over the messages to the queue producer.
 * @author mnxfst
 * @since Mar 5, 2015
 */
public class DirectResponseOperatorRuntimeEnvironment implements Runnable {

    /** our faithful logging facility ... ;-) */
    private static final Logger logger = Logger.getLogger(DirectResponseOperatorRuntimeEnvironment.class);

    /** identifier of processing node the runtime environment belongs to*/
    private final String processingNodeId;
    /** identifier of pipeline the runtime environment belongs to */
    private final String pipelineId;
    /** identifier of operator assigned to this runtime environment */
    private final String operatorId;
    /** operator instance executed by this runtime environment */
    private final DirectResponseOperator directResponseOperator;
    /** provides read access to assigned source queue */
    private final StreamingMessageQueueConsumer queueConsumer;
    /** provides write access to assigned destination queue */
    private final StreamingMessageQueueProducer queueProducer;
    /** indicates whether the operator runtime is still running or not */
    private boolean running = false;
    /** consumer queue wait strategy */
    private final StreamingMessageQueueWaitStrategy consumerQueueWaitStrategy;
    /** destination queue wait strategy */
    private final StreamingMessageQueueWaitStrategy destinationQueueWaitStrategy;
    /** message counter metric */
    private Counter messageCounter = null;
    /** message processing timer metric */
    private Timer messageProcessingTimer = null;

    /**
     * Initializes the operator runtime environment using the provided input
     * @param processingNodeId
     * @param pipelineId
     * @param directResponseOperator
     * @param queueConsumer
     * @param queueProducer
     */
    public DirectResponseOperatorRuntimeEnvironment(final String processingNodeId, final String pipelineId,
            final DirectResponseOperator directResponseOperator, final StreamingMessageQueueConsumer queueConsumer,
            final StreamingMessageQueueProducer queueProducer) throws RequiredInputMissingException {

        /////////////////////////////////////////////////////////////
        // input validation
        if (StringUtils.isBlank(processingNodeId))
            throw new RequiredInputMissingException("Missing required processing node identifier");
        if (StringUtils.isBlank(pipelineId))
            throw new RequiredInputMissingException("Missing required pipeline identifier");
        if (directResponseOperator == null)
            throw new RequiredInputMissingException("Missing required direct response operator");
        if (queueConsumer == null)
            throw new RequiredInputMissingException("Missing required queue consumer");
        if (queueProducer == null)
            throw new RequiredInputMissingException("Missing required queue producer");
        //
        /////////////////////////////////////////////////////////////

        this.processingNodeId = StringUtils.lowerCase(StringUtils.trim(processingNodeId));
        this.pipelineId = StringUtils.lowerCase(StringUtils.trim(pipelineId));
        this.operatorId = StringUtils.lowerCase(StringUtils.trim(directResponseOperator.getId()));
        this.directResponseOperator = directResponseOperator;
        this.queueConsumer = queueConsumer;
        this.queueProducer = queueProducer;
        this.running = true;
        this.consumerQueueWaitStrategy = queueConsumer.getWaitStrategy();
        this.destinationQueueWaitStrategy = queueProducer.getWaitStrategy();

        if (logger.isDebugEnabled())
            logger.debug("direct response operator init [node=" + this.processingNodeId + ", pipeline="
                    + this.pipelineId + ", operator=" + this.operatorId + "]");
    }

    /**
     * @see java.lang.Runnable#run()
     */
    public void run() {

        while (running) {

            try {
                StreamingDataMessage message = this.consumerQueueWaitStrategy.waitFor(this.queueConsumer);
                if (message != null && message.getBody() != null) {

                    @SuppressWarnings("resource") // context#close() calls context#stop -> avoid additional call, thus accept warning
                    Timer.Context timerContext = (this.messageProcessingTimer != null
                            ? this.messageProcessingTimer.time()
                            : null);

                    StreamingDataMessage[] responseMessages = this.directResponseOperator.onMessage(message);
                    if (responseMessages != null && responseMessages.length > 0) {
                        for (final StreamingDataMessage responseMessage : responseMessages) {
                            this.queueProducer.insert(responseMessage);
                        }
                        this.destinationQueueWaitStrategy.forceLockRelease();
                    }

                    if (timerContext != null)
                        timerContext.stop();

                    if (this.messageCounter != null)
                        this.messageCounter.inc();
                }

            } catch (InterruptedException e) {
                // do nothing - waiting was interrupted            
            } catch (Exception e) {
                logger.error("processing error [node=" + this.processingNodeId + ", pipeline=" + this.pipelineId
                        + ", operator=" + this.operatorId + "]: " + e.getMessage(), e);
                // TODO add handler for responding to errors 
            }
        }
    }

    /**
     * Shuts down the runtime environment as well as the attached {@link Operator}
     */
    public void shutdown() {
        this.running = false;
        try {
            this.directResponseOperator.shutdown();
        } catch (Exception e) {
            logger.error("operator shutdown error [node=" + this.processingNodeId + ", pipeline=" + this.pipelineId
                    + ", operator=" + this.operatorId + "]: " + e.getMessage(), e);
        }

        if (logger.isDebugEnabled())
            logger.debug("shutdown success [node=" + this.processingNodeId + ", pipeline=" + this.pipelineId
                    + ", operator=" + this.operatorId + "]");

    }

    /**
     * @return the running
     */
    public boolean isRunning() {
        return running;
    }

    /**
     * @param messageCounter the messageCounter to set
     */
    public void setMessageCounter(Counter messageCounter) {
        this.messageCounter = messageCounter;
    }

    /**
     * @param messageProcessingTimer the messageProcessingTimer to set
     */
    public void setMessageProcessingTimer(Timer messageProcessingTimer) {
        this.messageProcessingTimer = messageProcessingTimer;
    }

}