All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.remote.internal.SubProcess Maven / Gradle / Ivy

The newest version!
package org.openqa.selenium.remote.internal;

import org.openqa.selenium.internal.ProcessUtils;
import org.openqa.selenium.WebDriverException;

import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Basic class for working with subprocesses. Methods are provided for
 * starting and stopping a subprocess. Also provides a mechanism for
 * detecting if a subprocess dies before it is explicitly stopped.
 *
 * @author [email protected] (Jason Leyba)
 */
public class SubProcess {

  private final Object lock = new Object();
  private final ProcessBuilder processBuilder;
  private final OutputStream outputStream;

  private ExecutorService executorService;
  private Process currentProcess;
  private Future outputWatcher;

  /**
   * Creates a new {@link SubProcess} that will ignore all output from any
   * spawned process.
   *
   * @param processBuilder Used to launch new processes.
   * @see SubProcess(ProcessBuilder, OutputStream)
   */
  public SubProcess(ProcessBuilder processBuilder) {
    this(processBuilder, nullOutputStream());
  }

  /**
   * Creates a new {@link SubProcess} that will redirect all output to the
   * specified stream. The output written to stderr is always merged with the
   * output to stdout.
   *
   * @param processBuilder Used to launch new processes.
   * @param outputStream The stream to redirect all process output to.
   */
  public SubProcess(ProcessBuilder processBuilder,
                    OutputStream outputStream) {
    this.processBuilder = processBuilder.redirectErrorStream(true);
    this.outputStream = outputStream;
    this.executorService = null;
    this.currentProcess = null;
  }

  /**
   * Starts a new {@link Process} using this instance's {@link ProcessBuilder}.
   * If a {@code Process} is already running, this method will be a no-op.
   *
   * @throws WebDriverException If an I/O error occurs while starting the new
   *     process.
   */
  public void launch() {
    synchronized (lock) {
      if (!isRunning()) {
        try {
          currentProcess = processBuilder.start();
          executorService = Executors.newSingleThreadExecutor();
          outputWatcher = executorService.submit(
              new OutputWatcher(currentProcess.getInputStream(), outputStream));
        } catch (IOException e) {
          throw new WebDriverException(e);
        }
      }
    }
  }

  /**
   * 
   * @return The exit value for the managed subprocess.
   * @throws IllegalThreadStateException If the managed subprocess was never
   *     started, or if it was started but has not terminated yet.
   */
  public int exitValue() {
    synchronized (lock) {
      if (currentProcess == null) {
        throw new IllegalThreadStateException("Process has not yet launched");
      }
      return currentProcess.exitValue();
    }
  }

  /**
   * @return Whether the managed subprocess is currently running.
   */
  public boolean isRunning() {
    synchronized (lock) {
      if (currentProcess == null) {
        return false;
      }
      try {
        exitValue();
        return false;
      } catch (IllegalThreadStateException ignored) {
        return true;
      }
    }
  }

  /**
   * Shutsdown the {@link Process} currently being managed by this instance,
   * if any.
   *
   * @see Process#destroy()
   */
  public void shutdown() {
    synchronized (lock) {
      if (isRunning()) {
        outputWatcher.cancel(true);
        executorService.shutdownNow();
        ProcessUtils.killProcess(currentProcess);
      }
    }
  }

  private static OutputStream nullOutputStream() {
    return new OutputStream() {
      @Override
      public void write(int i) throws IOException {
        // Do nothing
      }
    };
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy