/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package edu.mit.csail.sdg.alloy4;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;
/** This provides a convenience wrapper around a Process object.
*
* <p> To launch a subprocess, simply write Subprocess.exec(timeout, args);
*
* <p><b>Thread Safety:</b> Safe.
*/
public final class Subprocess {
/** Timer used to schedule a timeout for the process. */
private static final Timer stopper = new Timer();
/** This field will store the program output (or "" if an error occurred) */
private String stdout = null;
/** This field will store an error message (or "" if no error occurred) */
private String stderr = null;
/** Constructor is private since we only allow the static method exec() to construct objects of this class. */
private Subprocess() { }
/** Executes the given command line, wait for its completion, then return its output.
* @param timeLimit - we will attempt to terminate the process after that many milliseconds have passed
* @param p - preconstructed Process object
*/
private static String exec (final long timeLimit, final Process p) throws Exception {
final Subprocess pro = new Subprocess();
final InputStream f1 = p.getInputStream(), f2 = p.getErrorStream();
TimerTask stoptask = new TimerTask() {
public void run() {
synchronized(pro) { if (pro.stdout!=null && pro.stderr!=null) return; pro.stdout=""; pro.stderr="Error: timeout"; }
p.destroy();
}
};
synchronized(Subprocess.class) { stopper.schedule(stoptask, timeLimit); }
new Thread(new Runnable() {
public void run() {
String err = null;
try { if (f2.read()>=0) err="Error: stderr"; } catch(Throwable ex) { err="Error: "+ex; }
synchronized(pro) { if (err!=null) {pro.stdout=""; pro.stderr=err;} else if (pro.stderr==null) pro.stderr=""; }
}
}).start();
p.getOutputStream().close();
StringBuilder output = new StringBuilder();
byte[] buf = new byte[8192];
while(true) {
int n = f1.read(buf);
if (n<0) break; else for(int i=0; i<n; i++) output.append((char)(buf[i]));
}
synchronized(pro) { if (pro.stdout==null) pro.stdout=output.toString(); }
for(int i=0; i<10; i++) {
synchronized(pro) { if (pro.stderr!=null) return pro.stderr + pro.stdout; }
Thread.sleep(500);
}
return "Error: wait timeout";
}
/** Executes the given command line, wait for its completion, then return its output.
* @param timeLimit - we will attempt to terminate the process after that many milliseconds have passed
* @param commandline - the command line
*/
public static String exec (final long timeLimit, final String[] commandline) {
Process p = null;
try {
p = Runtime.getRuntime().exec(commandline);
return exec(timeLimit, p);
} catch (Throwable ex) {
return "Error: "+ex;
} finally {
if (p!=null) p.destroy();
}
}
}