Class reloading (hot deploy)

redlemonbalmΚινητά – Ασύρματες Τεχνολογίες

10 Δεκ 2013 (πριν από 3 χρόνια και 10 μήνες)

65 εμφανίσεις

Class reloading (hot deploy) 1
ID: 146-kauri | Version: 1 | Date: 8/27/10 4:32:21 PM
Class reloading (hot deploy)
The problem
When working on front-end code, it is desirable to have a quick code-build-test cycle.
Kauri already has the advantage that it doesn't require heavy build and deploy processes (such as the creation
of a WAR). Once the Maven build is completed, you can (re)start Kauri and it will load the artifacts directly
from your local Maven repository or even from your Maven-style source tree.
However, for code which lives close to the web-tier, typically request handlers (a.k.a. controllers, resource
classes, action classes, ...) it can still be anoying and improductive to restart Kauri each time. Kauri by itself
is lightweight, but you might have application components which need some time to initialize (and caches to
fill, context to be build up, ...).
The scope
As mentioned, the class reloading is especially useful for request-related classes.
It might be helpful if changes to any sort of class are automatically picked up, however for long-running
services it is hard or impossible to do this transparently. Often you're better of doing a full restart. In systems
like OSGI, it is possible to restart only subsystems of the application, but that will only work as far as the
interfaces and objects exchanged over them don't change (at least I think so).
These sort of long-running services are typically easier and quicker to test using (unit) test cases, rather than
through manual user interface interaction, so this shouldn't pose much of a problem.
Nevertheless, there are cases, like when adding a new field, that you might want to be able to quickly/
interactively change 'backend' model classes and frontend code in one go.
Deployment vs. development time: the class reloading is on first sight not something that should be
supported on live systems. OTOH, it can be useful to make changes to a live system without requiring a full
restart, though these should mostly be limited to things like skinning.
The implementation solution
We'll assume the reader is familiar with Java class loading.
Let's establish this terminology:
 Reloading classloader: a classloader which is able to freshly load classes
 Dynamically loaded class: a class which the framework (Kauri) instantiates itself based on a configured
or auto-discovered class name, typically for things like resource classes.
The basic mechanism is: when a reloadable class changes, we need to create a new classloader and load it
from there.
The difficult part in this is reloadable class. How do we know which classes should be considered for
reloading, and which not?
Let's consider some extremes:
 consider all classes (in a Kauri module) to be reloadable
 consider only the dynamically loaded class (e.g. a resource class) to be reloadable (plus its inner/nested/
anonymous classes)
If we consider all classes to be reloadable, we can have the problem that the class of a long-running (Spring)
service is reloaded by the reloading classloader. In that case, if e.g. a resource class has a dependency on
such a long-running service, the system will be unable to resolve that dependency since the class object of
Class reloading (hot deploy) 2
ID: 146-kauri | Version: 1 | Date: 8/27/10 4:32:21 PM
the resource's dependency will be different from the class object of the long-running service in the Spring
container. Similar problems can occur for the classes of objects exchanged with these services.
Considering only the dynamically loaded class to be reloadable is rather limiting. If this class uses some
more helper class structures to do its work, then this won't work unless all these classes are nested classes,
since otherwise we can't possible know what other classes to consider for reloading.
A small variants of this is to consider classes listed in the Spring container to be not reloadable, and all the
rest reloadable, but again there can be a whole cloud of associated classes which should not be reloaded
either, and which the system can not figure out by itself.
After this discussion of what the reloadable classes can be, the only practical conclusion seems to be that it is
up to the user to make the distinction.
This could be done in the following ways:
 by having two separate source folders, e.g.
src/main/java
src/web/java
 by configuring a list of packages which should be reloadable (possibly with some reasonable default
like a package named 'web' or something)
More on Spring
Up to know, we have assumed that dynamically loaded classes will just be instantiated by Kauri. However,
to do their work, these classes will often need access to various Spring-instantiated services. Therefore in
practice these classes will be instantiated via Spring's AutowireCapableBeanFactory, so that @Autowire
dependencies are automatically injected into them.
There might be cases where we still want these classes to be explicitly declared in a Spring container. For
example to do explicit wiring, or for the usage of other Spring features such as AOP. I'm still in the process
of learning Spring, so I'm a bit clueless as to how important this is. If this would be needed, this is easy to
add at a later time.
In any case, for Spring bean XML we could follow a similar approach as for the classes: have a separate
Spring container for the dynamically loaded classes (created as child of the normal Spring container of the
Kauri Module) which can be recreated upon class changes.
Dynamic restservices
(didn't think to deeply about this)
One particular case where the dynamic reloading of spring-based services can be interesting is for exported
restservices. I.e. specifically developed restservices which are not based on Kauri's default routing
infrastructure. To avoid that each such restlet needs to implement a reloading proxy itself, it would be useful
if these could be added to the 'web' spring container. This will require some special treatment from the
kauri:export-restservice directive, as this only needs to be performed once, and will need to export a proxy
which dynamically fetched the bean from the possibly-reloaded spring container.
On Groovy
Since the runtime representation of Groovy code are simply Java classes, there are similar issues for Groovy
code, except if we would assume that Groovy is only used for reloadable things.
Class reloading (hot deploy) 3
ID: 146-kauri | Version: 1 | Date: 8/27/10 4:32:21 PM
Other systems
Looking at Seam, it seems they have come to similar conclusions and only support reloading for classes
which are either in src/actions or deployed in WEB-INF/dev.
Cocoon reloads its complete spring container on class changes, but it seems to me that this can potentially
be very costly, and is also more complex in the case of Kauri where there are multiple Spring containers
possibly using each others services.
Grails: see this message
1
and others in the same thread. Basically, Grails doesn't have magic reloading either.
Django: this blog entry
2
contains some interesting information.
Quote "Actually, Django registers changes to apps automatically by actually restarting the server. But
the restart happens automatically and so fast that you don't even notice it. I should know, I submitted
the patch for it to the Django project. :-) [...] So both project "cheats" by restarting the server and
therefore replacing *all* objects in memory, but on a fast machine, the performance hit is minimal."
Another quote: "Smalltalk's live code editing lets you swap in a method at a time at runtime.". Seems
very similar to what you can do with the JVM.
Another quote formulates something I've also explained to mpo today: "Bob is dead on, this can never
be made magic enough. Consider if you have a class named Kid with attributes "mother" and "father".
You decide to change it to a single attribute named "parents." How can reload possibly know how to
upgrade existing instances of Kid into the new one? You have to very carefully write custom code to
do it. The development overhead for this kind of system isn't worth it except in very few applications
like Bob's Telco example."
Conclusion: there's no magic! Whatever productivity gain these non-Java frameworks offers, it doesn't
come from partial code reloading.
RoR: not really about hot deploy, bug a nice explanation of the Fast-CGI based model in this blog entry
3
.
OSGI: doesn't support class reloading obviously, but it is possible to update individual bundles: javadoc
quote: "If this bundle has exported any packages, these packages must not be updated. Instead, the previous
package version must remain exported until the PackageAdmin.refreshPackages method has been has
been called or the Framework is relaunched." The refreshPackages method will basically need to restart a
cascading series of bundles to have a consistent situation, lousy OSGI implementations are also allowed to
simply do a full Framework restart.
"Impala" (never heard of it before): blog about micro hot deploy
4
, echo's my idea that a full restart will
take too long and puts too much on assumption on developers writing proper shutdown code for their
components. Probably worth to take a closer look this project.
Further considerations
After some more thinking, I'd like to add the following thought:
Each module would have two classloaders, the one for the non-reloading and one for the reloading classes,
with the latter being a child of the first one. A similar setup would be present for the spring containers. The
non-reloading spring container would read its classes only from the non-reloading classloader, and the 'self-
export' of a module's classes to the shared classloader would only export the non-reloading classes.
However, in non-dev mode, to avoid suprising behavior, we would need to keep having the same distinction
in classloaders, and this would break the convenient one-to-one correspondence between jar files and
classloaders (still neglecting the fact of how we could store in the jar file the distinction between these two
sets of classes).
1.http://archive.codehaus.org/grails/user/8bd9fdba0805120229i43b2efa6h677e45713a778ed4%40mail.gmail.com
Class reloading (hot deploy) 4
ID: 146-kauri | Version: 1 | Date: 8/27/10 4:32:21 PM
2.http://blog.delaguardia.com.mx/index.php?op=ViewArticle&articleId=56&blogId=1
3.http://blogs.codehaus.org/people/tirsen/
archives/001041_ruby_on_rails_and_fastcgi_scaling_using_processes_instead_of_threads.html
4.http://impalablog.blogspot.com/2007/09/rationale-of-micro-hot-deployment.html