Author: markt
Date: Mon Jul 20 21:37:30 2009
New Revision: 796030

URL: http://svn.apache.org/viewvc?rev=796030&view=rev
Log:
Sync up the Catalina and Jasper TLD scanning code prior to adding some optional 
extensions for the embedded use case.
A couple of options from the Jasper side have been removed as they were only 
ever used with one value.
The JSP 2.1 TCK passes with these changes.

Modified:
    tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/startup/TldConfig.java
    tomcat/trunk/java/org/apache/jasper/JspC.java
    tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java

Modified: tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties?rev=796030&r1=796029&r2=796030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Mon 
Jul 20 21:37:30 2009
@@ -90,19 +90,18 @@
 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.addListeners=Adding {0} listeners from TLD files
 tldConfig.cce=Lifecycle event data object {0} is not a Context
+tldConfig.classloaderFail=Failed to process ''{0}'' for TLDs.
 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 <taglib> 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}''
+tldConfig.webxmlSkip=Path ''{1}'' skipped since URI ''{0}'' is a duplicate
+tldConfig.webxmlStart=Scanning <taglib> elements in web.xml
 userConfig.database=Exception loading user database
 userConfig.deploy=Deploying web application for user {0}
 userConfig.deploying=Deploying user web applications

Modified: tomcat/trunk/java/org/apache/catalina/startup/TldConfig.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/TldConfig.java?rev=796030&r1=796029&r2=796030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/TldConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/TldConfig.java Mon Jul 20 
21:37:30 2009
@@ -19,26 +19,22 @@
 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.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
-import javax.naming.NameClassPair;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-import javax.servlet.ServletException;
+import javax.servlet.ServletContext;
 
 import org.apache.catalina.Context;
 import org.apache.catalina.Lifecycle;
@@ -49,6 +45,7 @@
 import org.apache.tomcat.util.res.StringManager;
 import org.apache.tomcat.util.digester.Digester;
 import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 
 /**
@@ -331,7 +328,7 @@
         tldScanWebXml();
         
         // Stage 3a - TLDs under WEB-INF (not lib or classes)
-        tldScanResourcePaths(context.getResources(), WEB_INF);
+        tldScanResourcePaths(WEB_INF);
 
         // Stage 3b - .jar files in WEB-INF/lib/
         tldScanWebInfLib();
@@ -343,7 +340,7 @@
         String list[] = getTldListeners();
 
         if( log.isDebugEnabled() )
-            log.debug(sm.getString("tdlConfig.addListeners",
+            log.debug(sm.getString("tldConfig.addListeners",
                     Integer.valueOf(list.length)));
 
         for( int i=0; list!=null && i<list.length; i++ ) {
@@ -362,6 +359,10 @@
 
     /**
      * Get the taglib entries from web.xml and add them to the map.
+     * 
+     * This is not kept in sync with o.a.j.compiler.TldLocationsCache as this
+     * code needs to scan the TLDs listed in web.xml whereas Jasper only needs
+     * the URI to TLD mappings.
      */
     private void tldScanWebXml() {
         
@@ -388,251 +389,108 @@
                             taglibs[i]));
                 }
                 try {
-                    tldScanTld(resourcePath);
+                    InputStream stream = context.getServletContext(
+                            ).getResourceAsStream(resourcePath);
+                    tldScanStream(stream);
                     taglibUris.add(taglibs[i]);
-                } catch (Exception e) {
+                } catch (IOException ioe) {
                     log.warn(sm.getString("tldConfig.webxmlFail", resourcePath,
-                            taglibs[i]), e);
+                            taglibs[i]), ioe);
                 }
             }
         }
     }
     
     /*
-     * Scans the web application's subdirectory identified by rootPath,
-     * along with its subdirectories, for TLDs.
+     * Scans the web application's sub-directory identified by startPath,
+     * along with its sub-directories, for TLDs.
      *
      * Initially, rootPath equals /WEB-INF/. The /WEB-INF/classes and
-     * /WEB-INF/lib subdirectories are excluded from the search, as per the
+     * /WEB-INF/lib sub-directories 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
+     * 
+     * Keep in sync with o.a.j.comiler.TldLocationsCache
      */
