You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Kurt Heston <kh...@delanobay.com> on 2002/12/16 09:59:11 UTC

custom ClassLoader Purgatory in Tomcat 3.3.1

Hello,

I'm having issues with using a custom classloader in Tomcat 3.3.1.  I have a
need to load servlets from a runtime-determined classpath.  The solution
worked fine in 3.2.x.  Here's what I think the problem is (let me know where
my understanding is flawed):

I'm pretty confident any custom classloader that tries to load servlets is
doomed on newer versions of Tomcat.  By this I mean, TC4x and TC5x won't
work either as they employ a similar classpath paradigm as that of TC3.3x.

It seems that the classloader alchemy the Jakarta people are doing to allow
web application classpaths to be independent of each other and that used by
the core Tomcat components is going to prevent me from instantiating a
servlet via a runtime-determined classpath.  A JVM process will only allow a
single instance of any class type within its process space.  Every JVM
process has a SystemClassLoader that manages loading the core java stuff,
like the String class.  Any other classLoader in the process space that
tries to load a String.class object after the SystemClassLoader has done so
will throw a NoClassDefFoundException or some other instantiation error.

What's nice about the SystemClassLoader is that any other ClassLoader in the
process can check with it to see whether it's created a certain class before
trying to do so, and use the one it has.  A ClassLoader can also do this if
it is chained with other class loaders.  It needs only traverse the chain
via the getParent() method and query each for the class in question.  The
problem with Tomcat creating its own class loaders is that my custom class
loader has no way of knowing what classes they've loaded nor can I use any
of those instances.  So, when I try to create my servlet object, its super
class, HttpServlet is also instantiated and pow, the JVM dumps a stack trace
claiming it can't find resources.

(sample Stack trace)
-----------------------
Attempting to load class: java.lang.NoClassDefFoundError

Class name: java.lang.NoClassDefFoundError
2002-12-15 23:51:07 - Ctx() : Exception in R(  + /servlet/MyServlet +
null) - java.lang.ExceptionInInitializerError:
java.util.MissingResourceException: Can't find bundle for base name
javax.servlet.http.LocalStrings, locale en_US
	at
java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:7
07)
	at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:678)
	at java.util.ResourceBundle.getBundle(ResourceBundle.java:541)
	at javax.servlet.http.HttpServlet.<clinit>(HttpServlet.java)
-----------------------

I'm guessing this is a nonsense error because the ResourceBundle it's
looking for is in the same jar that HttpServlet is in.  So, it's definitely
on the classpath.

Anyway, what else might I try to remedy this?

Thanks.


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: custom ClassLoader Purgatory in Tomcat 3.3.1

Posted by Bill Barker <wb...@wilshire.com>.
I'm still not sure what this is supposed to do, that can't be done much
easier using the standard J2EE-style ClassLoaders that Tomcat 3.3 and higher
provide.

In any case, your life will be made much easier if you use:
Thread.currentThread().getContextClassLoader()
as the parent of your CL (assuming that your code is being called from a
Servlet).  In Tomcat 3.3 and higher, this is always set to be the Web-app
ClassLoader while a Servlet is executing.  It should make a lot of the weird
errors go away, since you will be using the same ClassLoader as Tomcat to
load stuff like HttpServlet.

