Getting PID in Java/Scala (cross-platform)
The Scala/Java environment has a certain lack of features regarding process management - it's in a very good shape, but sometimes we need more. It's totally understable considering that the underlaying system is the JVM (in most cases), however once in a while we have to leave the sandbox. That was the case I've faced, and it got me to the problem how to get the ID of the process I started in JVM for various operating systems.
The solution I've got is cross-platform, and it requires both Reflection and JNA. It has been verified in:
- macOS;
- Windows 10;
- Linux: both real (ArchLinux) and virtualized (WSL).
Without further ado, let me just share the code - it's compact and pretty much self-explanatory:
\src: GetPidExample.scala
#!/usr/bin/env scalas
/***
scalaVersion := "2.12.3"
libraryDependencies += "net.java.dev.jna" % "jna-platform" % "4.5.0"
*/
import com.sun.jna.platform.win32.Kernel32
import com.sun.jna.platform.win32.WinNT.HANDLE
import com.sun.jna.Pointer
import sys.process.Process
def getPrivateLongField(proc: Any, name: String): Long = {
val privateField = proc.getClass.getDeclaredField(name)
privateField.synchronized {
privateField.setAccessible(true)
try {
privateField.getLong(proc)
} finally {
privateField.setAccessible(false)
}
}
}
def pidJava(proc: Any): Long = {
proc match {
case unixProc: Any
if unixProc.getClass.getName == "java.lang.UNIXProcess" => {
getPrivateLongField(unixProc, "pid")
}
case windowsProc: Any
if windowsProc.getClass.getName == "java.lang.ProcessImpl" => {
val processHandle = getPrivateLongField(windowsProc, "handle")
val kernel = Kernel32.INSTANCE
val winHandle = new HANDLE()
winHandle.setPointer(Pointer.createConstant(processHandle))
kernel.GetProcessId(winHandle)
}
case _ => throw new RuntimeException(
"Cannot get PID of a " + proc.getClass.getName)
}
}
def pid(p: Process): Long = {
val procField = p.getClass.getDeclaredField("p")
procField.synchronized {
procField.setAccessible(true)
val proc = procField.get(p)
try {
pidJava(proc)
} finally {
procField.setAccessible(false)
}
}
}
val processName = "ping -c1 8.8.8.8"
val proc = Process(processName).run
val id = pid(proc)
println(s"Process '$processName' has PID: $id")
Scalateχ \src: GetPid.scalatex
Comments
Post a Comment