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