"Kurt Heston" <kh...@delanobay.com> wrote in message
news:NDBBLACHEFJEEEOOGAJNIEIFKGAB.kheston@delanobay.com...
> Have a look.  Let me know what you think.
> ---------------------------------------------------
>
> import java.io.*;
> import java.lang.reflect.Method;
> import java.net.*;
> import java.util.*;
>
> import javax.servlet.http.*;
> import javax.servlet.*;
>
> public class ServletCaller
> {
> public static void callServlet(
> String servletName,
> ServletConfig config,
> HttpServletRequest req,
> HttpServletResponse resp)
> throws Exception
> {
> Class[] doGetArgTypes =
> { HttpServletRequest.class, HttpServletResponse.class };
> Class[] initArgType = { ServletConfig.class };
> Object[] doGetArgs = { req, resp };
> Object[] initArg = { config };
>
> ServletClassLoader ldr = new ServletClassLoader(getParentLoader());
> Class clz = ldr.loadClass(servletName, true);
> Object servlet = clz.newInstance();  // Blows up here
>
> Method initMethod = clz.getMethod("init",initArgType);
> Method doGetMethod = clz.getMethod("doGet",doGetArgTypes);
> initMethod.invoke(servlet, initArg);
> doGetMethod.invoke(servlet, doGetArgs);
> // RequestDispatcher won't work!!!
> // Using a custom HttpResponse object to read servlet output
> }
>
> public static Class getServletClass(String servletName) throws Exception
> {
> ServletClassLoader ldr = new ServletClassLoader();
> Class clz = ldr.loadClass(servletName);
>
> return clz;
> }
>
> private static class ServletClassLoader extends ClassLoader
> {
>
> private Hashtable classes = new Hashtable();
> private String root;
>
> public ServletClassLoader(ClassLoader parent)
> {
> super(parent);
> String rootDir = System.getProperty("SERVLETDIR");
> if (rootDir == null)
> throw new IllegalArgumentException("Null root directory");
> root = rootDir;
> }
>
> public ServletClassLoader()
> {
> super(ClassLoader.getSystemClassLoader());
> String rootDir = System.getProperty("SERVLETDIR");
> if (rootDir == null)
> throw new IllegalArgumentException("Null root directory");
> root = rootDir;
> }
>
> protected Class loadClass(String name, boolean resolve)
> throws ClassNotFoundException
> {
> Class clas = null;
> System.out.println("\nAttempting to load class: " + name);
>
> clas = findClass(name);
> if (clas != null) {
> if (resolve)
> resolveClass(clas);
> return clas;
> }
>
> if (clas == null)
> {
> try {
> byte[] buff = loadClassData(name);
> clas = defineClass(name, buff, 0, buff.length);
> if (resolve)
> resolveClass(clas);
>
> }
> catch (IOException e)
> {
> throw new ClassNotFoundException(
> "Error reading file: " + name);
> }
> }
> classes.put(name, clas);
> return clas;
> }
>
> private byte[] loadClassData(String filename) throws IOException
> {
> File f = new File(root, filename + ".class");
> int size = (int) f.length();
> byte buff[] = new byte[size];
> FileInputStream fis = new FileInputStream(f);
> DataInputStream dis = new DataInputStream(fis);
> dis.readFully(buff);
> dis.close();
>
> return buff;
> }
>
> protected Class findClass(String name) throws ClassNotFoundException
> {
> Class clas = (Class) classes.get(name);
>
> System.out.println("\nClass name: " + name);
>
> try
> {
> if (clas == null)
> clas = super.findLoadedClass(name);
> if (clas == null)
> {
> ClassLoader parent = getParent();
> clas = parent.loadClass(name);
> }
> if (clas == null)
> clas = super.findSystemClass(name);
> }
> catch (ClassNotFoundException e)
> {}
> if (clas != null)
> {
> if (clas.getClassLoader() != null)
> System.out.println(
> "\tClassloader: " + clas.getClassLoader().toString());
>
> // Store for future use
> if (classes.get(name) == null)
> classes.put(name, clas);
> }
>
> return clas;
> }
>
> public InputStream getResourceAsStream(String name)
> {
> return getParent().getResourceAsStream(name);
> }
>
> public URL getResource(String name)
> {
> return getParent().getResource(name);
> }
>
> public Class loadClass(String name) throws ClassNotFoundException
> {
> return loadClass(name, false);
> }
>
> public URL findResource(String name)
> {
> return super.findResource(name);
> }
>
> protected Enumeration findResources(String name) throws IOException
> {
> return super.findResources(name);
> }
>
> }
>
> private static ClassLoader getParentLoader() throws Exception
> {
> // Vector classUrls = new Vector();
> // String tcPath = ""+System.getProperty("tc_path_add");
> //
> // // Get tomcat classpath for 3.3 and later
> // StringTokenizer tknzr = new StringTokenizer(
> // tcPath,File.pathSeparator);
> // while( tknzr.hasMoreTokens() ) {
> // String tkn = tknzr.nextToken();
> // System.out.println(tkn);
> // classUrls.add( new URL("file:"+tkn) );
> // }
> //
> // tknzr = new StringTokenizer(
> // System.getProperty("java.class.path"),File.pathSeparator);
> // while( tknzr.hasMoreTokens() )
> // classUrls.add( new URL("file:"+tknzr.nextToken()) );
> //
> // URL [] classPathArray = new URL[classUrls.size()];
> // classUrls.copyInto( classPathArray );
>
> URL[] classPathArray = new URL[] {
> new URL("file://D:/apache/tomcat331/lib/common/servlet.jar"),
> new URL("file://D:/apache/tomcat331/lib/common/connector_util.jar"),
> new URL("file://D:/apache/tomcat331/lib/common/core_util.jar"),
> new URL("file://D:/apache/tomcat331/lib/common/etomcat.jar"),
> new URL("file://D:/apache/tomcat331/lib/common/jasper-runtime.jar"),
> new URL("file://D:/apache/tomcat331/lib/common/tomcat_core.jar"),
> new URL("file://D:/apache/tomcat331/lib/apps/xalan.jar"),
> new URL("file://D:/apache/tomcat331/lib/apps/xerces.jar"),
> new URL("file://D:/apache/tomcat331/lib/apps/xml4j.jar"),
> new URL("file://D:/apache/tomcat331/lib/apps/xsltc.jar"),
> new URL("file://D:/apache/tomcat331/lib/stop-tomcat.jar"),
> new URL("file://D:/apache/tomcat331/lib/tomcat.jar"),
> new URL("file://D:/apache/tomcat331/lib/container/crimson.jar"),
> new URL("file://D:/apache/tomcat331/lib/container/facade22.jar"),
> new URL("file://D:/apache/tomcat331/lib/container/jasper.jar"),
> new URL("file://D:/apache/tomcat331/lib/container/tomcat_modules.jar"),
> new URL("file://D:/apache/tomcat331/lib/container/tomcat_util.jar"),
> };
>
> // URL other_CP = new URL( System.getProperty("java.class.path") );
> ClassLoader loader = new URLClassLoader(classPathArray);
>
> // String classPathStr = System.getProperty("java.class.path")
> // + File.pathSeparatorChar
> // + System.getProperty("tc_path_add");
> // System.setProperty("java.class.path", classPathStr);
> // System.out.println("\n\njava.class.path= "
> // +System.getProperty("java.class.path") + "\n\n");
>
> return loader;
> }
> }
>
> ---------------------------------------------------
>
>
> -----Original Message-----
> From: Jacob Kjome [mailto:hoju@visi.com]
> Sent: Monday, December 16, 2002 4:55 AM
> To: Tomcat Users List
> Subject: Re: custom ClassLoader Purgatory in Tomcat 3.3.1
>
>
>
> The classloader behavior for webapps is specified in the servlet spec to
be
> exactly opposite that of the normal Java2 classloading behavior.  So,
> classes in the WebappClassLoader will *not* as the parent to load classes
> for it unless it can't find the class to load there first.  That's one
> thing to remember.  The other thing is that parent classloaders can't see
> their children, but children can see their parents.  If you have a class
in
> a parent classloader which is trying to load classes or other resources in
> the child classloader, you will have problems....unless you use the thread
> context classloader.  If you use Class.forName(String) you are asking for
> trouble.  The other version of Class.forName() which takes 3 parameters is
> your ticket out of this situation since you can pass it the thread context
> classloader to load the resource.
>
> Anyway, I'm pretty sure you will be able to do what you are attempting to
> do, but without sample code, no one can help you.
>
> Jake
>
> At 12:59 AM 12/16/2002 -0800, you wrote:
> >Hello,
> >
> >I'm having issues with using a custom classloader in Tomcat 3.3.1.  I
have
> a
> >need to load servlets from a runtime-determined classpath.  The solution
> >worked fine in 3.2.x.  Here's what I think the problem is (let me know
> where
> >my understanding is flawed):
> >
> >I'm pretty confident any custom classloader that tries to load servlets
is
> >doomed on newer versions of Tomcat.  By this I mean, TC4x and TC5x won't
> >work either as they employ a similar classpath paradigm as that of
TC3.3x.
> >
> >It seems that the classloader alchemy the Jakarta people are doing to
allow
> >web application classpaths to be independent of each other and that used
by
> >the core Tomcat components is going to prevent me from instantiating a
> >servlet via a runtime-determined classpath.  A JVM process will only
allow
> a
> >single instance of any class type within its process space.  Every JVM
> >process has a SystemClassLoader that manages loading the core java stuff,
> >like the String class.  Any other classLoader in the process space that
> >tries to load a String.class object after the SystemClassLoader has done
so
> >will throw a NoClassDefFoundException or some other instantiation error.
> >
> >What's nice about the SystemClassLoader is that any other ClassLoader in
> the
> >process can check with it to see whether it's created a certain class
> before
> >trying to do so, and use the one it has.  A ClassLoader can also do this
if
> >it is chained with other class loaders.  It needs only traverse the chain
> >via the getParent() method and query each for the class in question.  The
> >problem with Tomcat creating its own class loaders is that my custom
class
> >loader has no way of knowing what classes they've loaded nor can I use
any
> >of those instances.  So, when I try to create my servlet object, its
super
> >class, HttpServlet is also instantiated and pow, the JVM dumps a stack
> trace
> >claiming it can't find resources.
> >
> >(sample Stack trace)
> >-----------------------
> >Attempting to load class: java.lang.NoClassDefFoundError
> >
> >Class name: java.lang.NoClassDefFoundError
> >2002-12-15 23:51:07 - Ctx() : Exception in R(  + /servlet/MyServlet +
> >null) - java.lang.ExceptionInInitializerError:
> >java.util.MissingResourceException: Can't find bundle for base name
> >javax.servlet.http.LocalStrings, locale en_US
> >         at
>
>java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:
> 7
> >07)
> >         at
java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:678)
> >         at java.util.ResourceBundle.getBundle(ResourceBundle.java:541)
> >         at javax.servlet.http.HttpServlet.<clinit>(HttpServlet.java)
> >-----------------------
> >
> >I'm guessing this is a nonsense error because the ResourceBundle it's
> >looking for is in the same jar that HttpServlet is in.  So, it's
definitely
> >on the classpath.
> >
> >Anyway, what else might I try to remedy this?
> >
> >Thanks.
> >
> >
> >--
> >To unsubscribe, e-mail:
> <ma...@jakarta.apache.org>
> >For additional commands, e-mail:
> <ma...@jakarta.apache.org>





--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re[2]: custom ClassLoader Purgatory in Tomcat 3.3.1

Posted by Jacob Kjome <ho...@visi.com>.
Hello Kurt,

I can see why you might want to do this to create your own sort of
mini-container running outside another running Tomcat's JVM, but It
appears that you are running this within a currently running Tomcat
container.  Why do you need to do this?  To be honest, I know how to
deal with some common classloading issues, but I don't know so much
about using a custom classloader.  This might be a Tomcat-Dev list
question.

Jake

Monday, December 16, 2002, 9:35:07 AM, you wrote:

KH> Have a look.  Let me know what you think.
KH> ---------------------------------------------------

KH> import java.io.*;
KH> import java.lang.reflect.Method;
KH> import java.net.*;
KH> import java.util.*;

KH> import javax.servlet.http.*;
KH> import javax.servlet.*;

KH> public class ServletCaller
KH> {
KH>         public static void callServlet(
KH>                 String servletName,
KH>                 ServletConfig config,
KH>                 HttpServletRequest req,
KH>                 HttpServletResponse resp)
KH>                 throws Exception
KH>         {
KH>                 Class[] doGetArgTypes =
KH>                         { HttpServletRequest.class, HttpServletResponse.class };
KH>                 Class[] initArgType = { ServletConfig.class };
KH>                 Object[] doGetArgs = { req, resp };
KH>                 Object[] initArg = { config };

KH>                 ServletClassLoader ldr = new ServletClassLoader(getParentLoader());
KH>                 Class clz = ldr.loadClass(servletName, true);
KH>                 Object servlet = clz.newInstance();  // Blows up here

KH>                 Method initMethod = clz.getMethod("init",initArgType);
KH>                 Method doGetMethod = clz.getMethod("doGet",doGetArgTypes);
KH>                 initMethod.invoke(servlet, initArg);
KH>                 doGetMethod.invoke(servlet, doGetArgs);
KH>                 // RequestDispatcher won't work!!!
KH>                 // Using a custom HttpResponse object to read servlet output
KH>         }

KH>         public static Class getServletClass(String servletName) throws Exception
KH>         {
KH>                 ServletClassLoader ldr = new ServletClassLoader();
KH>                 Class clz = ldr.loadClass(servletName);

KH>                 return clz;
KH>         }

KH>         private static class ServletClassLoader extends ClassLoader
KH>         {

KH>                 private Hashtable classes = new Hashtable();
KH>                 private String root;

KH>                 public ServletClassLoader(ClassLoader parent)
KH>                 {
KH>                         super(parent);
KH>                         String rootDir = System.getProperty("SERVLETDIR");
KH>                         if (rootDir == null)
KH>                                 throw new IllegalArgumentException("Null root directory");
KH>                         root = rootDir;
KH>                 }

KH>                 public ServletClassLoader()
KH>                 {
KH>                         super(ClassLoader.getSystemClassLoader());
KH>                         String rootDir = System.getProperty("SERVLETDIR");
KH>                         if (rootDir == null)
KH>                                 throw new IllegalArgumentException("Null root directory");
KH>                         root = rootDir;
KH>                 }

KH>                 protected Class loadClass(String name, boolean resolve)
KH>                         throws ClassNotFoundException
KH>                 {
KH>                         Class clas = null;
KH>                         System.out.println("\nAttempting to load class: " + name);

KH>                         clas = findClass(name);
KH>                         if (clas != null) {
KH>                                 if (resolve)
KH>                                         resolveClass(clas);
KH>                                 return clas;
KH>                         }

KH>                         if (clas == null)
KH>                         {
KH>                                 try {
KH>                                         byte[] buff = loadClassData(name);
KH>                                         clas = defineClass(name, buff, 0, buff.length);
KH>                                         if (resolve)
KH>                                                 resolveClass(clas);

KH>                                 }
KH>                                 catch (IOException e)
KH>                                 {
KH>                                         throw new ClassNotFoundException(
KH>                                                 "Error reading file: " + name);
KH>                                 }
KH>                         }
KH>                         classes.put(name, clas);
KH>                         return clas;
KH>                 }

KH>                 private byte[] loadClassData(String filename) throws IOException
KH>                 {
KH>                         File f = new File(root, filename + ".class");
KH>                         int size = (int) f.length();
KH>                         byte buff[] = new byte[size];
KH>                         FileInputStream fis = new FileInputStream(f);
KH>                         DataInputStream dis = new DataInputStream(fis);
KH>                         dis.readFully(buff);
KH>                         dis.close();

KH>                         return buff;
KH>                 }

KH>                 protected Class findClass(String name) throws ClassNotFoundException
KH>                 {
KH>                         Class clas = (Class) classes.get(name);

KH>                         System.out.println("\nClass name: " + name);

KH>                         try
KH>                         {
KH>                                 if (clas == null)
KH>                                         clas = super.findLoadedClass(name);
KH>                                 if (clas == null)
KH>                                 {
KH>                                         ClassLoader parent = getParent();
KH>                                         clas = parent.loadClass(name);
KH>                                 }
KH>                                 if (clas == null)
KH>                                         clas = super.findSystemClass(name);
KH>                         }
KH>                         catch (ClassNotFoundException e)
KH>                         {}
KH>                         if (clas != null)
KH>                         {
KH>                                 if (clas.getClassLoader() != null)
KH>                                         System.out.println(
KH>                                                 "\tClassloader: " + clas.getClassLoader().toString());

KH>                                 // Store for future use
KH>                                 if (classes.get(name) == null)
KH>                                         classes.put(name, clas);
KH>                         }

KH>                         return clas;
KH>                 }

KH>                 public InputStream getResourceAsStream(String name)
KH>                 {
KH>                         return getParent().getResourceAsStream(name);
KH>                 }

KH>                 public URL getResource(String name)
KH>                 {
KH>                         return getParent().getResource(name);
KH>                 }

KH>                 public Class loadClass(String name) throws ClassNotFoundException
KH>                 {
KH>                         return loadClass(name, false);
KH>                 }

KH>                 public URL findResource(String name)
KH>                 {
KH>                         return super.findResource(name);
KH>                 }

KH>                 protected Enumeration findResources(String name) throws IOException
KH>                 {
KH>                         return super.findResources(name);
KH>                 }

KH>         }

KH>         private static ClassLoader getParentLoader() throws Exception
KH>         {
KH>                 //              Vector classUrls = new Vector();
KH>                 //              String tcPath = ""+System.getProperty("tc_path_add");
KH>                 //
KH>                 //              // Get tomcat classpath for 3.3 and later
KH>                 //              StringTokenizer tknzr = new StringTokenizer(
KH>                 //                      tcPath,File.pathSeparator);
KH>                 //              while( tknzr.hasMoreTokens() ) {
KH>                 //                      String tkn = tknzr.nextToken();
KH>                 //                      System.out.println(tkn);
KH>                 //                      classUrls.add( new URL("file:"+tkn) );
KH>                 //              }
KH>                 //
KH>                 //              tknzr = new StringTokenizer(
KH>                 //                      System.getProperty("java.class.path"),File.pathSeparator);
KH>                 //              while( tknzr.hasMoreTokens() )
KH>                 //                      classUrls.add( new URL("file:"+tknzr.nextToken()) );
KH>                 //
KH>                 //              URL [] classPathArray = new URL[classUrls.size()];
KH>                 //              classUrls.copyInto( classPathArray );

KH>                 URL[] classPathArray = new URL[] {
KH>                                 new URL("file://D:/apache/tomcat331/lib/common/servlet.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/common/connector_util.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/common/core_util.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/common/etomcat.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/common/jasper-runtime.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/common/tomcat_core.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/apps/xalan.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/apps/xerces.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/apps/xml4j.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/apps/xsltc.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/stop-tomcat.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/tomcat.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/container/crimson.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/container/facade22.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/container/jasper.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/container/tomcat_modules.jar"),
KH>                                 new URL("file://D:/apache/tomcat331/lib/container/tomcat_util.jar"),
KH>                                 };

KH>                 //              URL other_CP = new URL( System.getProperty("java.class.path") );
KH>                 ClassLoader loader = new URLClassLoader(classPathArray);

KH>                 //              String classPathStr = System.getProperty("java.class.path")
KH>                 //                      + File.pathSeparatorChar
KH>                 //                      + System.getProperty("tc_path_add");
KH>                 //              System.setProperty("java.class.path", classPathStr);
KH>                 //              System.out.println("\n\njava.class.path= "
KH>                 //                      +System.getProperty("java.class.path") + "\n\n");

KH>                 return loader;
KH>         }
KH> }

