com.mdsh.test.media.encoding.process.AbstractProcess.java Source code

Java tutorial

Introduction

Here is the source code for com.mdsh.test.media.encoding.process.AbstractProcess.java

Source

/*
 * 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());
        }
    }

}