The things I writing is virtually a practice base on Peter M.Chen's article. You can also see here
The reloading I talking about here is reloading the modified classes directly without reloading all the web application.
OK, let's go on step by step.
1) Download the source code of your current Tomcat. Please be sure the version is the same, or the best you can download a set of Tomcat binary and source togeher. For example download the tomcat 4.1.34 in the page:
2) Create a new class which the name is DynamicClassLoader
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.util. * ;
/** */ /**
* @author peter
*/
public class DynamicClassLoader extends URLClassLoader ... {
/**//* parent class loader, it's org.apache.catalina.loader.WebappClassLoader */
private ClassLoader parent = null;
/**//* list of already loaded classes names */
private List classNames = null;
/** *//**
*
* @param parent
*
* here isorg.apache.catalina.loader.WebappClassLoader
*
*/
public DynamicClassLoader(ClassLoader parent) ...{
super(new URL[0]);
classNames = new ArrayList();
this.parent = parent;
}
/** *//**
*
* this method will call in WebappClassLoader .
*
*
*
* @param name
*
* @param classData
*
* @param codeSource
*
* @return
*
* @throws ClassNotFoundException
*
*/
public Class loadClass(String name, byte[] classData, CodeSource codeSource)
throws ClassNotFoundException ...{
if (classNames.contains(name)) ...{
return loadClass(name);
} else ...{
classNames.add(name);
return defineClass(name, classData, 0, classData.length, codeSource);
}
}
/** *//**
*
* when a class not in classNames, let parent load it
*
* @see java.lang.ClassLoader#loadClass(java.lang.String)
*
*/
public Class loadClass(String name) throws ClassNotFoundException ...{
if (!classNames.contains(name)) ...{
return parent.loadClass(name);
}
return super.loadClass(name);
}
}
3) Append the code to be the end of org.apache.catalina.loader.WebappClassLoader
if (resourceEntries.containsKey(name)) ...{
resourceEntries.remove(name);
return true;
}
return false;
}
private boolean isReload = false ;
public boolean isReload() ... {
return isReload;
}
public void setReload( boolean isReload) ... {
this.isReload = isReload;
}
private DynamicClassLoader dynamicClassLoader = new DynamicClassLoader( this );
public void reCreateDynamicClassLoader() ... {
dynamicClassLoader = new DynamicClassLoader(this);
}
4) Find out the method findClass(String name) of WebappClassLoader. If it is private, set it to be public.
5) Find out the method findClassInternal(String name) of WebappClassLoader. Add the following line between the part of if (packageName != null) {} and CodeSource codeSource = new CodeSource(entry.codeBase, entry.certificates); :6) Still in the method findClassInternal(String name) of WebappClassLoader,
modify
to be
7) Still in the method findClassInternal(String name) of WebappClassLoader,
modfiy
clazz = defineClass(name, entry.binaryContent, 0,
entry.binaryContent.length,
codeSource);
entry.loadedClass = clazz;
} else ... {
clazz = entry.loadedClass;
}
to be
byte[] classData = new byte[entry.binaryContent.length];
System.arraycopy(entry.binaryContent, 0, classData, 0, classData.length);
clazz = isReload ? dynamicClassLoader.loadClass(name, classData, codeSource) : defineClass(name, classData, 0, classData.length, codeSource);
entry.loadedClass = clazz;
} else ... {
clazz = entry.loadedClass;
}
8) Compile WebappClassLoader and cope the corresponding classes to the right place of /server/lib/catalina.jar
9) Creating a new Jsp file which the name is test.jsp.
Add the the following code:
ClassLoader loader = (Thread.currentThread().getContextClassLoader());
Class clazz = loader.getClass();
java.lang.reflect.Method setReload = clazz.getMethod( " setReload " , new Class[] ... {boolean.class} );
java.lang.reflect.Method reCreate = clazz.getMethod( " reCreateDynamicClassLoader " , null );
java.lang.reflect.Method findClass = clazz.getMethod( " findClass " , new Class[] ... {String.class} );
reCreate.invoke(loader, null );
setReload.invoke(loader, new Object[] ... {new Boolean(true)} );
Class A = (Class)findClass.invoke(loader, new Object[] ... {"org.AClass"} );
setReload.invoke(loader, new Object[] ... {new Boolean(false)} );
A.newInstance();
%>