Java Programming: Hot Deploy 
This post is the second part of
Java Programming: First steps with ClassLoaders.
In the first part I explained what were class loaders, how they worked and why such part of the JVM might need customization.
A possible use for customized class loaders is hot deploy. This feature is the one I'm currently most interested and it's the one I'll be writing about.
I'll explain the motivation, the concept, the implementation and finally the integration. I'll also provide the source code of a simple proof of concept of everything that will be described in this post.
Like I said before
hot deploy is the name given to the ability of loading new versions of a class in runtime, such feature is quite popular among java containers like tomcat or jboss.
The motivation behind such feature is simple, availability. Nowadays, with all kind of services online and everyone using them, availability is even more critical than in the beginning of the internet. After all, now there are many companies doing business on the internet and downtime for them means less profit.
Switching classes in runtime avoids server's downtime, hence, better availability. That's why, in my opinion, most of the Java containers support hot deploy.
Although hot deploy seems a good feature, it's not natively supported by the Java Virtual Machine (JVM) because, like I've said in the previous post, once a given class is defined by a ClassLoader it cannot be redefined. That's where a custom class loader enters. The idea is simple, exploit the JVM behaviour.
Classes are not only identified by it's package name and class name, but also by the class loader instance that defined the class. Hence, using a new instance when needed will allow the developer to load the new class version into the JVM.
It might sound simple, but the process itself has implications regarding how to deal the new class casting. Other problems related with the use of class loaders are:
- Memory usage. Various definitions of classes will be loaded into memory, since new class loaders instances keep defining new versions of classes.
- The possible scenario of old instances and new instances co-existing, this is specially problematic if object serialization is being used.
These are the kind of trade-offs that should be thought through when thinking in implementing hot deploy within an application.
ImplementationI think by now it's clear that each time a new version needs to be loaded, a new instance of a class loader as to be created, that's what creates the problem of class casting. There are at least three ways of solving such problem:
- Using reflection: Treating every object loaded by the custom class loader as a java.lang.Object and using Java Reflection API to invoke methods. This, in my opinion, isn't the best solution.
- Using the same base class in hierarchy: This implies that a base class could never be hot deployed, since it could never be redefined and that all the class loaders instances would return the same definition (based on delegation).
- Using the same interface for each class hierarchy: This approach is similar to the one previously suggested, interfaces are delegated to base class loaders so are always the same and each class that supports hot deploy will have to implement one of those interfaces.
After presenting these solutions, it's once again visible that delegation plays an important role in the class loading system.
Another problem that should be solved - although not necessary - is making the new class loaders creation process transparent. The solution I suggest is encapsulation, instead of having only a single class loader class, actually there are two, but one of them - let's call it RuntimeClassLoader - won't be visible to the developer that is using the actual custom class loader.
The class loading cycle for the custom class loader can be described by the following flowchart:

The previous flowchart shows two interesting things:
- The class loader contains a delegation list. The idea is to let the developer decide which classes are loaded only once and which are reloaded.
- The class loader contains a class cache. If a class isn't in the delegation list, but it's still up to date reloading it would just be an useless overhead.
The cache is nothing more than a simple HashMap of class names related with an object that contains the actual Class object and the file system's modification timestamp for the class file (which in my implementation I called ClassHolder).
Show HotDeployClassLoader source code
package net.pabrantes.classLoaders;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyClassLoader
extends ClassLoader {
private Map<
String,ClassHolder> classTimeStamps;
private List<
String> classesToDelegate;
public MyClassLoader(
ClassLoader classLoader) {
super(classLoader);
classTimeStamps =
new HashMap<
String, ClassHolder>();
classesToDelegate =
new ArrayList<
String>();
}
public void addClassToDelegateList(
String name) {
classesToDelegate.add(name);
}
protected boolean isClassInDelagationList(
String name) {
return classesToDelegate.contains(name);
}
@Override
protected Class<?> findClass(
String name)
throws ClassNotFoundException {
if(!classTimeStamps.containsKey(name) || oldTimeStamp(name)) {
System.out.println(name +
" needs loading");
classTimeStamps.remove(name);
return new RuntimeClassLoader(
this).loadClass(name);
}
else {
System.out.println(name +
" is still up to date");
return classTimeStamps.get(name).getClazz();
}
}
private boolean oldTimeStamp(
String name) {
File file =
new File(name.replace(
".",
"/") +
".class");
return file.lastModified() > classTimeStamps.get(name).getTimeStamp();
}
@Override
public Class<?> loadClass(
String name)
throws ClassNotFoundException {
return (isClassInDelagationList(name)) ? getParent().loadClass(name)
: findClass(name);
}
private class ClassHolder {
private Class clazz;
private long timeStamp;
public ClassHolder(
Class clazz,
long timeStamp) {
this.clazz = clazz;
this.timeStamp = timeStamp;
}
public Class getClazz() {
return clazz;
}
public long getTimeStamp() {
return timeStamp;
}
}
private class RuntimeClassLoader
extends ClassLoader {
public RuntimeClassLoader(
ClassLoader classLoader) {
super(classLoader);
}
@Override
protected Class<?> findClass(
String name)
throws ClassNotFoundException {
return loadClass(name);
}
public Class<?> loadClass(
String name)
throws ClassNotFoundException {
if (isClassInDelagationList(name)) {
return getParent().loadClass(name);
}
byte[] classBytes =
null;
try {
classBytes = getBytes(name.replace(
".",
"/") +
".class");
}
catch (IOException e) {
return findSystemClass(name);
}
Class clazz = defineClass(name, classBytes, 0, classBytes.length);
File file =
new File(name.replace(
".",
"/") +
".class");
classTimeStamps.put(name,
new ClassHolder(clazz,file.lastModified()));
return clazz;
}
private byte[] getBytes(
String filename)
throws IOException {
File file =
new File(filename);
long len = file.length();
byte raw[] =
new byte[(
int) len];
FileInputStream fin =
new FileInputStream(file);
int r = fin.read(raw);
if (r != len)
throw new IOException(
"Can't read all, " + r +
" != " + len);
fin.close();
return raw;
}
}
}
IntegrationIn order to test the class loader a simple application was developed. It's a terminal application that has a command line and is implemented using the
command design pattern.
The objective is simple, create the application in a way that the commands are hot deployable.
The development is quite simple:
- Creation of a simple application based on the Command Design Pattern;
- Creation of a file system monitor that can identify changes in the file system;
- Integration of both sub-applications listed in 1 and 2 and the class loader.
To explain the integration process I'll use a top down approach because I think it will be simpler to understand. Below is the UML for the complete system.

