lohbihler.process.epoll.ProcessMonitor.java Source code

Java tutorial

Introduction

Here is the source code for lohbihler.process.epoll.ProcessMonitor.java

Source

/*
 * Copyright (c) 2017, Matthew Lohbihler
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package lohbihler.process.epoll;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ExecutorService;

import org.apache.commons.io.IOUtils;

/**
 * Synchronous process monitoring. The constructor blocks until the process
 * completes or times out.
 *
 * @author Matthew Lohbihler
 */
public class ProcessMonitor {
    private final String out;
    private final String err;

    public ProcessMonitor(final ProcessBuilder pb, final ExecutorService executorService, final long timeout)
            throws InterruptedException, IOException {
        this(pb.start(), executorService, timeout);
    }

    public ProcessMonitor(final Process process, final ExecutorService executorService, final long timeout)
            throws InterruptedException {
        final InputReader out = new InputReader(process.getInputStream());
        final InputReader err = new InputReader(process.getErrorStream());

        executorService.execute(out);
        executorService.execute(err);

        ProcessTimeout processTimeout = null;
        if (timeout > 0) {
            processTimeout = new ProcessTimeout(process, timeout);
            executorService.execute(processTimeout);
        }

        process.waitFor();
        out.join();
        err.join();
        process.destroy();

        // If we've made it this far, the process exited properly, so kill the
        // timeout thread if it exists.
        if (processTimeout != null)
            processTimeout.interrupt();

        this.out = out.getInput();
        this.err = err.getInput();
    }

    public String getOut() {
        return out;
    }

    public String getErr() {
        return err;
    }

    class InputReader implements Runnable {
        private final InputStreamReader reader;
        private final StringWriter writer = new StringWriter();
        private boolean done;

        InputReader(final InputStream is) {
            reader = new InputStreamReader(is);
        }

        public String getInput() {
            return writer.toString();
        }

        public void join() {
            synchronized (this) {
                if (!done) {
                    try {
                        wait();
                    } catch (final InterruptedException e) {
                        // no op
                    }
                }
            }
        }

        @Override
        public void run() {
            try {
                IOUtils.copy(reader, writer);
            } catch (final IOException e) {
                e.printStackTrace(new PrintWriter(writer));
            } finally {
                synchronized (this) {
                    done = true;
                    notifyAll();
                }
            }
        }
    }

    class ProcessTimeout implements Runnable {
        private final Process process;
        private final long timeout;
        private volatile boolean interrupted;

        ProcessTimeout(final Process process, final long timeout) {
            this.process = process;
            this.timeout = timeout;
        }

        public void interrupt() {
            synchronized (this) {
                interrupted = true;
                notifyAll();
            }
        }

        @Override
        public void run() {
            try {
                synchronized (this) {
                    wait(timeout);
                }

                if (!interrupted) {
                    // If the sleep time has expired, destroy the process.
                    process.destroy();
                }
            } catch (final InterruptedException e) {
                /* no op */
            }
        }
    }
}