From 3548f2453baa5b818a86ec4847fc6f7376d96d10 Mon Sep 17 00:00:00 2001 From: markt Date: Fri, 17 Jul 2009 16:57:30 +0000 Subject: [PATCH] Part 1 of a series of commits to align the Catalina and Jasper TLD scanning code and to provide additional features when embedding. This first commit modifies the Catalina code to: - better match the spec - improve logging The JSP TCK passes with this patch applied git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@795143 13f79535-47bb-0310-9956-ffa450edef68 --- .../catalina/startup/LocalStrings.properties | 11 + java/org/apache/catalina/startup/TldConfig.java | 442 ++++++++++++--------- 2 files changed, 266 insertions(+), 187 deletions(-) diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties index 6008fdd9b..b1e84689d 100644 --- a/java/org/apache/catalina/startup/LocalStrings.properties +++ b/java/org/apache/catalina/startup/LocalStrings.properties @@ -90,8 +90,19 @@ hostConfig.stop=HostConfig: Processing STOP hostConfig.undeploy=Undeploying context [{0}] hostConfig.undeploy.error=Error undeploying web application at context path {0} hostConfig.undeploying=Undeploying deployed web applications +tdlConfig.addListeners=Adding {0} listeners from TLD files tldConfig.cce=Lifecycle event data object {0} is not a Context tldConfig.execute=Error processing TLD files for context path {0} +tldConfig.jarStart=Scanning JAR ''{0}'' for TLDs +tldConfig.processingTld=Processing TLD found at ''{0}'' +tldConfig.webinflibStart=Scanning WEB-INF/lib for JARs containing META-INF/**/*.TLD +tldConfig.webinflibJarFail=Failed to scan JAR ''{0}'' for TLDs +tldConfig.webinfFail=Failed to process TLD found at ''{0}'' +tldConfig.webinfScan=Scanning WEB-INF for TLD files in ''{0}'' +tldConfig.webxmlStart=Scanning elements in web.xml +tldConfig.webxmlAdd=Adding path ''{0}'' for URI ''{1}'' +tldConfig.webxmlSkip=Path ''{1}'' skipped since URI ''{0}'' is a duplicate +tldConfig.webxmlFail=Failed to process TLD with path ''{1}'' and URI ''{0}'' userConfig.database=Exception loading user database userConfig.deploy=Deploying web application for user {0} userConfig.deploying=Deploying user web applications diff --git a/java/org/apache/catalina/startup/TldConfig.java b/java/org/apache/catalina/startup/TldConfig.java index 4155c3d84..bb03a0844 100644 --- a/java/org/apache/catalina/startup/TldConfig.java +++ b/java/org/apache/catalina/startup/TldConfig.java @@ -22,15 +22,13 @@ package org.apache.catalina.startup; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.JarURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.JarEntry; @@ -63,6 +61,11 @@ import org.xml.sax.InputSource; */ public final class TldConfig implements LifecycleListener { + private static final String JAR_EXT = ".jar"; + private static final String TLD_EXT = ".tld"; + private static final String WEB_INF = "/WEB-INF/"; + private static final String WEB_INF_LIB = "/WEB-INF/lib/"; + // Names of JARs that are known not to contain any TLDs private static HashSet noTldJars; @@ -315,34 +318,32 @@ public final class TldConfig implements LifecycleListener { long t1=System.currentTimeMillis(); /* - * Acquire the list of TLD resource paths, possibly embedded in JAR - * files, to be processed + * Priority order of URIs required by spec is: + * 1. J2EE platform taglibs - Tomcat doesn't provide these + * 2. web.xml entries + * 3. JARS in WEB-INF/lib & TLDs under WEB-INF (equal priority) + * 4. Additional entries from the container */ - Set resourcePaths = tldScanResourcePaths(); - Map jarPaths = getJarPaths(); - - // Scan each accumulated resource path for TLDs to be processed - Iterator paths = resourcePaths.iterator(); - while (paths.hasNext()) { - String path = paths.next(); - if (path.endsWith(".jar")) { - tldScanJar(path); - } else { - tldScanTld(path); - } - } - if (jarPaths != null) { - Iterator files = jarPaths.values().iterator(); - while (files.hasNext()) { - tldScanJar(files.next()); - } - } + // Stage 2 - web.xml entries + tldScanWebXml(); + + // Stage 3a - TLDs under WEB-INF (not lib or classes) + tldScanResourcePathsWebInf(context.getResources(), WEB_INF); + // Stage 3b - .jar files in WEB-INF/lib/ + tldScanWebInfLib(); + + // Stage 4 - Additional entries from the container + tldScanClassloaders(); + + // Now add all the listeners we found to the listeners for this context String list[] = getTldListeners(); if( log.isDebugEnabled() ) - log.debug( "Adding tld listeners:" + list.length); + log.debug(sm.getString("tdlConfig.addListeners", + Integer.valueOf(list.length))); + for( int i=0; list!=null && i items = resources.list(rootPath); + while (items.hasMoreElements()) { + NameClassPair item = items.nextElement(); + String resourcePath = rootPath + item.getName(); + if (!resourcePath.endsWith(TLD_EXT) + && (resourcePath.startsWith("/WEB-INF/classes/") + || resourcePath.startsWith("/WEB-INF/lib/"))) { + continue; + } + if (resourcePath.endsWith(TLD_EXT)) { + if (resourcePath.startsWith("/WEB-INF/tags") && + !resourcePath.endsWith("implicit.tld")) { + continue; + } + try { + tldScanTld(resourcePath); + } catch (Exception e) { + log.warn(sm.getString( + "tldConfig.webinfFail", resourcePath),e); + } + } else { + tldScanResourcePathsWebInf(resources, resourcePath + '/'); + } + } + } catch (NamingException e) { + // Silent catch: it's valid that no /WEB-INF directory exists + } + } + + /** + * Scan the JARs in the WEB-INF/lib directory. Skip the JARs known not to + * have any TLDs in them. + */ + private void tldScanWebInfLib() { + + if (log.isTraceEnabled()) { + log.trace(sm.getString("tldConfig.webinflibStart")); + } + + DirContext resources = context.getResources(); + try { + NamingEnumeration items = + resources.list(WEB_INF_LIB); + + while (items.hasMoreElements()) { + NameClassPair item = items.nextElement(); + String name = item.getName(); + if (name.endsWith(JAR_EXT) && !noTldJars.contains(name)) { + // Need to scan this JAR for TLDs + try { + tldScanJar(WEB_INF_LIB + name); + } catch (Exception e) { + log.warn(sm.getString("tldConfig.webinflibJarFail"), e); + } + } + } + } catch (NamingException e) { + // Silent catch: it's valid that no /WEB-INF/lib directory exists + } + } + /** * Scan the JAR file at the specified resource path for TLDs in the * META-INF subdirectory, and scan each TLD for application @@ -367,10 +488,6 @@ public final class TldConfig implements LifecycleListener { */ private void tldScanJar(String resourcePath) throws Exception { - if (log.isDebugEnabled()) { - log.debug(" Scanning JAR at resource path '" + resourcePath + "'"); - } - URL url = context.getServletContext().getResource(resourcePath); if (url == null) { throw new IllegalArgumentException @@ -400,36 +517,14 @@ public final class TldConfig implements LifecycleListener { * @param file JAR file whose TLD entries are scanned for application * listeners */ - private void tldScanJar(File file) throws Exception { + private void tldScanJar(File file) { JarFile jarFile = null; - String name = null; - String jarPath = file.getAbsolutePath(); try { jarFile = new JarFile(file); - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - name = entry.getName(); - if (!name.startsWith("META-INF/")) { - continue; - } - if (!name.endsWith(".tld")) { - continue; - } - if (log.isTraceEnabled()) { - log.trace(" Processing TLD at '" + name + "'"); - } - try { - tldScanStream(new InputSource(jarFile.getInputStream(entry))); - } catch (Exception e) { - log.error(sm.getString("contextConfig.tldEntryException", - name, jarPath, context.getPath()), - e); - } - } + tldScanJar(jarFile, jarPath); } catch (Exception e) { log.error(sm.getString("contextConfig.tldJarException", jarPath, context.getPath()), @@ -445,6 +540,38 @@ public final class TldConfig implements LifecycleListener { } } + private void tldScanJar(JarFile jarFile, String jarLocation) { + + if (log.isTraceEnabled()) { + log.trace(sm.getString("tldConfig.jarStart", jarLocation)); + } + + String name = null; + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + name = entry.getName(); + if (!name.startsWith("META-INF/")) { + continue; + } + if (!name.endsWith(TLD_EXT)) { + continue; + } + if (log.isTraceEnabled()) { + log.trace(sm.getString("tldConfig.processingTld", name)); + } + try{ + tldScanStream( + new InputSource(jarFile.getInputStream(entry))); + } catch (Exception e) { + log.error(sm.getString("contextConfig.tldEntryException", + name, jarLocation, context.getPath()), + e); + } + } + } + + /** * Scan the TLD contents in the specified input stream, and register * any application event listeners found there. NOTE - It is @@ -479,8 +606,8 @@ public final class TldConfig implements LifecycleListener { */ private void tldScanTld(String resourcePath) throws Exception { - if (log.isDebugEnabled()) { - log.debug(" Scanning TLD at resource path '" + resourcePath + "'"); + if (log.isTraceEnabled()) { + log.trace(sm.getString("tldConfig.processingTld", resourcePath)); } InputSource inputSource = null; @@ -504,123 +631,23 @@ public final class TldConfig implements LifecycleListener { } /** - * Accumulate and return a Set of resource paths to be analyzed for - * tag library descriptors. Each element of the returned set will be - * the context-relative path to either a tag library descriptor file, - * or to a JAR file that may contain tag library descriptors in its - * META-INF subdirectory. - * - * @exception IOException if an input/output error occurs while - * accumulating the list of resource paths - */ - private Set tldScanResourcePaths() throws IOException { - if (log.isDebugEnabled()) { - log.debug(" Accumulating TLD resource paths"); - } - Set resourcePaths = new HashSet(); - - // Accumulate resource paths explicitly listed in the web application - // deployment descriptor - if (log.isTraceEnabled()) { - log.trace(" Scanning elements in web.xml"); - } - String taglibs[] = context.findTaglibs(); - for (int i = 0; i < taglibs.length; i++) { - String resourcePath = context.findTaglib(taglibs[i]); - // Note: Whilst the Servlet 2.4 DTD implies that the location must - // be a context-relative path starting with '/', JSP.7.3.6.1 states - // explicitly how paths that do not start with '/' should be - // handled. - if (!resourcePath.startsWith("/")) { - resourcePath = "/WEB-INF/" + resourcePath; - } - if (log.isTraceEnabled()) { - log.trace(" Adding path '" + resourcePath + - "' for URI '" + taglibs[i] + "'"); - } - resourcePaths.add(resourcePath); - } - - DirContext resources = context.getResources(); - if (resources != null) { - tldScanResourcePathsWebInf(resources, "/WEB-INF", resourcePaths); - } - - // Return the completed set - return (resourcePaths); - - } - - /* - * Scans the web application's subdirectory identified by rootPath, - * along with its subdirectories, for TLDs. - * - * Initially, rootPath equals /WEB-INF. The /WEB-INF/classes and - * /WEB-INF/lib subdirectories are excluded from the search, as per the - * JSP 2.0 spec. - * - * @param resources The web application's resources - * @param rootPath The path whose subdirectories are to be searched for - * TLDs - * @param tldPaths The set of TLD resource paths to add to - */ - private void tldScanResourcePathsWebInf(DirContext resources, - String rootPath, - Set tldPaths) - throws IOException { - - if (log.isTraceEnabled()) { - log.trace(" Scanning TLDs in " + rootPath + " subdirectory"); - } - - try { - NamingEnumeration items = resources.list(rootPath); - while (items.hasMoreElements()) { - NameClassPair item = items.nextElement(); - String resourcePath = rootPath + "/" + item.getName(); - if (!resourcePath.endsWith(".tld") - && (resourcePath.startsWith("/WEB-INF/classes") - || resourcePath.startsWith("/WEB-INF/lib"))) { - continue; - } - if (resourcePath.endsWith(".tld")) { - if (log.isTraceEnabled()) { - log.trace(" Adding path '" + resourcePath + "'"); - } - tldPaths.add(resourcePath); - } else { - tldScanResourcePathsWebInf(resources, resourcePath, - tldPaths); - } - } - } catch (NamingException e) { - // Silent catch: it's valid that no /WEB-INF directory exists - } - } - - /** - * Returns a map of the paths to all JAR files that are accessible to the - * webapp and will be scanned for TLDs. - * - * The map always includes all the JARs under WEB-INF/lib, as well as - * shared JARs in the classloader delegation chain of the webapp's - * classloader. + * Scan the classloader hierarchy for JARs and, optionally, for JARs where + * the name doesn't end in .jar and directories that represent exploded + * JARs. The JARs under WEB-INF/lib will be skipped as they have been + * scanned previously. * - * The latter constitutes a Tomcat-specific extension to the TLD search + * This represents a Tomcat-specific extension to the TLD search * order defined in the JSP spec. It allows tag libraries packaged as JAR * files to be shared by web applications by simply dropping them in a * location that all web applications have access to (e.g., - * /common/lib). + * /common/lib). It also supports some of the weird and + * wonderful arrangements present when Tomcat gets embedded. * * The set of shared JARs to be scanned for TLDs is narrowed down by * the noTldJars class variable, which contains the names of JARs * that are known not to contain any TLDs. - * - * @return Map of JAR file paths */ - private Map getJarPaths() { - - HashMap jarPathMap = null; + private void tldScanClassloaders() { ClassLoader webappLoader = Thread.currentThread().getContextClassLoader(); ClassLoader loader = webappLoader; @@ -628,20 +655,60 @@ public final class TldConfig implements LifecycleListener { if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) loader).getURLs(); for (int i=0; i(); - jarPathMap.put(path, file); - } else if (!jarPathMap.containsKey(path)) { - jarPathMap.put(path, file); - } - } + + tldScanJar(file); } } loader = loader.getParent(); } + } - return jarPathMap; + // Extract the JAR name, if present, from a URL + private String getJarName(URL url) { + + String name = null; + + String path = url.getPath(); + int end = path.indexOf(JAR_EXT); + if (end != -1) { + int start = path.lastIndexOf('/', end); + name = path.substring(start + 1, end + 4); + } + + return name; } public void lifecycleEvent(LifecycleEvent event) { -- 2.11.0