The
CoreEngine class is the application core and it uses two things:
- The HotDeployClassLoader to be able to load new commands or modifications of existing ones in runtime.
- The FileSystemWatcher to be notified when it commands have changed.
The
FileSystemWatcher is a process that runs in an independent thread and monitors a given file system directory, in this case it will be monitoring the commands directory.
The main idea is to create a snapshot of certain file types, in this case class files (jars could also be monitored). By snapshot I mean a file representation containing it's name and modification timestamp.
In given intervals the
FileSystemWatcher will compare it's internal snapshot against the actual directory state and if it doesn't match it notifies all
INotifyClient's that are registered. In this case it's our
CoreEngine that is registered at the
FileSystemWatcher.
Show FileSystemWatcher source code
package net.pabrantes.fsWatcher;
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class FileSystemWatcher
implements Runnable {
private static Comparator<FileRepresentation> comparator =
new Comparator<FileRepresentation>() {
public int compare(FileRepresentation fr0, FileRepresentation fr1) {
return fr0.getName().compareTo(fr1.getName());
}
};
protected File directory;
protected Set<FileRepresentation> representations;
protected List<INotifyClient> clients;
private FileRepresentation[] cachedArray =
null;
private long sleepTime = 10000;
public FileSystemWatcher(
String directoryName) {
File directoryToWatch =
new File(directoryName);
if (!directoryToWatch.isDirectory()) {
throw new RuntimeException(
"It needs to be a directory");
}
directory = directoryToWatch;
representations = getCurrentRepresentation();
clients =
new ArrayList<INotifyClient>();
}
public void setSleepTime(
long sleepTime) {
this.sleepTime = sleepTime;
}
public long getSleepTime() {
return this.sleepTime;
}
public void registerClient(INotifyClient client) {
clients.add(client);
}
public Set<FileRepresentation> getCurrentRepresentation() {
Set<FileRepresentation> representations =
new TreeSet<FileRepresentation>(
comparator);
for (File file : directory.listFiles(
new JavaRunnableFilenameFilter())) {
representations.add(
new FileRepresentation(file.getName(), file
.lastModified()));
}
return representations;
}
private FileRepresentation[] getCachedArray() {
if (cachedArray ==
null) {
cachedArray =
new FileRepresentation[representations.size()];
representations.toArray(cachedArray);
}
return cachedArray;
}
public void notifyListeners() {
for(INotifyClient client : clients) {
client.notifyModification();
}
}
public void modificationTrigger(Set<FileRepresentation> newData) {
System.out.println(
"Detected modification");
representations = newData;
cachedArray =
null;
notifyListeners();
}
public void run() {
for (;;) {
Set<FileRepresentation> currentRepresentation = getCurrentRepresentation();
if (currentRepresentation.size() != representations.size()) {
modificationTrigger(currentRepresentation);
}
FileRepresentation[] inCache = getCachedArray();
FileRepresentation[] current =
new FileRepresentation[currentRepresentation.size()];
currentRepresentation.toArray(current);
for (
int i = 0; i < current.length; i++) {
if (!current[i].equals(inCache[i])) {
modificationTrigger(currentRepresentation);
}
}
try {
Thread.sleep(getSleepTime());
}
catch (InterruptedException e) {
return;
}
}
}
}
When the
CoreEngine is notified by the
FileSystemWatcher it reloads the commands.
On a regular Command Pattern is normal to see such piece of code:
public List<ICommand> loadCommands() {
List<ICommand> commands = new ArrayList<ICommand>();
commands.add(new Command1());
commands.add(new Command2());
return commands;
// When there's a new command add a line here
}
But since in this particular case the loading of ICommand classes is done at runtime in a dynamic way (meaning that can be added new commands or remove existing), no implementations can be specified in the code.
So the previous code actually becomes:
public List<ICommand> loadCommands() {
List<ICommand> commands =
new ArrayList<ICommand>();
try {
File directory =
new File(
"commands");
for (
String name : directory.list(
new JavaRunnableFilenameFilter())) {
Class commandToLoad =
this.hotDeployLoader.loadClass(
"commands." + name.split("\\.
")[0]);
if (!commandToLoad.isInterface()) {
newCommands.add((ICommand) commandToLoad.newInstance());
}
}
} catch (Exception e) {
System.err.println("Something went wrong, skipping class update");
System.err.println(e.getMessage());
//
return the old command list, so the application still
// keeps commands.
return this.commands;
}
return commands;
}
The test if a given loaded class is or isn't an interface is being done because ICommand - the interface that is being used - is also in the same package. This test could be discarded if the interfaces would be put in another package.
The complete source code of this proof of concept is also
available for download (no build file is provided). In order to test the application, the following steps have to be done:
- Code compilation
- Start CoreEngine
- Modify an existing command or create a new one putting it in the command directory
- Compilation of new and modified commands
ImprovementsAll applications can be improved. Even if it's only a simple example, there are still possible improvements. Here are some that I can recall at the moment.
- Improve notification system in order to notify which files have been modified, created and added.
- Improve modification detection algorithm.
- Create the concept of context in CoreEngine, so commands would know the context are being ran and use it, for example, to implement the undo task.
- Creation of a build file with regular build tasks and also hot deploy tasks.
ConclusionsHot deploy isn't the holy grail, but it may be a useful feature in some cases. It has been shown that implementing hot deploy isn't that hard but there are certain details that cannot be overlooked, such as the class casting problem and the memory usage.
Hope it has been useful. Comments are more than welcome.