/*
* Copyright (c) 2013-2017 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.concourse.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Map;
import com.cinchapi.concourse.annotate.UtilityClass;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
/**
* Utilities for dealing with {@link Process} objects.
*
* @author jnelson
*/
@UtilityClass
public final 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<String, String> 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<String> 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);
}
}
/**
* Get the stderr for {@code process}.
*
* @param process
* @return a collection of error lines
*/
public static List<String> getStdErr(Process process) {
return readStream(process.getErrorStream());
}
/**
* Get the stdout for {@code process}.
*
* @param process
* @return a collection of output lines
*/
public static List<String> 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 <pid>");
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) {
Throwables.propagate(e);
}
if(process != null) {
waitForSuccessfulCompletion(process);
List<String> lines = readStream(process.getInputStream());
for (String line : lines) {
if(line.contains(pid)) {
return true;
}
}
return false;
}
else {
return true;
}
}
/**
* 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
*/
public static void waitForSuccessfulCompletion(Process process) {
try {
int exitVal = process.waitFor();
if(exitVal != 0) {
throw new RuntimeException(getStdErr(process).toString());
}
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
/**
* Return the pid of the current process.
*
* @return pid.
*/
public static String getCurrentPid() {
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
}
/**
* Read an input stream.
*
* @param stream
* @return the lines in the stream
*/
private static List<String> readStream(InputStream stream) {
try {
BufferedReader out = new BufferedReader(
new InputStreamReader(stream));
String line;
List<String> output = Lists.newArrayList();
while ((line = out.readLine()) != null) {
output.add(line);
}
return output;
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
private Processes() {} /* no-op */
}