*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! :)))

Reply via email to