From: markt Date: Tue, 22 Sep 2009 14:54:28 +0000 (+0000) Subject: Refactor the TLD JAR scanning. This a) reduces duplication between Catalina and Jaspe... X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=4403e0f4ed91f86f61998838a8f97bde66d860ff;p=tomcat7.0 Refactor the TLD JAR scanning. This a) reduces duplication between Catalina and Jasper b) allows the Jar scanning to be controlled by configuration rather than system properties and c) (hopefully) allows the scanning code to be re-used for web-fragment.xml scanning The JSP TCK passes with this patch applied git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@817685 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/java/org/apache/catalina/Context.java b/java/org/apache/catalina/Context.java index 0f6c20a31..b8478f4ac 100644 --- a/java/org/apache/catalina/Context.java +++ b/java/org/apache/catalina/Context.java @@ -21,6 +21,7 @@ package org.apache.catalina; import javax.servlet.ServletContext; +import org.apache.tomcat.JarScanner; import org.apache.tomcat.util.http.mapper.Mapper; import org.apache.catalina.deploy.ApplicationParameter; @@ -1081,5 +1082,19 @@ public interface Context extends Container { */ public String getRealPath(String path); + /** + * Get the Jar Scanner to be used to scan for JAR resources for this + * context. + * @return The Jar Scanner configured for this context. + */ + public JarScanner getJarScanner(); + + /** + * Set the Jar Scanner to be used to scan for JAR resources for this + * context. + * @param jarScanner The Jar Scanner to be used for this context. + */ + public void setJarScanner(JarScanner jarScanner); + } diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java index d291f5d50..910861b69 100644 --- a/java/org/apache/catalina/core/StandardContext.java +++ b/java/org/apache/catalina/core/StandardContext.java @@ -85,6 +85,7 @@ import org.apache.catalina.deploy.SecurityConstraint; import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.session.StandardManager; import org.apache.catalina.startup.ContextConfig; +import org.apache.catalina.startup.DefaultJarScanner; import org.apache.catalina.startup.TldConfig; import org.apache.catalina.util.CharsetMapper; import org.apache.catalina.util.ExtensionValidator; @@ -99,6 +100,7 @@ import org.apache.naming.resources.FileDirContext; import org.apache.naming.resources.ProxyDirContext; import org.apache.naming.resources.WARDirContext; import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.JarScanner; import org.apache.tomcat.util.modeler.Registry; /** @@ -725,12 +727,30 @@ public class StandardContext */ private boolean useHttpOnly = true; + /** + * The Jar scanner to use to search for Jars that might contain + * configuration information such as TLDs or web-fragment.xml files. + */ + private JarScanner jarScanner = null; // ----------------------------------------------------- Context Properties + public JarScanner getJarScanner() { + if (jarScanner == null) { + jarScanner = new DefaultJarScanner(); + } + return jarScanner; + } + + + public void setJarScanner(JarScanner jarScanner) { + this.jarScanner = jarScanner; + } + + public InstanceManager getInstanceManager() { return instanceManager; } @@ -4459,6 +4479,11 @@ public class StandardContext // Create context attributes that will be required if (ok) { + getServletContext().setAttribute( + JarScanner.class.getName(), getJarScanner()); + } + + if (ok) { postWelcomeFiles(); } diff --git a/java/org/apache/catalina/startup/ContextRuleSet.java b/java/org/apache/catalina/startup/ContextRuleSet.java index 9f176e9c3..762ae73a5 100644 --- a/java/org/apache/catalina/startup/ContextRuleSet.java +++ b/java/org/apache/catalina/startup/ContextRuleSet.java @@ -203,6 +203,14 @@ public class ContextRuleSet extends RuleSetBase { digester.addCallMethod(prefix + "Context/WrapperListener", "addWrapperListener", 0); + digester.addObjectCreate(prefix + "Context/JarScanner", + "org.apache.catalina.deploy.DefaultJarScanner", + "className"); + digester.addSetProperties(prefix + "Context/JarScanner"); + digester.addSetNext(prefix + "Context/JarScanner", + "setJarScanner", + "org.apache.tomcat.JarScanner"); + } } diff --git a/java/org/apache/catalina/startup/DefaultJarScanner.java b/java/org/apache/catalina/startup/DefaultJarScanner.java new file mode 100644 index 000000000..cfb728fe9 --- /dev/null +++ b/java/org/apache/catalina/startup/DefaultJarScanner.java @@ -0,0 +1,214 @@ +package org.apache.catalina.startup; + +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.util.Iterator; +import java.util.Set; + +import javax.servlet.ServletContext; + +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.JarScannerCallback; +import org.apache.tomcat.util.res.StringManager; + +/** + * The default {@link JarScanner} implementation scans the WEB-INF/lib directory + * followed by the provided classloader and then works up the classloader + * hierarchy. This implementation is sufficient to meet the requirements of the + * Servlet 3.0 specification as well is to provide a number of Tomcat specific + * extensions. The extensions are: + * + * All of the extenions may be controlled via configuration. + */ +public class DefaultJarScanner implements JarScanner { + + private static final String JAR_EXT = ".jar"; + private static final String WEB_INF_LIB = "/WEB-INF/lib/"; + + private static org.apache.juli.logging.Log log= + org.apache.juli.logging.LogFactory.getLog( TldConfig.class ); + + /** + * The string resources for this package. + */ + private static final StringManager sm = + StringManager.getManager(Constants.Package); + + + /** + * Controls the classpath scanning extenion. + */ + private boolean scanClassPath = true; + public boolean isScanClassPath() { + return scanClassPath; + } + public void setScanClassPath(boolean scanClassPath) { + this.scanClassPath = scanClassPath; + } + + /** + * Controls the testing all files to see of they are JAR files extenion. + */ + private boolean scanAllFiles = false; + public boolean isScanAllFiles() { + return scanAllFiles; + } + public void setScanAllFiles(boolean scanAllFiles) { + this.scanAllFiles = scanAllFiles; + } + + /** + * Controls the testing all directories to see of they are exploded JAR + * files extenion. + */ + private boolean scanAllDirectories = false; + public boolean isScanAllDirectories() { + return scanAllDirectories; + } + public void setScanAllDirectories(boolean scanAllDirectories) { + this.scanAllDirectories = scanAllDirectories; + } + + /** + * {@inheritDoc} + */ + @Override + public void scan(ServletContext context, ClassLoader classloader, + JarScannerCallback callback, Set jarsToSkip) { + + if (log.isTraceEnabled()) { + log.trace(sm.getString("jarScan.webinflibStart")); + } + + // Scan WEB-INF/lib + Set dirList = context.getResourcePaths(WEB_INF_LIB); + if (dirList != null) { + Iterator it = dirList.iterator(); + while (it.hasNext()) { + String path = it.next(); + if (path.endsWith(JAR_EXT) && + !jarsToSkip.contains( + path.substring(path.lastIndexOf('/')))) { + // Need to scan this JAR + URL url = null; + try { + url = context.getResource(path); + process(callback, url); + } catch (IOException e) { + log.warn(sm.getString("jarScan.webinflibFail", url), e); + } + } + } + } + + // Scan the classpath + if (scanClassPath) { + if (log.isTraceEnabled()) { + log.trace(sm.getString("jarScan.classloaderStart")); + } + + ClassLoader loader = + Thread.currentThread().getContextClassLoader(); + + while (loader != null) { + if (loader instanceof URLClassLoader) { + URL[] urls = ((URLClassLoader) loader).getURLs(); + for (int i=0; i elements in web.xml userConfig.database=Exception loading user database userConfig.deploy=Deploying web application for user {0} diff --git a/java/org/apache/catalina/startup/TldConfig.java b/java/org/apache/catalina/startup/TldConfig.java index 530bb71b3..1dd6d9595 100644 --- a/java/org/apache/catalina/startup/TldConfig.java +++ b/java/org/apache/catalina/startup/TldConfig.java @@ -24,10 +24,6 @@ import java.io.FileInputStream; 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; @@ -45,6 +41,8 @@ import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardHost; +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.JarScannerCallback; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.digester.Digester; import org.xml.sax.InputSource; @@ -61,27 +59,10 @@ import org.xml.sax.SAXException; */ 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/"; - // Configuration properties - private static final boolean SCAN_CLASSPATH = Boolean.valueOf( - System.getProperty( - "org.apache.jasper.compiler.TldLocationsCache.SCAN_CLASSPATH", - "true")).booleanValue(); - - private static final boolean SCAN_ALL_FILES = Boolean.valueOf( - System.getProperty( - "org.apache.jasper.compiler.TldLocationsCache.SCAN_ALL_FILES", - "false")).booleanValue(); - - private static final boolean SCAN_ALL_DIRS = Boolean.valueOf( - System.getProperty( - "org.apache.jasper.compiler.TldLocationsCache.SCAN_ALL_DIRS", - "false")).booleanValue(); - // Names of JARs that are known not to contain any TLDs private static HashSet noTldJars; @@ -327,7 +308,18 @@ public final class TldConfig implements LifecycleListener { /** * Scan for and configure all tag library descriptors found in this * web application. + * + * This supports 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., + * /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. + * * @exception Exception if a fatal input/output or parsing error occurs */ public void execute() throws Exception { @@ -349,14 +341,12 @@ public final class TldConfig implements LifecycleListener { // Stage 3a - TLDs under WEB-INF (not lib or classes) tldScanResourcePaths(WEB_INF); - // Stage 3b - .jar files in WEB-INF/lib/ - tldScanWebInfLib(); + // Stages 3b & 4 + JarScanner jarScanner = context.getJarScanner(); + jarScanner.scan(context.getServletContext(), + context.getLoader().getClassLoader(), + new TldJarScannerCallback(), noTldJars); - // Stage 4 - Additional entries from the container - if (SCAN_CLASSPATH) { - tldScanClassloaders(); - } - // Now add all the listeners we found to the listeners for this context String list[] = getTldListeners(); @@ -375,6 +365,22 @@ public final class TldConfig implements LifecycleListener { } + private class TldJarScannerCallback implements JarScannerCallback { + + @Override + public void scan(JarURLConnection urlConn) throws IOException { + tldScanJar(urlConn); + } + + @Override + public void scan(File file) { + File metaInf = new File(file, "META-INF"); + if (metaInf.isDirectory()) { + tldScanDir(metaInf); + } + } + } + // -------------------------------------------------------- Private Methods @@ -478,136 +484,6 @@ public final class TldConfig implements LifecycleListener { } /* - * 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")); - } - ServletContext ctxt = context.getServletContext(); - - Set dirList = ctxt.getResourcePaths(WEB_INF_LIB); - if (dirList != null) { - Iterator 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 { - url = ctxt.getResource(path); - tldScanJar(url); - } catch (IOException e) { - log.warn(sm.getString("tldConfig.webinflibJarFail"), 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 - * scanned previously. - * - * 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., - * /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. - * - * Keep in sync with o.a.j.comiler.TldLocationsCache - */ - private void tldScanClassloaders() { - - if (log.isTraceEnabled()) { - log.trace(sm.getString("tldConfig.classloaderStart")); - } - - ClassLoader loader = - Thread.currentThread().getContextClassLoader(); - - while (loader != null) { - if (loader instanceof URLClassLoader) { - URL[] urls = ((URLClassLoader) loader).getURLs(); - for (int i=0; iNOTE - This * method ensure that the InputStream is correctly closed. @@ -779,4 +636,5 @@ public final class TldConfig implements LifecycleListener { tldDigester = createTldDigester(tldNamespaceAware, tldValidation); } } + } diff --git a/java/org/apache/jasper/compiler/JarScannerFactory.java b/java/org/apache/jasper/compiler/JarScannerFactory.java new file mode 100644 index 000000000..d7f1664fa --- /dev/null +++ b/java/org/apache/jasper/compiler/JarScannerFactory.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.jasper.compiler; + +import javax.servlet.ServletContext; + +import org.apache.tomcat.JarScanner; + +/** + * Provide a mechanism for Jasper to obtain a reference to the JarScanner + * impementation. + */ +public class JarScannerFactory { + + /* + * Don't want any instances so hide the default constructor. + */ + private JarScannerFactory() { + } + + /** + * Obtain the {@link JarScanner} associated with the specificed {@link + * ServletContext}. It is obtained via a context parameter. + */ + public static JarScanner getJarScanner(ServletContext ctxt) { + JarScanner jarScanner = + (JarScanner) ctxt.getAttribute(JarScanner.class.getName()); + if (jarScanner == null) { + ctxt.log(Localizer.getMessage("jsp.warning.noJarScanner")); + } + return jarScanner; + } + +} diff --git a/java/org/apache/jasper/compiler/TldLocationsCache.java b/java/org/apache/jasper/compiler/TldLocationsCache.java index 40a57fe0b..167d90070 100644 --- a/java/org/apache/jasper/compiler/TldLocationsCache.java +++ b/java/org/apache/jasper/compiler/TldLocationsCache.java @@ -23,10 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.JarURLConnection; import java.net.MalformedURLException; -import java.net.URISyntaxException; import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLConnection; import java.util.Enumeration; import java.util.Hashtable; import java.util.HashSet; @@ -45,6 +42,8 @@ import org.apache.jasper.xmlparser.ParserUtils; import org.apache.jasper.xmlparser.TreeNode; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.JarScannerCallback; /** * A container for all tag libraries that are defined "globally" @@ -98,22 +97,6 @@ public class TldLocationsCache { private static final String JAR_EXT = ".jar"; private static final String TLD_EXT = ".tld"; - // Configuration properties - private static final boolean SCAN_CLASSPATH = Boolean.valueOf( - System.getProperty( - "org.apache.jasper.compiler.TldLocationsCache.SCAN_CLASSPATH", - "true")).booleanValue(); - - private static final boolean SCAN_ALL_FILES = Boolean.valueOf( - System.getProperty( - "org.apache.jasper.compiler.TldLocationsCache.SCAN_ALL_FILES", - "false")).booleanValue(); - - private static final boolean SCAN_ALL_DIRS = Boolean.valueOf( - System.getProperty( - "org.apache.jasper.compiler.TldLocationsCache.SCAN_ALL_DIRS", - "false")).booleanValue(); - // Names of JARs that are known not to contain any TLDs private static HashSet noTldJars; @@ -257,16 +240,28 @@ public class TldLocationsCache { /* * Keep processing order in sync with o.a.c.startup.TldConfig + * + * This supports 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., + * /lib). It also supports some of the weird and + * wonderful arrangements present when Tomcat gets embedded. + * */ private void init() throws JasperException { if (initialized) return; try { tldScanWebXml(); tldScanResourcePaths(WEB_INF); - tldScanWebInfLib(); - if (SCAN_CLASSPATH) { - tldScanClassloaders(); + + JarScanner jarScanner = JarScannerFactory.getJarScanner(ctxt); + if (jarScanner != null) { + jarScanner.scan(ctxt, + Thread.currentThread().getContextClassLoader(), + new TldJarScannerCallback(), noTldJars); } + initialized = true; } catch (Exception ex) { throw new JasperException(Localizer.getMessage( @@ -274,6 +269,22 @@ public class TldLocationsCache { } } + private class TldJarScannerCallback implements JarScannerCallback { + + @Override + public void scan(JarURLConnection urlConn) throws IOException { + tldScanJar(urlConn); + } + + @Override + public void scan(File file) throws IOException { + File metaInf = new File(file, "META-INF"); + if (metaInf.isDirectory()) { + tldScanDir(metaInf); + } + } + } + /* * Populates taglib map described in web.xml. * @@ -414,115 +425,6 @@ public class TldLocationsCache { } /* - * 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 void tldScanWebInfLib() throws Exception { - - Set dirList = ctxt.getResourcePaths(WEB_INF_LIB); - if (dirList != null) { - Iterator 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); - } - } - } - } - - /* - * 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. - * - * 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., - * /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. - * - * Keep in sync with o.a.c.startup.TldConfig - */ - private void tldScanClassloaders() throws Exception { - - ClassLoader loader = - Thread.currentThread().getContextClassLoader(); - - while (loader != null) { - if (loader instanceof URLClassLoader) { - URL[] urls = ((URLClassLoader) loader).getURLs(); - for (int i=0; i jarsToSkip); + +} diff --git a/java/org/apache/tomcat/JarScannerCallback.java b/java/org/apache/tomcat/JarScannerCallback.java new file mode 100644 index 000000000..4d31fdf98 --- /dev/null +++ b/java/org/apache/tomcat/JarScannerCallback.java @@ -0,0 +1,18 @@ +package org.apache.tomcat; + +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; + +public interface JarScannerCallback { + + /** + * + * @param urlConn + * @throws IOException + */ + public void scan(JarURLConnection urlConn) throws IOException; + + public void scan(File file) throws IOException ; + +} diff --git a/webapps/docs/config/jar-scanner.xml b/webapps/docs/config/jar-scanner.xml new file mode 100644 index 000000000..1c4377ac6 --- /dev/null +++ b/webapps/docs/config/jar-scanner.xml @@ -0,0 +1,115 @@ + + + +]> + + + &project; + + + The Jar Scanner Component + + + + + +
+ +

