/* Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Mar 26, 2008 */ package com.bigdata.counters; import com.bigdata.util.DaemonThreadFactory; import org.apache.log4j.Logger; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * Command manages the execution and termination of a native process and an * object reading the output of that process. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class ActiveProcess { static protected final Logger log = Logger.getLogger(ActiveProcess.class); /** * Used to read {@link #is} and aggregate the performance data reported * by the {@link #process}. */ protected final ExecutorService readService = Executors .newSingleThreadExecutor(new DaemonThreadFactory(getClass() .getName() + ".readService")); protected Process process = null; protected InputStream is = null; protected volatile Future readerFuture; // This is used in tests protected ActiveProcess() { } /** * * @param command * The command to be executed. See * {@link ProcessBuilder#command(List)}. * @param collector */ public ActiveProcess(List<String> command, AbstractProcessCollector collector) { if (command == null) throw new IllegalArgumentException(); if (command.isEmpty()) throw new IllegalArgumentException(); if (collector == null) throw new IllegalArgumentException(); // log the command that will be run. { StringBuilder sb = new StringBuilder(); for (String s : command) { sb.append( s +" "); } if (log.isInfoEnabled()) log.info("command:\n" + sb); } try { ProcessBuilder tmp = new ProcessBuilder(command); collector.setEnvironment(tmp.environment()); process = tmp.start(); } catch (IOException e) { throw new RuntimeException(e); } /* * Note: Process is running but the reader on the process output has * not been started yet! */ } /** * Attaches the reader to the process, which was started by the ctor and * is already running. * * @param processReader * The reader. */ public void start(AbstractProcessReader processReader) { log.info(""); if (processReader == null) throw new IllegalArgumentException(); if (readerFuture != null) throw new IllegalStateException(); is = process.getInputStream(); assert is != null; /* * @todo restart processes if it dies before we shut it down, but no * more than some #of tries. if the process dies then we will not have * any data for this host. * * @todo this code for monitoring processes is a mess and should * probably be re-written from scratch. the processReader task * references the readerFuture via isAlive() but the readerFuture is not * even assigned until after we submit the processReader task, which * means that it can be running before the readerFuture is set! The * concrete implementations of ProcessReaderHelper all poll isAlive() * until the readerFuture becomes available. */ if (log.isInfoEnabled()) log.info("starting process reader: " + processReader); processReader.start(is); if (log.isInfoEnabled()) log.info("submitting process reader task: "+processReader); readerFuture = readService.submit(processReader); if (log.isInfoEnabled()) log.info("readerFuture: done="+readerFuture.isDone()); } /** * Stops the process */ public void stop() { if (readerFuture == null) { // not running. return; } // attempt to cancel the reader. readerFuture.cancel(true/* mayInterruptIfRunning */); // shutdown the thread running the reader. readService.shutdownNow(); if (process != null) { // destroy the running process. process.destroy(); process = null; is = null; } readerFuture = null; } /** * Return <code>true</code> unless the process is known to be dead. */ public boolean isAlive() { if(readerFuture==null || readerFuture.isDone() || process == null || is == null) { if (log.isInfoEnabled()) log.info("Not alive: readerFuture=" + readerFuture + (readerFuture != null ? "done=" + readerFuture.isDone() : "") + ", process=" + process + ", is=" + is); return false; } return true; } }