/** * 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.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.ottogroup.bi.spqr.exception.ComponentInitializationFailedException; import com.ottogroup.bi.spqr.exception.NonUniqueIdentifierException; import com.ottogroup.bi.spqr.exception.PipelineInstantiationFailedException; import com.ottogroup.bi.spqr.exception.QueueInitializationFailedException; import com.ottogroup.bi.spqr.exception.RequiredInputMissingException; import com.ottogroup.bi.spqr.pipeline.component.MicroPipelineComponent; import com.ottogroup.bi.spqr.repository.ComponentRepository; /** * Manages the instantiation and monitoring of {@link MicroPipeline micro pipelines} * @author mnxfst * @since Mar 13, 2015 * TODO using one execution service may cause troubles, need to check on that but for the moment having only one suffice */ public class MicroPipelineManager { private static final Logger logger = Logger.getLogger(MicroPipelineManager.class); /** keeps track of all registered pipeline instances */ private final Map<String, MicroPipeline> pipelines = new HashMap<>(); /** reference towards execution service which is to be used for runtime environment execution */ private final ExecutorService executorService; /** micro pipeline factory */ private final MicroPipelineFactory microPipelineFactory; /** identifier of processing node this factory lives on */ private final String processingNodeId; /** * Initializes the micro pipeline manager * @param processingNodeId identifier of node this manager lives on * @param componentRepository reference to {@link ComponentRepository} which provides access to all {@link MicroPipelineComponent} * @param maxNumberOfThreads max. number of threads assigned to {@link ExecutorService} (1 = single threaded, n = fixed number of threads, other = cached thread pool) * @throws RequiredInputMissingException */ public MicroPipelineManager(final String processingNodeId, final ComponentRepository componentRepository, final int maxNumberOfThreads) throws RequiredInputMissingException { ////////////////////////////////////////////////////////////////////////////// // validate provided input if(componentRepository == null) throw new RequiredInputMissingException("Missing required component repository"); if(StringUtils.isBlank(processingNodeId)) throw new RequiredInputMissingException("Missing required processing node identifier"); // ////////////////////////////////////////////////////////////////////////////// this.processingNodeId = StringUtils.lowerCase(StringUtils.trim(processingNodeId)); this.microPipelineFactory = new MicroPipelineFactory(this.processingNodeId, componentRepository); if(maxNumberOfThreads == 1) this.executorService = Executors.newSingleThreadExecutor(); else if(maxNumberOfThreads > 1) this.executorService = Executors.newFixedThreadPool(maxNumberOfThreads); else this.executorService = Executors.newCachedThreadPool(); } /** * Initializes the micro pipeline manager - <b>used for testing purpose only</b> * @param processingNodeId identifier of node this manager lives on * @param factory * @param executorService * @throws RequiredInputMissingException */ protected MicroPipelineManager(final String processingNodeId, final MicroPipelineFactory factory, final ExecutorService executorService) throws RequiredInputMissingException { ////////////////////////////////////////////////////////////////////////////// // validate provided input if(factory == null) throw new RequiredInputMissingException("Missing required component repository"); if(executorService == null) throw new RequiredInputMissingException("Missing required executor service"); if(StringUtils.isBlank(processingNodeId)) throw new RequiredInputMissingException("Missing required processing node identifier"); // ////////////////////////////////////////////////////////////////////////////// this.processingNodeId = StringUtils.lowerCase(StringUtils.trim(processingNodeId)); this.microPipelineFactory = factory; this.executorService = executorService; } /** * Instantiates and executes the {@link MicroPipeline} described by the given {@link MicroPipelineConfiguration} * @param configuration * @throws RequiredInputMissingException * @throws QueueInitializationFailedException * @throws ComponentInitializationFailedException * @throws PipelineInstantiationFailedException */ public String executePipeline(final MicroPipelineConfiguration configuration) throws RequiredInputMissingException, QueueInitializationFailedException, ComponentInitializationFailedException, PipelineInstantiationFailedException, NonUniqueIdentifierException { /////////////////////////////////////////////////////////// // validate input if(configuration == null) throw new RequiredInputMissingException("Missing required pipeline configuration"); // /////////////////////////////////////////////////////////// String id = StringUtils.lowerCase(StringUtils.trim(configuration.getId())); if(this.pipelines.containsKey(id)) throw new NonUniqueIdentifierException("A pipeline already exists for id '"+id+"'"); MicroPipeline pipeline = this.microPipelineFactory.instantiatePipeline(configuration, this.executorService); if(pipeline != null) this.pipelines.put(id, pipeline); else throw new PipelineInstantiationFailedException("Failed to instantiate pipeline '"+configuration.getId()+"'. Reason: null returned by pipeline factory"); if(logger.isDebugEnabled()) logger.debug("pipeline registered[id="+configuration.getId()+"]"); return id; } /** * Shuts down the referenced pipeline * @param pipelineId */ public String shutdownPipeline(final String pipelineId) { String id = StringUtils.lowerCase(StringUtils.trim(pipelineId)); MicroPipeline pipeline = this.pipelines.get(id); if(pipeline != null) { pipeline.shutdown(); this.pipelines.remove(id); if(logger.isDebugEnabled()) logger.debug("pipeline shutdown[id="+pipelineId+"]"); } return pipelineId; } /** * Returns true if a {@link MicroPipeline} for the given identifier exists otherwise * it returns false * @param pipelineId * @return */ public boolean hasPipeline(final String pipelineId) { return this.pipelines.containsKey(StringUtils.lowerCase(StringUtils.trim(pipelineId))); } /** * Returns the number of registered {@link MicroPipeline} instances * @return */ public int getNumOfRegisteredPipelines() { return this.pipelines.size(); } /** * Returns a list of identifiers belonging to registered {@link MicroPipeline} * @return */ public Set<String> getPipelineIds() { return (this.pipelines != null && !this.pipelines.isEmpty()) ? this.pipelines.keySet() : Collections.<String>emptySet(); } /** * Returns the {@link MicroPipelineConfiguration} for all registered {@link MicroPipeline} instances * @return */ public Map<String, MicroPipelineConfiguration> getPipelineConfigurations() { Map<String, MicroPipelineConfiguration> cfgs = new HashMap<>(); for(final MicroPipeline pipeline : this.pipelines.values()) { cfgs.put(pipeline.getId(), pipeline.getConfiguration()); } return cfgs; } /** * Shuts down the manager by stopping all running {@link MicroPipeline} instances */ public void shutdown() { for(final String pipelineId : this.pipelines.keySet()) { final MicroPipeline pipeline = this.pipelines.get(pipelineId); try { pipeline.shutdown(); logger.info("pipeline shutdown[id="+pipelineId+"]"); } catch(Exception e) { logger.error("failed to shutdown pipeline [id="+pipelineId+"]. Reason: " + e.getMessage(), e); } } } /** * Returns the processing node identifier * @return */ public String getProcessingNodeId() { return processingNodeId; } }