Friday, October 16, 2009

Faster Development with JRebel

Update: In response to the request that I post the problem to forum at Zero Turnaround I was debugging the problem that I had using the intellij run/debug with JRebel buttons some more and found that it was caused by a problem with our code and hibernate. The short version is that there was a String property with getX/setX methods but someone had also added an isX method that returned false if it was null. This usually worked fine, but for some reason when running with JRebel hibernate would get confused and try to set the string value with the boolean from isX() which caused a buried IllegalArgumentException. With that fixed, JRebel works perfectly out of the box. I've changed the post to remove my workaround as it's not necessary any longer.

Why JRebel?
I'm going to walk through the steps I took to get JRebel working with maven and Intellij 8 since I ran into a few snags along the way. But first, I should explain why bother install it all. JRebel is a great tool that promises to increase your productivity in Java by enabling all sorts of HotSwaps not normally allowed by the JVM. While you are running your software in debug mode, you can add or remove methods or classes and JRebel will swap these changes into the JVM. No more restarts when you decide to add a new method. JRebel even support making Spring or SpringMVC configuration changes without a restart and it supports plugins so that similar functionality can be added for most tools in the java eco-system. Beyond the obvious time-savings you get from not having to wait for your server to restart, I find it also helps me because I don't have breaks in concentration while I'm waiting for the server to restart. Check out the video from ZeroTurnAround's website for a quick demonstration of how it works.



My Setup

I get the feeling that the sticking point for JRebel in terms of adoption is that it can be tricky to configure. They actually provide a really good series of how-tos for different setups but it's hard to cover all of the bases. Most developers at my company use this setup:


  • The build is defined in a Maven build file

  • We debug the application in jetty running it with the maven run-jetty goal

  • We write code in IntelliJ 8 using the maven plugin to generate and update the project file

  • Debugging is done by running the app from IntelliJ using the maven Run configuration



I tried a few different ways to make this work, and the following setup is what ended up working and being the easiest in terms of development. Much of IntelliJ setup and tips came from here.

Step 1: Install JRebel

Download JRebel from their website and install it where ever your prefer. I put mine in /usr/local/java/jrebel

Step 2: Install the IntelliJ Plugin



In IntelliJ go to Setting->Plugins, click on the Available tab and right click on the JRebel Plugin and install it. You'll have to restart IntelliJ after this is complete.

Step 3: Configure JRebel Plugin

Go to Settings->JRebel in IntelliJ and set JRebel location to where you installed JRebel (e.g., /usr/local/java/jrebel)

Step 4: Make sure IntelliJ and Maven's output directories are the same



You need to ensure that IntelliJ and Maven are using the same build directories, otherwise when IntelliJ compiles a changed class it may put into a different directory from where JRebel expects to find it. Go to Project Structure->Modules->Paths and ensure that the output path is the same as what Maven uses when it builds.

Step 5: Make sure that IntelliJ does not build a WAR

There's no need for IntelliJ to build a WAR file since jetty-run uses an exploded WAR directory. So building one each time that you compile will just slow things down. Go to Project Structure->Modules click on the Web facet and then and under Java EE Build Settings make sure that "Create Web facet war file" is not checked.

Step 6: Turn off IntelliJ's hotswapping



If you don't do this, then everytime that you make a change that the normal JVM cannot hotswap, you will be informed that you've made unsupported changes. It's also a waste of time for it to try and hotswap it. That's why you're installing JRebel. Go to Settings and search for "hotswap". Click "never" for "Reload Classes After Compiliation".

Step 8 (optional): Remap the Compile command

I found it helpful to remap the save key (Command-S) to the compile command. So now whenever I make changes all I have to do is hit Command-S and the changes I've made are compiled into the running instance.


So that's it. Now you can start your application in debug mode. You will see a JRebel startup message in the console immediately. And then you can start adding classes, deleting methods, and when you compile, JRebel will swap it all into the JVM for you... no wait required. It gets you a lot closer to the experience of developing in a dynamic language. Now, if we could only do something about that boilerplate...