/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed 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.
*/
package li.strolch.utils.helper;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class ProcessHelper {
private static final Logger logger = LoggerFactory.getLogger(ProcessHelper.class);
public static ProcessResult runCommand(String... commandAndArgs) {
return runCommand(null, commandAndArgs);
}
public static ProcessResult runCommand(File workingDirectory, String... commandAndArgs) {
return runCommand(30, TimeUnit.SECONDS, workingDirectory, commandAndArgs);
}
public static ProcessResult runCommand(long timeout, TimeUnit unit, String... commandAndArgs) {
return runCommand(timeout, unit, null, commandAndArgs);
}
public static ProcessResult runCommand(long timeout, TimeUnit unit, File workingDirectory,
String... commandAndArgs) {
if (workingDirectory != null && !workingDirectory.isDirectory()) {
String msg = "Working directory does not exist or is not a directory at {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, workingDirectory.getAbsolutePath());
throw new RuntimeException(msg);
}
if (commandAndArgs == null || commandAndArgs.length == 0)
throw new RuntimeException("No command passed!"); //$NON-NLS-1$
final StringBuffer sb = new StringBuffer();
sb.append("=====================================\n"); //$NON-NLS-1$
try {
ProcessBuilder pb = new ProcessBuilder(commandAndArgs);
pb.environment();
pb.directory(workingDirectory);
long start = System.nanoTime();
logger.info(MessageFormat.format("Starting command (Timeout {0} {1}) {2}", timeout, unit.name(),
Arrays.stream(commandAndArgs).collect(Collectors.joining(" "))));
final Process process = pb.start();
int[] returnValue = new int[1];
try (BufferedReader errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
BufferedReader inputStream = new BufferedReader(new InputStreamReader(process.getInputStream()));) {
Thread errorIn = new Thread(() -> readStream(sb, "[ERROR] ", errorStream), "errorIn");
errorIn.start();
Thread infoIn = new Thread(() -> readStream(sb, "[INFO] ", inputStream), "infoIn");
infoIn.start();
boolean ok = process.waitFor(timeout, unit);
if (!ok)
logger.error("Command failed to end before timeout or failed to execute.");
if (!process.isAlive()) {
returnValue[0] = process.exitValue();
} else {
logger.error("Forcibly destroying as still running...");
process.destroyForcibly();
process.waitFor(5, TimeUnit.SECONDS);
returnValue[0] = -1;
}
errorIn.join(100l);
infoIn.join(100l);
sb.append("=====================================\n"); //$NON-NLS-1$
}
logger.info("Command ended after " + StringHelper.formatNanoDuration(System.nanoTime() - start));
return new ProcessResult(returnValue[0], sb.toString(), null);
} catch (IOException e) {
throw new RuntimeException("Failed to perform command: " + e.getLocalizedMessage(), e); //$NON-NLS-1$
} catch (InterruptedException e) {
logger.error("Interrupted!"); //$NON-NLS-1$
sb.append("[FATAL] Interrupted"); //$NON-NLS-1$
return new ProcessResult(-1, sb.toString(), e);
}
}
public static class ProcessResult {
public final int returnValue;
public final String processOutput;
public final Throwable throwable;
public ProcessResult(int returnValue, String processOutput, Throwable t) {
this.returnValue = returnValue;
this.processOutput = processOutput;
this.throwable = t;
}
}
static void readStream(StringBuffer sb, String prefix, BufferedReader bufferedReader) {
String line;
try {
while ((line = bufferedReader.readLine()) != null) {
sb.append(prefix + line + StringHelper.NEW_LINE);
}
} catch (IOException e) {
String msg = "Faild to read from {0} stream: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, prefix, e.getMessage());
sb.append("[FATAL] "); //$NON-NLS-1$
sb.append(msg);
sb.append(StringHelper.NEW_LINE);
}
}
public static void openFile(File pdfPath) {
ProcessResult processResult;
if (SystemHelper.isLinux()) {
processResult = runCommand("xdg-open " + pdfPath.getAbsolutePath()); //$NON-NLS-1$
} else if (SystemHelper.isMacOS()) {
processResult = runCommand("open " + pdfPath.getAbsolutePath()); //$NON-NLS-1$
} else if (SystemHelper.isWindows()) {
// remove the first char (/) from the report path (/D:/temp.....)
String pdfFile = pdfPath.getAbsolutePath();
if (pdfFile.charAt(0) == '/')
pdfFile = pdfFile.substring(1);
processResult = runCommand("rundll32 url.dll,FileProtocolHandler " + pdfFile); //$NON-NLS-1$
} else {
String msg = MessageFormat.format("Unexpected OS: {0}", SystemHelper.osName); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
logProcessResult(processResult);
}
public static void logProcessResult(ProcessResult processResult) {
if (processResult.returnValue == 0) {
logger.info("Process executed successfully"); //$NON-NLS-1$
} else if (processResult.returnValue == -1) {
logger.error("Process execution failed:\n" + processResult.processOutput); //$NON-NLS-1$
logger.error(processResult.throwable.getMessage(), processResult.throwable);
} else {
String msg = "Process execution was not successful with return value:{0}\n{1}"; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, processResult.returnValue, processResult.processOutput));
}
}
}