In-application JRebel support

JRebel is very useful during development. Now sometimes, you know you are doing things which are not compatible with live class reloading (like caching the results of reflection). In that case it is very useful to be able to hook into JRebel to be notified of class changes.

Unfortunately the guide for writing a plugin doesn’t work entirely. The plugin metadata is not picked up when loading JRebel (probably because the application is not yet in the classpath when JRebel loads).
I worked around this using Spring (as Spring is used in this application, it would work exactly the same using CDI).

First I have my service which caches reflection data in static variables. I included a package local clear method to allow clearing the caches.

@Component
public class ReflectionDataService {
 
    private static final Map<X,Y> MY_CACHED_DATA = ...;
 
    /**
     * Allow cached data to be cleared by JRebel plugin.
     */
    static void clear() {
        MY_CACHED_DATA.clear();
    }
 
    ... normal implementation ...
}

The JRebel plugin is quite simple. In this case, it has to react to any class which is annotated using Path.

public class ReflectionDataServiceJrebelPlugin implements Plugin {
 
    @Override
    public void preinit() {
        registerListener();
    }
 
    private void registerListener() {
        // Set up the reload listener
        ReloaderFactory.getInstance().addClassReloadListener(
                new ClassEventListener() {
                    public void onClassEvent(int eventType, Class klass) {
 
                        try {
                            if (klass.isAnnotationPresent(Path.class)) {
                                ReflectionDataService.clear();
                            }
                        } catch (Exception e) {
                            LoggerFactory.getInstance().error(e);
                            System.out.println(e);
                        }
                    }
 
                    public int priority() {
                        return 0;
                    }
                }
        );
 
    }
 
    @Override
    public boolean checkDependencies(ClassLoader classLoader, ClassResourceSource classResourceSource) {
        return classResourceSource.getClassResource("my.package.ReflectionDataService") != null;
    }
 
    @Override
    public String getId() {
        return "MyJrebelPlugin";
    }
 
    @Override
    public String getName() {
        return "My JRebel Plugin";
    }
 
    @Override
    public String getDescription() {
        return "Reload ReflectionDataService.";
    }
 
    @Override
    public String getAuthor() {
        return null;
    }
 
    @Override
    public String getWebsite() {
        return null;
    }
 
}

Now I have to make sure that this is loaded. The plugin discovery system cannot find it – so I have to register it later. I provided a bean to register the JRebel plugin (specifically the class reload listener).

@Component
public class ReflectionDataServiceJrebelLoader {
 
    private static final Logger LOG = LoggerFactory.getLogger(ReflectionDataServiceJrebelLoader.class);
 
    /**
     * Load jRebel plugin when jRebel is available.
     */
    @PostConstruct
    public void preinit() {
        try {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            Class<?> plugin = cl.loadClass("my.package.ReflectionDataServiceJrebelPlugin");
            Method method = plugin.getMethod("preinit");
            if (null != method) {
                Object instance = plugin.newInstance();
                method.invoke(instance);
            }
        } catch (Throwable ex) {
            LOG.debug("JRebel not found - will not reload directory service automatically.");
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

question razz sad evil exclaim smile redface biggrin surprised eek confused cool lol mad twisted rolleyes wink idea arrow neutral cry mrgreen

*