/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander 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; either version 3 of the License, or * (at your option) any later version. * * muCommander 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, see <http://www.gnu.org/licenses/>. */ package com.mucommander.process; import java.io.IOException; import java.io.InputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Used to monitor a process' stdout and stderr streams. * <p> * This class is used by {@link com.mucommander.process.AbstractProcess} to make sure that * processes do not stall because their stdout and stderr streams are not emptied. * </p> * <p> * This implementation is rather hackish, and should not be used directly: it works, but is not * meant to support anything but the very specific needs of {@link com.mucommander.process.AbstractProcess}. * </p> * @author Nicolas Rinaudo */ class ProcessOutputMonitor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(ProcessOutputMonitor.class); // - Instance fields ------------------------------------------------------- // ------------------------------------------------------------------------- /** Stream to read from. */ private InputStream in; private String encoding; /** Listener to notify of updates. */ private ProcessListener listener; /** Process to wait on once the stream is closed. */ private AbstractProcess process; /** Whether the process is still being monitored. */ private boolean monitor; // - Initialisation -------------------------------------------------------- // ------------------------------------------------------------------------- /** * Creates a news ProcessOutputMonitor that will read from <code>in</code> and notify <code>listener</code>. * @param in input stream to 'empty'. * @param listener where to send the content of the stream. */ public ProcessOutputMonitor(InputStream in, String encoding, ProcessListener listener) { this.listener = listener; this.in = in; monitor = true; this.encoding = encoding; } /** * Creates a news ProcessOutputMonitor that will read from <code>in</code> and notify <code>listener</code>. * <p> * A process monitor created that way will also wait on the specified <code>process</code> before quiting, * and notify the listener when the process has actually died. * </p> * @param in input stream to 'empty'. * @param listener where to send the content of the stream. * @param process process to wait on. */ public ProcessOutputMonitor(InputStream in, String encoding, ProcessListener listener, AbstractProcess process) { this(in, encoding, listener); this.process = process; } // - Main code ------------------------------------------------------------- // ------------------------------------------------------------------------- /** * Empties the content of the stream and notifies the listener. */ public void run() { byte[] buffer; // Where to store the stream's output. int read; // Number of bytes read in the last read operation. buffer = new byte[512]; // Reads the content of the stream. try { while(monitor && ((read = in.read(buffer, 0, buffer.length)) != -1)) { if(listener != null) { listener.processOutput(buffer, 0, read); if(encoding == null) listener.processOutput(new String(buffer, 0, read)); else listener.processOutput(new String(buffer, 0, read, encoding)); } } } // Ignore this exception: either there's nothing we can do about it anyway, // or it's 'normal' (the process has been killed). catch(IOException e) { LOGGER.debug("IOException thrown while monitoring process", e); } LOGGER.debug("Process output stream emptied, closing"); // Closes the stream. try { if(in != null) in.close(); } catch(IOException e) { LOGGER.debug("IOException thrown while closing process stream", e); } // If a process was set, perform 'cleanup' tasks. if(process != null) { // Waits for the process to die. try {process.waitFor();} catch(Exception e) { LOGGER.debug("Caught Exception while waiting for process "+process, e); } // If this process is still being monitored, notifies its // listener that it has exited. if(monitor && (listener != null)) listener.processDied(process.exitValue()); } } /** * Notifies the monitor that it should stop reading from the stream it's been affected to. * <p> * Calling this method will cause the process' stream to stop being read. For this reason, * it should only be called right before the process is killed, as it will otherwise stall. * </p> */ public void stopMonitoring() { // Closes the input stream. try {in.close();} catch(Exception e) {} // Notifies the main thread that it should stop monitoring the stream. in = null; monitor = false; } }