KH> ---------------------------------------------------


KH> -----Original Message-----
KH> From: Jacob Kjome [mailto:hoju@visi.com]
KH> Sent: Monday, December 16, 2002 4:55 AM
KH> To: Tomcat Users List
KH> Subject: Re: custom ClassLoader Purgatory in Tomcat 3.3.1



KH> The classloader behavior for webapps is specified in the servlet spec to be
KH> exactly opposite that of the normal Java2 classloading behavior.  So,
KH> classes in the WebappClassLoader will *not* as the parent to load classes
KH> for it unless it can't find the class to load there first.  That's one
KH> thing to remember.  The other thing is that parent classloaders can't see
KH> their children, but children can see their parents.  If you have a class in
KH> a parent classloader which is trying to load classes or other resources in
KH> the child classloader, you will have problems....unless you use the thread
KH> context classloader.  If you use Class.forName(String) you are asking for
KH> trouble.  The other version of Class.forName() which takes 3 parameters is
KH> your ticket out of this situation since you can pass it the thread context
KH> classloader to load the resource.

KH> Anyway, I'm pretty sure you will be able to do what you are attempting to
KH> do, but without sample code, no one can help you.

KH> Jake

KH> At 12:59 AM 12/16/2002 -0800, you wrote:
>>Hello,
>>
>>I'm having issues with using a custom classloader in Tomcat 3.3.1.  I have
KH> a
>>need to load servlets from a runtime-determined classpath.  The solution
>>worked fine in 3.2.x.  Here's what I think the problem is (let me know
KH> where
>>my understanding is flawed):
>>
>>I'm pretty confident any custom classloader that tries to load servlets is
>>doomed on newer versions of Tomcat.  By this I mean, TC4x and TC5x won't
>>work either as they employ a similar classpath paradigm as that of TC3.3x.
>>
>>It seems that the classloader alchemy the Jakarta people are doing to allow
>>web application classpaths to be independent of each other and that used by
>>the core Tomcat components is going to prevent me from instantiating a
>>servlet via a runtime-determined classpath.  A JVM process will only allow
KH> a
>>single instance of any class type within its process space.  Every JVM
>>process has a SystemClassLoader that manages loading the core java stuff,
>>like the String class.  Any other classLoader in the process space that
>>tries to load a String.class object after the SystemClassLoader has done so
>>will throw a NoClassDefFoundException or some other instantiation error.
>>
>>What's nice about the SystemClassLoader is that any other ClassLoader in
KH> the
>>process can check with it to see whether it's created a certain class
KH> before
>>trying to do so, and use the one it has.  A ClassLoader can also do this if
>>it is chained with other class loaders.  It needs only traverse the chain
>>via the getParent() method and query each for the class in question.  The
>>problem with Tomcat creating its own class loaders is that my custom class
>>loader has no way of knowing what classes they've loaded nor can I use any
>>of those instances.  So, when I try to create my servlet object, its super
>>class, HttpServlet is also instantiated and pow, the JVM dumps a stack
KH> trace
>>claiming it can't find resources.
>>
>>(sample Stack trace)
>>-----------------------
>>Attempting to load class: java.lang.NoClassDefFoundError
>>
>>Class name: java.lang.NoClassDefFoundError
>>2002-12-15 23:51:07 - Ctx() : Exception in R(  + /servlet/MyServlet +
>>null) - java.lang.ExceptionInInitializerError:
>>java.util.MissingResourceException: Can't find bundle for base name
>>javax.servlet.http.LocalStrings, locale en_US
>>         at
>>java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:
KH> 7
>>07)
>>         at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:678)
>>         at java.util.ResourceBundle.getBundle(ResourceBundle.java:541)
>>         at javax.servlet.http.HttpServlet.<clinit>(HttpServlet.java)
>>-----------------------
>>
>>I'm guessing this is a nonsense error because the ResourceBundle it's
>>looking for is in the same jar that HttpServlet is in.  So, it's definitely
>>on the classpath.
>>
>>Anyway, what else might I try to remedy this?
>>
>>Thanks.
>>
>>
>>--
>>To unsubscribe, e-mail:
KH> <ma...@jakarta.apache.org>
>>For additional commands, e-mail:
KH> <ma...@jakarta.apache.org>


KH> --
KH> To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
KH> For additional commands, e-mail: <ma...@jakarta.apache.org>



-- 
Best regards,
 Jacob                            mailto:hoju@visi.com


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


RE: custom ClassLoader Purgatory in Tomcat 3.3.1

Posted by Kurt Heston <kh...@delanobay.com>.
Have a look.  Let me know what you think.
---------------------------------------------------

import java.io.*;
import java.lang.reflect.Method;
import java.net.*;
import java.util.*;

import javax.servlet.http.*;
import javax.servlet.*;

public class ServletCaller
{
	public static void callServlet(
		String servletName,
		ServletConfig config,
		HttpServletRequest req,
		HttpServletResponse resp)
		throws Exception
	{
		Class[] doGetArgTypes =
			{ HttpServletRequest.class, HttpServletResponse.class };
		Class[] initArgType = { ServletConfig.class };
		Object[] doGetArgs = { req, resp };
		Object[] initArg = { config };

		ServletClassLoader ldr = new ServletClassLoader(getParentLoader());
		Class clz = ldr.loadClass(servletName, true);
		Object servlet = clz.newInstance();  // Blows up here

		Method initMethod = clz.getMethod("init",initArgType);
		Method doGetMethod = clz.getMethod("doGet",doGetArgTypes);
		initMethod.invoke(servlet, initArg);
		doGetMethod.invoke(servlet, doGetArgs);
		// RequestDispatcher won't work!!!
		// Using a custom HttpResponse object to read servlet output
	}

