Jevgeni has posted 19 posts at DZone. You can read more from them at their website. View Full User Profile

5 JRebel Features You Couldn’t Do In The JVM

03.05.2010
| 7425 views |
  • submit to reddit

One common comment we hear when talking about JRebel is that class updates should be implemented in the standard JVM (see feature comparison and behind-the-scenes notes to find out more about JRebel and HotSwap). However even if Oracle or IBM would announce tomorrow that they implemented the support for full schema change HotSwap in the next version of the JVM, JRebel would still be a worthwhile investment. Why?

1. Adding new classes

HotSwap protocol works by specifying the class name and the updated bytecode. It works for existing classes because there’s a relatively unique correspondence between the class name and its content. HotSwap ignores the fact that each class is loaded in a specific class loader. However to add a new class you need to choose that class loader, which HotSwap doesn’t support.

In exploded deployment (where WARs, EARs and JARs are deployed unpacked as directories) new classes can be loaded directly from the file system. But in packaged deployment (WARs, EARs and JARs deployed as archives) adding new classes is a challenge. Even adding the new class to the archive will not do the trick, as the archives are commonly unpacked when deployed and updating the archive either causes redeployment or no change whatsoever.

To solve this problem in a standard & compatible way you’d need:

  • A new standard API for Java EE that would allow you to find the corresponding class loader for a specified application. This would need to be implemented by the application server.
  • Extensions to the HotSwap protocol to allow sending new classes for a specified class loader, OR a different API/protocol to allow remote interaction with the IDE. This would need to be implemented in the JVM.
  • each IDE to be aware of what application the new classes belong to. This would need to be supported by the IDE.

JRebel solves this by

  • Extending the application server class loaders to search for classes from JRebel sources.
  • Using the rebel.xml configuration file to match each specific class loader to the classes in the IDE.

2. Resources

The HotSwap protocol doesn’t make any mention of the resources packaged along with classes. Java applications include quite a few of these (XMLs, property files, graphic files, etc). If you use packaged deployment these resources will not be updated until you make a full build and redeploy.

To solve this problem in a standard & compatible way you’d need:

  • A new standard API for Java EE that would allow you to find the corresponding class loader for a specified application. This would need to be implemented by the application server.
  • Extensions to the ClassLoader API to allow adding external sources for finding resources. This would need to be implemented by the application server.
  • Extensions to the HotSwap protocol to allow sending resources for a specified class loader or a different API/protocol to allow remote interaction with the IDE. This would need to be implemented in the JVM. A big problem with this approach is that resource names are often not unique and several distinct resources with the same name in the same application are quite common.
  • The IDE to be aware of which application the resources belong to. This would need to be supported by the IDE. Possibly you’d need to know even more about resources to solve the problem described in the previous point.

JRebel solves this by

  • Extending the application server class loaders to search for resources from JRebel sources.
  • Using the rebel.xml configuration file to match specific class loader to the resources in the IDE. JRebel solves the problem with multiple resources by giving the user control via rebel.xml to affect the order in which those resources are returned to the application.

3. Web resources

Besides the class loader resources, that are looked up via the ClassLoader API, every web application has a wealth of web resources like HTML, JSP, CSS, JS, images and so on. Unlike the class loader resources these are available in two ways:

  1. Served to the end user via the HTTP protocol.
  2. Read by the application using the ServletContext API.

If you use packaged deployment, then to update these resources you would need to do a full build and redeploy. HotSwap isn’t aware of these resources (and probably shouldn’t be), since ClassLoaders do not participate in any way in finding these resources.

To solve this problem in a standard & compatible way you’d need:

  • A new standard API for Java EE that would allow you to find the corresponding ServletContext or similar API for a specified application. This would need to be implemented by the application server.
  • A remote API for updating/injecting web resources into a specified ServletContext. These resources must also be served via HTTP when requested. This would need to be implemented by the application server. Luckily, unlike the class loader resources, web resources are uniquely identified by their name.
  • The IDE to be aware of which application the web resources belong to. This would need to be supported by the IDE.

JRebel solves this by

  • Extending the application server ServletContext implementation and HTTP server implementations to search for web resources from JRebel sources.
  • Using the rebel.xml configuration file to match a specific web application to the web resources in the IDE.

4. Caches

So let’s say the powers that be in the Java world have teamed up and implemented everything we described so far. You start your application, add a new method to the class, open your browser and stare at a juicy MethodNotFoundError. As it turns out, Java servers and frameworks tend to aggressively cache:

  • Class structure information. This means that the servers/frameworks won’t be aware of the new methods and will fail. A good example is JSP EL and Scriptlets, which will fail in most implementations when trying to access a new method.
  • Resources. Here we have both positive caches (the updated content is ignored) and negative caches (the new resource isn’t visible). Almost every server and some frameworks will include both of these caches.
  • Configuration. Many resources (and class annotations) are only scanned once, and all changes are ignored afterwards. E.g. Spring XML configuration or Annotations.

This means that even after both JVM and the application server APIs are updated to support application updates, you still need to re-engineer a lot of Java code to accept the fact that classes and resources can change.

JRebel solves this by

  • Purging the class structure information using listeners called when a class is updated and a lot of bytecode instrumentation to change the current framework implementations.
  • Integrating with servers and frameworks to intelligently disable or alter the positive and negative caches. JRebel introduces its own update-aware cache to speed up resource lookup and delivery.
  • Deeply integrating with frameworks to allow updates to the configuration. E.g. for Spring, JRebel causes the XMLs to be reread and the classpath to be rescanned.

5. Managed Components (EJBs, Spring beans,…)

Finally, when all is said and done, we still have the framework- or server-managed components like EJBs. These components often have:

  • Some kind of state associated with an instance or an instance pool.
  • Layers of proxies implementing aspects of functionality.
  • Dependency injection.

These features mean that updating the class schema is nowhere near enough. When a class is updated:

  • State may need to be updated to correspond to changes in the configuration. E.g. another method should now be executed inside a transaction.
  • Proxies may need to be reconstructed or updated to apply to the added methods. Otherwise you might be looking at the MethodNotFoundError again.
  • New dependencies must be injected. Otherwise NullPointerExceptions will abound.

Implementing this in current servers and frameworks will mean a substantial re-engineering across the codebase. JRebel solves this by re-implementing some server/framework features to allow for such updates to take place.

Conclusions

Although it could be possible to implement all of JRebel’s features in the Java ecosystem, it would take much more than just an update to the JVM. You’d need to re-engineer standards, introduce new APIs, implement them across app servers and then re-engineer much of the existing server/framework code base to take the possibility of updates into account. JRebel not only allows updates to class schema to take place, but also solves all those problems so that you can just update your code and see the change immediately. On average, that saves 2.5 minutes per redeploy — 5 times per hour of coding, and as Craig Pardey noticed, a whole bunch of context switching as well.
Published at DZone with permission of its author, Jevgeni Kabanov.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: