Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Process.getAllProcess #80

Merged
merged 1 commit into from
Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package io.smallrye.common.os;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.function.Predicate;
import java.util.regex.Pattern;

final class GetAllProcessesInfoAction implements PrivilegedAction<List<ProcessInfo>> {

private final static Predicate<String> IS_NUMBER = Pattern.compile("\\d+").asPredicate();

@Override
public List<ProcessInfo> run() {
// The ProcessHandle API does not exist, so we have to rely on external processes to get this information
switch (OS.current()) {
case LINUX:
return readLinuxProcesses();
case MAC:
return readMacProcesses();
case WINDOWS:
return readWindowsProcesses();
default:
throw new UnsupportedOperationException(
"Listing all processes is not supported in JDK 8 in " + OS.current().name());
}
}

private List<ProcessInfo> readLinuxProcesses() {
List<ProcessInfo> processes = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get("/proc"))) {
for (Path procPath : stream) {
String name = procPath.getFileName().toString();
if (IS_NUMBER.test(name)) {
long pid = Long.parseLong(name);
try (BufferedReader reader = Files
.newBufferedReader(procPath.resolve("cmdline"), StandardCharsets.UTF_8)) {
String line = reader.readLine();
if (line != null) {
int idx = line.indexOf(0);
String cmdLine = idx == -1 ? line : line.substring(0, idx);
processes.add(new ProcessInfo(pid, cmdLine));
}
} catch (IOException ignored) {
// ignore case where process exits right before we read cmdline
}
gastaldi marked this conversation as resolved.
Show resolved Hide resolved
}
}
} catch (IOException ignored) {
// ignore
}
return processes;
}

private List<ProcessInfo> readMacProcesses() {
List<ProcessInfo> processes = new ArrayList<>();
java.lang.Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder("ps", "-ax", "-o", "pid=,comm=");
String thisCmd = String.join(" ", processBuilder.command());
process = processBuilder.start();
try (Scanner scanner = new Scanner(process.getInputStream())) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
int separator = line.indexOf(" ");
String cmd = line.substring(separator + 1);
if (!thisCmd.equals(cmd)) {
long pid = Long.parseLong(line.substring(0, separator));
processes.add(new ProcessInfo(pid, cmd));
}
}
}
} catch (IOException e) {
// ignored
} finally {
if (process != null) {
process.destroy();
}
}
return processes;
}

private List<ProcessInfo> readWindowsProcesses() {
List<ProcessInfo> processes = new ArrayList<>();
java.lang.Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder("tasklist.exe", "/fo", "csv", "/nh");
String thisCmd = String.join(" ", processBuilder.command());
process = processBuilder.start();
try (Scanner sc = new Scanner(process.getInputStream())) {
// Skip first line
if (sc.hasNextLine()) {
sc.nextLine();
}
while (sc.hasNextLine()) {
String line = sc.nextLine().trim();
String[] parts = line.split(",");
String cmdLine = parts[0].substring(1).replaceFirst(".$", "");
if (!thisCmd.equals(cmdLine)) {
long pid = Long.parseLong(parts[1].substring(1).replaceFirst(".$", ""));
processes.add(new ProcessInfo(pid, cmdLine));
}
}
}
} catch (IOException e) {
// ignored
} finally {
if (process != null) {
process.destroy();
}
}
return processes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@

/**
*/
final class GetProcessInfoAction implements PrivilegedAction<Object[]> {
final class GetProcessInfoAction implements PrivilegedAction<ProcessInfo> {
GetProcessInfoAction() {
}

public Object[] run() {
@Override
public ProcessInfo run() {
long pid = -1L;
String processName = "<unknown>";
final RuntimeMXBean runtime;
try {
runtime = ManagementFactory.getPlatformMXBean(RuntimeMXBean.class);
} catch (Exception ignored) {
return new Object[] { pid, processName };
return new ProcessInfo(pid, processName);
}
// TODO: on Java 9, use ProcessHandle.current().pid()
// Process ID
Expand Down Expand Up @@ -101,6 +102,6 @@ public Object[] run() {
if (processName == null) {
processName = "<unknown>";
}
return new Object[] { pid, processName };
return new ProcessInfo(pid, processName);
}
}
32 changes: 25 additions & 7 deletions os/src/main/java/io/smallrye/common/os/Process.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,18 @@

import static java.security.AccessController.doPrivileged;

import java.util.List;

/**
* Utilities for getting information about the current process.
*
* @author <a href="mailto:[email protected]">David M. Lloyd</a>
*/
public final class Process {
private static final long processId;
private static final String processName;
private static final ProcessInfo currentProcess;

static {
Object[] array = doPrivileged(new GetProcessInfoAction());
processId = (Long) array[0];
processName = (String) array[1];
currentProcess = doPrivileged(new GetProcessInfoAction());
}

private Process() {
Expand All @@ -44,7 +43,7 @@ private Process() {
* @return the process name (not {@code null})
*/
public static String getProcessName() {
return processName;
return currentProcess.getCommand();
}

/**
Expand All @@ -54,6 +53,25 @@ public static String getProcessName() {
* @return the ID of this process, or -1 if it cannot be determined
*/
public static long getProcessId() {
return processId;
return currentProcess.getId();
}

/**
* Returns information about the current process
*
* @return the current process
*/
public static ProcessInfo getCurrentProcess() {
return currentProcess;
}

/**
* Returns all the running processes.
*
* @return a list of all the running processes. May throw an exception if running on an unsupported JDK
* @throws UnsupportedOperationException if running on JDK 8
*/
public static List<ProcessInfo> getAllProcesses() {
return doPrivileged(new GetAllProcessesInfoAction());
}
}
22 changes: 22 additions & 0 deletions os/src/main/java/io/smallrye/common/os/ProcessInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.smallrye.common.os;

/**
* Returns information about a Process
*/
public class ProcessInfo {
private final long id;
private final String command;

public ProcessInfo(long id, String command) {
this.id = id;
this.command = command;
}

public long getId() {
return id;
}

public String getCommand() {
return command;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.smallrye.common.os;

import java.security.PrivilegedAction;
import java.util.List;
import java.util.stream.Collectors;

final class GetAllProcessesInfoAction implements PrivilegedAction<List<ProcessInfo>> {

@Override
public List<ProcessInfo> run() {
return ProcessHandle.allProcesses()
.map(processHandle -> new ProcessInfo(processHandle.pid(), processHandle.info().command().orElse(null)))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@

/**
*/
final class GetProcessInfoAction implements PrivilegedAction<Object[]> {
final class GetProcessInfoAction implements PrivilegedAction<ProcessInfo> {
GetProcessInfoAction() {
}

public Object[] run() {
public ProcessInfo run() {
final ProcessHandle processHandle = ProcessHandle.current();
final long pid = processHandle.pid();
String processName = System.getProperty("jboss.process.name");
Expand Down Expand Up @@ -82,6 +82,6 @@ public Object[] run() {
if (processName == null) {
processName = "<unknown>";
}
return new Object[] { pid, processName };
return new ProcessInfo(pid, processName);
}
}
7 changes: 7 additions & 0 deletions os/src/test/java/io/smallrye/common/os/ProcessTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.smallrye.common.os;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

Expand All @@ -11,6 +12,12 @@ class ProcessTest {
public void testProcessInfo() {
assertNotEquals(0L, Process.getProcessId());
assertNotNull(Process.getProcessName());
assertNotNull(Process.getCurrentProcess());
}

@Test
public void testAllProcessInfo() {
assertFalse(Process.getAllProcesses().isEmpty());
}

}