I have just released to public domain batch of few interesting exercises in process spawning (fork) in Java, JVM process discovery, inter process communication, MBeans, JMX, Jvmstat, Java Attach API and handling child process output.
https://bitbucket.org/espinosa/a-java-playground/src/321b1c2f1c04b30ba7810ea76b89101091a931cf/src/main/java/my/code/a003/process?at=master
Have you ever wonder how JDK tools like VisualVM , JPS and JConsole discover all local running applications? How they manage to get hold of that internal information about them?
Featured exercises
- Spawn children process, spawn another JVM application with ProcessBuilder - A011SpawnAnotherJvmRun.java.
- Demonstrate that spawned process outlives parent process. Child process is not stopped when parent process exits - A03SpawnChildProcessAndStopMain.java.
- Capture child process full output to a String and using it in a JUnit style test - A011SpawnAnotherJvmRun.java
- Call method on another JVM process using JMX. It is an ready to use, convenient way how to implement remote method invocation, alternative to RMI or hand written socket communication. Convenient especially because one does not have to deal with ports, namely handling situations when port is taken. - A06CallRemoteMethodViaJMX.java
- get own PID (process ID) for current application
- using Jvmstat way (A05GetOwnPidUsingJvmstat.java)
- using RumtimeJMXBean (A05GetOwnPidUsingRuntimeMXBean.java)
- spawn children process, spawn another JVM application, and get its PID.
- list all running JVM applications running on localhost, print their PIDs (process IDs) and other
characteristic, like JPS tool or VisualVM does.
- using Jvmstat way (A04ListLocalRunningJvmsUsingJvmstatMonitoredHost.java)
- using Attach API way (A042ListLocalRunningJvmsUsingAttachApi.java)
- Implement tee (unix command) and tail like functionality for child process output - StreamHandler.java
- Redirecting child process output to current process output - the Java 7 way - A014SpawnChildProcessAndRedirectItToStdout.java
- Load management agent and get access to target JVM process system properties - A06CallRemoteMethodViaJMX.java
- Load extra functionality in form of a Java class, PrintMessage POJO, into target application,
LoiteringApp, an already running unsuspected simple application, expose as JMXBean as remotely
callable method, and make it to do naughty things like print "Hello world!" to standard output on
JMX command, something it was not originally designed to do.
- A07InjectClassToUnsuspectedApplicationAndExposeItAsJmxBean.java
Unfinished
Get full command line for current and (external) target JVM process the same way VisualVM gets them. I'm nearly there, just unfinished due to lack of time. Simple sun.java.command system property provides only basic arguments like main class name and following arguments but not JVM attributes, like -D... or tuning -X: and -XX:See: A021PrintActualExecutedCommandUsingSystemProperty.java and A022PrintActualExecutedCommandUsingRuntimeMXBean.java.
Lessons learned
There is no generic pure Java way how to get PID of started child process. If it is non JVM process then you are out of luck.There is no straightforward way how to get PID of started child JVM process, however if it is a JVM application, one can use monitoring and attaching API from tools.jar distributed with JDK, but not JRE. tools.jar cannot be obtained from Maven, it has to come from the Java distribution.
There is no generic way how to kill non-java process. If it is a child process and you still hold reference to process, you can use Process#destroy() If it is not and the target process in not JVM, you are out of luck.
There is not easy straightforward way how to kill a process even if it is JVM application. There is no API call for that, be it Jvmstat or Attach or JMXBean. the only chance is to inject extra behaviour, using Java Agents, like MBean capable of shutting application down and capable of being operated remotely.
There is no easy, straightforward, way how to connect to a JVM application using JMX if you know the PID of the target application. To establish JMX connection PID has to be converted to Local Connector Address, then converted to JMXServiceURL. To get Local Connector Address from PID is tricky and involves loading management agent to target application (management-agent.jar) using Attach API. Agent then exposes
com.sun.management.jmxremote.localConnectorAddress
. A bit hackish but it is the official way recommended by Oracle (in Bugzilla).There was no easy way how to redirect child process output to current stdout prior Java 7.
In Java 6+ applications you don't have to set up JMXBean server, it is capable of discovery and response to client JMXBean calls. You can get a lot of runtime information this way. To expose more part of running JVM app you have to load Java Agent, like management-agent.jar. Yep, you are loading new behaviour to unsuspected application!
Stopping child process, even when you have Process reference to it, and using standard Process#destroy() can still fail if the spawned child process spawned process another process itself (that is grand-children process). This may be issues at least on some (Windows) platforms.
See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4770092.
Java Agents carry a great potential. You can instrument application to do things not originally supposed to do, not only using special startup parameters, like with AOP LTW (Load Time Weaving), but also for already running application, completely unsuspecting JVM process! What are the security implications?
Useful links
I would get nowhere if those guys has not shared their wisdom, (in random order) :- http://docs.oracle.com/javase/tutorial/jmx/remote/custom.html
- https://blogs.oracle.com/jmxetc/entry/how_to_retrieve_remote_jvm
- http://barecode.org/blog.php/establishing-jmx-connections-to-a
- http://www.pongasoft.com/blog/yan/entry/connecting_to_a_local_vm
- http://www.javaworld.com/community/node/470
- http://dhruba.name/2010/02/07/creation-dynamic-loading-and-instrumentation-with-javaagents/
- https://blogs.oracle.com/CoreJavaTechTips/entry/the_attach_api
- http://blog.42.nl/articles/connecting-to-a-jvm-programmatically/
- http://stackoverflow.com/questions/13958318/is-it-possible-to-get-the-command-used-to-launch-the-jvm-in-java
- http://stackoverflow.com/questions/7713177/which-api-does-javas-jps-tool-use-internally
- http://stackoverflow.com/a/7690178/1185845
- http://stackoverflow.com/questions/35842/how-can-a-java-program-get-its-own-process-id
- http://stackoverflow.com/questions/4750470 - How to get PID of process I've just started within java program?
VisualVM sources
- com.sun.tools.visualvm.application.jvm.Jvm#ApplicationSupport#createCurrentApplication()
- com.sun.tools.visualvm.jvmstat.application.JvmstatApplicationProvider#processNewApplicationsByPids()}
- com.sun.tools.visualvm.jvmstat.application.JvmstatApplicationProvider#registerJvmstatConnection()