Creator of the Apache Tapestry web application framework and the Apache HiveMind dependency injection container. Howard has been an active member of the Java community since 1997. He specializes in all things Tapestry, including on-site Tapestry training and mentoring, but has lately been spreading out into fun new areas including functional programming (with Clojure), and NodeJS. Howard is a DZone MVB and is not an employee of DZone and has posted 80 posts at DZone. You can read more from them at their website. View Full User Profile

Live Service Reloading in Tapestry 5.2

  • submit to reddit

A common question I get during Tapestry training sessions is: Why can't Tapestry reload my services as well as my pages and components?. It does seem odd that I talk about how agile Tapestry is, with the live class reloading, and how nicely OO it is, what with services ... but when you move common logic to a service, you lose the agility because services do not live reload.

This came up yet again, during my latest training session, in London.

I've considered this before, and I've been opposed to live service reloading for a couple of reasons. First, live reloading requires creating new class loaders, and that causes conflicts with other frameworks and libraries. You get those crazy ClassCastExceptions even though the class name is correct (same name, different class loader, different class). Further, in Tapestry IoC, services can be utilized to make contributions to other services ... changing one service implementation, or one module, can cause a ripple effect across an untraceable number of other services. How do you know what needs to be reloaded or re-initialized?

When I last really considered this, back in the HiveMind days, my conclusion was that it was not possible to create a perfect reloading process: one that would ensure that the live-reloaded Registry (and all of its services with all their internal state) would be an exact match for what configuration you'd get by doing a cold restart.

So I shelved the idea, thinking that simply redeploying the WAR containing the application (and the services and modules) would accomplish the desired effect.

But as they say, The Perfect Is The Enemy Of The Good. One very sharp student, Andreas Pardeike, asked: Why not just reload the service implementations?.

Why not indeed? Why not limit the behavior to something understandable, trackable, and not very expensive. Most of the infrastructure was already present, used for reloading of component classes. What about ClassCastExceptions? In Tapestry, service implementations are already buried under multiple layers of dynamically generated proxies that implement the service interface. The underlying service implementation is never automatically exposed.

A few hours of work later ... and we have live service reloading. Each reloadable service gets its own class loader, just to load the service interface class. When Tapestry is periodically checking for updated files, it checks each reloadable service. If necessary, the existing instance, class and class loader is discarded and a new class loader created for the updated .class file.

This is going to make a big difference for me, and for most Tapestry developers. Both applications I'm working on have enough Hibernate entities and other clutter to take some time (20 - 30 seconds) to restart, and most functionality is hidden past a login page. Being able to change a service, for example to tweak a Hibernate query, with the same speed with which I can tweak a template or component class, is just one more thing to keep me in the flow and super productive.

Give it a try ... it's one more step towards making Tapestry so compelling, you wouldn't think of using anything else!


Published at DZone with permission of Howard Lewis Ship, author and DZone MVB.

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



Howard Lewis Ship replied on Mon, 2010/03/15 - 11:20am

This makes me happy ... this is a much more important posting from my blog than my general gripes about Eclipse!  Live service reloading takes Tapestry to an even higher level of effortless productivity and agility.

Jakub Exner replied on Tue, 2010/03/16 - 4:44am

This sounds good. I am also interested in the following details, could you help me answering them (Howard or somebody else knowledgeable :)?
1. What if I change an arbitraty class, which is called from my service implementation? Is it live-reloaded? Would a tool like JRebel help here?
2. When can we realistically expect T 5.2 with Portlet support to be available?
3. Will T 5.2 support JSR-286 portlets?

Howard Lewis Ship replied on Tue, 2010/03/16 - 10:55am

The reloading solution so far is limited to just service implementation classes. It's not a complete solution like JRebel, but it is also free and well integrated into Tapestry. I may extend it to other types of proxiable classes (i.e., implementation classes that conform to a specific known interface, even if not a service).

 In terms of portlets; there's been multiple contributions of code to be reviewed, and T5 was largely architected specifically to support the Portlet style of seperation action and render requests ... but it's still a time commitment to implement the portlet support and test it against multiple containers. In the T4 time frame, this didn't happen until a particular client stepped up to fund the effort.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.