Author: jboynes Date: Mon Aug 12 02:05:59 2013 New Revision: 1513028 URL: http://svn.apache.org/r1513028 Log: Refactor TldLocationsCache to use scan results from the initializer. This means we only scan the application and classpath once to locate TLDs and eliminates duplicate code that needed to be kept in sync between Catalina's TldConfig and Jasper's TldLocationsCache. Deprecate TldLocation to be replaced with TldResourcePath once TagLibraryInfo and dependency management can be decoupled.
Modified: tomcat/trunk/java/org/apache/jasper/JspC.java tomcat/trunk/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java tomcat/trunk/java/org/apache/jasper/compiler/TldLocation.java tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java tomcat/trunk/java/org/apache/jasper/servlet/JasperInitializer.java Modified: tomcat/trunk/java/org/apache/jasper/JspC.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/JspC.java?rev=1513028&r1=1513027&r2=1513028&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/JspC.java (original) +++ tomcat/trunk/java/org/apache/jasper/JspC.java Mon Aug 12 02:05:59 2013 @@ -51,12 +51,14 @@ import org.apache.jasper.compiler.Locali import org.apache.jasper.compiler.TagPluginManager; import org.apache.jasper.compiler.TldLocationsCache; import org.apache.jasper.servlet.JspCServletContext; +import org.apache.jasper.servlet.TldScanner; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.util.FileUtils; +import org.xml.sax.SAXException; /** * Shell for the jspc compiler. Handles all options associated with the @@ -1419,7 +1421,14 @@ public class JspC extends Task implement URL resourceBase = new File(uriRoot).getCanonicalFile().toURI().toURL(); context = new JspCServletContext(log, resourceBase, classLoader); - tldLocationsCache = TldLocationsCache.getInstance(context); + TldScanner scanner = new TldScanner(context, true, false); + try { + scanner.scan(); + } catch (SAXException e) { + throw new JasperException(e); + } + tldLocationsCache = new TldLocationsCache(scanner.getTaglibMap()); + context.setAttribute(TldLocationsCache.KEY, tldLocationsCache); rctxt = new JspRuntimeContext(context, this); jspConfig = new JspConfig(context); tagPluginManager = new TagPluginManager(context); Modified: tomcat/trunk/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java?rev=1513028&r1=1513027&r2=1513028&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java (original) +++ tomcat/trunk/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java Mon Aug 12 02:05:59 2013 @@ -296,22 +296,22 @@ class TagLibraryInfoImpl extends TagLibr } /* - * @param uri The uri of the TLD @param ctxt The compilation context + * @param uri The uri of the TLD + * @param ctxt The compilation context * - * @return String array whose first element denotes the path to the TLD. If - * the path to the TLD points to a jar file, then the second element denotes - * the name of the TLD entry in the jar file, which is hardcoded to - * META-INF/taglib.tld. + * @return the location of the TLD identified by the uri */ @SuppressWarnings("null") // url can't be null private TldLocation generateTLDLocation(String uri, JspCompilationContext ctxt) throws JasperException { - int uriType = TldLocationsCache.uriType(uri); - if (uriType == TldLocationsCache.ABS_URI) { - err.jspError("jsp.error.taglibDirective.absUriCannotBeResolved", - uri); - } else if (uriType == TldLocationsCache.NOROOT_REL_URI) { + // TODO: this matches the current implementation but the URL logic looks fishy + // map URI to location per JSP 7.3.6.2 + if (uri.indexOf(':') != -1) { + // abs_uri, this was not found in the taglibMap so raise an error + err.jspError("jsp.error.taglibDirective.absUriCannotBeResolved", uri); + } else if (uri.charAt(0) != '/') { + // noroot_rel_uri, resolve against the current JSP page uri = ctxt.resolveRelativeUri(uri); } Modified: tomcat/trunk/java/org/apache/jasper/compiler/TldLocation.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/compiler/TldLocation.java?rev=1513028&r1=1513027&r2=1513028&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/compiler/TldLocation.java (original) +++ tomcat/trunk/java/org/apache/jasper/compiler/TldLocation.java Mon Aug 12 02:05:59 2013 @@ -17,6 +17,7 @@ package org.apache.jasper.compiler; +@Deprecated public class TldLocation { private final String entryName; 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=1513028&r1=1513027&r2=1513028&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java (original) +++ tomcat/trunk/java/org/apache/jasper/compiler/TldLocationsCache.java Mon Aug 12 02:05:59 2013 @@ -16,47 +16,30 @@ */ package org.apache.jasper.compiler; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.JarURLConnection; -import java.net.URL; -import java.util.Collection; import java.util.Hashtable; -import java.util.Iterator; -import java.util.Set; +import java.util.Map; import javax.servlet.ServletContext; -import javax.servlet.descriptor.JspConfigDescriptor; -import javax.servlet.descriptor.TaglibDescriptor; import org.apache.jasper.JasperException; -import org.apache.jasper.util.ExceptionUtils; -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.JarScanType; -import org.apache.tomcat.JarScanner; -import org.apache.tomcat.JarScannerCallback; -import org.apache.tomcat.util.scan.Jar; -import org.apache.tomcat.util.scan.JarFactory; +import org.apache.tomcat.util.descriptor.tld.TldResourcePath; /** * A container for all tag libraries that are defined "globally" * for the web application. - * + * <p/> * Tag Libraries can be defined globally in one of two ways: - * 1. Via <taglib> elements in web.xml: - * the uri and location of the tag-library are specified in - * the <taglib> element. - * 2. Via packaged jar files that contain .tld files - * within the META-INF directory, or some subdirectory - * of it. The taglib is 'global' if it has the <uri> - * element defined. - * + * 1. Via <taglib> elements in web.xml: + * the uri and location of the tag-library are specified in + * the <taglib> element. + * 2. Via packaged jar files that contain .tld files + * within the META-INF directory, or some subdirectory + * of it. The taglib is 'global' if it has the <uri> + * element defined. + * <p/> * A mapping between the taglib URI and its associated TaglibraryInfoImpl * is maintained in this container. * Actually, that's what we'd like to do. However, because of the @@ -65,7 +48,7 @@ import org.apache.tomcat.util.scan.JarFa * across page invocations. A bug has been submitted to the spec lead. * In the mean time, all we do is save the 'location' where the * TLD associated with a taglib URI can be found. - * + * <p/> * When a JSP page has a taglib directive, the mappings in this container * are first searched (see method getLocation()). * If a mapping is found, then the location of the TLD is returned. @@ -76,389 +59,53 @@ import org.apache.tomcat.util.scan.JarFa * @author Pierre Delisle * @author Jan Luehe */ - +@Deprecated public class TldLocationsCache { - private static final Log log = LogFactory.getLog(TldLocationsCache.class); - - private static final String KEY = TldLocationsCache.class.getName(); + public static final String KEY = TldLocationsCache.class.getName(); - /** - * The types of URI one may specify for a tag library - */ - public static final int ABS_URI = 0; - 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_CLASSES = "/WEB-INF/classes/"; - private static final String WEB_INF_LIB = "/WEB-INF/lib/"; - private static final String JAR_EXT = ".jar"; - private static final String TLD_EXT = ".tld"; - - // Flag that indicates that an INFO level message has been provided that - // there are JARs that could be skipped - private static volatile boolean showTldScanWarning = true; - - /** - * The mapping of the 'global' tag library URI to the location (resource - * path) of the TLD associated with that tag library. The location is - * returned as a String array: - * [0] The location - * [1] If the location is a jar file, this is the location of the tld. - */ + private static final Log log = LogFactory.getLog(TldLocationsCache.class); private final Hashtable<String, TldLocation> mappings; - private volatile boolean initialized; - private final ServletContext ctxt; - - /** Constructor. - * - * @param ctxt the servlet context of the web application in which Jasper - * is running - */ - public TldLocationsCache(ServletContext ctxt) { - this.ctxt = ctxt; - mappings = new Hashtable<>(); - initialized = false; + public TldLocationsCache(Map<String, TldResourcePath> taglibMap) { + mappings = new Hashtable<>(taglibMap.size()); + for (Map.Entry<String, TldResourcePath> entry : taglibMap.entrySet()) { + String uri = entry.getKey(); + TldResourcePath tldResourcePath = entry.getValue(); + String url = tldResourcePath.getUrl().toExternalForm(); + String entryName = tldResourcePath.getEntryName(); + TldLocation tldLocation; + if (entryName == null) { + tldLocation = new TldLocation(url); + } else { + tldLocation = new TldLocation(entryName, url); + } + mappings.put(uri, tldLocation); + } } /** - * Obtains the TLD location cache for the given {@link ServletContext} and - * creates one if one does not currently exist. + * Obtains the TLD location cache for the given {@link ServletContext}. */ - public static synchronized TldLocationsCache getInstance( - ServletContext ctxt) { + public static synchronized TldLocationsCache getInstance(ServletContext ctxt) { if (ctxt == null) { throw new IllegalArgumentException("ServletContext was null"); } - TldLocationsCache cache = (TldLocationsCache) ctxt.getAttribute(KEY); - if (cache == null) { - cache = new TldLocationsCache(ctxt); - ctxt.setAttribute(KEY, cache); - } - return cache; + return (TldLocationsCache) ctxt.getAttribute(KEY); } /** * Gets the 'location' of the TLD associated with the given taglib 'uri'. - * + * <p/> * Returns null if the uri is not associated with any tag library 'exposed' * in the web application. A tag library is 'exposed' either explicitly in * web.xml or implicitly via the uri tag in the TLD of a taglib deployed * in a jar file (WEB-INF/lib). * * @param uri The taglib uri - * - * @return An array of two Strings: The first element denotes the real - * path to the TLD. If the path to the TLD points to a jar file, then the - * second element denotes the name of the TLD entry in the jar file. - * Returns null if the uri is not associated with any tag library 'exposed' - * in the web application. + * @return the TldLocation for this uri */ public TldLocation getLocation(String uri) throws JasperException { - if (!initialized) { - init(); - } return mappings.get(uri); } - - /** - * Returns the type of a URI: - * ABS_URI - * ROOT_REL_URI - * NOROOT_REL_URI - */ - public static int uriType(String uri) { - if (uri.indexOf(':') != -1) { - return ABS_URI; - } else if (uri.startsWith("/")) { - return ROOT_REL_URI; - } else { - return NOROOT_REL_URI; - } - } - - /* - * 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 synchronized void init() throws JasperException { - if (initialized) return; - try { - tldScanWebXml(); - tldScanResourcePaths(WEB_INF, false); - - JarScanner jarScanner = JarScannerFactory.getJarScanner(ctxt); - if (jarScanner != null) { - jarScanner.scan(JarScanType.TLD, ctxt, - new TldJarScannerCallback()); - } - - initialized = true; - } catch (Exception ex) { - throw new JasperException(Localizer.getMessage( - "jsp.error.internal.tldinit", ex.getMessage()), ex); - } - } - - private class TldJarScannerCallback implements JarScannerCallback { - - @Override - public void scan(JarURLConnection urlConn, boolean isWebapp) - throws IOException { - // Note: TLDs are processed the same way for application and - // container provided JARs. - tldScanJar(urlConn); - } - - @Override - public void scan(File file, boolean isWebapp) throws IOException { - // Note: TLDs are processed the same way for application and - // container provided JARs. - File metaInf = new File(file, "META-INF"); - if (metaInf.isDirectory()) { - tldScanDir(metaInf); - } - } - - @Override - public void scanWebInfClasses() throws IOException { - tldScanResourcePaths(WEB_INF_CLASSES, true); - } - } - - /* - * Populates taglib map described in web.xml. - * - * This is not kept in sync with o.a.c.startup.TldConfig as a) Jasper only - * needs the URI to TLD mappings and b) Jasper can obtain the information - * from the ServletContext. - */ - private void tldScanWebXml() throws Exception { - - JspConfigDescriptor jspConfig = ctxt.getJspConfigDescriptor(); - if (jspConfig == null) { - return; - } - - Collection<TaglibDescriptor> taglibs = jspConfig.getTaglibs(); - - for (TaglibDescriptor taglib : taglibs) { - - String tagUri = taglib.getTaglibURI(); - String tagLoc = taglib.getTaglibLocation(); - - // Save this location if appropriate - if (tagLoc == null) - continue; - if (uriType(tagLoc) == NOROOT_REL_URI) - tagLoc = "/WEB-INF/" + tagLoc; - TldLocation location; - if (tagLoc.endsWith(JAR_EXT)) { - location = new TldLocation("META-INF/taglib.tld", - ctxt.getResource(tagLoc).toString()); - } else { - location = new TldLocation(tagLoc); - } - mappings.put(tagUri, location); - } - } - - /* - * 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 unless the JarScanner is configured to treat - * /WEB-INF/classes/ as an exploded JAR. - * - * Keep code in sync with o.a.c.startup.TldConfig - */ - private void tldScanResourcePaths(String startPath, boolean webInfAsJar) - throws IOException { - - 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/") - && !webInfAsJar)) { - continue; - } - if (path.endsWith(TLD_EXT)) { - if (path.startsWith("/WEB-INF/tags/") && - !path.endsWith("implicit.tld")) { - continue; - } - InputStream stream = ctxt.getResourceAsStream(path); - try { - tldScanStream(path, null, stream); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } - } - } - } else { - tldScanResourcePaths(path, false); - } - } - } - } - - /* - * Scans the directory identified by startPath, along with its - * sub-directories, for TLDs. - * - * Keep in sync with o.a.c.startup.TldConfig - */ - private void tldScanDir(File start) throws IOException { - - File[] fileList = start.listFiles(); - if (fileList != null) { - for (int i = 0; i < fileList.length; i++) { - // Scan recursively - if (fileList[i].isDirectory()) { - tldScanDir(fileList[i]); - } else if (fileList[i].getAbsolutePath().endsWith(TLD_EXT)) { - InputStream stream = null; - try { - stream = new FileInputStream(fileList[i]); - tldScanStream( - fileList[i].toURI().toString(), null, stream); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } - } - } - } - } - } - } - - /* - * 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 jarConn The JarURLConnection to the JAR file to scan - * - * Keep in sync with o.a.c.startup.TldConfig - */ - private void tldScanJar(JarURLConnection jarConn) throws IOException { - - Jar jar = null; - InputStream is; - boolean foundTld = false; - - URL resourceURL = jarConn.getJarFileURL(); - String resourcePath = resourceURL.toString(); - - try { - jar = JarFactory.newInstance(jarConn.getURL()); - - jar.nextEntry(); - String entryName = jar.getEntryName(); - while (entryName != null) { - if (entryName.startsWith("META-INF/") && - entryName.endsWith(".tld")) { - is = null; - try { - is = jar.getEntryInputStream(); - foundTld = true; - tldScanStream(resourcePath, entryName, is); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException ioe) { - // Ignore - } - } - } - } - jar.nextEntry(); - entryName = jar.getEntryName(); - } - } finally { - if (jar != null) { - jar.close(); - } - } - - if (!foundTld) { - if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage("jsp.tldCache.noTldInJar", - resourcePath)); - } else if (showTldScanWarning) { - // Not entirely thread-safe but a few duplicate log messages are - // not a huge issue - showTldScanWarning = false; - log.info(Localizer.getMessage("jsp.tldCache.noTldSummary")); - } - } - } - - /* - * 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) { - TldLocation location; - if (entryName == null) { - location = new TldLocation(resourcePath); - } else { - location = new TldLocation(entryName, resourcePath); - } - mappings.put(uri, location); - } - } catch (JasperException e) { - // Hack - makes exception handling simpler - throw new IOException(e); - } - } - } Modified: tomcat/trunk/java/org/apache/jasper/servlet/JasperInitializer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/servlet/JasperInitializer.java?rev=1513028&r1=1513027&r2=1513028&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/servlet/JasperInitializer.java (original) +++ tomcat/trunk/java/org/apache/jasper/servlet/JasperInitializer.java Mon Aug 12 02:05:59 2013 @@ -17,6 +17,7 @@ package org.apache.jasper.servlet; import java.io.IOException; +import java.util.Map; import java.util.Set; import javax.servlet.ServletContainerInitializer; @@ -24,8 +25,10 @@ import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.apache.jasper.compiler.Localizer; +import org.apache.jasper.compiler.TldLocationsCache; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.descriptor.tld.TldResourcePath; import org.xml.sax.SAXException; /** @@ -60,5 +63,8 @@ public class JasperInitializer implement for (String listener : scanner.getListeners()) { context.addListener(listener); } + + Map<String, TldResourcePath> taglibMap = scanner.getTaglibMap(); + context.setAttribute(TldLocationsCache.KEY, new TldLocationsCache(taglibMap)); } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org