remm 02/02/07 11:20:36 Modified: catalina/src/share/org/apache/catalina/loader Tag: tomcat_40_branch WebappClassLoader.java WebappLoader.java Log: - Port to the 4.0.x branch the modification to the classloader made in the HEAD branch. This includes fixes for a variety of problems, including: - 4830: Catalina class loader throws exception while getting a directory's URL using Class.getResource() - 6248: Using xerces in webapp causes class cast exception - Cleanup of source URLs generation - Add support for external repositories, so that the CL fully emulates the URLClassLoader Revision Changes Path No revision No revision 1.15.2.9 +229 -67 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java Index: WebappClassLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java,v retrieving revision 1.15.2.8 retrieving revision 1.15.2.9 diff -u -r1.15.2.8 -r1.15.2.9 --- WebappClassLoader.java 20 Nov 2001 05:06:34 -0000 1.15.2.8 +++ WebappClassLoader.java 7 Feb 2002 19:20:35 -0000 1.15.2.9 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java,v 1.15.2.8 2001/11/20 05:06:34 remm Exp $ - * $Revision: 1.15.2.8 $ - * $Date: 2001/11/20 05:06:34 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java,v 1.15.2.9 2002/02/07 19:20:35 remm Exp $ + * $Revision: 1.15.2.9 $ + * $Date: 2002/02/07 19:20:35 $ * * ==================================================================== * @@ -122,7 +122,7 @@ * * @author Remy Maucherat * @author Craig R. McClanahan - * @version $Revision: 1.15.2.8 $ $Date: 2001/11/20 05:06:34 $ + * @version $Revision: 1.15.2.9 $ $Date: 2002/02/07 19:20:35 $ */ public class WebappClassLoader extends URLClassLoader @@ -146,6 +146,49 @@ } + // ------------------------------------------------------- Static Variables + + + /** + * The set of trigger classes that will cause a proposed repository not + * to be added if this class is visible to the class loader that loaded + * this factory class. Typically, trigger classes will be listed for + * components that have been integrated into the JDK for later versions, + * but where the corresponding JAR files are required to run on + * earlier versions. + */ + private static final String[] triggers = { + "com.sun.jndi.ldap.LdapCtxFactory", // LDAP added in 1.3 + "com.sun.net.ssl.internal.ssl.Provider", // JSSE added in 1.4 + "javax.security.auth.Subject", // JAAS added in 1.4 + //"javax.net.SocketFactory", // JSSE added in 1.4 + //"javax.security.cert.X509Certificate", // JSSE added in 1.4 + //"javax.sql.DataSource", // JDBC ext. added in 1.4 + //"javax.xml.parsers.DocumentBuilder", // JAXP added in 1.4 + "javax.servlet.Servlet", // Servlet API + // "org.apache.crimson.jaxp.DocumentBuilderImpl", + // Crimson added in 1.4 + }; + + + /** + * The set of trigger classes that will cause a proposed repository not + * to be added if this class is visible to the class loader that loaded + * this factory class. Typically, trigger classes will be listed for + * components that have been integrated into the JDK for later versions, + * but where the corresponding JAR files are required to run on + * earlier versions. + */ + private static final String[] classTriggers = { + "javax.net.", // JSSE added in 1.4 + "javax.security.cert.", // JSSE added in 1.4 + "javax.naming.", // JNDI added in 1.3 + "javax.xml.", // JAXP added in 1.4 + "org.xml.sax.", + "org.w3c.dom." + }; + + // ----------------------------------------------------------- Constructors @@ -340,6 +383,12 @@ /** + * Has external repositories. + */ + protected boolean hasExternalRepositories = false; + + + /** * All permission. */ private Permission allPermission = new java.security.AllPermission(); @@ -398,13 +447,15 @@ * * @param path file directory path */ - public void setPermissions(String path) { + public void addPermission(String path) { if (securityManager != null) { + Permission permission = null; if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) { - permissionList.add(new JndiPermission(path + "*")); + permission = new JndiPermission(path + "*"); } else { - permissionList.add(new FilePermission(path + "-","read")); + permission = new FilePermission(path + "-","read"); } + addPermission(permission); } } @@ -415,8 +466,20 @@ * * @param url URL for a file or directory on local system */ - public void setPermissions(URL url) { - setPermissions(url.toString()); + public void addPermission(URL url) { + addPermission(url.toString()); + } + + + /** + * If there is a Java SecurityManager create a Permission. + * + * @param url URL for a file or directory on local system + */ + public void addPermission(Permission permission) { + if ((securityManager != null) && (permission != null)) { + permissionList.add(permission); + } } @@ -465,6 +528,7 @@ try { URL url = new URL(repository); super.addURL(url); + hasExternalRepositories = true; } catch (MalformedURLException e) { throw new IllegalArgumentException(e.toString()); } @@ -544,13 +608,6 @@ } - JarFile[] result2 = new JarFile[jarFiles.length + 1]; - for (i = 0; i < jarFiles.length; i++) { - result2[i] = jarFiles[i]; - } - result2[jarFiles.length] = jarFile; - jarFiles = result2; - try { // Register the JAR for tracking @@ -574,8 +631,21 @@ lastModifiedDates = result3; } catch (NamingException e) { + // Ignore } + // If the JAR currently contains invalid classes, don't actually use it + // for classloading + if (!validateJarFile(file)) + return; + + JarFile[] result2 = new JarFile[jarFiles.length + 1]; + for (i = 0; i < jarFiles.length; i++) { + result2[i] = jarFiles[i]; + } + result2[jarFiles.length] = jarFile; + jarFiles = result2; + // Add the file to the list File[] result4 = new File[jarRealFiles.length + 1]; for (i = 0; i < jarRealFiles.length; i++) { @@ -862,7 +932,7 @@ log(" -->RuntimeException Rethrown", e); throw e; } - if (clazz == null) { + if ((clazz == null) && hasExternalRepositories) { try { clazz = super.findClass(name); } catch(AccessControlException ace) { @@ -922,7 +992,7 @@ url = entry.source; } - if (url == null) + if ((url == null) && hasExternalRepositories) url = super.findResource(name); if (debug >= 3) { @@ -965,7 +1035,7 @@ // Note : Not getting an exception here means the resource was // found try { - result.addElement(new File(files[i], name).toURL()); + result.addElement(getURL(new File(files[i], name))); } catch (MalformedURLException e) { // Ignore } @@ -978,7 +1048,7 @@ JarEntry jarEntry = jarFiles[i].getJarEntry(name); if (jarEntry != null) { try { - String jarFakeUrl = jarRealFiles[i].toURL().toString(); + String jarFakeUrl = getURL(jarRealFiles[i]).toString(); jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name; result.addElement(new URL(jarFakeUrl)); } catch (MalformedURLException e) { @@ -988,10 +1058,14 @@ } // Adding the results of a call to the superclass - Enumeration otherResourcePaths = super.findResources(name); + if (hasExternalRepositories) { + + Enumeration otherResourcePaths = super.findResources(name); + + while (otherResourcePaths.hasMoreElements()) { + result.addElement(otherResourcePaths.nextElement()); + } - while (otherResourcePaths.hasMoreElements()) { - result.addElement(otherResourcePaths.nextElement()); } return result.elements(); @@ -1122,7 +1196,7 @@ log(" --> Returning stream from local"); stream = findLoadedResource(name); try { - if (stream == null) + if (hasExternalRepositories && (stream == null)) stream = url.openStream(); } catch (IOException e) { ; // Ignore @@ -1372,9 +1446,9 @@ URL[] urls = new URL[length]; for (i = 0; i < length; i++) { if (i < filesLength) { - urls[i] = files[i].toURL(); + urls[i] = getURL(files[i]); } else if (i < filesLength + jarFilesLength) { - urls[i] = jarRealFiles[i - filesLength].toURL(); + urls[i] = getURL(jarRealFiles[i - filesLength]); } else { urls[i] = external[i - filesLength - jarFilesLength]; } @@ -1402,6 +1476,15 @@ /** + * Get the lifecycle listeners associated with this lifecycle. If this + * Lifecycle has no listeners registered, a zero-length array is returned. + */ + public LifecycleListener[] findLifecycleListeners() { + return new LifecycleListener[0]; + } + + + /** * Remove a lifecycle event listener from this component. * * @param listener The listener to remove @@ -1451,6 +1534,7 @@ jarNames = new String[0]; lastModifiedDates = new long[0]; paths = new String[0]; + hasExternalRepositories = false; required.clear(); permissionList.clear(); @@ -1486,7 +1570,7 @@ entry = findResourceInternal(name, classPath); } - if (entry == null) + if ((entry == null) || (entry.binaryContent == null)) throw new ClassNotFoundException(name); Class clazz = entry.loadedClass; @@ -1592,14 +1676,17 @@ String fullPath = repositories[i] + path; - resource = (Resource) resources.lookup(fullPath); + Object lookupResult = resources.lookup(fullPath); + if (lookupResult instanceof Resource) { + resource = (Resource) lookupResult; + } // Note : Not getting an exception here means the resource was // found entry = new ResourceEntry(); try { - entry.source = new File(files[i], path).toURL(); + entry.source = getURL(new File(files[i], path)); } catch (MalformedURLException e) { return null; } @@ -1607,30 +1694,36 @@ (ResourceAttributes) resources.getAttributes(fullPath); contentLength = (int) attributes.getContentLength(); entry.lastModified = attributes.getLastModified(); - try { - binaryStream = resource.streamContent(); - } catch (IOException e) { - return null; - } - // Register the full path for modification checking - synchronized (paths) { + if (resource != null) { - int j; - - long[] result2 = new long[lastModifiedDates.length + 1]; - for (j = 0; j < lastModifiedDates.length; j++) { - result2[j] = lastModifiedDates[j]; + try { + binaryStream = resource.streamContent(); + } catch (IOException e) { + return null; } - result2[lastModifiedDates.length] = entry.lastModified; - lastModifiedDates = result2; - String[] result = new String[paths.length + 1]; - for (j = 0; j < paths.length; j++) { - result[j] = paths[j]; + // Register the full path for modification checking + synchronized (paths) { + + int j; + + long[] result2 = + new long[lastModifiedDates.length + 1]; + for (j = 0; j < lastModifiedDates.length; j++) { + result2[j] = lastModifiedDates[j]; + } + result2[lastModifiedDates.length] = entry.lastModified; + lastModifiedDates = result2; + + String[] result = new String[paths.length + 1]; + for (j = 0; j < paths.length; j++) { + result[j] = paths[j]; + } + result[paths.length] = fullPath; + paths = result; + } - result[paths.length] = fullPath; - paths = result; } @@ -1651,7 +1744,7 @@ entry = new ResourceEntry(); try { - String jarFakeUrl = jarRealFiles[i].toURL().toString(); + String jarFakeUrl = getURL(jarRealFiles[i]).toString(); jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path; entry.source = new URL(jarFakeUrl); } catch (MalformedURLException e) { @@ -1677,27 +1770,31 @@ return null; } - byte[] binaryContent = new byte[contentLength]; + if (binaryStream != null) { - try { - int pos = 0; - while (true) { - int n = binaryStream.read(binaryContent, pos, - binaryContent.length - pos); - if (n <= 0) - break; - pos += n; + byte[] binaryContent = new byte[contentLength]; + + try { + int pos = 0; + while (true) { + int n = binaryStream.read(binaryContent, pos, + binaryContent.length - pos); + if (n <= 0) + break; + pos += n; + } + binaryStream.close(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; } - binaryStream.close(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - entry.binaryContent = binaryContent; + entry.binaryContent = binaryContent; + + } // Add the entry in the local resource repository synchronized (this) { @@ -1814,6 +1911,7 @@ return false; // Looking up the package + /* String packageName = null; int pos = name.lastIndexOf('.'); if (pos != -1) @@ -1829,8 +1927,72 @@ return false; if (packageName.equals("javax.servlet.jsp.tagext")) return false; + */ + for (int i = 0; i < classTriggers.length; i++) { + if (name.startsWith(classTriggers[i])) + return false; + } return true; + + } + + + /** + * Check the specified JAR file, and return <code>true</code> if it does + * not contain any of the trigger classes. + * + * @param jarFile The JAR file to be checked + * + * @exception IOException if an input/output error occurs + */ + private boolean validateJarFile(File jarfile) + throws IOException { + + if (triggers == null) + return (true); + JarFile jarFile = new JarFile(jarfile); + for (int i = 0; i < triggers.length; i++) { + Class clazz = null; + try { + if (parent != null) { + clazz = parent.loadClass(triggers[i]); + } else { + clazz = Class.forName(triggers[i]); + } + } catch (Throwable t) { + clazz = null; + } + if (clazz == null) + continue; + String name = triggers[i].replace('.', '/') + ".class"; + if (debug >= 2) + log(" Checking for " + name); + JarEntry jarEntry = jarFile.getJarEntry(name); + if (jarEntry != null) { + jarFile.close(); + return (false); + } + } + jarFile.close(); + return (true); + + } + + + /** + * Get URL. + */ + protected URL getURL(File file) + throws MalformedURLException { + + File realFile = file; + try { + realFile = realFile.getCanonicalFile(); + } catch (IOException e) { + // Ignore + } + return new URL("file:" + realFile.getPath()); } 1.12.2.5 +11 -11 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappLoader.java Index: WebappLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappLoader.java,v retrieving revision 1.12.2.4 retrieving revision 1.12.2.5 diff -u -r1.12.2.4 -r1.12.2.5 --- WebappLoader.java 12 Dec 2001 20:54:27 -0000 1.12.2.4 +++ WebappLoader.java 7 Feb 2002 19:20:35 -0000 1.12.2.5 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappLoader.java,v 1.12.2.4 2001/12/12 20:54:27 craigmcc Exp $ - * $Revision: 1.12.2.4 $ - * $Date: 2001/12/12 20:54:27 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappLoader.java,v 1.12.2.5 2002/02/07 19:20:35 remm Exp $ + * $Revision: 1.12.2.5 $ + * $Date: 2002/02/07 19:20:35 $ * * ==================================================================== * @@ -119,7 +119,7 @@ * * @author Craig R. McClanahan * @author Remy Maucherat - * @version $Revision: 1.12.2.4 $ $Date: 2001/12/12 20:54:27 $ + * @version $Revision: 1.12.2.5 $ $Date: 2002/02/07 19:20:35 $ */ public class WebappLoader @@ -805,7 +805,7 @@ try { URL rootURL = servletContext.getResource("/"); - classLoader.setPermissions(rootURL); + classLoader.addPermission(rootURL); String contextRoot = servletContext.getRealPath("/"); if (contextRoot != null) { @@ -813,7 +813,7 @@ contextRoot = (new File(contextRoot)).getCanonicalPath() + File.separator; - classLoader.setPermissions(contextRoot); + classLoader.addPermission(contextRoot); } catch (IOException e) { // Ignore } @@ -822,11 +822,11 @@ URL classesURL = servletContext.getResource("/WEB-INF/classes/"); if (classesURL != null) - classLoader.setPermissions(classesURL); + classLoader.addPermission(classesURL); URL libURL = servletContext.getResource("/WEB-INF/lib/"); if (libURL != null) { - classLoader.setPermissions(libURL); + classLoader.addPermission(libURL); } if (contextRoot != null) { @@ -840,7 +840,7 @@ } catch (IOException e) { } if (path != null) - classLoader.setPermissions(path); + classLoader.addPermission(path); } } else { @@ -856,7 +856,7 @@ path = libDir.getCanonicalPath() + File.separator; } catch (IOException e) { } - classLoader.setPermissions(path); + classLoader.addPermission(path); } if (classesURL != null) { File classesDir = @@ -867,7 +867,7 @@ + File.separator; } catch (IOException e) { } - classLoader.setPermissions(path); + classLoader.addPermission(path); } }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>