	public static Class getServletClass(String servletName) throws Exception
	{
		ServletClassLoader ldr = new ServletClassLoader();
		Class clz = ldr.loadClass(servletName);

		return clz;
	}

	private static class ServletClassLoader extends ClassLoader
	{

		private Hashtable classes = new Hashtable();
		private String root;

		public ServletClassLoader(ClassLoader parent)
		{
			super(parent);
			String rootDir = System.getProperty("SERVLETDIR");
			if (rootDir == null)
				throw new IllegalArgumentException("Null root directory");
			root = rootDir;
		}

		public ServletClassLoader()
		{
			super(ClassLoader.getSystemClassLoader());
			String rootDir = System.getProperty("SERVLETDIR");
			if (rootDir == null)
				throw new IllegalArgumentException("Null root directory");
			root = rootDir;
		}

		protected Class loadClass(String name, boolean resolve)
			throws ClassNotFoundException
		{
			Class clas = null;
			System.out.println("\nAttempting to load class: " + name);

			clas = findClass(name);
			if (clas != null) {
				if (resolve)
					resolveClass(clas);
				return clas;
			}

			if (clas == null)
			{
				try {
					byte[] buff = loadClassData(name);
					clas = defineClass(name, buff, 0, buff.length);
					if (resolve)
						resolveClass(clas);

				}
				catch (IOException e)
				{
					throw new ClassNotFoundException(
						"Error reading file: " + name);
				}
			}
			classes.put(name, clas);
			return clas;
		}

		private byte[] loadClassData(String filename) throws IOException
		{
			File f = new File(root, filename + ".class");
			int size = (int) f.length();
			byte buff[] = new byte[size];
			FileInputStream fis = new FileInputStream(f);
			DataInputStream dis = new DataInputStream(fis);
			dis.readFully(buff);
			dis.close();

			return buff;
		}

		protected Class findClass(String name) throws ClassNotFoundException
		{
			Class clas = (Class) classes.get(name);

			System.out.println("\nClass name: " + name);

			try
			{
				if (clas == null)
					clas = super.findLoadedClass(name);
				if (clas == null)
				{
					ClassLoader parent = getParent();
					clas = parent.loadClass(name);
				}
				if (clas == null)
					clas = super.findSystemClass(name);
			}
			catch (ClassNotFoundException e)
			{}
			if (clas != null)
			{
				if (clas.getClassLoader() != null)
					System.out.println(
						"\tClassloader: " + clas.getClassLoader().toString());

				// Store for future use
				if (classes.get(name) == null)
					classes.put(name, clas);
			}

			return clas;
		}

		public InputStream getResourceAsStream(String name)
		{
			return getParent().getResourceAsStream(name);
		}

		public URL getResource(String name)
		{
			return getParent().getResource(name);
		}