The Jar Scanner element represents the component that is + used to scan the web application for JAR files. It is typically used during + web application start to identify configuration files such as TLDs or + web-fragment.xml files that must be processed as part of the web application + initialisation.

+ +

A Jar Scanner element MAY be nested inside a + Context component. If it is not included, + a default Jar Scanner configuration will be created automatically, which + is sufficient for most requirements.

+ +
+ + +
+ + + +

All implementations of Jar Scanner + support the following attributes:

+ + + + +

Java class name of the implementation to use. This class must + implement the org.apache.tomcat.JarScanner interface. + If not specified, the standard value (defined below) will be used.

+
+ +
+ +
+ + + + +

The standard implementation of Jar Scanner is + org.apache.catalina.deploy.DefaultJarScanner. + It supports the following additional attributes (in addition to the + common attributes listed above):

+ + + + +

If true, the full web application classpath, including the shared and + common classloaders will be scanned for Jar files in addition to the + web application. The default is true.

+
+ + +

If true, any files found on the classpath will be checked to see if + they are Jar files rather than relying on the file extension being + .jar. The default is false

+
+ + +

If true, any directories found on the classpath will be checked to see + if are expanded Jar files. The default is false

+
+ +
+ +
+ + +
+ + +
+

No components may be nested inside a Jar Scanner element. +

+
+ + +
+

No special features are associated with a Jar Scanner + element.

+
+ + + +
diff --git a/webapps/docs/config/project.xml b/webapps/docs/config/project.xml index 4b4cff5a2..1b63be023 100644 --- a/webapps/docs/config/project.xml +++ b/webapps/docs/config/project.xml @@ -57,6 +57,7 @@ +