Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Thursday, February 11, 2010

Live reloading of Tapestry services?

During today's Tapestry Training at SkillsMatter, the question about live class reloading for Tapestry services came up.

Now, my normal response is to talk about class loaders, and mysterious class-cast exceptions it would cause, and the need to shut down and restart the container, etc.

But an idea went around ... what about just live reloading of implementation classes? That sparked some thoughts.

See, it seems to me that it should be possible to create a class loader that loads a single class (and, perhaps, inner classes of that single class), much as Tapestry uses a class loader to load pages and components.

In fact, it should be possible to have separate class loader for every implementation class that just performs the reload of that one class. A periodic check of the file modification date stamps could trigger the release of the class loader (and the current instance) and the instantiation of a new class loader, and the loading of the updated class.

You wouldn't be able to change service interfaces this way, or module classes (including contributions and the like) ... but changing a service implementation should be a snap. This would especially be useful for DAOs while creating and tuning database queries.

I think there would be some limitations here: services that are built via builder methods would not work; neither would services that export this (typically, as a listener to events published by another service). However, the vast majority of services could, I think, be automatically reloaded.

This is worth spending some time on ... if I can pull it off, it would be an incredible coup!

The only downside is that some services may need to move from tapestry-core to tapestry-ioc and some of those may, in fact, be public already (but not widely used).


Kalle Korhonen said...

Huh? How is this different from hot code swapping that has been part of standard JDK ever since 1.4? I've always been able to load implementation changes to my Tapestry services without restarting the JVM.

Anton Litvinenko said...

This sounds cool! But, there is already awesome product on the market which handles reloads on the fly: JRebel. The single trouble with that is: I have live reloading in pages and components thanks to Tapestry and live reloading in service implementations and layers classes hidden by service interfaces by JRebel. But when I change something in the service interface or value/entity objects, then reloading doesn't work, as Tapestry doesn't pick up those changes.

My point is (though being quite a stretch since JRebel is a commercial product) maybe it would make more sense to develop some sort of plugin for JRebel to handle page and component reloads as well and rely on that within the Tapestry?

Unknown said...

@Anton: I'm glad you have JRebel, but that is a proprietary product that only some people have access to. Tapestry has been doing live class reloading since 2006, where was JRebel then? Further, JRebel doesn't address the other needs of Tapestry: transforming classes as they are loaded, so it's not just a matter of delegating to this other product. Mostly, though, Tapestry can't and won't be dependent on a proprietary product.

Anton Litvinenko said...

Howard: Yeap! I totally understand your point and agree with that.

If Tapestry would reload changes in implementing classes then this would definitely be another strong point in favour of Tapestry and it will weaken the proposition of JRebel for me a lot (as long as I develop with Tapestry) :)

Anyway, good to know that you continue to cook something "magical" (in words of S.Jobs) for all of us :) Thanks!

Unknown said...

Hi Howard, my friends of gaedo told me about this post, so here is my contribution.
Concerning the case of obejcts exporting this, i suggest they could use proxies to export a reloading-aware version of the service.
well, in fact, from what i understand of your services, the default implementation used for service interfaces could be that version-aware proxy, ensuring the visible service is always up-to-date, even when used by a listener

Buddy Casino said...

Tapestry working nicely with JRebel would be be a goal worth pursuing. Don't know if Tapstry's reloading interferes with JRebel.

I have it at my workplace and don't want to miss it.

Robin K. said...

I would be just HUGE if that could be.

Service live reloading would be a strong argument for making tapestry-ioc as a container of choice.

Unknown said...

The real problem is that we have a fairly simple set up here - Tapestry 5, A 10 module project, jettey embedded, and maven. In Eclipse, LCR works nicely, with little configuraton required. In IntelliJ, it doesn't work at all.

For that matter JRebel doesn't work either in this scenario.

It's virtually impossible to diagnose why this doesn't work, but it means I'm locked into one product I particularly don't want to use: Eclipse.

I can't help but think that LCR should have been done, sorted, and just working, a long long time ago. Tapestry's LCR is great but it seems to only work when you have a very simple single-module project set-up. Grails' LCR works like a dream, regardless of scenario.