/**
* 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.source;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.codahale.metrics.Counter;
import com.ottogroup.bi.spqr.exception.RequiredInputMissingException;
import com.ottogroup.bi.spqr.pipeline.message.StreamingDataMessage;
import com.ottogroup.bi.spqr.pipeline.queue.StreamingMessageQueueProducer;
/**
* Runtime environment for {@link Source} instances
* @author mnxfst
* @since Mar 5, 2015
*/
public class SourceRuntimeEnvironment implements Runnable, IncomingMessageCallback {
/** our faithful logging facility ... ;-) */
private static final Logger logger = Logger.getLogger(SourceRuntimeEnvironment.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 source assigned to this runtime environment */
private final String sourceId;
/** source instances executed by this runtime environment */
private final Source source;
/** attached queue producer which receives incoming messages */
private final StreamingMessageQueueProducer queueProducer;
/** executor service that will run the source instance */
private final ExecutorService executorService;
/** local executor service? - must be shut down as well, otherwise the provider must take care of it */
private boolean localExecutorService = false;
/** message counter metric */
private Counter messageCounter;
/**
* Initializes the runtime environment using the provided input
* @param processingNodeId
* @param pipelineId
* @param source
* @param queueProducer
* @throws RequiredInputMissingException
*/
public SourceRuntimeEnvironment(final String processingNodeId, final String pipelineId, final Source source, final StreamingMessageQueueProducer queueProducer) throws RequiredInputMissingException {
this(processingNodeId, pipelineId, source, queueProducer, Executors.newCachedThreadPool());
this.localExecutorService = true;
}
/**
* Initializes the runtime environment using the provided input
* @param processingNodeId
* @param pipelineId
* @param source
* @param queueProducer
* @param executorService
* @throws RequiredInputMissingException
*/
public SourceRuntimeEnvironment(final String processingNodeId, final String pipelineId, final Source source, final StreamingMessageQueueProducer queueProducer,
final ExecutorService executorService) throws RequiredInputMissingException {
///////////////////////////////////////////////////////////////
// validate input
if(StringUtils.isBlank(processingNodeId))
throw new RequiredInputMissingException("Missing required processing node identifier");
if(StringUtils.isBlank(pipelineId))
throw new RequiredInputMissingException("Missing required pipeline identifier");
if(source == null)
throw new RequiredInputMissingException("Missing required source");
if(queueProducer == null)
throw new RequiredInputMissingException("Missing required queue producer");
if(executorService == null)
throw new RequiredInputMissingException("Missing required executor service");
//
///////////////////////////////////////////////////////////////
this.processingNodeId = StringUtils.lowerCase(StringUtils.trim(processingNodeId));
this.pipelineId = StringUtils.lowerCase(StringUtils.trim(pipelineId));
this.sourceId = StringUtils.lowerCase(StringUtils.trim(source.getId()));
this.source = source;
this.source.setIncomingMessageCallback(this);
this.queueProducer = queueProducer;
this.executorService = executorService;
this.executorService.submit(source);
if(logger.isDebugEnabled())
logger.debug("source runtime environment initialized [id="+source.getId()+"]");
}
/**
* @see java.lang.Runnable#run()
*/
public void run() {
}
/**
* @see com.ottogroup.bi.spqr.pipeline.component.source.IncomingMessageCallback#onMessage(com.ottogroup.bi.spqr.pipeline.message.StreamingDataMessage)
*/
public void onMessage(StreamingDataMessage message) {
this.queueProducer.insert(message);
this.queueProducer.getWaitStrategy().forceLockRelease();
if(this.messageCounter != null)
this.messageCounter.inc();
}
/**
* Shuts down the runtime environment as well as the attached {@link Source}
*/
public void shutdown() {
try {
this.source.shutdown();
} catch(Exception e) {
logger.error("source shutdown error [node="+this.processingNodeId+", pipeline="+this.pipelineId+", source="+this.sourceId+"]: " + e.getMessage(), e);
}
if(this.localExecutorService) {
try {
this.executorService.shutdownNow();
} catch(Exception e) {
logger.error("exec service shutdown error [node="+this.processingNodeId+", pipeline="+this.pipelineId+", source="+this.sourceId+"]: " + e.getMessage(), e);
}
}
if(logger.isDebugEnabled())
logger.debug("source shutdown [node="+this.processingNodeId+", pipeline="+this.pipelineId+", source="+this.sourceId+"]");
}
/**
* Sets the message {@link Counter}
* @param counter
*/
public void setMessageCounter(final Counter counter) {
this.messageCounter = counter;
}
}