Friday 28 June 2013

How to get Process ID for any JVM application?

How to get Process ID for any JVM application? How to get PID for current process?

There is plenty of solutions on Stackoverflow, mostly involving operating system hacks [1], or libraries with native code [2] or ManagementFactory.getRuntimeMXBean() based solutions [3], limited to current process only.

I would like to introduce another solution, JConsole, and potentially jps and VisualVM uses. It is based on classes from sun.jvmstat.monitor.* package, from tool.jar.

Jvmstat solution

This simple program gets list of all running JVMs on localhost, with their PIDs, and then extracting their main their classes comparing them to given main class name, returning PID when they match.

import sun.jvmstat.monitor.HostIdentifier;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;


public class GetOwnPid {

    public static void main(String[] args) {
        new GetOwnPid().run();
    }

    public void run() {
        System.out.println(getPid(this.getClass()));
    }

    public Integer getPid(Class<?> mainClass) {
        MonitoredHost monitoredHost;
        Set<Integer> activeVmPids;
        try {
            monitoredHost = MonitoredHost.getMonitoredHost(new HostIdentifier((String) null));
            activeVmPids = monitoredHost.activeVms();
            MonitoredVm mvm = null;
            for (Integer vmPid : activeVmPids) {
                try {
                    mvm = monitoredHost.getMonitoredVm(new VmIdentifier(vmPid.toString()));
                    String mvmMainClass = MonitoredVmUtil.mainClass(mvm, true);
                    if (mainClass.getName().equals(mvmMainClass)) {
                        return vmPid;
                    }
                } finally {
                    if (mvm != null) {
                        mvm.detach();
                    }
                }
            }
        } catch (java.net.URISyntaxException e) {
            throw new InternalError(e.getMessage());
        } catch (MonitorException e) {
            throw new InternalError(e.getMessage());
        }
        return null;
    }
}

There are few catches:
  • The tool.jar is a library distributed with Oracle JDK but not JRE!
  • You cannot get tool.jar from Maven repo; configure it with Maven is a bit tricky, see [6].
  • The tool.jar probably contains platform dependent (native?) code so it is not easily distributable
  • It runs under assumption that all (local) running JVM apps are "monitorable". It looks like that from Java 6 all apps generally are (unless you actively configure opposite)
  • It probably works only for Java 6+
  • Eclipse does not publish main class, so you will not get Eclipse PID easily. It is probably because Eclipse is a JVM application, but is launched as eclipse.exe, a native executable somehow encapsulating JVM. You yould have to employ more sophisticated measures, like VisualVM does, to recognize Eclipse like checking characteristic JVM properties.

Comments on another questionable but popular solution

There is one more popular solution, mentioned for example here [4], and that is to use Process implementation class and get PID from its internal private data. 
Process process = Runtime.getRuntime().exec(command);
and then check returned implementation; allegedly on Unix it is java.lang.UnixProcess which has a private field called pid. But on Windows Process implementation returned is java.lang.ProcessImpl and it has no such field, no notion of PID. This is not a cross platform solution, besides relying on forcefully accessed private fields is always hackish and can stop working in the next Java release.

 Links

[1] http://www.jroller.com/santhosh/entry/get_current_java_process_id
[2] https://support.hyperic.com/display/SIGAR/Home
[3] http://stackoverflow.com/questions/35842/how-can-a-java-program-get-its-own-process-id
[4] http://stackoverflow.com/a/4750632/1185845
[6] http://stackoverflow.com/questions/3080437/jdk-tools-jar-as-maven-dependency  

Friday 14 June 2013

GWT + Maven + Eclipse - alternative project setup

Mission

Create alternative GWT project template ideal for multiple small applications, multiple EntryPoints, a playground project setup, where one click on EntryPoint class will launch browser will run that EntryPoint and only that EntryPoint.

Project sources