		public Class loadClass(String name) throws ClassNotFoundException
		{
			return loadClass(name, false);
		}

		public URL findResource(String name)
		{
			return super.findResource(name);
		}

		protected Enumeration findResources(String name) throws IOException
		{
			return super.findResources(name);
		}

	}

	private static ClassLoader getParentLoader() throws Exception
	{
		//		Vector classUrls = new Vector();
		//		String tcPath = ""+System.getProperty("tc_path_add");
		//
		//		// Get tomcat classpath for 3.3 and later
		//		StringTokenizer tknzr = new StringTokenizer(
		//			tcPath,File.pathSeparator);
		//		while( tknzr.hasMoreTokens() ) {
		//			String tkn = tknzr.nextToken();
		//			System.out.println(tkn);
		//			classUrls.add( new URL("file:"+tkn) );
		//		}
		//
		//		tknzr = new StringTokenizer(
		//			System.getProperty("java.class.path"),File.pathSeparator);
		//		while( tknzr.hasMoreTokens() )
		//			classUrls.add( new URL("file:"+tknzr.nextToken()) );
		//
		//		URL [] classPathArray = new URL[classUrls.size()];
		//		classUrls.copyInto( classPathArray );

		URL[] classPathArray = new URL[] {
				new URL("file://D:/apache/tomcat331/lib/common/servlet.jar"),
				new URL("file://D:/apache/tomcat331/lib/common/connector_util.jar"),
				new URL("file://D:/apache/tomcat331/lib/common/core_util.jar"),
				new URL("file://D:/apache/tomcat331/lib/common/etomcat.jar"),
				new URL("file://D:/apache/tomcat331/lib/common/jasper-runtime.jar"),
				new URL("file://D:/apache/tomcat331/lib/common/tomcat_core.jar"),
				new URL("file://D:/apache/tomcat331/lib/apps/xalan.jar"),
				new URL("file://D:/apache/tomcat331/lib/apps/xerces.jar"),
				new URL("file://D:/apache/tomcat331/lib/apps/xml4j.jar"),
				new URL("file://D:/apache/tomcat331/lib/apps/xsltc.jar"),
				new URL("file://D:/apache/tomcat331/lib/stop-tomcat.jar"),
				new URL("file://D:/apache/tomcat331/lib/tomcat.jar"),
				new URL("file://D:/apache/tomcat331/lib/container/crimson.jar"),
				new URL("file://D:/apache/tomcat331/lib/container/facade22.jar"),
				new URL("file://D:/apache/tomcat331/lib/container/jasper.jar"),
				new URL("file://D:/apache/tomcat331/lib/container/tomcat_modules.jar"),
				new URL("file://D:/apache/tomcat331/lib/container/tomcat_util.jar"),
				};

		//		URL other_CP = new URL( System.getProperty("java.class.path") );
		ClassLoader loader = new URLClassLoader(classPathArray);

		//		String classPathStr = System.getProperty("java.class.path")
		//			+ File.pathSeparatorChar
		//			+ System.getProperty("tc_path_add");
		//		System.setProperty("java.class.path", classPathStr);
		//		System.out.println("\n\njava.class.path= "
		//			+System.getProperty("java.class.path") + "\n\n");

		return loader;
	}
}

---------------------------------------------------


-----Original Message-----
From: Jacob Kjome [mailto:hoju@visi.com]
Sent: Monday, December 16, 2002 4:55 AM
To: Tomcat Users List
Subject: Re: custom ClassLoader Purgatory in Tomcat 3.3.1



The classloader behavior for webapps is specified in the servlet spec to be
exactly opposite that of the normal Java2 classloading behavior.  So,
classes in the WebappClassLoader will *not* as the parent to load classes
for it unless it can't find the class to load there first.  That's one
thing to remember.  The other thing is that parent classloaders can't see
their children, but children can see their parents.  If you have a class in
a parent classloader which is trying to load classes or other resources in
the child classloader, you will have problems....unless you use the thread
context classloader.  If you use Class.forName(String) you are asking for
trouble.  The other version of Class.forName() which takes 3 parameters is
your ticket out of this situation since you can pass it the thread context
classloader to load the resource.

Anyway, I'm pretty sure you will be able to do what you are attempting to
do, but without sample code, no one can help you.

Jake

