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!

--
Posted By Howard to Tapestry Central at 3/12/2010 09:59:00 AM

Reply via email to