blob: ae0a224f037d9d3c6df9542feede0ccb0654e07b [file] [log] [blame]
/*
* 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.
*/
/*
* Adopted by John Xing for Nutch Project from
* http://blog.fivesight.com/prb/space/Call+an+External+Command+from+Java/,
* which explains the code in detail.
* [Original author is moving his site to http://mult.ifario.us/ -peb]
*
* Comments by John Xing on 20040621:
* (1) EDU.oswego.cs.dl.util.concurrent.* is in j2sdk 1.5 now.
* Modifications are needed if we move to j2sdk 1.5.
* (2) The original looks good, not much to change.
*
* This code is in the public domain and comes with no warranty.
*/
package org.apache.nutch.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InterruptedIOException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CommandRunner {
private boolean _waitForExit = true;
private String _command;
private int _timeout = 10;
private InputStream _stdin;
private OutputStream _stdout;
private OutputStream _stderr;
private static final int BUF = 4096;
private int _xit;
private Throwable _thrownError;
private CyclicBarrier _barrier;
public int getExitValue() {
return _xit;
}
public void setCommand(String s) {
_command = s;
}
public String getCommand() {
return _command;
}
public void setInputStream(InputStream is) {
_stdin = is;
}
public void setStdOutputStream(OutputStream os) {
_stdout = os;
}
public void setStdErrorStream(OutputStream os) {
_stderr = os;
}
public void evaluate() throws IOException {
this.exec();
}
/**
*
* @return process exit value (return code) or -1 if timed out.
* @throws IOException
*/
public int exec() throws IOException {
Process proc = Runtime.getRuntime().exec(_command);
_barrier = new CyclicBarrier(3 + ((_stdin != null) ? 1 : 0));
PullerThread so = new PullerThread("STDOUT", proc.getInputStream(), _stdout);
so.setDaemon(true);
so.start();
PullerThread se = new PullerThread("STDERR", proc.getErrorStream(), _stderr);
se.setDaemon(true);
se.start();
PusherThread si = null;
if (_stdin != null) {
si = new PusherThread("STDIN", _stdin, proc.getOutputStream());
si.setDaemon(true);
si.start();
}
boolean _timedout = false;
long end = System.currentTimeMillis() + _timeout * 1000;
try {
if (_timeout == 0) {
_barrier.await();
} else {
_barrier.await(_timeout, TimeUnit.SECONDS);
}
} catch (TimeoutException ex) {
_timedout = true;
} catch (BrokenBarrierException bbe) {
/* IGNORE */
} catch (InterruptedException e) {
/* IGNORE */
}
// tell the io threads we are finished
if (si != null) {
si.interrupt();
}
so.interrupt();
se.interrupt();
_xit = -1;
if (!_timedout) {
if (_waitForExit) {
do {
try {
Thread.sleep(1000);
_xit = proc.exitValue();
} catch (InterruptedException ie) {
if (Thread.interrupted()) {
break; // stop waiting on an interrupt for this thread
} else {
continue;
}
} catch (IllegalThreadStateException iltse) {
continue;
}
break;
} while (!(_timedout = (System.currentTimeMillis() > end)));
} else {
try {
_xit = proc.exitValue();
} catch (IllegalThreadStateException iltse) {
_timedout = true;
}
}
}
if (_waitForExit) {
proc.destroy();
}
return _xit;
}
public Throwable getThrownError() {
return _thrownError;
}
private class PumperThread extends Thread {
private OutputStream _os;
private InputStream _is;
private boolean _closeInput;
protected PumperThread(String name, InputStream is, OutputStream os,
boolean closeInput) {
super(name);
_is = is;
_os = os;
_closeInput = closeInput;
}
public void run() {
try {
byte[] buf = new byte[BUF];
int read = 0;
while (!isInterrupted() && (read = _is.read(buf)) != -1) {
if (read == 0)
continue;
_os.write(buf, 0, read);
_os.flush();
}
} catch (InterruptedIOException iioe) {
// ignored
} catch (Throwable t) {
_thrownError = t;
} finally {
try {
if (_closeInput) {
_is.close();
} else {
_os.close();
}
} catch (IOException ioe) {
/* IGNORE */
}
}
try {
_barrier.await();
} catch (InterruptedException ie) {
/* IGNORE */
} catch (BrokenBarrierException bbe) {
/* IGNORE */
}
}
}
private class PusherThread extends PumperThread {
PusherThread(String name, InputStream is, OutputStream os) {
super(name, is, os, false);
}
}
private class PullerThread extends PumperThread {
PullerThread(String name, InputStream is, OutputStream os) {
super(name, is, os, true);
}
}
public int getTimeout() {
return _timeout;
}
public void setTimeout(int timeout) {
_timeout = timeout;
}
public boolean getWaitForExit() {
return _waitForExit;
}
public void setWaitForExit(boolean waitForExit) {
_waitForExit = waitForExit;
}
public static void main(String[] args) throws Exception {
String commandPath = null;
String filePath = null;
int timeout = 10;
String usage = "Usage: CommandRunner [-timeout timeoutSecs] commandPath filePath";
if (args.length < 2) {
System.err.println(usage);
System.exit(-1);
}
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-timeout")) {
timeout = Integer.parseInt(args[++i]);
} else if (i != args.length - 2) {
System.err.println(usage);
System.exit(-1);
} else {
commandPath = args[i];
filePath = args[++i];
}
}
CommandRunner cr = new CommandRunner();
cr.setCommand(commandPath);
cr.setInputStream(new java.io.FileInputStream(filePath));
cr.setStdErrorStream(System.err);
cr.setStdOutputStream(System.out);
cr.setTimeout(timeout);
cr.evaluate();
System.err.println("output value: " + cr.getExitValue());
}
}