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