*Tomcat reload class at runtime(version 5.0.28)* 1.
Before start My English is pool, so I can't say complete and clear any more, sometimes look my code maybe easy to look my description word. thanks. 1. Why do this? When I writing program, running at tomcat, if I want to see the result when I recompile some classes, I must reload web application(same as set reloadable="true"), or restart Tomcat! it spent much time. So I think if I can reload class at runtime, that can help me more. My friends tell me, in WEBLOGIC, WEBSPHERE, even RESIN, they can reload classes at runtime, and have a cool name that call "developer mode". but tomcat can't. so, I must change a little source code of Tomcat and let it has this function, but... not automatic reload, it's manual drive reload. 1. Modify process 1. description Please put the new code to the java file end, because I want to keep old line of code when I point out what line of code I change. 1. Remove resource entry in org.apache.catalina.loaderWebappClassLoader�� has a property resourceEntries�� it remember the classes who comes from 'WEB-INF/classes' directory�� so when I need to reload a class�� I need to delete it from resourceEntries�� I write a method to call�� *public * *boolean* removeResourceEntry(String name) { *if* (resourceEntries.containsKey(name)) { resourceEntries.remove(name); *return* *true*; } *return* *false*; } 1. A flag point out whether WebappClassLoader need to reload a class *private* *boolean* isReload = *false*; *public* *boolean* isReload() { *return* isReload; } *public* *void* setReload(*boolean* isReload) { *this* .isReload = isReload; } 1. DynamicClassLoader I need to create a simple class loader to dynamic load class because ��One class loader only can load a class once��. �� *package* org.apache.catalina.loader; *import* java.net.URL; *import* java.net.URLClassLoader; *import* java.security.CodeSource; *import* java.util.*; /** * * [EMAIL PROTECTED] *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*; /** * [EMAIL PROTECTED] *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 . * * [EMAIL PROTECTED] *name * [EMAIL PROTECTED] *classData * [EMAIL PROTECTED] *codeSource * [EMAIL PROTECTED] * * [EMAIL PROTECTED] *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); } } 1. Add DynamicClassLoader into WebappClassLoader 1. Add property *private* DynamicClassLoader dynamicClassLoader = *new* DynamicClassLoader(* this*); 1. Add recreate method, that I can override old class loader when begin new reload. *public* *void* reCreateDynamicClassLoader() { dynamicClassLoader = *new* DynamicClassLoader(*this*); } 1. modify WebappClassLoader 1. line 832, set findClass method to public *public* Class findClass(String name) *throws* ClassNotFoundException { 1. line 1569: add next line code *if* (isReload) removeResourceEntry(name); 1. line1577: maybe is a bug, i forget the reason *if* ((entry == *null*) || (entry.binaryContent == *null*)) change to ==> *if* ((entry == *null*) || (entry.loadedClass == *null* && entry.binaryContent == *null*)) 1. line 1633~1636 *if* (entry.loadedClass == *null*) { clazz = defineClass(name, entry.binaryContent, 0, entry.binaryContent.length , codeSource); change to ==> * byte*[] classData = *new* *byte*[entry.binaryContent.length]; System.arraycopy(entry.binaryContent, 0, classData, 0, classData.length); *if* (entry.loadedClass == *null*) { clazz = isReload ? dynamicClassLoader.loadClass(name, classData, codeSource) : defineClass(name, classData, 0, classData.length, codeSource); 1. Test code 1. test.jsp I write to $CATALINA_HOME/webapps/ROOT/test.jsp�� I use reflection code to call WebappClassLoader method because it wouldn't load Tomcat kernel class in a web app�� <% 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[]{*true*}); Class A = (Class)findClass.invoke(loader, *new* Object[]{"org.AClass"}); setReload.invoke(loader, *new* Object[]{*false*}); A.newInstance(); // If u use next line code�� when recompile the class code�� please change the jsp code , also let jsp recompile. //org.AClass a = (org.AClass)A.newInstance(); // the next code it used to test when load a class who not in DynamicClassLoader.classNames list. //a.test(); //java.lang.reflect.Method test = a.getClass().getMethod("test", null); //test.invoke(a, null); %> 1. org.AClass *package* org; *public* *class* AClass { *public* AClass() { // u can change next line to test whethere the tomcat reload it System.out.println("AClass v3"); } *public* *void* createBClass() { *new* BClass(); } } 1. org.BClass *package* org; *public* *class* BClass { *public* BClass() { // u can change next line to test whethere the tomcat reload it System.out.println("BClass v1"); } } 1. Test step 1. modify and build Tomcat�� 2. compile org.AClass and org.BClass 3. startup Tomcat and use browser to browse http://localhost:8080/test.jsp 4. modify System.out.println(); in org.AClass and recompile it�� 5. refresh browser�� 6. see Tomcat console to check whether it reload the class? 7. Good Luck! :)))