Goals

  • Developers friendly setup
  • Provide project template for test, playground and technical spike style projects.
  • Perfect for ultra short modify-execute development cycles. Change is reflected immediately and browser opens with the right content.
  • Focus only on Client Side code (Java to JS) - create environment perfect for simple testing of things like positioning, events, DOM manipulation, visual stuff,..
  • Maven only based setup. Rely only on Maven provided libraries. No GWT and Web project plugin dependencies. Preven library duplication often introduced when using combined Maven and GWT plugin.
  • Rely only on Code Server on-the-fly compilation from Java to JS. It is light fast this way! Changes to Java sources are reflected immediately. Avoid slow full Java to JS compilation, either by Maven or Eclipse GWT plugin, in build time.
  • Avoid need of Eclipse launchers. With currently necessary to add client source directories to classpath)
  • start GWT app as easily as possible and as quickly as possible.
  • support large number of GWT apps easily in one project. Support one application == one class == one click to run. Ideal for testing or demonstrating specific topics in isolation. Vanilla GWT setup is not very helpful in that.
  • Have more control on app start process. Be able to influence any part of the process, be able to debug Code Server or Http Server. Expose what is under the hood of individual components.
  • This setup is not intended for production code. Default setup created by Maven GWT plugin or Eclipse GWT is perfect for production indented project, stick with it.
  • No need to touch GWT module descriptor when adding new EntryPoint.

How to run it

  • Clone project from my Git repository.
  • Run usual mvn clean install.
    • Do it from command line or, if you have Eclipse Maven plugin e2m installed, there is a menu for it.
    • Ideally, you would need to run Maven build just once, on the beginning, to set up everything.
  • Write your entry EntryPoint class
    • It can be an entry to a bigger application or a mini application in one class
    • Write as many EntryPoints as you wish
    • Class must be placed in the client code package (currently my.code.client)
    • Check provided Sample1.java.
  • Write app runner class, a class with main method, and one line of code
    • Its a class with main() method making it directly runnable class in all IDE, not only Eclipse. It that main() method is one call; it will check if servers are running and opens browser on the right URL for you
    • It is not required, it is a recommended convenience
    • See Sample1Run.java. More or less a copy and paste.
  • Run HttpServer (RunHttpServe.java)
  • Run GWT CodeRerver (RunCodeServer.java)
    • If you forget to run any of the required servers you will be kindly reminded, detection mechanism is in place. Keep this server running between individual your EntryPoint application runs.
  • Run your application
    • your EntryPoint, ideally with the prepared runner. Runner conveniently opens browser for you on the right URL, like this one http://localhost:8080/my.code.client.Sample3.do?gwt.codesvr=localhost:9998
    • Your Entry Point class is immediately executed and you are presented with results in a new browser tab or window.
    • Edit your code, re-run your launcher, it should immediately reflect your changes.
    • Recompilation is fully transparent, courtesy of running Code Server, it is incremental, and is fast.
    • Edit, rerun, again, and again, and again..
    • And new EntryPoint. There is no need to touch GWT module descriptor (*.gwt.xml).

How to run it - alternative

  • Creating a runner class for your Entry Point class is not essential but convenient. You can run you app by entering URL directly, the format is:
    http://localhost:8080/[qualified entry class point class name].do?gwt.codesvr=localhost:9998 

Screenshot of the template in action. Note, no GWT plugin is installed, just plain Eclipse and Maven Eclipse plugin (M2E), all relevant directories are unfolded:
For comparison an original setup, note several apparent issues, like multiple version of GWT libraries and necessity to use launcher to add sources to classpath (highlighted items):
 

TODOs and leftovers

  • I am working on a YouTube show case video
  • I am considering to make the whole web app a project resource. That is everything there, html files, css files, etc, will end up on classpath.
    Benefits: any change to CSS would be immediately reflected in application; no need to call Maven. New servlet would be needed - ExposeResourceDirectoryServlet (based on ExposeDirectoryServlet)
  • Check Maven resource definition, perhaps too restrictive excludes/includes.
  • gwt-dev should not be in the classpath. It contains potentially conflicting classes, like Apache IOUtils. The whole library is mess! But where else I can get Code Server classes! (DevMode, ..)
  • Adding tools.jar is not without troubles. This library is not in any Maven repository, it is part of JDK installation. The only way how to make it Maven dependency is to use SystemPath dependency type, otherwise strongly discouraged. There are known issues with OSX systems having non-standard location and name of tools.jar, and openJDK allegedly have tools.jar already in the classpath. Classes from tools.jar are used to monitor if required servers are running.
  • Much of the set up infrastructure has not been widely tested. It is just on "works for me" basis. It has been tested on Windows and Linux, but not on OS X.
  • Code Server prints annoying useless (?) exception when browser is closed com.google.gwt.dev.shell.BrowserChannel$RemoteDeathError: Remote connection lost.
    I don’t know how to suppress this, but is is harmless.