
com.cinchapi.common.process.Processes Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of accent4j Show documentation
Show all versions of accent4j Show documentation
Accent4J is a suite of libraries, helpers and data structures that make Java programming idioms more fluent.
/*
* Copyright (c) 2013-2016 Cinchapi Inc.
*
* 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 com.cinchapi.common.process;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import com.cinchapi.common.base.CheckedExceptions;
import com.cinchapi.common.base.Platform;
import com.google.common.collect.Lists;
import com.google.common.io.CharStreams;
import com.google.common.util.concurrent.MoreExecutors;
/**
* Utility functions for safely handling {@link Process} objects.
*
* @author Jeff Nelson
*/
public class Processes {
/**
* Create a {@link ProcessBuilder} that, on the appropriate platforms,
* sources the standard interactive profile for the user (i.e.
* ~/.bash_profile).
*
* @param commands a string array containing the program and its arguments
* @return a {@link ProcessBuilder}
*/
public static ProcessBuilder getBuilder(String... commands) {
ProcessBuilder pb = new ProcessBuilder(commands);
if(!Platform.isWindows()) {
Map env = pb.environment();
env.put("BASH_ENV",
System.getProperty("user.home") + "/.bash_profile");
}
return pb;
}
/**
* Create a {@link ProcessBuilder} that, on the appropriate platforms,
* sources the standard interactive profile for the user (i.e.
* ~/.bash_profile) and supports the use of the pipe (|) redirection on
* platforms that allow it.
*
* @param commands a string array containing the program and its arguments
* @return a {@link ProcessBuilder}
*/
public static ProcessBuilder getBuilderWithPipeSupport(String... commands) {
if(!Platform.isWindows()) {
List listCommands = Lists
.newArrayListWithCapacity(commands.length + 2);
// Need to invoke a shell in which the commands can be run. That
// shell will properly interpret the pipe(|).
listCommands.add("/bin/sh");
listCommands.add("-c");
for (String command : commands) {
listCommands.add(command);
}
return getBuilder(listCommands.toArray(commands));
}
else {
return getBuilder(commands);
}
}
/**
* Return the pid of the current process.
*
* @return pid.
*/
public static String getCurrentPid() {
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
}
/**
* Get the stderr for {@code process}.
*
* @param process
* @return a collection of error lines
*/
public static List getStdErr(Process process) {
return readStream(process.getErrorStream());
}
/**
* Get the stdout for {@code process}.
*
* @param process
* @return a collection of output lines
*/
public static List getStdOut(Process process) {
return readStream(process.getInputStream());
}
/**
* Check if the process with the processId is running.
*
* @param pid Id for the input process.
* @return true if its running, false if not.
*/
public static boolean isPidRunning(String pid) {
Process process = null;
try {
if(Platform.isLinux() || Platform.isMacOsX()
|| Platform.isSolaris()) {
ProcessBuilder pb = getBuilderWithPipeSupport(
"ps aux | grep ");
process = pb.start();
}
else if(Platform.isWindows()) {
process = Runtime.getRuntime().exec(
"TASKLIST /fi \"PID eq " + pid + "\" /fo csv /nh");
}
else {
throw new UnsupportedOperationException(
"Cannot check pid on the underlying platform");
}
}
catch (IOException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
if(process != null) {
ProcessResult result = waitForSuccessfulCompletion(process);
for (String line : result.out()) {
if(line.contains(pid)) {
return true;
}
}
return false;
}
else {
return true;
}
}
/**
* Execute {@link Process#waitFor()} while reading everything from the
* {@code process}'s standard out and error to prevent the process from
* hanging.
*
* @param process the {@link Process} for which to wait
* @return a map containing all the {@link ProcessData} (e.g. exit code,
* stdout and stderr)
*/
public static ProcessResult waitFor(Process process) {
AtomicBoolean finished = new AtomicBoolean(false);
List stdout = Lists.newArrayList();
List stderr = Lists.newArrayList();
CountDownLatch latch = new CountDownLatch(2);
try {
// Asynchronously exhaust stdout so process doesn't hang
executor.execute(() -> {
try {
InputStreamReader reader = new InputStreamReader(
process.getInputStream());
while (!finished.get()) {
stdout.addAll(CharStreams.readLines(reader));
}
reader.close();
latch.countDown();
}
catch (IOException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
});
// Asynchronously exhaust stderr so process doesn't hang
executor.execute(() -> {
try {
InputStreamReader reader = new InputStreamReader(
process.getErrorStream());
while (!finished.get()) {
stderr.addAll(CharStreams.readLines(reader));
}
reader.close();
latch.countDown();
}
catch (IOException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
});
int code = process.waitFor();
finished.set(true);
latch.await();
return new ProcessResult(code, stdout, stderr);
}
catch (InterruptedException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
}
/**
* Similar to {@link Process#waitFor()} but will throw a
* {@link RuntimeException} if the process does not have an exit code of
* {@code 0}.
*
* @param process
* @return the {@link ProcessResult}
*/
public static ProcessResult waitForSuccessfulCompletion(Process process) {
ProcessResult result = waitFor(process);
if(result.exitCode() != 0) {
List msg = result.out().isEmpty() ? result.err()
: result.out();
throw new RuntimeException(msg.toString());
}
return result;
}
/**
* Read an input stream.
*
* @param stream
* @return the lines in the stream
*/
private static List readStream(InputStream stream) {
try (BufferedReader out = new BufferedReader(
new InputStreamReader(stream))) {
String line;
List output = Lists.newArrayList();
while ((line = out.readLine()) != null) {
output.add(line);
}
return output;
}
catch (Exception e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
}
/**
* An {@link Executor} that is used to asynchronously read input from a
* processe's standard out and error streams.
*/
private static final ExecutorService executor = MoreExecutors
.getExitingExecutorService(
(ThreadPoolExecutor) Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors() * 2));
private Processes() {} /* no-op */
/**
* The result of a {@link Process} that has been
* {@link Processes#waitFor(Process) waited on}.
*
* @author Jeff Nelson
*/
public static final class ProcessResult {
private final int exitCode;
private final List stderr;
private final List stdout;
/**
* Construct a new instance.
*
* @param exitCode
* @param stdout
* @param stderr
*/
private ProcessResult(int exitCode, List stdout,
List stderr) {
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
}
/**
* Return the {@link Process process's} standard error.
*
* @return stderr
*/
public List err() {
return Collections.unmodifiableList(stderr);
}
/**
* Return the {@link Process process's} exit code.
*
* @return the exit code
*/
public int exitCode() {
return exitCode;
}
/**
* Return the {@link Process process's} standard output.
*
* @return stdout
*/
public List out() {
return Collections.unmodifiableList(stdout);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy