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

gw.util.ProcessStarter Maven / Gradle / Ivy

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

package gw.util;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @deprecated use {@link gw.util.process.ProcessRunner} instead
 */
@Deprecated
public class ProcessStarter {

  @SuppressWarnings({"UnusedDeclaration"})
  public static final OutputHandler IGNORE = new NullOutputHandler();

  @SuppressWarnings("redundant")
  private static final String CONSOLE_NEWLINE = new String( "\n" ); //necessary for the == below, to distinguish between
                                                                     //constructed new lines and real newlines
  private final ProcessBuilder _pb;

  private boolean _withCMD = false;
  private boolean _inludeStdErrInOutput;
  private boolean _dontThrowOnNonZeroReturn;
  private final String _rawCmd;
  private Writer _stdOut;
  private Writer _stdErr;
  private String _charset = null;

  public ProcessStarter(String command) {
    _rawCmd = command;
    ArrayList args = new ArrayList();
    args.addAll( parseCommandLine( command ) );
    _pb = new ProcessBuilder(args);
  }

  private List parseCommandLine( String str )
  {
    List strs = new ArrayList();
    StringBuilder currentToken = new StringBuilder();

    boolean inString = false;
    char stringStart = '0';

    for( int i = 0; i < str.length(); i++ )
    {
      char c = str.charAt( i );

      if( Character.isWhitespace( c ) )
      {
        //keep whitespace in strings
        if( inString )
        {
          currentToken.append( c );
        }
        //If there is an existing token, store it and begin a new one
        else if( currentToken.length() > 0 )
        {
          strs.add( currentToken.toString() );
          currentToken.setLength( 0 );
        }
      }
      //support single or double quoted strings, each supporting the other unquoted within it
      else if( '\'' == c || '"' == c )
      {
        if( inString )
        {
          if( stringStart == c )
          {
            strs.add( currentToken.toString() );
            currentToken.setLength( 0 );
            inString = false;
          }
          else
          {
            currentToken.append( c );
          }
        }
        else
        {
          stringStart = c;
          inString = true;
        }
      }
      else if( '\\' == c )
      {
        if( inString )
        {
          if( i + 1 < str.length() )
          {
            //handle backslash escaping in string start characters only
            char nextC = str.charAt( i + 1 );
            if( nextC == stringStart )
            {
              currentToken.append( nextC );
              i++;
            }
            else
            {
              currentToken.append( c );
            }
          }
        }
        else
        {
          currentToken.append( c );
        }
      }
      else
      {
        currentToken.append( c );
      }
    }

    if( currentToken.length() > 0 )
    {
      strs.add( currentToken.toString() );
    }
    return strs;
  }

