Hi all! A lot of people has been having problems with Tomcat 3.2, classloaders, jndi, jndi.properties, rmi and stubs when trying to call ejbs from Tomcat in a different JVM. The problem is simple: because jndi and rmi classes are loaded by the system (or bootstrap, I can't remember it but it really doesn't matters) classloader -see <jdk1.3>\docs\tooldocs\findingclasses.html - they can not see the resources and classes of your context (loaded by the context classloader, which is a descendant -i mean in the classloader delegation hierarchy- of the system classloader). An obvious and bad solution is to put your classes in the CLASSPATH (don't do this!) to allow them to be loaded once for all of your contexts by the system classloader. Instead, to fix the problem you should add the request interceptor showed below to your server.xml (I have read an email saying that it should be the last request interceptor in the chain so I put it the last, but I haven't tried with another combinations): <RequestInterceptor className="org.apache.tomcat.request.Jdk12Interceptor" /> This is in order to call the Thread.setContextClassLoader() during each request. The following is taken from a post by C. McClanahan in tomcat-user: "Note that Tomcat 4.0, because it is guaranteed a Java2 platform as a prerequisite, calls Thread.setContextClassLoader() on every request by default". But even after doing this, jndi.properties in your WEB-INF/classes (or in a .jar in WEB-INF/lib) won't be loaded. Keep reading the following paragraphs quoted from an email by Christopher Audley in tomcat-dev! "" The following discussion applies to tomcat 3.2.1 running under Sun JDK 1.3 I spent this afternoon tracking down why a call to new InitialContext() from a web application did not appear to be finding the jndi.properties located under WEB-INF/classes. I am using the Jdk12Interceptor and verified that Thread.currentThread().getClassLoader() was the tomcat AdaptiveClassLoader before the call to new InitialContext(). After some investigation I determined that the JNDI implementation uses the classloader method getResources to find all occurances of the file jndi.properties in the classpath. AdaptiveClassLoader does not have this method implemented, the default implementation calls findResources which simply returns an empty Enumeration unless overriden. Attached are patches to AdaptiveClassLoader and ClassRepository to implement findResources so that JNDI will behave correctly when a jndi.properties is placed in the web application classpath. I have moved some code from AdaptiveClassLoader to ClassRepository to avoid code duplication and changed the implementation of getResource to findResource for consistency. I hope that this can be incorporated into the sources before 3.2.2. "" Finally, after aplying the patch (I've attached it as you can see) and recompiling the sources you get the desired behaviour. See you, Carlos -------------------------------------------------------------- diff -ubr jakarta-tomcat-3.2.1-src.orig/src/share/org/apache/tomcat/loader/AdaptiveCla ssLoader.java jakarta-tomcat-3.2.1-src/src/share/org/apache/tomcat/loader/AdaptiveClassLoa der.java --- jakarta-tomcat-3.2.1-src.orig/src/share/org/apache/tomcat/loader/AdaptiveCla ssLoader.java Sun Mar 11 19:21:12 2001 +++ jakarta-tomcat-3.2.1-src/src/share/org/apache/tomcat/loader/AdaptiveClassLoa der.java Sun Mar 11 18:40:46 2001 @@ -762,69 +762,45 @@ * @param name the name of the resource, to be used as is. * @return an URL on the resource, or null if not found. */ - public URL getResource(String name) { - if( debug > 0 ) log( "getResource() " + name ); - // First ask the parent class loader to fetch it, if possible - URL u = null; - if (parent != null) { - u = parent.getResource(name); - if (u != null) - return (u); - } - // Second ask the system class loader to fetch it from the classpath - u = getSystemResource(name); - if (u != null) { - return u; - } - + protected URL findResource(String name) { if (name == null) { return null; } - // Third, check our own repositories + // check our own repositories Enumeration repEnum = repository.elements(); while (repEnum.hasMoreElements()) { ClassRepository cp = (ClassRepository) repEnum.nextElement(); - File file = cp.getFile(); - // Construct a file://-URL if the repository is a directory - if (file.isDirectory()) { - String fileName = name.replace('/', File.separatorChar); - File resFile = new File(file, fileName); - if (resFile.exists()) { - // Build a file:// URL form the file name - try { - return new URL("file", null, resFile.getAbsolutePath()); - } catch(java.net.MalformedURLException badurl) { - badurl.printStackTrace(); - return null; - } + URL resourceURL = cp.findResource(name); + if ( resourceURL != null ) + return resourceURL; } - } - else { - // a jar:-URL *could* change even between minor releases, but - // didn't between JVM's 1.1.6 and 1.3beta. Tested on JVM's from - // IBM, Blackdown, Microsoft, Sun @ Windows and Sun @ Solaris - try { - ZipFile zf = new ZipFile(file.getAbsolutePath()); - ZipEntry ze = zf.getEntry(name); - if (ze != null) { - try { - return new URL("jar:file:" + file.getAbsolutePath() + "!/" + name); - } catch(java.net.MalformedURLException badurl) { - badurl.printStackTrace(); - return null; - } - } - } catch (IOException ioe) { - ioe.printStackTrace(); + // Not found return null; } + + /** + * Find all occurences of a given resource in the class loaders + * repositories. + * + * @param name the name of the resource, to be used as is. + * @return an Enumeration of URLs of to this resource. + */ + protected Enumeration findResources(String name) { + if( debug > 0 ) log( "findResources() " + name ); + + Vector urls = new Vector( repository.size() ); + if ( name != null ) { + Enumeration repEnum = repository.elements(); + while( repEnum.hasMoreElements() ) { + ClassRepository cp = (ClassRepository) repEnum.nextElement(); + URL resourceURL = cp.findResource(name); + if( resourceURL != null ) + urls.add( resourceURL ); } } - - // Not found - return null; + return urls.elements(); } public String toString() { diff -ubr jakarta-tomcat-3.2.1-src.orig/src/share/org/apache/tomcat/loader/ClassReposi tory.java jakarta-tomcat-3.2.1-src/src/share/org/apache/tomcat/loader/ClassRepository. java --- jakarta-tomcat-3.2.1-src.orig/src/share/org/apache/tomcat/loader/ClassReposi tory.java Sun Mar 11 19:22:31 2001 +++ jakarta-tomcat-3.2.1-src/src/share/org/apache/tomcat/loader/ClassRepository. java Sun Mar 11 19:01:44 2001 @@ -61,6 +61,8 @@ import java.io.*; import java.lang.*; import java.util.*; +import java.util.zip.*; +import java.net.*; public class ClassRepository { private File file; @@ -77,5 +79,43 @@ public Object getProtectionDomain() { return protectionDomain; + } + + public URL findResource(String name) { + if (file.isDirectory()) { + String fileName = name.replace('/', File.separatorChar); + File resFile = new File(file, fileName); + if (resFile.exists()) { + // Build a file:// URL form the file name + try { + return new URL("file", null, resFile.getAbsolutePath()); + } catch(java.net.MalformedURLException badurl) { + badurl.printStackTrace(); + return null; + } + } + } + else { + // a jar:-URL *could* change even between minor releases, but + // didn't between JVM's 1.1.6 and 1.3beta. Tested on JVM's from + // IBM, Blackdown, Microsoft, Sun @ Windows and Sun @ Solaris + try { + ZipFile zf = new ZipFile(file.getAbsolutePath()); + ZipEntry ze = zf.getEntry(name); + + if (ze != null) { + try { + return new URL("jar:file:" + file.getAbsolutePath() + "!/" + name); + } catch(java.net.MalformedURLException badurl) { + badurl.printStackTrace(); + return null; + } + } + } catch (IOException ioe) { + ioe.printStackTrace(); + return null; + } + } + return null; } }