diff --git a/os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java b/os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java new file mode 100644 index 00000000..b2c20f48 --- /dev/null +++ b/os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java @@ -0,0 +1,114 @@ +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> { + + private final static Predicate IS_NUMBER = Pattern.compile("\\d+").asPredicate(); + + @Override + public List 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 readLinuxProcesses() { + List processes = new ArrayList<>(); + try (DirectoryStream 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 + } + } + } + } catch (IOException ignored) { + // ignore + } + return processes; + } + + private List readMacProcesses() { + List processes = new ArrayList<>(); + java.lang.Process process = null; + try { + process = new ProcessBuilder("ps", "-ax", "-o", "pid=,comm=").start(); + try (Scanner scanner = new Scanner(process.getInputStream())) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + int separator = line.indexOf(" "); + long pid = Long.parseLong(line.substring(0, separator)); + String cmd = line.substring(separator + 1); + processes.add(new ProcessInfo(pid, cmd)); + } + } + } catch (IOException e) { + // ignored + } finally { + if (process != null) { + process.destroy(); + } + } + return processes; + } + + private List readWindowsProcesses() { + List processes = new ArrayList<>(); + java.lang.Process process = null; + try { + process = new ProcessBuilder("tasklist.exe", "/fo", "csv", "/nh").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(".$", ""); + 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; + } +} diff --git a/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java b/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java index 0148b722..e74b08ae 100644 --- a/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java +++ b/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java @@ -31,6 +31,7 @@ final class GetProcessInfoAction implements PrivilegedAction { GetProcessInfoAction() { } + @Override public Object[] run() { long pid = -1L; String processName = ""; diff --git a/os/src/main/java/io/smallrye/common/os/Process.java b/os/src/main/java/io/smallrye/common/os/Process.java index b9b102ef..dab005e7 100644 --- a/os/src/main/java/io/smallrye/common/os/Process.java +++ b/os/src/main/java/io/smallrye/common/os/Process.java @@ -20,19 +20,19 @@ import static java.security.AccessController.doPrivileged; +import java.util.List; + /** * Utilities for getting information about the current process. * * @author David M. Lloyd */ 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 = new ProcessInfo((Long) array[0], (String) array[1]); } private Process() { @@ -44,7 +44,7 @@ private Process() { * @return the process name (not {@code null}) */ public static String getProcessName() { - return processName; + return currentProcess.getCommand(); } /** @@ -54,6 +54,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 getAllProcesses() { + return doPrivileged(new GetAllProcessesInfoAction()); } } diff --git a/os/src/main/java/io/smallrye/common/os/ProcessInfo.java b/os/src/main/java/io/smallrye/common/os/ProcessInfo.java new file mode 100644 index 00000000..fde7b6d0 --- /dev/null +++ b/os/src/main/java/io/smallrye/common/os/ProcessInfo.java @@ -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; + } +} diff --git a/os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java b/os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java new file mode 100644 index 00000000..d16de967 --- /dev/null +++ b/os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java @@ -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> { + + @Override + public List run() { + return ProcessHandle.allProcesses() + .map(processHandle -> new ProcessInfo(processHandle.pid(), processHandle.info().command().orElse(null))) + .collect(Collectors.toList()); + } +} diff --git a/os/src/test/java/io/smallrye/common/os/ProcessTest.java b/os/src/test/java/io/smallrye/common/os/ProcessTest.java index d3de07c0..c84571b5 100644 --- a/os/src/test/java/io/smallrye/common/os/ProcessTest.java +++ b/os/src/test/java/io/smallrye/common/os/ProcessTest.java @@ -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; @@ -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()); } }