1. Why and when
What JRebel does is the reloading of classes. When you change something in your code, your changes get picked up
automatically and are instantly reflected in your application's behavior. But sometimes, merely reloading the
classes falls short of what really needs to be done to fully update the application – without restarting it.
That's because all the objects in the memory remain the same – they are not recreated. For most cases, this is a
desired thing (avoiding the system re-initialization penalty is what JRebel is all about!). But when you alter something
in your code that has to do with the way the application initializes (and that would results in memory objects being in
a different state after a normal application restart), those changes will of course not be reflected in your application
state, as the initialization code is not re-run.
This is exactly when creating a custom JRebel plugin could help you. With a plugin, you can monitor the reloading of
resources, and execute any desired custom code in reaction. Basically you'll use a JRebel integration plugin to say:
"After this certain kind of a reloading event occurs, go through these-and-these initialization steps to update my
application's state."
A typical scenario would be the following: your application is using a neat third-party framework. When the application
starts up, the framework initializes by loading its configuration from various resources (external XML files,
Java annotations, etc). Some of these resources change as you continue developing the application (e.g. you add a new
bean in your spring
applicationContext.xml and wire it with others).
Without an integration plugin explicitly telling what to do, these changes won't reflect in your application's
running state without a restart. But using an integration plugin, you could for example say:
"Hey, if you see that
I've changed something in applicationContext.xml, please be kind and perform a full reloading of all
my beans!".
As that kind of scenarios are common for all applications using some given framework, the plugins should usually be
created
per framework, not
per application!
2. Implementing a plugin
A JRebel plugin is nothing more than a small
.jar-file containing at least a class that implements
the
Plugin interface of the JRebel SDK, and a special manifest entry telling JRebel the name of that
class. It is highly recommended to start creating any plugin project from the provided
template.
The key API in plugin development is the JRebel SDK which provides means to communicate with the JRebel private APIs. Your
plugin code can also make use of whatever else available on the JVM you are running in. For example, if you are developing
a plugin for Struts2 framework (that will get activated only if Struts2 really
is available in the JVM), you can
safely import and use classes from Struts2 in your plugin (without providing them yourself).

The Plugin interface you need to implement looks like that:
public interface Plugin {
void preinit();
boolean checkDependencies(ClassLoader cl, ClassResourceSource crs);
String getId();
String getName();
String getDescription();
String getAuthor();
String getWebsite();
String getSupportedVersions();
String getTestedVersions();
} |
The most important methods here are:
preinit() – The main method. Here, set up the CBPs, listeners and everything else.
checkDependencies(ClassLoader cl, ClassResourceSource crs) –
Checks if this plugin should be loaded for this particular classloader. The safest way to do it is like it's
been done in the example plugin (by just checking whether a framework class that you rely on is available in
that classloader):
public boolean checkDependencies(ClassLoader classLoader,
ClassResourceSource classResourceSource)
{
return classResourceSource.getClassResource(
"org.zeroturnaround.jrebel.flagDemo.AbstractCanvas") != null;
} |
Refer to JRebel SDK API reference for additional details.
2.1. The demo application
To demonstrate working with JRebel plugins, we have created a very simple Swing desktop application (
get the source and look for the app under the
org.zeroturnaround.jrebel.flagDemo package).
It just draws three
Image objects on a
Canvas object, which is in turn placed inside
a
JFrame object and displayed. It looks like that:

.. and its class hierarchy looks like this:

As discussed above, a typical situation for writing a JRebel plugin is when you want to provide JRebel integration for a framework
maintained by a third party. Let's imagine that the class
AbstractCanvas is our third-party "framework" that we want
to provide better JRebel integration for. Therefore, we have a restriction that we can't directly edit its code (if we had full
control over the code in
AbstractCanvas, we could just add our modifications directly into it and problem solved).
Let's further imagine that
FlagsCanvas is our "application" that we are developing. Our goal is to build a plugin that
provides better JRebel support for hotloading any client code of the AbstractCanvas "framework" (i.e. while modifying
the
FlagsCanvas class, in our case).
Start off by download the demo app and making yourself familiar with it. To run it with JRebel, you need to edit the file
build.properties and make
jrebel.jar.path point to
jrebel.jar on your local machine. There
are three Ant targets that execute the application:
ant run – runs the app without JRebel
ant run-with-jrebel – runs the app with JRebel, but without the JRebel plugin
ant run-with-jrebel-and-plugin – runs the app with JRebel, and with our ready-made JRebel
integration plugin from lib/jr-plugin-template.jar
The code that controls where the flags get painted to is in these two methods of
FlagsCanvas.java:
private void init() {
estoniaX = 65;
estoniaY = 50;
switzerlandX = 400;
switzerlandY = 50;
}
public void paint(Graphics g) {
System.out.println(" Painting the canvas ..");
g.drawImage(switzerlandImg, switzerlandX, switzerlandY, this);
g.drawImage(estoniaImg, estoniaX, estoniaY, this);
g.drawImage(iranImg, 650, 50, this);
} |
Execute the app with JRebel but
without the plugin. The
paint() method gets called by Swing every
now-and-then when some event happen with the frame (it gains or looses focus, etc). Try modifying the coordinates of the
flags and see what happens (of course, something needs to re-build your
.class files after editing them..
it's best to configure your IDE to do that automatically whenever you save your source files). When you re-focus
to the demo-application,
paint() gets called and the Iranian flag should indeed move if you changed its
coordinates in the Java code. The other flags demonstrate now reaction whatsoever. That's because their locations are controlled
by the instance variables of the
FlagsCanvas class that get initialized by the
init() method when
a new instance of the class is created. So, although you might have changed their values in the
init() method,
the values in the memory remain the same as nobody is calling that method after the instance is already created.
2.2. Class bytecode processors (CBPs)
So the class reloading works, but this doesn't do the whole job here as the in-memory state of the application doesn't get
updated. Notice that all that we would need to do to fix that is make an additional call to
init() whenever
we call
paint(). The JRebel SDK fortunately provides suitable tools for the job. Whenever you need to modify the
behavior of third-party code before it runs in your JVM, you'll use the so-called
class bytecode processors (
CBPs),
extending the
JavassistClassBytecodeProcessor class. When set up properly, the CBPs intercept the initial class
loading in the JVM and load the third-party classes with our modifications already in place. Let's see what a CBP looks like:
public class ReloadAbstractCanvasStateCBP extends JavassistClassBytecodeProcessor {
public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception {
LoggerFactory.getInstance().echo("Patching the AbstactCanvas class ..");
try {
CtMethod paintMethod = ctClass.getDeclaredMethod("repaint");
paintMethod.insertBefore(
"org.zeroturnaround.javarebel.integration.pluginTemplate.DemoAppConfigReloader.reinitialize($0);"
);
} catch (NotFoundException e) {
LoggerFactory.getInstance().error(e);
}
}
} |
What this CBP does is it inserts an extra statement into the top of the
repaint() method
of the
AbstractCanvas class, transforming the control flow to a public static method implemented in our plugin. The current
instance is passed as an argument (
$0 stands for
this in Javassist code-strings).
The method
reinitialize() implemented in our plugin can now make arbitrary customizations and then hand the control back to
AbstractCanvas#paint(). Currently, it all it does is just calling the
init() method with Reflection API.
Notice that here it would actually made more sense to call the
init() method right away:
paintMethod.insertBefore("init()"); |
That approach might be reasonable when the customization you need to do is trivial. Anyhow, as things get any more complicated, forwarding the call
might be more comfortable than maintaining larger chunks of code inside Java strings.
The CBP also has to be registered in the plugin class for things to work:
public class PluginTemplate implements Plugin {
public void preinit() {
// Register the CBP
Integration i = IntegrationFactory.getInstance();
ClassLoader cl = PluginTemplate.class.getClassLoader();
i.addIntegrationProcessor(
cl,
"org.zeroturnaround.jrebel.flagDemo.AbstractCanvas",
new ReloadAbstractCanvasStateCBP()
);
// ..
}
// ..
} |
Integration is the class through which CBPs can be set up; instance of it is acquired through
IntegrationFactory ..
sounds simple enough.
A word of warning: be very careful with referencing the classes you are about to patch in the CBP code itself (or other code
that is called by the CBP). Remember: the CBP is all about intercepting a class
before it gets loaded. If your CBP
now tries to do something with the very same class, JVM would have to load it to do it. This would create a cyclic dependency, blowing
the thing!
2.3. Class reload listeners
If you are prepared to spend some time, I recommend you take the demo application and the plugin template, reduce the plugin to
what we have done so far (basically just comment out call of
registerListener() method in
preinit() method), re-build the plugin jar and
replace the old plugin with the new one in demo application's
./lib directory. If you are not, you'll just have to
believe me for now. Our AbstractCanvas-to-JRebel integration still has a small flaw. Namely, if you change the coordinates of
flags and recompile the code, repainting of the canvas happens when
Swing wants it to be repainted, not when
you
actually changed anything. (Try splitting your desktop between Eclipse and the application frame and editing the code in Eclipse
while you application is inactive. Whatever you do with the code, Swing won't repaint until you focus back to the demo app.)
It is obviously not a very big problem, as Swing wants to repaint things almost always when you actually do anything with the app,
but let's still try to fix that – at least for the educational purpose.
This is an ideal moment for introducing reload listeners. The JRebel SDK provides an API to register listeners that get notified
when JRebel reloads anything. By creating an implementor of the
ClassEventListener interface and registering it through
Reloader, we can implement custom reactions to class reload events. This is exactly what we'll do:
public void preinit() {
// ..
private void registerListener() {
// Set up the reload listener
ReloaderFactory.getInstance().addClassReloadListener(
new ClassEventListener() {
public void onClassEvent(int eventType, Class klass) {
try {
String cn = "org.zeroturnaround.jrebel.flagDemo.AbstractCanvas";
Class abstractCanvasClass = Class.forName(cn);
// Check if it is child of AbstractCanvas
if (abstractCanvasClass.isAssignableFrom(klass)) {
DemoAppConfigReloader.repaint();
LoggerFactory.getInstance().echo("Repainted the canvas");
}
} catch (Exception e) {
LoggerFactory.getInstance().error(e);
}
}
// ..
}
);
} |
What this chunk of code does is actually simple: if we see that a class has been reloaded, and it happens to be a subclass of
AbstractCanvas, we force the repainting of the canvas (without waiting for Swing to do it at some later time).
The repainting itself is implemented in method
DemoAppConfigReloader.repaint().
Now our AbsractCanvas JRebel integration is starting to look much better. Our plugin added the following functionality:
- If any subclass of
AbstractCanvas gets reloaded, we'll repaint the canvas.
- At any time we repaint, we'll also call the
init() method to update the application state.
We demonstrated some cornerstones of the JRebel SDK API:
- class bytecode processors (CBPs)
- class reload listeners
Some basic things can already be done with this knowledge, and you are of course encouraged to browse the rest of the API to make yourself familiar with the rest of its possibilites – to create better JRebel integration plugins!
3. Enabling a plugin
While playing around with the demo application, we used the ready-made Ant targets to enable the plugin. Anyhow, it would be nice to know
how to enable your plugins yourself.
All that the Ant target
run-with-jrebel-and-plugin actually does is it passes some additional JVM arguments to
java through
the command line. The full command to do the same thing directly from command line would be:
java -classpath ./bin -noverify -javaagent:/path/to/jrebel.jar
-Drebel.plugins=./lib/jr-plugin-template.jar
-Drebel.jr-plugin-template=true org.zeroturnaround.jrebel.flagDemo.SDKDemoApp
Everything else is as always, except for that two new parameters have appeared:
-Drebel.plugins=/path/to/plugin1,/path/to/plugin2 – a comma-separated list of paths to plugin jarfiless that
you want JRebel to load
-Drebel.name-of-your-plugin=true – turns the specified plugin on. The name has to match with whatever is returned
by the getId() method of your main plugin class.
4. Examples
4.1 Liferay plugin
The JRebel plugin for
Liferay is open sourced and can be used as an example.
The plugin includes monitoring resource files and re-running parts of the frameworks code when they change to update the metadata. This functionality can be found in
org.zeroturnaround.jrebel.liferay.LiferayReloader
A good example of making your plugin work with different versions of a framework is contained in
org.zeroturnaround.jrebel.liferay.cbp.PortletLocalServiceImplCBP