JavaRebel plugin development
JavaRebel plugin development
This page is meant to serve as a FAQ/QuickStart to JavaRebel plugin development. Any comments/questions are welcome at the forum or via support@zeroturnaround.com.
Why do we need JavaRebel plugins?
JavaRebel is very good at reloading classes, but it doesn't reload your frameworks' configurations by default. This means that even though you make changes to your classes and JavaRebel reloads them, you won't see the change happen because the framework has caches that have not been invalidated.
JavaRebel plugins integrate with frameworks to tell them to reload configurations, empty caches, reinstantiate some instances, etc, so that you can see the changes you make to your classes. It's quite easy to set up, and there are many JavaRebel plugins out there already.
Can I see an example?
Sure. Lets talk about URL mappings. Many frameworks provide a way to map URLs to some methods in controllers. Lets say there is some naming convention going on. For example, every method starting with the word mapping is mapped to an URL.
So a method named mappingEditShip(...) will be invoked at one point when the URL /editShip is accessed.
The framework at initialization has scanned some predefined packages for this naming convention. It has cached all the values and when a request comes in at url YYY, it will check the cache for a method mappingYYY(...). Typically, it is not found because the classes were scanned once at startup and you've just now added the new method.
How can this be fixed?
JavaRebel OSS SDK provides ways to integrate with the framework. The most common way is bytecode postprocessing. Lets say the cache from the previous example is in a Map and there is some code that looks like this:
[java]
function getMappedMethod(String name) {
if (cache.contains(name)) {
return cache.get(name);
}
throw new NoSuchHandlerFoundException(name);
}
[/java]
Now we would write a plugin. The idea of the plugin is to re-initialize the cache if a mapper is not found and after that just try to lookup the handler once more. Usually we would write something along these lines:
[java]
package example;
class MyPlugin implements Plugin {
public void preinit() {
// register processor that is invoked when the class in question is loaded
IntegrationFactory.getInstance().
addIntegrationProcessor("class.to.be.Patched", new MyClassBytecodeProcessor());
}
}
.....
class MyClassBytecodeProcessor {
public void process(.....) {
m = ctClass.getDeclaredMethod("getMappedMethod");
m.setBody(" if (cache.contains(name)) {\n" +
" return cache.get(name);\n" +
" }\n" +
" \n" +
" reinitCache(name);\n" +
" \n" +
" if (cache.contains(name)) {\n" +
" return cache.get(name);\n" +
" }\n" +
" \n" +
" throw new NoSuchHandlerFoundException(name);");
}
}
[/java]
This ensures that once the class class.to.be.Patched is loaded, we will change the body of the method getMappedMethod(...) to contain the invalidation code also.
You guys are crazy! Why are you using bytecode processing? This is just dumb!
It is quite difficult to get a dependency into the never-ending list of Java frameworks out there. This is a way for us to release plugins quickly, not maintain custom patched versions or deal with jar hell and skip the long process of getting dependencies into projects. And of course having an OSS SDK is not enough for the OSS projects to embrace us so we use a shortcut.
How do I start?
JavaRebel plugin is just a class implementing Plugin interface that is packaged into a JAR with a "JavaRebel-Plugin=example.MyPlugin" line in the MANIFEST.MF.
Plugins are best developed using Maven. Just add the repository at http://repos.zeroturnaround.com/maven2/ and the following dependencies:
- org.zeroturnaround:jr-sdk:RELEASE
- org.zeroturnaround:jr-utils:RELEASE
The jr-sdk dependency includes the SDK interfaces and the jr-utils dependency includes some helper classes and the bundled Javassist library under the org.zeroturnaround.bundled.javassist package. All dependencies have source and JavaDoc attached.
You should also add a line to the plugin JAR MANIFEST.MF like this:
[xml]
[/xml]
To run a plugin you should put it in the classpath next to the processed code (e.g. Spring plugin should be put in the same dir as the spring.jar). Alternatively if you implement the checkDependencies() method you can add the plugin jar to the -Drebel.plugins system property and it will be injected into the relevant place automatically.
What can I do?
You can register a listener and get events on class reloads. On these events you can empty your caches, re-initialize your caches and read Wulffmorgenthaler.
For registering the listener see usage API and consult the listener API docs for the event information.


