package org.nextprot.api.commons.utils; import com.google.common.base.Preconditions; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * A wrapper class around {@code ProcessBuilder} that execute an external command and expose standard and error outputs */ // Updated code coming from article http://alvinalexander.com/java/java-exec-processbuilder-process-1 public class SystemCommandExecutor { private final List<String> command; private ThreadedStreamHandler inputStreamHandler; private ThreadedStreamHandler errorStreamHandler; /** * Pass in the system command you want to run as a List of Strings, as shown here: * <p> * List<String> commands = new ArrayList<String>(); * commands.add("/sbin/ping"); * commands.add("-c"); * commands.add("5"); * commands.add("www.google.com"); * SystemCommandExecutor commandExecutor = new SystemCommandExecutor(commands); * commandExecutor.executeCommand(); * <p> * Note: I've removed the other constructor that was here to support executing * the sudo command. I'll add that back in when I get the sudo command * working to the point where it won't hang when the given password is * wrong. * * @param command The command you want to run. */ public SystemCommandExecutor(final List<String> command) { Objects.requireNonNull(command, "Cannot execute undefined command."); Preconditions.checkArgument(!command.isEmpty(), "Cannot execute empty command."); this.command = command; } public int executeCommand() throws IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder(command); Process process = pb.start(); InputStream inputStream = process.getInputStream(); InputStream errorStream = process.getErrorStream(); // these need to run as java threads to get the standard output and error from the command. List<Callable<String>> tasks = new ArrayList<>(); tasks.add(inputStreamHandler = new ThreadedStreamHandler(inputStream)); tasks.add(errorStreamHandler = new ThreadedStreamHandler(errorStream)); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.invokeAll(tasks); executor.shutdown(); executor.awaitTermination(10L, TimeUnit.SECONDS); return process.waitFor(); } /** * @return true if command has been executed else false */ public boolean hasBeenExecuted() { return inputStreamHandler != null; } /** * Get the standard output (stdout) from the command you just exec'd. */ public String getLastExecutionStandardOutput() { if (!hasBeenExecuted()) return "no command has been executed"; return inputStreamHandler.getOutputBuffer().toString(); } /** * Get the standard error (stderr) from the command you just exec'd. */ public String getLastExecutionStandardError() { if (!hasBeenExecuted()) return "no command has been executed"; return errorStreamHandler.getOutputBuffer().toString(); } private static class ThreadedStreamHandler implements Callable<String> { private final InputStream inputStream; private StringBuilder outputBuffer = new StringBuilder(); ThreadedStreamHandler(InputStream inputStream) { this.inputStream = inputStream; } @Override public String call() throws IOException { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { String line; while ((line = bufferedReader.readLine()) != null) { outputBuffer.append(line).append("\n"); } } return outputBuffer.toString(); } public StringBuilder getOutputBuffer() { return outputBuffer; } } }