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

net.grinder.engine.agent.ProcessWorker Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2000 Paco Gomez
// Copyright (C) 2000 - 2011 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package net.grinder.engine.agent;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import net.grinder.common.UncheckedInterruptedException;
import net.grinder.common.processidentity.WorkerIdentity;
import net.grinder.engine.common.EngineException;
import net.grinder.util.StreamCopier;


/**
 * This class knows how to start a child process. It redirects the
 * child process standard output and error streams to our streams.
 *
 * @author Paco Gomez
 * @author Philip Aston
 * @see net.grinder.engine.process.WorkerProcessEntryPoint
 *
 */
final class ProcessWorker implements Worker {

  private final WorkerIdentity m_workerIdentity;
  private final Process m_process;
  private final Redirector m_stdoutRedirector;
  private final Redirector m_stderrRedirector;

  /**
   * Constructor.
   *
   * @param workerIdentity The process identity.
   * @param commandLine Command line arguments and working directory.
   * @param outputStream Output stream to which child process stdout
   * should be redirected. Will not be closed by this class.
   * @param errorStream Output stream to which child process stderr
   * should be redirected. Will not be closed by this class.
   * @throws EngineException If an error occurs.
   */
  public ProcessWorker(WorkerIdentity workerIdentity,
                       CommandLine commandLine,
                       OutputStream outputStream,
                       OutputStream errorStream)
    throws EngineException {

    m_workerIdentity = workerIdentity;

    final ProcessBuilder processBuilder =
      new ProcessBuilder(commandLine.getCommandList());

    processBuilder.directory(commandLine.getWorkingDirectory().getFile());

    try {
      m_process = processBuilder.start();
    }
    catch (IOException e) {
      UncheckedInterruptedException.ioException(e);
      throw new EngineException("Could not start process", e);
    }

    m_stdoutRedirector =
      new Redirector(m_process.getInputStream(),
                     outputStream,
                     m_process.toString());

    m_stderrRedirector =
      new Redirector(m_process.getErrorStream(),
                     errorStream,
                     m_process.toString());
  }

  /**
   * Return the worker name.
   *
   * @return The worker name.
   */
  public WorkerIdentity getIdentity() {
    return m_workerIdentity;
  }

  /**
   * Return an output stream connected to the input stream for the
   * child process.
   *
   * @return The stream.
   */
  public OutputStream getCommunicationStream() {
    return m_process.getOutputStream();
  }

  /**
   * Wait until the worker has completed. Return the exit status.
   *
   * @return See {@link net.grinder.engine.process.WorkerProcessEntryPoint} for
   * valid values.
   */
  public int waitFor() {
    try {
      m_process.waitFor();
    }
    catch (InterruptedException e) {
      throw new UncheckedInterruptedException(e);
    }
    finally {
      m_stdoutRedirector.stop();
      m_stderrRedirector.stop();
    }

    return m_process.exitValue();
  }

  /**
   * Destroy the worker.
   */
  public void destroy() {
    // Experimentation shows we can't interrupt threads blocked waiting
    // on a live process stream, nor can we close that stream.
    //m_stdoutRedirector.stop();
    //m_stderrRedirector.stop();

    // Calling destroy sometimes stoves W2K in such a way that some types
    // of new process can't be launched. (Including Java, Cygwin processes).
    // Replicated with: JRockit 1.4.2_05, and SUN JRE's 1.4.2_05 and 1.5.0_03.
    // A brief sleep appears to be a workaround.

    try {
      Thread.sleep(100);
    }
    catch (InterruptedException e) {
      throw new UncheckedInterruptedException(e);
    }

    m_process.destroy();
  }

  private static class Redirector {

    private final Thread m_thread;

    public Redirector(InputStream inputStream,
                      OutputStream outputStream,
                      String processName) {
      m_thread =
        new Thread(new StreamCopier(4096, false).getRunnable(inputStream,
                                                             outputStream),
                   "Stream redirector for process " + processName);
      m_thread.setDaemon(true);
      m_thread.start();
    }

    public void stop() {
      // We used to interrupt our thread, but that's a dumb idea since there
      // may be pending output to flush.

      while (m_thread.isAlive()) {
        try {
          m_thread.join();
        }
        catch (InterruptedException e) {
          throw new UncheckedInterruptedException(e);
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy