package codeine.utils.os_process; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import codeine.model.ExitStatus; import codeine.model.Result; import codeine.utils.MapUtils; import codeine.utils.ThreadUtils; import codeine.utils.os.OsUtils; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class ProcessExecuter { private static final Logger log = Logger.getLogger(ProcessExecuter.class); private List<String> cmd; private long timeoutInMinutes; private Function<String, Void> function; private String runFromDir; private Map<String, String> env; private String user; private boolean simpleCleanupOnly; private ProcessExecuter(List<String> cmd, List<String> cmdForOutput, long timeoutInMinutes, Function<String, Void> function, String runFromDir, Map<String, String> env, String user, boolean simpleCleanupOnly) { super(); this.cmd = cmd; this.timeoutInMinutes = timeoutInMinutes; this.function = function; this.runFromDir = runFromDir; this.env = env; this.user = user; this.simpleCleanupOnly = simpleCleanupOnly; } public Result execute() { log.debug("executing " + cmd); Process process = null; ProcessExecuterWorker worker = null; Result result = null; try { ProcessBuilder pb = new ProcessBuilder(cmd); pb.environment().putAll(env); pb.directory(new File(runFromDir)); pb.redirectErrorStream(true); process = pb.start(); worker = new ProcessExecuterWorker(process, function, cmd); worker.start(); long timeout = TimeUnit.MINUTES.toMillis(timeoutInMinutes); worker.join(timeout); if (worker.exitStatus() != null) { result = new Result(worker.exitStatus(), worker.output()); return result; } else { ThreadUtils.sleep(100); result = new Result(ExitStatus.TIMEOUT, worker.output() + "\n...Got timeout...\n"); return result; } } catch (IOException e) { log.warn("got IOException " + e.getMessage()); String output = null == worker ? "" : worker.output(); output += "\n...Got IOException...\n"; result = new Result(ExitStatus.IO_ERROR, output); return result; } catch (InterruptedException ex) { log.warn("got InterruptedException " + ex.getMessage()); worker.interrupt(); Thread.currentThread().interrupt(); throw new RuntimeException(ex); } finally { try { cleanup(process, result); } catch (RuntimeException e) { log.warn("failed in cleanup", e); } } } public void cleanup(Process process, Result result) { if (null == process) { return; } if (OsUtils.isLinux() && !simpleCleanupOnly && (result == null || result.exit() < 0)) { try { new LinuxProcessCleaner(process, user).cleanup(); } catch (RuntimeException e) { log.warn("failed in cleanup " + cmd + " " + e.getMessage()); } } process.destroy(); } public static class ProcessExecuterBuilder{ private String user; private boolean simpleCleanupOnly; private List<String> cmd; private List<String> cmdForOutput; private long timeoutInMinutes = 2; private String runFromDir; private Map<String, String> env = Maps.newHashMap(); @SuppressWarnings({ "unchecked", "rawtypes" }) private Function<String, Void> function = (Function)Functions.constant(null); public ProcessExecuterBuilder(List<String> cmd, String runFromDir) { this.cmd = cmd; this.cmdForOutput = cmd; this.runFromDir = runFromDir; } public ProcessExecuterBuilder(List<String> cmd) { this(cmd, "."); } public ProcessExecuter build(){ return new ProcessExecuter(cmd, cmdForOutput, timeoutInMinutes, function, runFromDir, MapUtils.noNullsMap(env), user, simpleCleanupOnly); } public ProcessExecuterBuilder cmd(List<String> cmd){ this.cmd = cmd; return this; } public ProcessExecuterBuilder user(String user){ this.user = user; return this; } public ProcessExecuterBuilder simpleCleanupOnly(boolean simpleCleanupOnly){ this.simpleCleanupOnly = simpleCleanupOnly; return this; } public ProcessExecuterBuilder env(Map<String, String> env){ this.env = env; return this; } public ProcessExecuterBuilder cmdForOutput(List<String> cmdForOutput){ this.cmdForOutput = cmdForOutput; return this; } public ProcessExecuterBuilder timeoutInMinutes(long timeoutInMinutes){ this.timeoutInMinutes = timeoutInMinutes; return this; } public ProcessExecuterBuilder function(Function<String, Void> function){ this.function = function; return this; } } public static Result execute(String cmd) { List<String> cmdList = Lists.newArrayList(Splitter.on(" ").omitEmptyStrings().split(cmd)); return new ProcessExecuterBuilder(cmdList).build().execute(); } public static String executeSuccess(String cmd) { Result r = execute(cmd); if (!r.success()) { throw new RuntimeException("fail with exit status " + r.exit() + " output: " + r.output()); } return r.output(); } public String executeSuccess() { Result r = execute(); if (!r.success()) { throw new RuntimeException("fail with exit status " + r.exit() + " output: " + r.output()); } return r.output(); } // public static void main(String[] args) { // ArrayList<String> cmd = Lists.newArrayList("/workarea/oshai/4_2_eclipse/workspace/codeintel/deployment/project/test_project/plugins/restart"); // Function<String, Void> f = new Function<String, Void>(){ // @Override // public Void apply(String input){ // System.out.println(input); // return null; // } // }; // new ProcessExecuter(cmd, cmd, 2, f).execute(); // } }