-    private void tldScanResourcePaths(DirContext resources,
-                                            String rootPath) {
+    private void tldScanResourcePaths(String startPath) {
 
         if (log.isTraceEnabled()) {
-            log.trace(sm.getString("tldConfig.webinfScan", rootPath));
+            log.trace(sm.getString("tldConfig.webinfScan", startPath));
         }
 
-        try {
-            NamingEnumeration<NameClassPair> items = resources.list(rootPath);
-            while (items.hasMoreElements()) {
-                NameClassPair item = items.nextElement();
-                String resourcePath = rootPath + item.getName();
-                if (!resourcePath.endsWith(TLD_EXT)
-                        && (resourcePath.equals("/WEB-INF/classes")
-                            || resourcePath.equals("/WEB-INF/lib"))) {
+        ServletContext ctxt = context.getServletContext();
+
+        Set<String> dirList = ctxt.getResourcePaths(startPath);
+        if (dirList != null) {
+            Iterator<String> it = dirList.iterator();
+            while (it.hasNext()) {
+                String path = it.next();
+                if (!path.endsWith(TLD_EXT)
+                        && (path.startsWith(WEB_INF_LIB)
+                                || path.startsWith("/WEB-INF/classes/"))) {
                     continue;
                 }
-                if (resourcePath.endsWith(TLD_EXT)) {
-                    if (resourcePath.startsWith("/WEB-INF/tags") &&
-                            !resourcePath.endsWith("implicit.tld")) {
+                if (path.endsWith(TLD_EXT)) {
+                    if (path.startsWith("/WEB-INF/tags/") &&
+                            !path.endsWith("implicit.tld")) {
                         continue;
                     }
+                    InputStream stream = ctxt.getResourceAsStream(path);
                     try {
-                        tldScanTld(resourcePath);
-                    } catch (Exception e) {
-                        log.warn(sm.getString(
-                                "tldConfig.webinfFail", resourcePath),e);
+                        tldScanStream(stream);
+                    } catch (IOException ioe) {
+                        log.warn(sm.getString("tldConfig.webinfFail", path),
+                                ioe);
+                    } finally {
+                        if (stream != null) {
+                            try {
+                                stream.close();
+                            } catch (Throwable t) {
+                                // do nothing
+                            }
+                        }
                     }
                 } else {
-                    tldScanResourcePaths(resources, resourcePath + '/');
+                    tldScanResourcePaths(path);
                 }
             }
-        } 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.
+     * 
+     * Keep in sync with o.a.j.comiler.TldLocationsCache
      */
     private void tldScanWebInfLib() {
 
         if (log.isTraceEnabled()) {
             log.trace(sm.getString("tldConfig.webinflibStart"));
         }
-
-        DirContext resources = context.getResources();
-        try {
-            NamingEnumeration<NameClassPair> 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)) {
+        ServletContext ctxt = context.getServletContext();
+        
+        Set<String> dirList = ctxt.getResourcePaths(WEB_INF_LIB);
+        if (dirList != null) {
+            Iterator<String> it = dirList.iterator();
+            while (it.hasNext()) {
+                String path = it.next();
+                if (path.endsWith(JAR_EXT) &&
+                        !noTldJars.contains(
+                                path.substring(path.lastIndexOf('/')))) {
                     // Need to scan this JAR for TLDs
+                    URL url = null;
                     try {
-                        tldScanJar(WEB_INF_LIB + name);
-                    } catch (Exception e) {
+                        url = ctxt.getResource(path);
+                        tldScanJar(url);
+                    } catch (IOException 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
-     * <code>META-INF</code> subdirectory, and scan each TLD for application
-     * event listeners that need to be registered.
-     *
-     * @param resourcePath Resource path of the JAR file to scan
-     *
-     * @exception Exception if an exception occurs while scanning this JAR
-     */
-    private void tldScanJar(String resourcePath) throws Exception {
-
-        URL url = context.getServletContext().getResource(resourcePath);
-        if (url == null) {
-            throw new IllegalArgumentException
-                                (sm.getString("contextConfig.tldResourcePath",
-                                              resourcePath));
-        }
-
-        File file = null;
-        try {
-            file = new File(url.toURI());
-        } catch (URISyntaxException e) {
-            // Ignore, probably an unencoded char
-            file = new File(url.getFile());
-        }
-        try {
-            file = file.getCanonicalFile();
-        } catch (IOException e) {
-            // Ignore
-        }
-        tldScanJar(file);
-
-    }
-
-    /**
-     * Scans all TLD entries in the given JAR for application listeners.
-     *
-     * @param file JAR file whose TLD entries are scanned for application
-     * listeners
-     */
-    private void tldScanJar(File file) {
-
-        JarFile jarFile = null;
-        String jarPath = file.getAbsolutePath();
-
-        try {
-            jarFile = new JarFile(file);
-            tldScanJar(jarFile, jarPath);
-        } catch (Exception e) {
-            log.error(sm.getString("contextConfig.tldJarException",
-                                   jarPath, context.getPath()),
-                      e);
-        } finally {
-            if (jarFile != null) {
-                try {
-                    jarFile.close();
-                } catch (Throwable t) {
-                    // Ignore
-                }
-            }
-        }
-    }
-
-    private void tldScanJar(JarFile jarFile, String jarLocation) {
-
-        if (log.isTraceEnabled()) {
-            log.trace(sm.getString("tldConfig.jarStart", jarLocation));
-        }
-
-        String name = null;
-        Enumeration<JarEntry> 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.  <b>NOTE</b> - It is
-     * the responsibility of the caller to close the InputStream after this
-     * method returns.
-     *
-     * @param resourceStream InputStream containing a tag library descriptor
-     *
-     * @exception Exception if an exception occurs while scanning this TLD
-     */
-    private void tldScanStream(InputSource resourceStream)
-        throws Exception {
-
-        synchronized (tldDigester) {
-            try {
-                tldDigester.push(this);
-                tldDigester.parse(resourceStream);
-            } finally {
-                tldDigester.reset();
-            }
-        }
-
-    }
-
-    /**
-     * Scan the TLD contents at the specified resource path, and register
-     * any application event listeners found there.
-     *
-     * @param resourcePath Resource path being scanned
-     *
-     * @exception Exception if an exception occurs while scanning this TLD
-     */
-    private void tldScanTld(String resourcePath) throws Exception {
-
-        if (log.isTraceEnabled()) {
-            log.trace(sm.getString("tldConfig.processingTld", resourcePath));
-        }
-
-        InputSource inputSource = null;
-        try {
-            InputStream stream =
-                context.getServletContext().getResourceAsStream(resourcePath);
-            if (stream == null) {
-                throw new IllegalArgumentException
-                (sm.getString("contextConfig.tldResourcePath",
-                        resourcePath));
-            }
-            inputSource = new InputSource(stream);
-            tldScanStream(inputSource);
-        } catch (Exception e) {
-             throw new ServletException
-                 (sm.getString("contextConfig.tldFileException", resourcePath,
-                               context.getPath()),
-                  e);
-        } 
-
-    }
-
-    /**
+    /*
      * 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
@@ -642,100 +500,102 @@
      * 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.,
-     * <CATALINA_HOME>/common/lib). It also supports some of the weird and
+     * <CATALINA_HOME>/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 <tt>noTldJars</tt> class variable, which contains the names of JARs
      * that are known not to contain any TLDs.
+     * 
+     * Keep in sync with o.a.j.comiler.TldLocationsCache
      */
     private void tldScanClassloaders() {
 
-        ClassLoader webappLoader = 
Thread.currentThread().getContextClassLoader();
-        ClassLoader loader = webappLoader;
+        ClassLoader loader = 
+            Thread.currentThread().getContextClassLoader();
+        
         while (loader != null) {
             if (loader instanceof URLClassLoader) {
                 URL[] urls = ((URLClassLoader) loader).getURLs();
                 for (int i=0; i<urls.length; i++) {
-                    URL url = urls[i];
-                    
                     // Extract the jarName if there is one to be found
-                    String jarName = getJarName(url);
+                    String jarName = getJarName(urls[i]);
                     
-                    // Skip JARs in WEB-INF/lib - we already scanned them
-                    if (jarName != null &&
-                            url.getPath().contains(WEB_INF_LIB + jarName)) {
-                        continue;
-                    }
-                    
-                    // Skip JARs we know we don't want to scan
-                    if (jarName != null && noTldJars.contains(jarName)) {
-                        continue;
-                    }
-
-                    // Handle JAR URLs
-                    if ("jar".equals(url.getProtocol())) {
-                        JarFile jarFile = null;
+                    // Skip JARs with known not to contain TLDs and JARs in
+                    // WEB-INF/lib we have already scanned
+                    if (!(noTldJars.contains(jarName) ||
+                            urls[i].toString().contains(
+                                    WEB_INF_LIB + jarName))) {
                         try {
-                            JarURLConnection conn =
-                                (JarURLConnection) url.openConnection();
-                            // Avoid the possibility of locking the JAR
-                            conn.setUseCaches(false);
-                            jarFile = conn.getJarFile(); 
-                            tldScanJar(jarFile, 
conn.getJarFileURL().toString());
-                        } catch (Exception e) {
-                            
log.error(sm.getString("contextConfig.tldJarException",
-                                                   url, context.getPath()),
-                                      e);
-                        } finally {
-                            if (jarFile != null) {
-                                try {
-                                    jarFile.close();
-                                } catch (Throwable t) {
-                                    // ignore
-                                }
-                            }
+                            tldScanJar(urls[i]);
+                        } catch (IOException ioe) {
+                            log.warn(sm.getString(
+                                    "tldConfig.classloaderFail",urls[i]), ioe);
                         }
-                        
-                        // Move on to the next URL
-                        continue;
                     }
+                }
+            }
+            loader = loader.getParent();
+        }
+    }
 
-                    // At this point, if it isn't a file URL - can't handle it
-                    if (!"file".equals(url.getProtocol())) {
-                        continue;
-                    }
-                    
-                    // File URLs may %xx encoded or not depending on the class
-                    // loader
-                    File file = null;
-                    try {
-                        file = new File(url.toURI());
-                    } catch (URISyntaxException e) {
-                        // Ignore, probably an unencoded char
-                        file = new File(urls[i].getFile());
-                    }
-                    try {
-                        file = file.getCanonicalFile();
-                    } catch (IOException e) {
-                        // Ignore
-                    }
-                    if (!file.exists()) {
-                        continue;
-                    }
-                    String path = file.getAbsolutePath();
-                    if (!path.endsWith(JAR_EXT)) {
-                        continue;
-                    }
+    /*
+     * Keep in sync with o.a.j.comiler.TldLocationsCache
+     */
+    private void tldScanJar(URL url) throws IOException {
+        URLConnection conn = url.openConnection();
+        if (conn instanceof JarURLConnection) {
+            tldScanJar((JarURLConnection) conn);
+        } else {
+            String urlStr = url.toString();
+            if (urlStr.startsWith("file:")
+                    && urlStr.endsWith(JAR_EXT)) {
+                URL jarURL = new URL("jar:" + urlStr + "!/");
+                tldScanJar((JarURLConnection) jarURL.openConnection());
+            }
+        }
+    }
+
+    /*
+     * Scans the given JarURLConnection for TLD files located in META-INF
+     * (or a sub-directory of it).
+     *
+     * @param conn The JarURLConnection to the JAR file to scan
+     * 
+     * Keep in sync with o.a.j.comiler.TldLocationsCache
+     */
+    private void tldScanJar(JarURLConnection conn) throws IOException {
 
-                    tldScanJar(file);
+        JarFile jarFile = null;
+        try {
+            conn.setUseCaches(false);
+            jarFile = conn.getJarFile();
+            Enumeration<JarEntry> entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = entries.nextElement();
+                String name = entry.getName();
+                if (!name.startsWith("META-INF/")) continue;
+                if (!name.endsWith(".tld")) continue;
+                InputStream stream = jarFile.getInputStream(entry);
+                tldScanStream(stream);
+            }
+        } finally {
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {
+                    // ignore
                 }
             }
-            loader = loader.getParent();
         }
     }
 
-    // Extract the JAR name, if present, from a URL
+
+    /*
+     * Extract the JAR name, if present, from a URL
+     * 
+     * Keep in sync with o.a.j.comiler.TldLocationsCache
+     */
     private String getJarName(URL url) {
         
         String name = null;
@@ -750,6 +610,39 @@
         return name;
     }
 
+    /*
+     * Scan the TLD contents in the specified input stream, and register
+     * any application event listeners found there.  <b>NOTE</b> - This 
+     * method ensure that the InputStream is correctly closed.
+     *
+     * @param resourceStream InputStream containing a tag library descriptor
+     *
+     * @throws IOException  If the file cannot be read
+     */
+    private void tldScanStream(InputStream resourceStream) throws IOException {
+        
+        InputSource source = new InputSource(resourceStream);
+        
+        synchronized (tldDigester) {
+            try {
+                tldDigester.push(this);
+                tldDigester.parse(source);
+            } catch (SAXException s) {
+                // Hack - makes exception handling simpler
+                throw new IOException(s);
+            } finally {
+                tldDigester.reset();
+                if (resourceStream != null) {
+                    try {
+                        resourceStream.close();
+                    } catch (Throwable t) {
+                        // do nothing
+                    }
+                }
+            }
+        }
+    }
+
     public void lifecycleEvent(LifecycleEvent event) {
         // Identify the context we are associated with
         try {

Modified: tomcat/trunk/java/org/apache/jasper/JspC.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/JspC.java?rev=796030&r1=796029&r2=796030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/JspC.java (original)
+++ tomcat/trunk/java/org/apache/jasper/JspC.java Mon Jul 20 21:37:30 2009
@@ -1248,7 +1248,7 @@
             context =new JspCServletContext
                 (new PrintWriter(System.out),
                  new URL("file:" + uriRoot.replace('\\','/') + '/'));
-            tldLocationsCache = new TldLocationsCache(context, true);
+            tldLocationsCache = new TldLocationsCache(context);
         } catch (MalformedURLException me) {
             System.out.println("**" + me);
         }

Modified: tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java?rev=796030&r1=796029&r2=796030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java 
(original)
+++ tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java Mon Jul 
20 21:37:30 2009
@@ -17,6 +17,7 @@
 
 package org.apache.jasper.compiler;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.JarURLConnection;
 import java.net.MalformedURLException;
@@ -87,9 +88,12 @@
     public static final int ROOT_REL_URI = 1;
     public static final int NOROOT_REL_URI = 2;
 
+    private static final String WEB_INF = "/WEB-INF/";
+    private static final String WEB_INF_LIB = "/WEB-INF/lib/";
     private static final String WEB_XML = "/WEB-INF/web.xml";
     private static final String FILE_PROTOCOL = "file:";
-    private static final String JAR_FILE_SUFFIX = ".jar";
+    private static final String JAR_EXT = ".jar";
+    private static final String TLD_EXT = ".tld";
 
     // Names of JARs that are known not to contain any TLDs
     private static HashSet<String> noTldJars;
@@ -105,7 +109,6 @@
 
     private boolean initialized;
     private ServletContext ctxt;
-    private boolean redeployMode;
 
     //*********************************************************************
     // Constructor and Initilizations
@@ -162,10 +165,6 @@
         noTldJars.add("sunpkcs11.jar");
     }
     
-    public TldLocationsCache(ServletContext ctxt) {
-        this(ctxt, true);
-    }
-
     /** Constructor. 
      *
      * @param ctxt the servlet context of the web application in which Jasper 
@@ -176,9 +175,8 @@
      * because of JDK bug 4211817 fixed in this release.
      * If redeployMode is false, a faster but less capable mode will be used.
      */
-    public TldLocationsCache(ServletContext ctxt, boolean redeployMode) {
+    public TldLocationsCache(ServletContext ctxt) {
         this.ctxt = ctxt;
-        this.redeployMode = redeployMode;
         mappings = new Hashtable<String, String[]>();
         initialized = false;
     }
@@ -244,9 +242,10 @@
     private void init() throws JasperException {
         if (initialized) return;
         try {
-            processWebDotXml();
-            tldScanResourcePaths("/WEB-INF/");
-            scanJars();
+            tldScanWebXml();
+            tldScanResourcePaths(WEB_INF);
+            tldScanWebInfLib();
+            tldScanClassloaders();
             initialized = true;
         } catch (Exception ex) {
             throw new JasperException(Localizer.getMessage(
@@ -256,8 +255,12 @@
 
     /*
      * Populates taglib map described in web.xml.
+     * 
+     * This is not kept in sync with o.a.c.startup.TldConfig as the Jasper only
+     * needs the URI to TLD mappings from scan web.xml whereas TldConfig needs
+     * to scan the actual TLD files.
      */    
-    private void processWebDotXml() throws Exception {
+    private void tldScanWebXml() throws Exception {
 
         InputStream is = null;
 
@@ -326,7 +329,7 @@
                 if (uriType(tagLoc) == NOROOT_REL_URI)
                     tagLoc = "/WEB-INF/" + tagLoc;
                 String tagLoc2 = null;
-                if (tagLoc.endsWith(JAR_FILE_SUFFIX)) {
+                if (tagLoc.endsWith(JAR_EXT)) {
                     tagLoc = ctxt.getResource(tagLoc).toString();
                     tagLoc2 = "META-INF/taglib.tld";
                 }
@@ -341,81 +344,14 @@
         }
     }
 
-    /**
-     * Scans the given JarURLConnection for TLD files located in META-INF
-     * (or a subdirectory of it), adding an implicit map entry to the taglib
-     * map for any TLD that has a <uri> element.
-     *
-     * @param conn The JarURLConnection to the JAR file to scan
-     * @param ignore true if any exceptions raised when processing the given
-     * JAR should be ignored, false otherwise
-     */
-    private void scanJar(JarURLConnection conn, boolean ignore)
-                throws JasperException {
-
-        JarFile jarFile = null;
-        String resourcePath = conn.getJarFileURL().toString();
-        try {
-            if (redeployMode) {
-                conn.setUseCaches(false);
-            }
-            jarFile = conn.getJarFile();
-            Enumeration<JarEntry> entries = jarFile.entries();
-            while (entries.hasMoreElements()) {
-                JarEntry entry = entries.nextElement();
-                String name = entry.getName();
-                if (!name.startsWith("META-INF/")) continue;
-                if (!name.endsWith(".tld")) continue;
-                InputStream stream = jarFile.getInputStream(entry);
-                try {
-                    String uri = getUriFromTld(resourcePath, stream);
-                    // Add implicit map entry only if its uri is not already
-                    // present in the map
-                    if (uri != null && mappings.get(uri) == null) {
-                        mappings.put(uri, new String[]{ resourcePath, name });
-                    }
-                } finally {
-                    if (stream != null) {
-                        try {
-                            stream.close();
-                        } catch (Throwable t) {
-                            // do nothing
-                        }
-                    }
-                }
-            }
-        } catch (Exception ex) {
-            if (!redeployMode) {
-                // if not in redeploy mode, close the jar in case of an error
-                if (jarFile != null) {
-                    try {
-                        jarFile.close();
-                    } catch (Throwable t) {
-                        // ignore
-                    }
-                }
-            }
-            if (!ignore) {
-                throw new JasperException(ex);
-            }
-        } finally {
-            if (redeployMode) {
-                // if in redeploy mode, always close the jar
-                if (jarFile != null) {
-                    try {
-                        jarFile.close();
-                    } catch (Throwable t) {
-                        // ignore
-                    }
-                }
-            }
-        }
-    }
-
     /*
-     * Searches the filesystem under /WEB-INF for any TLD files, and adds
-     * an implicit map entry to the taglib map for any TLD that has a <uri>
-     * element.
+     * Scans the web application's sub-directory identified by startPath,
+     * along with its sub-directories, for TLDs and adds an implicit map entry
+     * to the taglib map for any TLD that has a <uri> element.
+     *
+     * Initially, rootPath equals /WEB-INF/. The /WEB-INF/classes and
+     * /WEB-INF/lib sub-directories are excluded from the search, as per the
+     * JSP 2.0 spec.
      * 
      * Keep code in sync with o.a.c.startup.TldConfig
      */
@@ -427,20 +363,19 @@
             Iterator<String> it = dirList.iterator();
             while (it.hasNext()) {
                 String path = it.next();
-                if (!path.endsWith(".tld")
-                        && (path.startsWith("/WEB-INF/lib/")
+                if (!path.endsWith(TLD_EXT)
+                        && (path.startsWith(WEB_INF_LIB)
                                 || path.startsWith("/WEB-INF/classes/"))) {
                     continue;
                 }
-                if (path.endsWith(".tld")) {
+                if (path.endsWith(TLD_EXT)) {
                     if (path.startsWith("/WEB-INF/tags/") &&
                             !path.endsWith("implicit.tld")) {
                         continue;
                     }
                     InputStream stream = ctxt.getResourceAsStream(path);
-                    String uri = null;
                     try {
-                        uri = getUriFromTld(path, stream);
+                        tldScanStream(path, null, stream);
                     } finally {
                         if (stream != null) {
                             try {
@@ -450,114 +385,196 @@
                             }
                         }
                     }
-                    // Add implicit map entry only if its uri is not already
-                    // present in the map
-                    if (uri != null && mappings.get(uri) == null) {
-                        mappings.put(uri, new String[] { path, null });
-                    }
                 } else {
                     tldScanResourcePaths(path);
                 }
-
             }
         }
     }
 
     /*
-     * Returns the value of the uri element of the given TLD, or null if the
-     * given TLD does not contain any such element.
+     * Scan the JARs in the WEB-INF/lib directory. Skip the JARs known not to
+     * have any TLDs in them.
+     * 
+     * Keep in sync with o.a.c.startup.TldConfig
      */
-    private String getUriFromTld(String resourcePath, InputStream in) 
-        throws JasperException
-    {
-        // Parse the tag library descriptor at the specified resource path
-        TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in);
-        TreeNode uri = tld.findChild("uri");
-        if (uri != null) {
-            String body = uri.getBody();
-            if (body != null)
-                return body;
+    private void tldScanWebInfLib() throws Exception {
+        
+        Set<String> dirList = ctxt.getResourcePaths(WEB_INF_LIB);
+        if (dirList != null) {
+            Iterator<String> it = dirList.iterator();
+            while (it.hasNext()) {
+                String path = it.next();
+                if (path.endsWith(JAR_EXT) &&
+                        !noTldJars.contains(
+                                path.substring(path.lastIndexOf('/')))) {
+                    // Need to scan this JAR for TLDs
+                    URL url = null;
+                    url = ctxt.getResource(path);
+                    tldScanJar(url);
+                }
+            }
         }
-
-        return null;
     }
 
     /*
-     * Scans all JARs accessible to the webapp's classloader and its
-     * parent classloaders for TLDs.
-     * 
-     * The list of JARs always includes the JARs under WEB-INF/lib, as well as
-     * all 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.
      *
-     * Considering JARs in the classloader delegation chain 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.,
-     * <CATALINA_HOME>/common/lib).
+     * <CATALINA_HOME>/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 <tt>noTldJars</tt> class variable, which contains the names of JARs
      * that are known not to contain any TLDs.
+     * 
+     * Keep in sync with o.a.c.startup.TldConfig
      */
-    private void scanJars() throws Exception {
+    private void tldScanClassloaders() throws Exception {
 
-        ClassLoader webappLoader
-            = Thread.currentThread().getContextClassLoader();
-        ClassLoader loader = webappLoader;
+        ClassLoader loader =
+            Thread.currentThread().getContextClassLoader();
 
         while (loader != null) {
             if (loader instanceof URLClassLoader) {
                 URL[] urls = ((URLClassLoader) loader).getURLs();
                 for (int i=0; i<urls.length; i++) {
-                    URLConnection conn = urls[i].openConnection();
-                    if (conn instanceof JarURLConnection) {
-                        if (needScanJar(loader, webappLoader,
-                                        ((JarURLConnection) 
conn).getJarFile().getName())) {
-                            scanJar((JarURLConnection) conn, true);
-                        }
-                    } else {
-                        String urlStr = urls[i].toString();
-                        if (urlStr.startsWith(FILE_PROTOCOL)
-                                && urlStr.endsWith(JAR_FILE_SUFFIX)
-                                && needScanJar(loader, webappLoader, urlStr)) {
-                            URL jarURL = new URL("jar:" + urlStr + "!/");
-                            scanJar((JarURLConnection) jarURL.openConnection(),
-                                    true);
-                        }
+                    // Extract the jarName if there is one to be found
+                    String jarName = getJarName(urls[i]);
+
+                    // Skip JARs with known not to contain TLDs and JARs in
+                    // WEB-INF/lib we have already scanned
+                    if (!(noTldJars.contains(jarName) ||
+                            urls[i].toString().contains(
+                                    "WEB-INF/lib/" + jarName))) {
+                        tldScanJar(urls[i]);
                     }
                 }
             }
-
             loader = loader.getParent();
         }
     }
 
     /*
-     * Determines if the JAR file with the given <tt>jarPath</tt> needs to be
-     * scanned for TLDs.
-     *
-     * @param loader The current classloader in the parent chain
-     * @param webappLoader The webapp classloader
-     * @param jarPath The JAR file path
-     *
-     * @return TRUE if the JAR file identified by <tt>jarPath</tt> needs to be
-     * scanned for TLDs, FALSE otherwise
+     * Keep in sync with o.a.c.startup.TldConfig
      */
-    private boolean needScanJar(ClassLoader loader, ClassLoader webappLoader,
-                                String jarPath) {
-        if (loader == webappLoader) {
-            // JARs under WEB-INF/lib must be scanned unconditionally according
-            // to the spec.
-            return true;
+    private void tldScanJar(URL url) throws IOException {
+        URLConnection conn = url.openConnection();
+        if (conn instanceof JarURLConnection) {
+            scanJar((JarURLConnection) conn);
         } else {
-            String jarName = jarPath;
-            int slash = jarPath.lastIndexOf('/');
-            if (slash >= 0) {
-                jarName = jarPath.substring(slash + 1);
+            String urlStr = url.toString();
+            if (urlStr.startsWith(FILE_PROTOCOL)
+                    && urlStr.endsWith(JAR_EXT)) {
+                URL jarURL = new URL("jar:" + urlStr + "!/");
+                scanJar((JarURLConnection) jarURL.openConnection());
             }
-            return (!noTldJars.contains(jarName));
         }
     }
+
+    /*
+     * Scans the given JarURLConnection for TLD files located in META-INF
+     * (or a subdirectory of it), adding an implicit map entry to the taglib
+     * map for any TLD that has a <uri> element.
+     *
+     * @param conn The JarURLConnection to the JAR file to scan
+     * 
+     * Keep in sync with o.a.c.startup.TldConfig
+     */
+    private void scanJar(JarURLConnection conn) throws IOException {
+
+        JarFile jarFile = null;
+        String resourcePath = conn.getJarFileURL().toString();
+        try {
+            conn.setUseCaches(false);
+            jarFile = conn.getJarFile();
+            Enumeration<JarEntry> entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = entries.nextElement();
+                String name = entry.getName();
+                if (!name.startsWith("META-INF/")) continue;
+                if (!name.endsWith(".tld")) continue;
+                InputStream stream = jarFile.getInputStream(entry);
+                tldScanStream(resourcePath, name, stream);
+            }
+        } finally {
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /*
+     * Extract the JAR name, if present, from a URL
+     * 
+     * Keep in sync with o.a.c.startup.TldConfig
+     */
+    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;
+    }
+
+    /*
+     * Scan the TLD contents in the specified input stream and add any new URIs
+     * to the map.
+     * 
+     * @param resourcePath  Path of the resource
+     * @param entryName     If the resource is a JAR file, the name of the 
entry
+     *                      in the JAR file
+     * @param stream        The input stream for the resource
+     * @throws IOException
+     */
+    private void tldScanStream(String resourcePath, String entryName,
+            InputStream stream) throws IOException {
+        try {
+            // Parse the tag library descriptor at the specified resource path
+            String uri = null;
+
+            TreeNode tld =
+                new ParserUtils().parseXMLDocument(resourcePath, stream);
+            TreeNode uriNode = tld.findChild("uri");
+            if (uriNode != null) {
+                String body = uriNode.getBody();
+                if (body != null)
+                    uri = body;
+            }
+
+            // Add implicit map entry only if its uri is not already
+            // present in the map
+            if (uri != null && mappings.get(uri) == null) {
+                mappings.put(uri, new String[]{ resourcePath, entryName });
+            }
+        } catch (JasperException e) {
+            // Hack - makes exception handling simpler
+            throw new IOException(e);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (Throwable t) {
+                    // do nothing
+                }
+            }
+        }
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to