//Dstl (c) Crown Copyright 2017 package uk.gov.dstl.baleen.core.pipelines; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Strings; import com.google.common.io.Files; import uk.gov.dstl.baleen.core.manager.AbstractBaleenComponent; import uk.gov.dstl.baleen.core.metrics.Metrics; import uk.gov.dstl.baleen.core.metrics.MetricsFactory; import uk.gov.dstl.baleen.core.utils.YamlConfiguration; import uk.gov.dstl.baleen.exceptions.BaleenException; /** * Manages one or more BaleenPipelines */ public class BaleenPipelineManager extends AbstractBaleenComponent{ private final ConcurrentMap<String, BaleenPipeline> pipelines = new ConcurrentHashMap<>(); private final ExecutorService es = Executors.newCachedThreadPool(); protected Metrics metrics; protected Logger logger; /** * Constructor */ public BaleenPipelineManager(){ this.metrics = MetricsFactory.getMetrics(BaleenPipelineManager.class); this.logger = LoggerFactory.getLogger(BaleenPipelineManager.class); } @Override public void configure(YamlConfiguration configuration) throws BaleenException { logger.debug("Configuring {} manager", getType()); List<Map<String, Object>> initial = configuration.getAsListOfMaps(getConfigurationKey()); int count = 0; for (Map<String, Object> p : initial) { count++; String name = (String) p.getOrDefault("name", getType() + count); String file = (String) p.get("file"); if (Strings.isNullOrEmpty(file)) { logger.warn("File name omited for {} {} - will be skipped", getType(), name); metrics.getCounter("errors").inc(); } else { try { logger.info("Attempting to create {} {}", getType(), name); create(name, new File(file)); } catch (Exception e) { logger.warn("Unable to create {} {} from file {} ", getType(), name, file, e); } } } logger.info("Configuration of {} manager completed", getType()); } @Override public void stop() throws BaleenException { logger.info("Stopping {} manager, and removing instances", getType()); try { pipelines.values().forEach(bop -> bop.destroy()); } finally { pipelines.clear(); } } /** * Get the number of pipelines */ public int getCount() { return pipelines.size(); } /** * Get the names of all pipelines */ public Set<String> getNames() { return Collections.unmodifiableSet(pipelines.keySet()); } /** * Get a given pipeline by name */ public Optional<BaleenPipeline> get(String name) { return Optional.ofNullable(pipelines.get(name)); } /** * Get all pipelines */ public Collection<BaleenPipeline> getAll() { return pipelines.values(); } /** * Returns true if a pipeline with the given name exists */ public boolean has(String name){ return pipelines.containsKey(name); } /** * Pause all pipelines */ public void pauseAll(){ pipelines.values().forEach(bop -> bop.pause()); } /** * Unpause all pipelines */ public void unpauseAll(){ pipelines.values().forEach(bop -> bop.unpause()); } /** * Create a new pipeline from the provided YAML, with the given name */ public BaleenPipeline create(String name, String yaml) throws BaleenException { if (pipelines.containsKey(name)) { throw new BaleenException("A " + getType() + " of that name already exists"); } logger.info("Creating {}", name); BaleenPipeline pipeline = toPipeline(name, yaml); pipelines.put(name, pipeline); es.submit(pipeline); metrics.getCounter("created").inc(); return pipeline; } /** * Create a new pipeline from the provided YAML, with the given name */ public BaleenPipeline create(String name, InputStream yaml) throws BaleenException { try { return create(name, IOUtils.toString(yaml)); } catch (IOException e) { throw new BaleenException(e); } } /** * Create a new pipeline from the provided YAML file, with the given name */ public BaleenPipeline create(String name, File file) throws BaleenException { try { return create(name, Files.toString(file, StandardCharsets.UTF_8)); } catch (IOException e) { throw new BaleenException(e); } } /** * Remove a pipeline */ public boolean remove(BaleenPipeline pipeline) { return remove(pipeline.getName()); } /** * Remove a pipeline by name */ public boolean remove(String name) { BaleenPipeline pipeline = pipelines.remove(name); if (pipeline != null) { logger.info("Removing {} {}", getType(), name); try{ pipeline.destroy(); }catch(Exception e){ logger.error("Error destroying {} {}", getType(), name, e); } metrics.getCounter("removed").inc(); return true; } else { logger.warn("Unable to find to remove {} {}", getType(), name); return false; } } /** * Provide the type of pipeline for use in logging and naming */ protected String getType(){ return "pipeline"; } /** * Provide the configuration key to get the list of pipeline files from */ protected String getConfigurationKey(){ return "pipelines"; } /** * Take the name and YAML and create a new pipeline. * * Provided so that sub-classes can override this to create, for example, a BaleenJob */ protected BaleenPipeline toPipeline(String name, String yaml) throws BaleenException{ PipelineBuilder pb = new PipelineBuilder(name, yaml); return pb.createNewPipeline(); } }