Launching without compiling, a main interface... a Mark Reinhold story
During jdk 1.1 development, there was obviously no consensus on test suites (i.e., JUnit), and the JavaSoft JCK tests required a ridiculous amount of html documentation for tracing to specs. Mark Reinhold, the jdk tech lead, refused to have his team write tests in JCK form, so instead he wrote a small harness with a compiling class loader: point the harness at a directory tree of .java files, and it would run them all, compiling as needed. The test interface was just a main function, and if it threw an exception, it failed. Pure and simple.
But probably the best magic trick for Java programmers is debugger hot reload. You write some empty methods, start the debugger, and then code iteratively, reloading as needed. (Set up the data and interfaces right, and change method bodies as you wish.) It's so much easier than recompile-println when you're just getting started, and after you've grown up and out, it's easier than rebuild/redeploy when you're working on a big system.
Hey, that's (close to) the traditional Smalltalk introduction-trick! And this has been available since 1.1? How does one concretely do that in Java and why is it not widely known?
1. There's no way to react to hot reload in the normal Java API, so you can't easily clear state.
2. The JDWP protocol lets debuggers redefine nearly anything, but HotSpot doesn't implement the full protocol. In practice many kinds of reload don't work.
3. To fix those issues app frameworks went with classloaders for partial app reloads instead, or reimplemented hotswap logic themselves (see JRebel).
There's no fundamental reason these can't be fixed, and in fact the Espresso JVM fixes both 1 and 2:
You can write JVM plugins that are invoked after redefinition occurs, allowing you (really your app framework) to do something sensible like partially reload state. And it implements the full protocol, so many more kinds of redefinition work.
Using it is easy. Just install Espresso as your JDK, point your build system at it, then debug your app. Recompiling what attached will cause hot reload.
Interesting, I've never heard of Espresso, I've always just used Jetbrains Runtime[1] instead which is the successor of DCEVM[2] in a way. As for plugins I used HotswapAgent[3] at times however I found it to be rather clunky, does Espresso improve on this aspect?
Well, it doesn't sit that well with mutable global/local state. There is a saying that hot reloading works better if you can replace at a smaller, atomic granularity. In the JVM, this kind of reload is at the method granularity, which may or may not be ideal.
But the JVM still has a few tricks up its sleeve, e.g. the class loader can dynamically re-load a newly compiled version of a class at runtime - jrebel is a proprietary extension that uses both tricks, but this latter can also be used by spring for hot swapping many parts of a running system.
Basically, in the JVM a class is unique within the class loader, so with a new class loader you can load as many class instances of the same, or slightly different class as you wish.
> But probably the best magic trick for Java programmers is debugger hot reload. You write some empty methods, start the debugger, and then code iteratively, reloading as needed. (Set up the data and interfaces right, and change method bodies as you wish.)
Do you have any example you can point to for this ?
During jdk 1.1 development, there was obviously no consensus on test suites (i.e., JUnit), and the JavaSoft JCK tests required a ridiculous amount of html documentation for tracing to specs. Mark Reinhold, the jdk tech lead, refused to have his team write tests in JCK form, so instead he wrote a small harness with a compiling class loader: point the harness at a directory tree of .java files, and it would run them all, compiling as needed. The test interface was just a main function, and if it threw an exception, it failed. Pure and simple.
But probably the best magic trick for Java programmers is debugger hot reload. You write some empty methods, start the debugger, and then code iteratively, reloading as needed. (Set up the data and interfaces right, and change method bodies as you wish.) It's so much easier than recompile-println when you're just getting started, and after you've grown up and out, it's easier than rebuild/redeploy when you're working on a big system.