/** * 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()); } }