// $Id: RunSystem.java,v 1.7 2007-09-04 15:55:38 tigran Exp $ package diskCacheV111.util ; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.PrintWriter; import java.io.StringWriter; public class RunSystem implements Runnable { private static final Logger _log = LoggerFactory.getLogger(RunSystem.class); private static final Runtime __runtime = Runtime.getRuntime() ; private final String[] _exec ; private final int _maxLines ; private final long _timeout ; private final Thread _readErrorThread ; private final Thread _readOutputThread ; private final Thread _processThread ; private final int _id = nextId() ; private Process _process ; private int _stoppedReader; private boolean _processDone; private boolean _linesExceeded; private boolean _interrupted; private BufferedReader _stdout; private BufferedReader _stderr; private final PrintWriter _errorPrintWriter ; private final PrintWriter _outputPrintWriter ; private final StringWriter _errorStringWriter ; private final StringWriter _outputStringWriter; private static int __counter = 100 ; private static synchronized int nextId(){ return __counter++ ; } public RunSystem(int maxLines, long timeout, String ... exec) { _exec = exec ; _maxLines = maxLines ; _timeout = timeout ; _readErrorThread = new Thread( this , "error" ) ; _readOutputThread = new Thread( this , "output" ) ; _processThread = new Thread( this , "process" ) ; _outputStringWriter = new StringWriter() ; _errorStringWriter = new StringWriter() ; _outputPrintWriter = new PrintWriter( _outputStringWriter ) ; _errorPrintWriter = new PrintWriter( _errorStringWriter ) ; } private void say(String str) { if (_log.isDebugEnabled()) { _log.debug("[" + _id + "] " + str); } } private void interruptReaders(){ _readOutputThread.interrupt() ; _readErrorThread.interrupt() ; } public void go() throws IOException { _process = __runtime.exec( _exec ) ; _stdout = new BufferedReader( new InputStreamReader( _process.getInputStream() ) ); _stderr = new BufferedReader( new InputStreamReader( _process.getErrorStream() ) ); /* * we do not need stdin of the process. To avoid file descriptor leaking close it. */ _process.getOutputStream().close(); synchronized( this ){ _readErrorThread.start() ; _readOutputThread.start() ; _processThread.start() ; try{ long end = System.currentTimeMillis() + _timeout ; while( ( _stoppedReader < 2 ) || ( ! _processDone ) ){ long rest = end - System.currentTimeMillis() ; if( rest <= 0 ) { break; } wait( rest ) ; say( "Master : Wait returned : "+statusPrintout() ) ; } }catch(InterruptedException ie ){ // say("Master : interrupted (interrupting the others) // interruptAll() ; if( ! _processDone ){ say( "Master : Destroying process" ) ; _process.destroy() ; } } say( "Master : Wait stopped : "+statusPrintout() ) ; // // now wait some time for a regular shutdown condition // ( we have to do it 2 times because, on a regular // job finish, the interruptAll had not been called. // for( int l = 0 ; l < 20000 ; l++ ){ say( "Master : Wait loop "+l+" started" ) ; try{ long end = System.currentTimeMillis() + 5 * 1000 ; while( ( _stoppedReader < 2 ) || ( ! _processDone ) ){ long rest = end - System.currentTimeMillis() ; if( rest <= 0 ) { break; } wait( rest ) ; say( "Master : Wait 2 returned : "+statusPrintout() ) ; } }catch(InterruptedException ie ){ say("Master : wait2 interrupted" ) ; } say( "Master : Wait2 loop : "+l+" : "+statusPrintout() ) ; if( ( _stoppedReader > 1 ) && _processDone ) { break; } if( ! _processDone ){ say( "Master : Wait 2 loop : Destroying process" ) ; _process.destroy() ; } if( ( l > 2 ) && ( _stoppedReader < 2 ) ){ say( "Master : Wait 2 loop : Interrupting readers" ) ; interruptReaders() ; } } } } private String statusPrintout(){ return ";interrupt="+_interrupted+ ";done="+_processDone+ ";count="+_stoppedReader ; } @Override public void run(){ if( Thread.currentThread() == _readErrorThread ){ runReader( _stderr , _errorPrintWriter ) ; }else if( Thread.currentThread() == _readOutputThread ){ runReader( _stdout , _outputPrintWriter ) ; }else if( Thread.currentThread() == _processThread ){ runProcess() ; } } public String getErrorString(){ return _errorStringWriter.getBuffer().toString() ; } public String getOutputString(){ return _outputStringWriter.getBuffer().toString() ; } public int getExitValue() throws IllegalThreadStateException { return _process.exitValue() ; } private void runProcess(){ try{ say( "Process : waitFor called" ) ; int rr = _process.waitFor() ; say( "Process : waitFor returned ="+rr+"; waiting for sync" ) ; }catch( InterruptedException ie ){ synchronized(this){ _interrupted = true ; say( "Process : waitFor was interrupted " ) ; } }finally{ synchronized(this){ _processDone = true ; say("Process : done" ) ; notifyAll() ; } } } private void runReader( BufferedReader in , PrintWriter out ){ int lines = 0 ; String line; try{ say( "Reader started" ) ; while( ( ! Thread.interrupted() ) && ( ( line = in.readLine() ) != null ) ){ if( (lines++) < _maxLines ) { out.println(line); } } }catch( InterruptedIOException iioe ){ say( "Reader interruptedIoException" ) ; }catch( Exception ioe ){ say( "Reader Exception : "+ioe ) ; }finally{ say( "Reader closing streams" ) ; try{ in.close() ; }catch(IOException e){} out.close() ; synchronized(this){ say("Reader finished" ) ; if( lines >= _maxLines ) { say("Lines (" + lines + ") have been truncated after " + _maxLines); } _stoppedReader++ ; notifyAll() ; } } } public static void main( String [] args ) throws Exception { if( args.length < 3 ){ System.err.println( "Usage : ... <systemClass> <maxLines> <timeout>" ) ; System.exit(4) ; } long timeout = Long.parseLong( args[2] ) * 1000 ; int maxLines = Integer.parseInt( args[1] ) ; RunSystem run = new RunSystem(maxLines, timeout, args[0] ) ; run.go() ; int rc = run.getExitValue() ; System.out.println("Exit Value : "+rc ) ; System.out.println("--------------- Output ------------" ) ; System.out.println( run.getOutputString() ) ; System.out.println("--------------- Error ------------" ) ; System.out.println( run.getErrorString() ) ; System.exit(rc) ; } }