/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package bradswebdavclient.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Use this to execute a native script file, such as a bash script on linux or a batch file on windows
*
* The exec method blocks until the script has terminated. Success is determined by the script return code. Information
* returned by the script is buffered and will be added to an exception if thrown to support logging
*
* This class explicitly supports win32 batch files. If setWin32Batch(true) is called then the process argument is assumed to
* be a batch file and cmd.exe /c is inserted into the called command.
*
* @author mcevoyb
*
*/
public class ScriptExecutor {
final String process;
final String[] args;
final int successCode;
boolean win32Batch;
/**
*
* @param process - name and/or path of the script file. May be a win32 batch file
* @param args - array of string arguments to be set on the command line
* @param successCode - numeric code which will indicate successful completion. This is normally 0
*/
public ScriptExecutor(String process, String[] args, int successCode) {
this.process = process;
this.args = args;
this.successCode = successCode;
if( process.endsWith(".bat") ) setWin32Batch(true); // this is to help with running on windows workstations
}
public boolean isWin32Batch() {
return win32Batch;
}
/**
* Set this to true to force the class to format the command to execute cmd.exe /c process {args..}
*
* @param win32Batch
*/
public void setWin32Batch(boolean win32Batch) {
this.win32Batch = win32Batch;
}
/**
* Synchronously executes the script file. Once complete, the return code will be inspected for success
*
* @throws ScriptException -
*/
public void exec() throws ScriptException {
int cmdSize = args.length+1;
if( isWin32Batch() ) cmdSize+=2;
String[] cmd = new String[cmdSize];
int param=0;
if( isWin32Batch() ) {
cmd[param++] = "cmd.exe";
cmd[param++] = "/c";
}
cmd[param++] = process;
for( int i=0; i<args.length; i++ ) {
String s = args[i];
if( s == null ) s = "";
cmd[param++] = s;
}
for (int i = 0; i < cmd.length; i++) {
System.out.println("cmd: " + i + " = " + cmd[i]);
}
Runtime rt = Runtime.getRuntime();
try {
Process proc = rt.exec( cmd );
StreamDiscarder errorDiscarder = new StreamDiscarder( proc.getErrorStream() );
ScriptOutputReader output = new ScriptOutputReader( proc.getInputStream() );
errorDiscarder.start();
output.start();
int exitVal = proc.waitFor();
if ( exitVal != successCode ) {
throw new ScriptException(exitVal,output.toString());
}
output.join( 10000 ); // 10 sec at most!
errorDiscarder.join( 1000 ); // 1 more sec at most!
if ( output.isAlive() ) {
output.interrupt();
}
if ( errorDiscarder.isAlive() ) {
errorDiscarder.interrupt();
}
} catch ( IOException ioe ) {
throw new ScriptException(ioe);
} catch ( InterruptedException ie ) {
throw new ScriptException(ie);
}
}
private abstract static class StreamReader extends Thread {
private InputStream is;
protected abstract void processLine(String line);
public StreamReader( InputStream is ) {
this.is = is;
}
@Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader( isr );
String line = null;
while ( (line = br.readLine()) != null ) {
if ( interrupted() ) {
break;
}
processLine( line );
}
br.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
private static class StreamDiscarder extends StreamReader {
public StreamDiscarder( InputStream is ) {
super( is );
}
protected void processLine( String line ) {
}
}
private static class ScriptOutputReader extends StreamReader {
private StringBuffer sb = new StringBuffer();
private boolean found = false;
public ScriptOutputReader( InputStream is ) {
super( is );
}
protected void processLine( String cmdOut ) {
sb.append( cmdOut );
}
@Override
public String toString() {
return sb.toString();
}
}
}