|
SimpleJavaRebelPlugin
Describes how to create a simple JavaRebel plugin for integrating a custom framework or container.
IntroductionThese instructions will only work for the JavaRebel 1.2 branch. In this article we will review how to use the JavaRebel SDK and Integration projects to create a simple JavaRebel plugin for making Apache Commons EL reset it's cache after a class has been reloaded. Step 1: Determining the changesThe issues with Commons EL is that it will cache the results of reflection in the BeanInfoManager class and will not see the new properties in the classes reloaded by JavaRebel. To remedy that we need to add a simple fallback case to the getProperty() method: Before: public BeanInfoProperty getProperty (String pPropertyName)
throws ELException
{
checkInitialized();
return (BeanInfoProperty) mPropertyByName.get (pPropertyName);
}After: public BeanInfoProperty getProperty (String pPropertyName)
throws ELException
{
checkInitialized(pLogger);
BeanInfoProperty beanInfoProperty = (BeanInfoProperty) mPropertyByName.get(pPropertyName);
if (beanInfoProperty == null) {
// lets give it another try, maybe the class has changed
mInitialized = false;
Introspector.flushFromCaches(mBeanClass);
this.checkInitialized(pLogger);
}
return (BeanInfoProperty) mPropertyByName.get(pPropertyName);
}This code will cause the properties to be reread from the bean class if a property has not been found. Now let's make a plugin that will make this work. Step 2: Preparing the pluginThe plugin is a class that implements the Plugin interface. This class should be packed in a JAR and a custom attribute "JavaRebel-Plugin" must be added to the META-INF/MANIFEST.MF. This attribute be set to the plugin class name, then JavaRebel will find the plugin from the classpath and call the preinit() method before the application is started. To create the plugin you can use the template project that we have created. It includes an Ant script for building the plugin JAR and Eclipse project files. Edit the build.properties file to set the name of the plugin and the name of the class implementing the Plugin interface: plugin.name = javarebel-commons-el plugin.class_name = org.zeroturnaround.javarebel.commons.el.CommonsElPlugin You should also drop the javarebel.jar to the lib/ directory. This JAR includes both the SDK and Integration Plugin classes and source files. It will have all the dependencies we will need. Step 3: Registering the bytecode processorNow that we have the plugin frame set up and we know how we need to change the relevant method we can create the actual plugin class. It will be very simple and just register the bytecode processor for the BeanInfoManager class: public class CommonsElPlugin implements Plugin{
public void preinit() {
IntegrationFactory.getInstance()
.addIntegrationProcessor(
"org.apache.commons.el.BeanInfoManager",
new BeanInfoManagerClassBytecodeProcessor());
}
}Step 4: Creating the bytecode processorNow we are left to create the actual bytecode processor. JavaRebel bundles Javassist, which is recommended for implementing the bytecode processing. To avoid versioning conflicts all Javassist classes have org.zeroturnaround.bundled. prepended to their names. To make use of Javassist it's easiest to extend the JavassistClassBytecodeProcessor that creates a CtClass that you can process further. Javassist then allows you to easily inspect and alter the method bytecode using an embedded Java-like language. You can read about it in the tutorial. public class BeanInfoManagerClassBytecodeProcessor extends
JavassistClassBytecodeProcessor {
public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception {
CtMethod m = ctClass.getDeclaredMethod("getProperty");
m.setBody(
"{ checkInitialized($2);" +
"org.apache.commons.el.BeanInfoProperty beanInfoProperty = " +
"(org.apache.commons.el.BeanInfoProperty) mPropertyByName.get($1);" +
"if (beanInfoProperty == null) {" +
" mInitialized = false;" +
" java.beans.Introspector.flushFromCaches(mBeanClass);" +
" this.checkInitialized($2);" +
"} " +
"return (org.apache.commons.el.BeanInfoProperty) mPropertyByName.get($1); }");
}
}ConclusionsThe plugin we created is actually embedded into the JavaRebel Integration plugin. The BeanInfoManagerClassBytecodeProcessor class is not so different from the one we created and it is used to make sure that all properties get reloaded when the JavaRebel agent is on. You can use all of the JavaRebel SDK to develop plugins that override classloaders and react to classes being reloaded or enable on-the-fly instrumentation for classes managed by JavaRebel and so on. We suggest you look at the Integration Plugin project for more examples of using the SDK. |
Sign in to add a comment

So I now have a plugin, but how do you tell JavaRebel? about it? Is it enough to just put it in the classpath or do you have to add the plugin somewhere to the JVM options as well?
thanks! Brendan
It's enough to just put it in the classpath.