Java tutorial
/* * Copyright (c) 2016 Mark Himsley * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ package com.mdsh.test.media.encoding.process; import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; /** * @author mdsh * */ public abstract class AbstractProcess implements Closeable { /** * @return the log */ public abstract Logger getLogger(); private String executable; /** * @return */ public String getExecutable() { return this.executable; } /** * @param executable */ protected void setExecutable(final String executable) { this.executable = executable; } private final List<String> params = new ArrayList<String>(); /** * @return the params */ public List<String> getParams() { return this.params; } private Process process = null; /** * @return the process */ public Process getProcess() { return this.process; } /** * @param process the process to set */ public void setProcess(final Process process) { this.process = process; } /** * Internally creates the command line to execute and executes it. * @return the executing process * @throws IOException when the process could not be executed */ protected synchronized Process openProcess() throws IOException { if (null != getProcess()) throw new IOException("process is already created"); // create local copy of params and prepend the executable name final List<String> params = new ArrayList<String>(1 + getParams().size()); params.add(getExecutable()); params.addAll(getParams()); // log complete command line getLogger().info("execute command line: {}", paramsToString(params)); // create process Process process; try { process = Runtime.getRuntime().exec(params.toArray(new String[params.size()])); } catch (final IOException e) { final String error = "could not start process: " + getExecutable() + " - " + e.toString(); getLogger().warn(error, e); throw new IOException(error, e); } setProcess(process); return process; } /** * Waits for the given process to end. * <p>Warning: will throw an IOException if an InterruptedException * is caught. * @param process * @throws IOException when the process exit value is non-zero or when * the current thread is interrupted before the process has ended. */ protected void waitForProcess(final Process process) throws IOException { try { process.waitFor(); } catch (final InterruptedException e) { throw new IOException("interrupted while waiting for process: " + e.toString(), e); } final int ret = process.exitValue(); if (0 != ret) throw new IOException("precess exit value was: " + ret); } /** * Waits for the given thread to end. * <p>Note: InterruptedExceptions are caught and logged, * but otherwise ignored. * @param thread */ protected void waitForThread(final Thread thread) { try { thread.join(); } catch (final InterruptedException e) { getLogger().debug("waiting for thread was interrupted: {}", e.toString(), e); } } /** * Creates a thread to read from a Stream and send a ProcessLogEvent * for each line read from the stream. * @param is the InputStream to read from * @param handler the handler to send ProcessLogEvents to * @return */ protected Thread createProcessLogEventHandlerThread(final InputStream is, final ProcessLogEventHandler handler) { final BufferedReader reader = new BufferedReader(new InputStreamReader(is)); return Executors.defaultThreadFactory().newThread(new Runnable() { @Override public void run() { getLogger().trace("thread started"); readerToHandler(reader, handler); // alert that process has finished getLogger().trace("thread finished"); } }); } /** * Sends a ProcessLogEvent for each line read from the Reader. * @param reader the Reader to read from * @param handler the handler to send ProcessLogEvents to */ private void readerToHandler(final BufferedReader reader, final ProcessLogEventHandler handler) { String line; try { while (reader != null && (line = reader.readLine()) != null) { handler.handleEvent(new ProcessLogEvent(line)); } } catch (final Exception e) { getLogger().debug("could not read a line from stream - this is normally not a problem", e); } finally { IOUtils.closeQuietly(reader); } } protected String paramsToString(final List<String> params) { final StringBuilder sBuffer = new StringBuilder(params.size() * 2); for (final String string : params) { if (sBuffer.length() > 0) sBuffer.append(" "); sBuffer.append(string); } return sBuffer.toString(); } /** * Close the process. * <p>Warning: do not close the process before you've finished reading from * the processes OutputStream and ErrorStream. * @see java.io.Closeable#close() */ @Override public void close() throws IOException { if (null != getProcess()) closeProcess(); setProcess(null); } protected synchronized void closeProcess() { final Process process = getProcess(); if (null != process) { IOUtils.closeQuietly(process.getInputStream()); IOUtils.closeQuietly(process.getOutputStream()); IOUtils.closeQuietly(process.getErrorStream()); } } }