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

gw.util.process.ProcessRunner Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Guidewire Software, Inc.
 */

package gw.util.process;

import gw.util.OSPlatform;
import gw.util.StreamUtil;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ProcessRunner {

  private final List _rawCmd = new ArrayList();

  private OutputBuffer _buffer = null;
  private Integer _exitCode = null;

  // properties set by builders:
  private boolean _withCMD;
  private File _workingDirectory;
  private Map _env = new HashMap();
  private boolean _echo;
  private String _input;
  private boolean _bufferStdOut;
  private boolean _bufferStdErr;
  private List _stdOutHandlers = new ArrayList();
  private List _stdErrHandlers = new ArrayList();
  private String _charset = "UTF-8";

  public static String execWithCharset(String charset, String... command) {
    return new ProcessRunner(command)
            .withStdOutBuffered()
            .withCharset(charset)
            .withCMD()
            .exec()
            .getBuffer();
  }

  public static String exec(String... command) {
    return new ProcessRunner(command)
            .withStdOutBuffered()
            .withCMD()
            .exec()
            .getBuffer();
  }

  public ProcessRunner(String... command) {
    this(Arrays.asList(command));
  }

  public ProcessRunner(List command) {
    for (String elt : command) {
      _rawCmd.add(elt);
    }
  }

  /**
   * 

Executes the given command as if it had been executed from the command line of the host OS * (cmd.exe on windows, /bin/sh on *nix). The resulting exit code and the output buffer are * accessible after this call, with {@link #getExitCode()} and {@link #getBuffer()}, respectively. * *

This method blocks on the execution of the command.

* *

* Example Usages: *

   *   var currentDir = new ProcessRunner("dir").exec() // windows
   *   var currentDir = new ProcessRunner("ls").exec() // *nix
   *   new ProcessRunner( "rm -rf " + directoryToNuke ).exec()
   * 
*

* * @return this object for chaining */ public ProcessRunner exec() { List command = new ArrayList(); if (OSPlatform.isWindows() && _withCMD) { command.add("CMD.EXE"); command.add("/C"); } command.addAll(_rawCmd); ProcessBuilder pb = new ProcessBuilder(command); if (_workingDirectory != null) { pb.directory(_workingDirectory); } for (Map.Entry entry : _env.entrySet()) { if (entry.getValue() != null) { pb.environment().put(entry.getKey(), entry.getValue()); } else { pb.environment().remove(entry.getKey()); } } ChainedOutputHandler stdOut = new ChainedOutputHandler(); ChainedOutputHandler stdErr = new ChainedOutputHandler(); if (_bufferStdOut || _bufferStdErr) { _buffer = new OutputBuffer(); if (_bufferStdOut) { stdOut.add(_buffer); } if (_bufferStdErr) { stdErr.add(_buffer); } } EchoOutputEmitter echo = null; if (_echo) { echo = new EchoOutputEmitter(getRawCmdStr(), System.out, System.err); stdOut.add(echo.getStdOutHandler()); stdErr.add(echo.getStdErrHandler()); } for (OutputHandler handler : _stdOutHandlers) { stdOut.add(handler); } for (OutputHandler handler : _stdErrHandlers) { stdErr.add(handler); } try { Process process = pb.start(); feedInput(process, _input, _charset); if (echo != null) { echo.processStarted(); } _exitCode = nomNomNom(process, stdOut.maybeReduce(), stdErr.maybeReduce(), _charset); if (echo != null) { echo.processFinished(); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { StreamUtil.close(stdOut, stdErr); } catch (IOException e) { e.printStackTrace(); } } return this; } private static void feedInput(Process process, String input, String charset) throws IOException { Writer inputEmitter = null; try { if (input != null) { inputEmitter = new OutputStreamWriter(process.getOutputStream(), charset); inputEmitter.write(input); } } finally { try { StreamUtil.close(inputEmitter); } catch (IOException e) { e.printStackTrace(); } } } private static Integer nomNomNom(Process process, OutputHandler stdOut, OutputHandler stdErr, String charset) { Gobbler outputGobbler = new Gobbler( process.getInputStream(), stdOut, charset ); Gobbler errorGobbler = new Gobbler( process.getErrorStream(), stdErr, charset ); // kick off the gobblers errorGobbler.start(); outputGobbler.start(); try { int exitCode = process.waitFor(); errorGobbler.join(); outputGobbler.join(); return exitCode; } catch (InterruptedException e) { //ignore } finally { try { StreamUtil.close(process.getErrorStream(), process.getInputStream()); } catch ( IOException e) { e.printStackTrace(); } } return null; } public String getRawCmdStr() { StringBuilder rawCmdStr = new StringBuilder(); for (String item : _rawCmd) { if (rawCmdStr.length() > 0) { rawCmdStr.append(' '); } if (item.contains(" ")) { rawCmdStr.append('"').append(item).append('"'); } else { rawCmdStr.append(item); } } return rawCmdStr.toString(); } //================================================================================= // Process results accessors //================================================================================= /** * Returns any output buffered from the process' stdout or stderr, depending on if * {@link #withStdOutBuffered()} and/or {@link #withStdErrBuffered()} were used. * * If a buffer was desired and the process printed nothing out, an empty string is * returned. * * @return the buffer, or null if nothing was to be buffered */ public String getBuffer() { return _buffer == null ? null : _buffer.toString(); } /** * Returns the process' exit code, if it finished. * * @return the exit code, or null if the process never completed */ public Integer getExitCode() { return _exitCode; } //================================================================================= // Builder methods //================================================================================= /** * Sets this process' working directory. * * @param dir this process' working directory * @return this object for chaining */ public ProcessRunner withWorkingDirectory(File dir) { _workingDirectory = dir; return this; } /** * Adds an argument to the command. * * @param arg the command line argument to add * @return this object for chaining */ public ProcessRunner withArg(String arg) { _rawCmd.add(arg); return this; } /** * Adds a name-value pair into this process' environment. * This can be called multiple times in a chain to set multiple * environment variables. * * @param name the variable name * @param value the variable value * @return this object for chaining * * @see ProcessBuilder * @see System#getenv() */ public ProcessRunner withEnvironmentVariable(String name, String value) { _env.put(name, value); return this; } /** * Sets the charset with which to write to this process' input and read its output. * * If unused, this process will by default use UTF-8. * * @param cs the charset to use * @return this object for chaining */ @SuppressWarnings({"UnusedDeclaration"}) public ProcessRunner withCharset(String cs) { _charset = cs; return this; } /** * Sets this process' stdout stream to be stored in the buffer accessible by {@link #getBuffer()}. * * @return this object for chaining */ public ProcessRunner withStdOutBuffered() { _bufferStdOut = true; return this; } /** * Sets this process' stdout stream to be stored in the buffer accessible by {@link #getBuffer()}. * * @return this object for chaining */ public ProcessRunner withStdErrBuffered() { _bufferStdErr = true; return this; } /** * Sets this process' output to be displayed the parent process' stdout and stderr. * * @return this object for chaining */ public ProcessRunner withEcho() { _echo = true; return this; } /** * Sets the text to be directed into this process' stdin. * * @param input the text to direct into stdin * @return this object for chaining */ public ProcessRunner input(String input) { _input = input; return this; } /** * The process built up will used CMD.EXE if this is a windows platform. This is necessary because on windows certain * basic commands such as "dir" are not programs, but rather are built into CMD. Thanks, Microsoft. * * @return this object for chaining */ @SuppressWarnings({"UnusedDeclaration"}) public ProcessRunner withCMD() { _withCMD = true; return this; } /** * Adds a block to handle lines output this process' stderr. * * This can be called multiple times in a chain to add multiple handlers. * * @param stdErrHandler handler that will be called with every line of output to stderr * @return this object for chaining */ public ProcessRunner withStdErrHandler( OutputHandler stdErrHandler ) { _stdErrHandlers.add(stdErrHandler); return this; } /** * Adds a block to handle lines output this process' stdout. * * This can be called multiple times in a chain to add multiple handlers. * * @param stdOutHandler handler that will be called with every line of output to stdout * @return this object for chaining */ public ProcessRunner withStdOutHandler( OutputHandler stdOutHandler ) { _stdOutHandlers.add(stdOutHandler); return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy