tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Akoulov, Alexandre [IT]" <alexandre.akou...@citigroup.com>
Subject Problem with the classloader in jakarta-tomcat-5.0.28 - cannot add a jar file to class repository
Date Thu, 21 Apr 2005 08:29:22 GMT
Hi all,

I'd greatly appreciate your thoughts on the following issue (and the proposed solution ):



When adding a jar file (eg, "foo/bar.jar") to the class loader's repository it treats as a
directory and therefore it cannot load any classes from this jar. The following explains why
it happens.
	
org.apache.catalina.startup.ClassLoaderFactory is responsible for creating class loader instances.
Each instance is of org.apache.catalina.loader.StandardClassLoader type, which in its turn
extends java.net.URLClassLoader:
-----------------------------------------------------------------------------
			public class StandardClassLoader extends URLClassLoader
-----------------------------------------------------------------------------
	
ClassLoaderFactory#createClassLoader(File unpacked[], File packed[], URL urls[], ClassLoader
parent) is the actual method that creates class loaders. The very first argument to this method
contains jar files or directories ( that is where the name "unpacked" comes from ). 
	
ClassLoaderFactory#createClassLoader method adds File.separator to the end of the jar file
path ( file.getCanonicalPath() + File.separator ) when constructing a URL instance to represent
a jar file and then adds a string representation of a newly created URL to its list of repositories
( list.add(url.toString()) ) :
-----------------------------------------------------------------------------
			if (unpacked != null) {
            	for (int i = 0; i < unpacked.length; i++)  {
	                File file = unpacked[i];
	                if (!file.exists() || !file.canRead())
	                    continue;
	                if (debug >= 1)
	                    log("  Including directory or JAR " 
	                        + file.getAbsolutePath());
	                URL url = new URL("file", null,
	                                  file.getCanonicalPath() + File.separator);
	                list.add(url.toString());
	            }
	        }
-----------------------------------------------------------------------------
	        
For instance, if "unpacked"	argument contains '/home/aa/lib/velocity.jar' then a URL object
is 'file:/home/aa/lib/velocity.jar/' - a forward slash / (which is a Unix file separator)
has been added to the url.
	
After ClassLoaderFactory#createClassLoader adds all repositories to its repository list it
converts this list to array and constructs StandardClassLoader with it:
	
-----------------------------------------------------------------------------
			String array[] = (String[]) list.toArray(new String[list.size()]);
	        StandardClassLoader classLoader = null;
	        if (parent == null)
	            classLoader = new StandardClassLoader(array);
-----------------------------------------------------------------------------
	    
StandardClassLoader( String[] ) constructor converts each repository found in the given array
argument to a URL object:
-----------------------------------------------------------------------------
		    protected static URL[] convert(String input[], URLStreamHandlerFactory factory) {
		    	.....
		    	url[i] = new URL(null, input[i], streamHandler);
		    	.....
		    }
-----------------------------------------------------------------------------
                                   
For instance, if the repositories array of String type contains 'file:/home/aa/lib/velocity.jar/'
then a URL object is 'file:/home/aa/lib/velocity.jar/'. If the repository holds a path on
Windows machine then the URL object would have all backslashes  replaced all  with forward
slashes ( URL object crated with new URL(null, "file:I:\lib\velocity.jar\", streamHandler)
would have "file:I:/lib/velocity.jar/" string representation ).
    
Once StandardClassLoader( String[] ) converts repository array of a String type into a URL
type it calls its super constructor, which in fact is a URLClassLoader( URL[] ).
    
However, the contract for URLClassLoader( URL[] ) constructor indicates that "Any URL that
ends with a '/' is assumed to refer to a directory. " and therefore a jar file gets ignored
by the loader.
    
For instance, if the repositories array contains 'file:/home/aa/lib/velocity.jar/' url object
the URLClassLoader( URL[] ) constructor treats this url as a directory and therefore a jar
file is never properly loaded.
    
    
Therefore, a File.separator that got added to a jar file in ClassLoaderFactory#createClassLoader
method made it invalid because the actual class loader assumes that this jar file is a directory.
    
==================================================    
Proposed solution
==================================================

ClassLoaderFactory#createClassLoader(File unpacked[], File packed[], URL urls[], ClassLoader
parent) is to be modified so that it does not add File.separator to the end of jar files found
in the unpacked argument:
     
-----------------------------------------------------------------------------
			if (unpacked != null) {
            	for (int i = 0; i < unpacked.length; i++)  {
	                File file = unpacked[i];
	                if (!file.exists() || !file.canRead())
	                    continue;
	                if (debug >= 1)
	                    log("  Including directory or JAR " 
	                        + file.getAbsolutePath());
	                
	                // THE FIX
	                StringBuffer filePath = new StringBuffer(file.getCanonicalPath());
	                if ( file.isDirectory() ) { 
	                	// Only add a file separator if a file represents a directory
	                	filePath.append(File.separator);
	                }
	                                
	                URL url = new URL("file", null, filePath.toString());
	                list.add(url.toString());
	            }
	        }	
-----------------------------------------------------------------------------
			
	


Kind regards,

Alex.
	

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-user-help@jakarta.apache.org


Mime
View raw message