import javax.servlet.ServletContext;
+import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.http.mapper.Mapper;
import org.apache.catalina.deploy.ApplicationParameter;
*/
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);
+
}
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;
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;
/**
*/
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;
}
// Create context attributes that will be required
if (ok) {
+ getServletContext().setAttribute(
+ JarScanner.class.getName(), getJarScanner());
+ }
+
+ if (ok) {
postWelcomeFiles();
}
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");
+
}
}
--- /dev/null
+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:
+ * <ul>
+ * <li>Scanning the classloader hierarchy (enabled by default)</li>
+ * <li>Testing all files to see if they are JARs (disabled by default)</li>
+ * <li>Testing all directories to see if they are exploded JARs
+ * (disabled by default)</li>
+ * </ul>
+ * 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<String> jarsToSkip) {
+
+ if (log.isTraceEnabled()) {
+ log.trace(sm.getString("jarScan.webinflibStart"));
+ }
+
+ // Scan WEB-INF/lib
+ Set<String> dirList = context.getResourcePaths(WEB_INF_LIB);
+ if (dirList != null) {
+ Iterator<String> 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<urls.length; i++) {
+ // Extract the jarName if there is one to be found
+ String jarName = getJarName(urls[i]);
+
+ // Skip JARs with known not to be interesting and JARs
+ // in WEB-INF/lib we have already scanned
+ if (!(jarsToSkip.contains(jarName) ||
+ urls[i].toString().contains(
+ WEB_INF_LIB + jarName))) {
+ try {
+ process(callback, urls[i]);
+ } catch (IOException ioe) {
+ log.warn(sm.getString(
+ "jarScan.classloaderFail",urls[i]), ioe);
+ }
+ }
+ }
+ }
+ loader = loader.getParent();
+ }
+
+ }
+ }
+
+ /*
+ * Scan a URL for JARs with the optional extensions to look at all files
+ * and all directories.
+ */
+ private void process(JarScannerCallback callback, URL url)
+ throws IOException {
+
+ if (log.isTraceEnabled()) {
+ log.trace(sm.getString("jarScan.jarUrlStart", url));
+ }
+
+ URLConnection conn = url.openConnection();
+ if (conn instanceof JarURLConnection) {
+ callback.scan((JarURLConnection) conn);
+ } else {
+ String urlStr = url.toString();
+ if (urlStr.startsWith("file:")) {
+ if (urlStr.endsWith(JAR_EXT)) {
+ URL jarURL = new URL("jar:" + urlStr + "!/");
+ callback.scan((JarURLConnection) jarURL.openConnection());
+ } else {
+ File f;
+ try {
+ f = new File(url.toURI());
+ if (f.isFile() && scanAllFiles) {
+ // Treat this file as a JAR
+ URL jarURL = new URL("jar:" + urlStr + "!/");
+ callback.scan((JarURLConnection) jarURL.openConnection());
+ } else if (f.isDirectory() && scanAllDirectories) {
+ File metainf = new File(f.getAbsoluteFile() +
+ File.separator + "META-INF");
+ if (metainf.isDirectory()) {
+ callback.scan(f);
+ }
+ }
+ } catch (URISyntaxException e) {
+ // Wrap the exception and re-throw
+ IOException ioe = new IOException();
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+ }
+ }
+
+ }
+
+ /*
+ * Extract the JAR name, if present, from a URL
+ */
+ private String getJarName(URL url) {
+
+ String name = null;
+
+ String path = url.getPath();
+ int end = path.indexOf(JAR_EXT);
+ if (end != -1) {
+ int start = path.lastIndexOf('/', end);
+ name = path.substring(start + 1, end + 4);
+ }
+
+ return name;
+ }
+
+}
hostConfig.undeploy=Undeploying context [{0}]
hostConfig.undeploy.error=Error undeploying web application at context path {0}
hostConfig.undeploying=Undeploying deployed web applications
+jarScan.classloaderFail=Failed to scan [{0}] from classloader hierarchy
+jarScan.classloaderStart=Scanning for JARs in classloader hierarchy
+jarScan.jarUrlStart=Scanning JAR at URL [{0}]
+jarScan.webinflibFail=Failed to scan JAR [{0}] from WEB-INF/lib
+jarScan.webinflibStart=Scanning WEB-INF/lib for JARs
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.classloaderStart=Scanning for TLDs in classloader hierarchy
-tldConfig.dirScan=Scanning for TLD files in directory ''{0}''
+tldConfig.dirFail=Failed to process directory [{0}] for TLD files
+tldConfig.dirScan=Scanning for TLD files in directory [{0}]
tldConfig.execute=Error processing TLD files for context path {0}
-tldConfig.jarUrlStart=Scanning for TLD files in URL ''{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.webxmlAdd=Adding path ''{0}'' for URI ''{1}''
-tldConfig.webxmlFail=Failed to process TLD with path ''{1}'' and URI ''{0}''
-tldConfig.webxmlSkip=Path ''{1}'' skipped since URI ''{0}'' is a duplicate
+tldConfig.webinfFail=Failed to process TLD found at [{0}]
+tldConfig.webinfScan=Scanning WEB-INF for TLD files in [{0}]
+tldConfig.webxmlAdd=Adding path [{0}] for URI [{1}]
+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}
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 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;
*/
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<String> noTldJars;
/**
* 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.,
+ * <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.
+ *
* @exception Exception if a fatal input/output or parsing error occurs
*/
public void execute() throws Exception {
// 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();
}
+ 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
}
/*
- * 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<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 {
- 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.,
- * <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() {
-
- 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; i<urls.length; i++) {
- // 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))) {
- try {
- tldScanJar(urls[i]);
- } catch (IOException ioe) {
- log.warn(sm.getString(
- "tldConfig.classloaderFail",urls[i]), ioe);
- }
- }
- }
- }
- loader = loader.getParent();
- }
- }
-
- /*
- * Keep in sync with o.a.j.comiler.TldLocationsCache
- */
- private void tldScanJar(URL url) throws IOException {
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("tldConfig.jarUrlStart", url));
- }
-
- URLConnection conn = url.openConnection();
- if (conn instanceof JarURLConnection) {
- tldScanJar((JarURLConnection) conn);
- } else {
- String urlStr = url.toString();
- if (urlStr.startsWith("file:")) {
- if (urlStr.endsWith(JAR_EXT)) {
- URL jarURL = new URL("jar:" + urlStr + "!/");
- tldScanJar((JarURLConnection) jarURL.openConnection());
- } else {
- File f;
- try {
- f = new File(url.toURI());
- if (f.isFile() && SCAN_ALL_FILES) {
- // Treat this file as a JAR
- URL jarURL = new URL("jar:" + urlStr + "!/");
- tldScanJar((JarURLConnection) jarURL.openConnection());
- } else if (f.isDirectory() && SCAN_ALL_DIRS) {
- File metainf = new File(f.getAbsoluteFile() +
- File.separator + "META-INF");
- if (metainf.isDirectory()) {
- tldScanDir(metainf);
- }
- }
- } catch (URISyntaxException e) {
- // Wrap the exception and re-throw
- IOException ioe = new IOException();
- ioe.initCause(e);
- throw ioe;
- }
- }
- }
- }
- }
-
- /*
* Scans the directory identified by startPath, along with its
* sub-directories, for TLDs.
*
/*
- * 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;
-
- 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 register
* any application event listeners found there. <b>NOTE</b> - This
* method ensure that the InputStream is correctly closed.
tldDigester = createTldDigester(tldNamespaceAware, tldValidation);
}
}
+
}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
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;
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"
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<String> noTldJars;
/*
* 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.,
+ * <CATALINA_HOME>/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(
}
}
+ 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.
*
}
/*
- * 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<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);
- }
- }
- }
- }
-
- /*
- * 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.,
- * <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 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<urls.length; i++) {
- // 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();
- }
- }
-
- /*
- * Keep in sync with o.a.c.startup.TldConfig
- */
- 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:")) {
- if (urlStr.endsWith(JAR_EXT)) {
- URL jarURL = new URL("jar:" + urlStr + "!/");
- tldScanJar((JarURLConnection) jarURL.openConnection());
- } else {
- File f;
- try {
- f = new File(url.toURI());
- if (f.isFile() && SCAN_ALL_FILES) {
- // Treat this file as a JAR
- URL jarURL = new URL("jar:" + urlStr + "!/");
- tldScanJar((JarURLConnection) jarURL.openConnection());
- tldScanJar((JarURLConnection) jarURL.openConnection());
- } else if (f.isDirectory() && SCAN_ALL_DIRS) {
- File metainf = new File(f.getAbsoluteFile() +
- File.separator + "META-INF");
- if (metainf.isDirectory()) {
- tldScanDir(metainf);
- }
- }
- } catch (URISyntaxException e) {
- // Wrap the exception and re-throw
- IOException ioe = new IOException();
- ioe.initCause(e);
- throw ioe;
- }
- }
- }
- }
- }
-
- /*
* Scans the directory identified by startPath, along with its
* sub-directories, for TLDs.
*
}
/*
- * 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.
*
jsp.error.tag.invalid.trimdirectivewhitespaces=Tag directive: invalid value for trimDirectiveWhitespaces
jsp.error.page.conflict.trimdirectivewhitespaces=Page directive: illegal to have multiple occurrences of 'trimDirectiveWhitespaces' with different values (old: {0}, new: {1})
jsp.error.tag.conflict.trimdirectivewhitespaces=Tag directive: illegal to have multiple occurrences of 'trimDirectiveWhitespaces' with different values (old: {0}, new: {1})
+
+# JarScanner
+jsp.warning.noJarScanner=Warning: No org.apache.tomcat.JarScanner set in ServletContext. Jar scanning for TLDs and web-fragment.xml files is disabled
\ No newline at end of file
--- /dev/null
+/*
+ * 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.tomcat;
+
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Scans a web application and classloader hierarchy for JAR files. Uses
+ * include TLD scanning and web-fragment.xml scanning. Uses a call-back
+ * mechanism so the caller can process each JAR found.
+ */
+public interface JarScanner {
+
+ /**
+ * Scan the provided ServletContext and classloader for JAR files. Each JAR
+ * file found will be passed to the callback handler to be processed.
+ *
+ * @param context The ServletContext - used to locate and access
+ * WEB-INF/lib
+ * @param classloader The classloader - used to access JARs not in
+ * WEB-INF/lib
+ * @param callback The handler to process any JARs found
+ * @param jarsToSkip List of JARs to ignore
+ */
+ public void scan(ServletContext context, ClassLoader classloader,
+ JarScannerCallback callback, Set<String> jarsToSkip);
+
+}
--- /dev/null
+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 ;
+
+}
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<!DOCTYPE document [
+ <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="jar-scanner.html">
+
+ &project;
+
+ <properties>
+ <title>The Jar Scanner Component</title>
+ </properties>
+
+<body>
+
+
+<section name="Introduction">
+
+ <p>The <strong>Jar Scanner</strong> 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.</p>
+
+ <p>A Jar Scanner element MAY be nested inside a
+ <a href="context.html">Context</a> component. If it is not included,
+ a default Jar Scanner configuration will be created automatically, which
+ is sufficient for most requirements.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+ <subsection name="Common Attributes">
+
+ <p>All implementations of <strong>Jar Scanner</strong>
+ support the following attributes:</p>
+
+ <attributes>
+
+ <attribute name="className" required="false">
+ <p>Java class name of the implementation to use. This class must
+ implement the <code>org.apache.tomcat.JarScanner</code> interface.
+ If not specified, the standard value (defined below) will be used.</p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+
+ <subsection name="Standard Implementation">
+
+ <p>The standard implementation of <strong>Jar Scanner</strong> is
+ <strong>org.apache.catalina.deploy.DefaultJarScanner</strong>.
+ It supports the following additional attributes (in addition to the
+ common attributes listed above):</p>
+
+ <attributes>
+
+ <attribute name="scanClassPath" required="false">
+ <p>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 <code>true</code>.</p>
+ </attribute>
+
+ <attribute name="scanAllFiles" required="false">
+ <p>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
+ <code>.jar</code>. The default is <code>false</code></p>
+ </attribute>
+
+ <attribute name="scanAllDirectories" required="false">
+ <p>If true, any directories found on the classpath will be checked to see
+ if are expanded Jar files. The default is <code>false</code></p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+
+</section>
+
+
+<section name="Nested Components">
+ <p>No components may be nested inside a <strong>Jar Scanner</strong> element.
+ </p>
+</section>
+
+
+<section name="Special Features">
+ <p>No special features are associated with a <strong>Jar Scanner</strong>
+ element.</p>
+</section>
+
+</body>
+
+</document>
<item name="Listeners" href="listeners.html"/>
<item name="Global Resources" href="globalresources.html"/>
<item name="Loader" href="loader.html"/>
+ <item name="JarScanner" href="jar-scanner.html"/>
<item name="Manager" href="manager.html"/>
<item name="Realm" href="realm.html"/>
<item name="Resources" href="resources.html"/>