[Isolate-interest] RE: JNI, RMI and Processes

Curt Cox ccox at tripos.com
Wed Mar 16 12:54:49 EST 2005


Someone has expressed interest in the details of the technique described
here:
http://altair.cs.oswego.edu/pipermail/isolate-interest/2004-February/000087.
html

I'm posting to the list just in case more people are interested.  The
details are all pretty straightforward.

The JNI over RMI stuff is a tiny, tiny part of a huge desktop application.
I developed it rather than trying to fix core-dumps in a sea of code that
I'm not familiar with.  To date, it has only been used for debugging
(determining which native library is killing the JVM) and as a way to allow
continued development in the face of bugs that would otherwise prevent it.
Those bugs have always been fixed before deployment.

How has it evolved?
- The JNI and native libraries predated my involvement.  Each native library
was wrapped by a single class.
- I started adding a layer of indirection via interfaces.
Before:
public class Foo {
    public native double bar(double d);
}

After:
public interface Foo {
    public static final LOCAL   = new LocalFoo();
    public static final REMOTE  = new RemoteFoo();
    // local vs. remote is now a runtime decision ... 
    public static final DEFAULT = Boolean.getBoolean("Foo.remote")
        ? REMOTE : LOCAL;
    public double bar(double d);
}
So, clients now use Foo.DEFAULT, instead of creating a new Foo.
- I wrote an extremely minimal JVM launcher using Runtime.exec().  The
system properties "java.home", "java.class.path", and "java.library.path"
are crucial for this.
- I wrote a "KillSwitch" class.  The first thing a sacrificial JVM does is
arm the kill switch.  If a predetermined amount of time passes without
anything touching the killswitch, the sacrificial JVM exits.  The basic idea
is to prevent useless zombie JVMs from hanging around when the main
application has exited.
- After doing this for a few native libraries, I started to get tired of
writing lots of boilerplate RMI methods.  I wrote some helper classes that
use dynamic proxies, so that I wouldn't need to write any more RMI code.  As
an added benefit, I now only have one stub to maintain.
http://java.sun.com/j2se/1.5.0/docs/guide/reflection/proxy.html

When we move to Tiger, that will drop to zero.
http://java.sun.com/j2se/1.5.0/docs/guide/rmi/relnotes.html

What are the current issues?
The stuff above all works very well for development.  The code to detect
whether a JVM has gone down, and create a new one if needed, is a bit
flakey, however.  The current scheme will attempt to create a new
sacrificial JVM, whenever there are problems invoking the current method.  A
better scheme would actively ensure the required sacrificial JVM was ready
to go ahead of time. 

Where do I see it going?
- Shared Stubs.  Using the shared stubs code in Sun's JNI book as a starting
point, I've written a library that allows access to native functions without
using JNI.  The code in the book, however, is now a hidden implementation
detail.  Instead, a factory class is given the name of a library and a Java
interface to use for accessing it.  I may not write any more code that uses
JNI directly. 
http://java.sun.com/docs/books/jni/html/stubs.html
- Isolates.  There isn't currently a reliable standard way for a JVM to
create other JVMs.  When isolates become available, they will be an obvious
implementation choice--if they provide the same protection against JVM
crashes.  
- Commons Launcher.  It might be worthwhile to use this.
http://jakarta.apache.org/commons/launcher/
- JMX.  As stated above, a better method needs to be found to ensure that a
live JVM is always ready to go.  I'm not sure if the answer is bare sockets,
JMX, or merely smarter use of RMI.
http://java.sun.com/j2se/1.5.0/docs/guide/jmx/overview/JMXoverviewTOC.html
Rendezvous.  For this to be really useful for desktop applications as more
than a development tool, the user shouldn't need to fiddle with the
configuration to take advantage of a fast compute server, idle peers, or
peers with otherwise unavailable libraries.
http://developer.apple.com/macosx/rendezvous/
http://jmdns.sourceforge.net/

There are also some nice fringe benefits to this technique.
- The user interface of the application can be deployed to a machine, even
if the native libraries haven't been ported to it.  The machine obviously
needs to have network access to another one that can run the native
libraries in order to use their functions.  Certain libraries just aren't
practical to port.  Lack of access to the underlying source code and
dependence on native APIs are two common reasons.
- The application can be trivially distributed to take advantage of a fast
compute server.  In order for this to be worthwhile, the execution time on
the compute server needs to be faster by more than the network overhead.
This is easily possible with lengthy calculations.  Moreover, there isn't
much cost to requesting the same calculation both locally and remotely.
Once one result is available, the other calculation can be cancelled.

Feel free to contact me if you have further questions.

- Curt





More information about the Isolate-interest mailing list