At 12:59 AM 12/16/2002 -0800, you wrote:
>Hello,
>
>I'm having issues with using a custom classloader in Tomcat 3.3.1.  I have
a
>need to load servlets from a runtime-determined classpath.  The solution
>worked fine in 3.2.x.  Here's what I think the problem is (let me know
where
>my understanding is flawed):
>
>I'm pretty confident any custom classloader that tries to load servlets is
>doomed on newer versions of Tomcat.  By this I mean, TC4x and TC5x won't
>work either as they employ a similar classpath paradigm as that of TC3.3x.
>
>It seems that the classloader alchemy the Jakarta people are doing to allow
>web application classpaths to be independent of each other and that used by
>the core Tomcat components is going to prevent me from instantiating a
>servlet via a runtime-determined classpath.  A JVM process will only allow
a
>single instance of any class type within its process space.  Every JVM
>process has a SystemClassLoader that manages loading the core java stuff,
>like the String class.  Any other classLoader in the process space that
>tries to load a String.class object after the SystemClassLoader has done so
>will throw a NoClassDefFoundException or some other instantiation error.
>
>What's nice about the SystemClassLoader is that any other ClassLoader in
the
>process can check with it to see whether it's created a certain class
before
>trying to do so, and use the one it has.  A ClassLoader can also do this if
>it is chained with other class loaders.  It needs only traverse the chain
>via the getParent() method and query each for the class in question.  The
>problem with Tomcat creating its own class loaders is that my custom class
>loader has no way of knowing what classes they've loaded nor can I use any
>of those instances.  So, when I try to create my servlet object, its super
>class, HttpServlet is also instantiated and pow, the JVM dumps a stack
trace
>claiming it can't find resources.
>
>(sample Stack trace)
>-----------------------
>Attempting to load class: java.lang.NoClassDefFoundError
>
>Class name: java.lang.NoClassDefFoundError
>2002-12-15 23:51:07 - Ctx() : Exception in R(  + /servlet/MyServlet +
>null) - java.lang.ExceptionInInitializerError:
>java.util.MissingResourceException: Can't find bundle for base name
>javax.servlet.http.LocalStrings, locale en_US
>         at
>java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:
7
>07)
>         at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:678)
>         at java.util.ResourceBundle.getBundle(ResourceBundle.java:541)
>         at javax.servlet.http.HttpServlet.<clinit>(HttpServlet.java)
>-----------------------
>
>I'm guessing this is a nonsense error because the ResourceBundle it's
>looking for is in the same jar that HttpServlet is in.  So, it's definitely
>on the classpath.
>
>Anyway, what else might I try to remedy this?
>
>Thanks.
>
>
>--
>To unsubscribe, e-mail:
<ma...@jakarta.apache.org>
>For additional commands, e-mail:
<ma...@jakarta.apache.org>


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: custom ClassLoader Purgatory in Tomcat 3.3.1

Posted by Jacob Kjome <ho...@visi.com>.
The classloader behavior for webapps is specified in the servlet spec to be 
exactly opposite that of the normal Java2 classloading behavior.  So, 
classes in the WebappClassLoader will *not* as the parent to load classes 
for it unless it can't find the class to load there first.  That's one 
thing to remember.  The other thing is that parent classloaders can't see 
their children, but children can see their parents.  If you have a class in 
a parent classloader which is trying to load classes or other resources in 
the child classloader, you will have problems....unless you use the thread 
context classloader.  If you use Class.forName(String) you are asking for 
trouble.  The other version of Class.forName() which takes 3 parameters is 
your ticket out of this situation since you can pass it the thread context 
classloader to load the resource.

Anyway, I'm pretty sure you will be able to do what you are attempting to 
do, but without sample code, no one can help you.

Jake

At 12:59 AM 12/16/2002 -0800, you wrote:
>Hello,
>
>I'm having issues with using a custom classloader in Tomcat 3.3.1.  I have a
>need to load servlets from a runtime-determined classpath.  The solution
>worked fine in 3.2.x.  Here's what I think the problem is (let me know where
>my understanding is flawed):
>
>I'm pretty confident any custom classloader that tries to load servlets is
>doomed on newer versions of Tomcat.  By this I mean, TC4x and TC5x won't
>work either as they employ a similar classpath paradigm as that of TC3.3x.
>
>It seems that the classloader alchemy the Jakarta people are doing to allow
>web application classpaths to be independent of each other and that used by
>the core Tomcat components is going to prevent me from instantiating a
>servlet via a runtime-determined classpath.  A JVM process will only allow a
>single instance of any class type within its process space.  Every JVM
>process has a SystemClassLoader that manages loading the core java stuff,
>like the String class.  Any other classLoader in the process space that
>tries to load a String.class object after the SystemClassLoader has done so
>will throw a NoClassDefFoundException or some other instantiation error.
>
>What's nice about the SystemClassLoader is that any other ClassLoader in the
>process can check with it to see whether it's created a certain class before
>trying to do so, and use the one it has.  A ClassLoader can also do this if
>it is chained with other class loaders.  It needs only traverse the chain
>via the getParent() method and query each for the class in question.  The
>problem with Tomcat creating its own class loaders is that my custom class
>loader has no way of knowing what classes they've loaded nor can I use any
>of those instances.  So, when I try to create my servlet object, its super
>class, HttpServlet is also instantiated and pow, the JVM dumps a stack trace
>claiming it can't find resources.
>
>(sample Stack trace)
>-----------------------
>Attempting to load class: java.lang.NoClassDefFoundError
>
>Class name: java.lang.NoClassDefFoundError
>2002-12-15 23:51:07 - Ctx() : Exception in R(  + /servlet/MyServlet +
>null) - java.lang.ExceptionInInitializerError:
>java.util.MissingResourceException: Can't find bundle for base name
>javax.servlet.http.LocalStrings, locale en_US
>         at
>java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:7
>07)
>         at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:678)
>         at java.util.ResourceBundle.getBundle(ResourceBundle.java:541)
>         at javax.servlet.http.HttpServlet.<clinit>(HttpServlet.java)
>-----------------------
>
>I'm guessing this is a nonsense error because the ResourceBundle it's
>looking for is in the same jar that HttpServlet is in.  So, it's definitely
>on the classpath.
>
>Anyway, what else might I try to remedy this?
>
>Thanks.
>
>
>--
>To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
>For additional commands, e-mail: <ma...@jakarta.apache.org>