  /**
   * 

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) and returns all content sent to standard out as a string. If the command * finishes with a non zero return value, a {@link CommandFailedException} is thrown.

* *

Content sent to standard error by the command will be forwarded to standard error for this JVM.

* *

This method blocks on the execution of the command.

* *

* Example Usages: *

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

* * @return the content of standard out * @throws CommandFailedException if the process finishes with a non-zero return value */ public String exec() { Writer stdOut = _stdOut != null ? _stdOut : new StringWriter(); Writer stdErr = _stdErr != null ? _stdErr : new StdErrWriter( _inludeStdErrInOutput ? stdOut : new StringWriter() ); handleCommand( stdOut, stdErr ); flush( stdOut ); flush( stdErr ); return stdOut.toString(); } private void flush( Writer stdOut ) { try { stdOut.flush(); } catch( IOException e ) { throw new RuntimeException( e ); } } /** *

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) and pipes all data sent to this processes stdout, stderr, and stdin. If the command * finishes with a non zero return value, a {@link CommandFailedException} is thrown.

*

Stdout and Stderr from the sub-process will be piped to the current process' stdout and stderr. Any input in * this processes stdin will be piped to the sub-process' stdin * *

Content sent to standard error by the command will be forwarded to standard error for this JVM.

* *

This method blocks on the execution of the command.

* *

* Example Usages: *

   *   Shell.execWithPipe( "read \"are you there?\"" )
   * 
*

* * @throws CommandFailedException if the process finishes with a non-zero return value */ public void execWithPipe() { final Process process; try { process = startImpl(); } catch( IOException e ) { throw new RuntimeException( e ); } Thread outThread = new Thread(new StreamPipe(process.getInputStream(), System.out, false), "stdout"); Thread errThread = new Thread(new StreamPipe(process.getErrorStream(), System.err, false), "stderr"); StreamPipe inPipe = new StreamPipe(System.in, process.getOutputStream(), true); Thread inThread = new Thread(inPipe, "stdin"); // kick off the gobblers errThread.start(); outThread.start(); inThread.start(); try { int i = process.waitFor(); inPipe.setDone(); errThread.join(); outThread.join(); inThread.join(); if( i != 0 ) { String command = ""; for (String c : _pb.command()) { command += c + " "; } StringBuilder s = new StringBuilder().append( "The command \"" ).append( command ).append("\" failed with code " ).append( i ).append( "." ); throw new CommandFailedException( i, s.toString() ); } } catch (InterruptedException e) { //ignore } finally { // close all three streams. try { process.getErrorStream().close(); } catch ( IOException e) { e.printStackTrace(); } try { process.getInputStream().close(); } catch( IOException e ) { e.printStackTrace(); } try { process.getOutputStream().close(); } catch( IOException e ) { e.printStackTrace(); } } } private Process startImpl() throws IOException { if( OSPlatform.isWindows() && _withCMD ) { _pb.command().clear(); _pb.command().add( "CMD.EXE" ); _pb.command().add( "/C" ); _pb.command().add(_rawCmd); } return _pb.start(); } /** *

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) and calls the provided handler with the newly created process. * *

NOTE: In gosu, you should take advantage of the block-to-interface coercion provided and pass * blocks in as the handler. See the examples below.

*

* @param handler the process handler for this process. */ public void processWithHandler(ProcessHandler handler) { Process process = null; try { process = startImpl(); ShellProcess shellProcess = new ShellProcess(process); handler.run(shellProcess); try { process.exitValue(); } catch (IllegalThreadStateException e) { // Process not yet dead so kill process.destroy(); } } catch( IOException e ) { throw new RuntimeException( e ); } finally { if (process != null) { // close all three streams. try { process.getErrorStream().close(); } catch ( IOException e) { e.printStackTrace(); } try { process.getInputStream().close(); } catch( IOException e ) { e.printStackTrace(); } try { process.getOutputStream().close(); } catch( IOException e ) { e.printStackTrace(); } } } } /** * Starts a new process using the attributes of this process starter. * *

The new process will * invoke the command and arguments given by {@link ProcessBuilder#command()}, * in a working directory as given by {@link #getDirectory()}, * with a process environment as given by {@link #getEnvironment()}. * *

This method calls directly to {@link ProcessBuilder#start() ProcessBuilder.start}. * * @return A new {@link Process} object for managing the subprocess * * @throws NullPointerException * If an element of the command list is null * * @throws IndexOutOfBoundsException * If the command is an empty list (has size 0) * * @throws SecurityException * If a security manager exists and its * {@link SecurityManager#checkExec checkExec} * method doesn't allow creation of the subprocess * * @throws IOException * If an I/O error occurs * * @see Runtime#exec(String[], String[], java.io.File) * @see SecurityManager#checkExec(String) */ public Process start() throws IOException { return startImpl(); } /** * Returns a modifiable string map view of this process' environment. * * Whenever a process starter is created, the environment is * initialized to a copy of the current process environment (see * {@link System#getenv()}). Subprocesses subsequently started by * this object will use this map as their environment. * *

The returned object may be modified using ordinary {@link * java.util.Map Map} operations. These modifications will be * visible to subprocesses. Two ProcessStarter instances always * contain independent process environments, so changes to the * returned map will never be reflected in any other * ProcessStarter instance or the values returned by * {@link System#getenv System.getenv}. * * There are many system-dependant restrictions placed on the returned map. * See {@link ProcessBuilder ProcessBuilder} for more information * * @return This process environment * * @throws SecurityException * If a security manager exists and its * {@link SecurityManager#checkPermission checkPermission} * method doesn't allow access to the process environment * * @see ProcessBuilder * @see System#getenv() */ public Map getEnvironment() { return _pb.environment(); } /** * Returns this process' working directory. * * Subprocesses subsequently started by this object will use this as their working directory. * The returned value may be null -- this means to use * the working directory of the current Java process, usually the * directory named by the system property user.dir, * as the working directory of the child process.

* * @return This process's working directory */ public File getDirectory() { return _pb.directory(); } /** * Sets this process' working directory. * * Subprocesses subsequently started by this object will use this as their working directory. * The returned value may be null -- this means to use * the working directory of the current Java process, usually the * directory named by the system property user.dir, * as the working directory of the child process.

* * @param directory This process' working directory */ public void setDirectory(File directory) { _pb.directory(directory); } private void handleCommand( Writer stdOut, Writer stdErr ) { Process process; try { process = startImpl(); } catch( IOException e ) { throw new RuntimeException( e ); } 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 i = process.waitFor(); errorGobbler.join(); outputGobbler.join(); if( i != 0 ) { String command = ""; for (String c : _pb.command()) { command += c + " "; } StringBuilder s = new StringBuilder().append( "The command \"" ).append( command ).append("\" failed with code " ).append( i ).append( "." ); if( stdErr instanceof StdErrWriter ) { s.append( " StdErr was : \n").append("\n" ).append( indent( ((StdErrWriter)stdErr).getString() ) ); } if( !_dontThrowOnNonZeroReturn ) { throw new CommandFailedException( i, s.toString() ); } } } catch (InterruptedException e) { //ignore } finally { // close all three streams. try { process.getErrorStream().close(); } catch ( IOException e) { e.printStackTrace(); } try { process.getInputStream().close(); } catch( IOException e ) { e.printStackTrace(); } try { process.getOutputStream().close(); } catch( IOException e ) { e.printStackTrace(); } } } private static String indent( String string ) { String[] strings = string.split( System.getProperty( "line.separator" ) ); StringBuilder sb = new StringBuilder(); for( String s : strings ) { sb.append( " " ).append( s ); } return sb.toString(); } public ProcessStarter withCharset(String cs) { _charset = cs; return this; } private static class Gobbler extends Thread { private InputStream _streamToGobble; private Writer _buffer; private String _gobblerCharset = null; public Gobbler( InputStream streamToGobble, Writer buffer, String charSet ) { _streamToGobble = streamToGobble; _buffer = buffer; _gobblerCharset = charSet; } @Override public void run() { try { Reader inputStreamReader; if (_gobblerCharset == null) { inputStreamReader = StreamUtil.getInputStreamReader(_streamToGobble); } else { inputStreamReader = StreamUtil.getInputStreamReader(_streamToGobble, _gobblerCharset); } BufferedReader br = new BufferedReader(inputStreamReader); String line; while( (line = br.readLine()) != null ) { _buffer.append( line ).append( CONSOLE_NEWLINE ); } } catch (IOException ioe) { //ignore } } } private static class StdErrWriter extends Writer { Writer _str; public StdErrWriter( Writer stringWriter ) { _str = stringWriter; } public void write( char cbuf[], int off, int len ) throws IOException { System.err.print( new String( cbuf, off, len ) ); _str.write( cbuf, off, len ); } public void flush() throws IOException { _str.flush(); } public void close() throws IOException { _str.close(); } public String getString() { return _str.toString(); } } public interface OutputHandler { public void handleLine( String line ); } public interface ProcessHandler { public void run( ShellProcess proc ); } private static class HandlerWriter extends Writer { private final OutputHandler _handler; public HandlerWriter( OutputHandler handler ) { _handler = handler; } @Override public void write( String str, int off, int len ) throws IOException { //ignore this warning if( str != CONSOLE_NEWLINE ) { _handler.handleLine( str ); } } public void write( char cbuf[], int off, int len ) throws IOException { } public void flush() throws IOException { } public void close() throws IOException { } @Override public String toString() { return ""; //returns no output } } private static class StreamPipe implements Runnable { private OutputStream _outStream; private InputStream _inStream; private boolean _poll; private boolean _done = false; public StreamPipe(InputStream inStream, OutputStream outStream, boolean poll) { _inStream = inStream; _outStream = outStream; _poll = poll; } public void setDone() { _done = true; } public void run() { try { byte buf[] = new byte[4096]; BufferedInputStream bis = new BufferedInputStream(_inStream); while (!_done) { int avail = bis.available(); if (avail > 0) { if (avail > 4096) { avail = 4096; } int numRead = bis.read(buf, 0, avail); if (numRead == 0) { break; } try { _outStream.write(buf, 0, numRead); _outStream.flush(); } catch (IOException ex) { break; } } else if (!_poll) { int ch = bis.read(); if (ch == -1) { break; } try { _outStream.write(ch); _outStream.flush(); } catch (IOException ex) { break; } } else { try { Thread.sleep(100); } catch (InterruptedException e) { break; } } } } catch (IOException e) { throw new RuntimeException(e); } } } //================================================================================= // Builder methods //================================================================================= /** * The process built up will use CMD 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. */ public ProcessStarter withCMD() { _withCMD = true; return this; } /** * If called, this ProcessStarter will include the StdErr output in the return string of this * process. Note that this has no effect if {@link #withStdErrHandler(OutputHandler)} is called. * * @return this object for chaining */ public ProcessStarter includeStdErrInOutput() { _inludeStdErrInOutput = true; return this; } /** * If called, this ProcessStarter will not throw an exception if the underlying process exits with * a non-zero return code * * @return this object for chaining */ public ProcessStarter doNotThrowOnNonZeroReturnVal() { _dontThrowOnNonZeroReturn = true; return this; } /** * @param stdErrHandler handler that will be called with every line of output to stderr * * @return this object for chaining */ public ProcessStarter withStdErrHandler( OutputHandler stdErrHandler ) { _stdErr = new HandlerWriter( stdErrHandler ); return this; } /** * @param stdOutHandler handler that will be called with every line of output to stdout * * @return this object for chaining */ public ProcessStarter withStdOutHandler( OutputHandler stdOutHandler ) { _stdOut = new HandlerWriter( stdOutHandler ); return this; } public static class NullOutputHandler implements OutputHandler { public void handleLine( String line ) {} } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy