init
This commit is contained in:
414
java/org/apache/jasper/servlet/TldScanner.java
Normal file
414
java/org/apache/jasper/servlet/TldScanner.java
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* 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.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.descriptor.JspConfigDescriptor;
|
||||
import javax.servlet.descriptor.TaglibDescriptor;
|
||||
|
||||
import org.apache.jasper.compiler.JarScannerFactory;
|
||||
import org.apache.jasper.compiler.Localizer;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.Jar;
|
||||
import org.apache.tomcat.JarScanType;
|
||||
import org.apache.tomcat.JarScanner;
|
||||
import org.apache.tomcat.JarScannerCallback;
|
||||
import org.apache.tomcat.util.descriptor.tld.TaglibXml;
|
||||
import org.apache.tomcat.util.descriptor.tld.TldParser;
|
||||
import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Scans for and loads Tag Library Descriptors contained in a web application.
|
||||
*/
|
||||
public class TldScanner {
|
||||
private final Log log = LogFactory.getLog(TldScanner.class); // must not be static
|
||||
private static final String MSG = "org.apache.jasper.servlet.TldScanner";
|
||||
private static final String TLD_EXT = ".tld";
|
||||
private static final String WEB_INF = "/WEB-INF/";
|
||||
private final ServletContext context;
|
||||
private final TldParser tldParser;
|
||||
private final Map<String, TldResourcePath> uriTldResourcePathMap = new HashMap<>();
|
||||
private final Map<TldResourcePath, TaglibXml> tldResourcePathTaglibXmlMap = new HashMap<>();
|
||||
private final List<String> listeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initialise with the application's ServletContext.
|
||||
*
|
||||
* @param context the application's servletContext
|
||||
* @param namespaceAware should the XML parser used to parse TLD files be
|
||||
* configured to be name space aware
|
||||
* @param validation should the XML parser used to parse TLD files be
|
||||
* configured to use validation
|
||||
* @param blockExternal should the XML parser used to parse TLD files be
|
||||
* configured to be block references to external
|
||||
* entities
|
||||
*/
|
||||
public TldScanner(ServletContext context,
|
||||
boolean namespaceAware,
|
||||
boolean validation,
|
||||
boolean blockExternal) {
|
||||
this.context = context;
|
||||
|
||||
this.tldParser = new TldParser(namespaceAware, validation, blockExternal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for TLDs in all places defined by the specification:
|
||||
* <ol>
|
||||
* <li>Tag libraries defined by the platform</li>
|
||||
* <li>Entries from <jsp-config> in web.xml</li>
|
||||
* <li>A resources under /WEB-INF</li>
|
||||
* <li>In jar files from /WEB-INF/lib</li>
|
||||
* <li>Additional entries from the container</li>
|
||||
* </ol>
|
||||
*
|
||||
* @throws IOException if there was a problem scanning for or loading a TLD
|
||||
* @throws SAXException if there was a problem parsing a TLD
|
||||
*/
|
||||
public void scan() throws IOException, SAXException {
|
||||
scanPlatform();
|
||||
scanJspConfig();
|
||||
scanResourcePaths(WEB_INF);
|
||||
scanJars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of URI to TldResourcePath built by this scanner.
|
||||
*
|
||||
* @return the map of URI to TldResourcePath
|
||||
*/
|
||||
public Map<String, TldResourcePath> getUriTldResourcePathMap() {
|
||||
return uriTldResourcePathMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of TldResourcePath to parsed XML files built by this
|
||||
* scanner.
|
||||
*
|
||||
* @return the map of TldResourcePath to parsed XML files
|
||||
*/
|
||||
public Map<TldResourcePath,TaglibXml> getTldResourcePathTaglibXmlMap() {
|
||||
return tldResourcePathTaglibXmlMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all listeners declared by scanned TLDs.
|
||||
*
|
||||
* @return a list of listener class names
|
||||
*/
|
||||
public List<String> getListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class loader used by the digester to create objects as a result
|
||||
* of this scan. Normally this only needs to be set when using JspC.
|
||||
*
|
||||
* @param classLoader Class loader to use when creating new objects while
|
||||
* parsing TLDs
|
||||
*/
|
||||
public void setClassLoader(ClassLoader classLoader) {
|
||||
tldParser.setClassLoader(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for TLDs required by the platform specification.
|
||||
*/
|
||||
protected void scanPlatform() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for TLDs defined in <jsp-config>.
|
||||
* @throws IOException Error reading resources
|
||||
* @throws SAXException XML parsing error
|
||||
*/
|
||||
protected void scanJspConfig() throws IOException, SAXException {
|
||||
JspConfigDescriptor jspConfigDescriptor = context.getJspConfigDescriptor();
|
||||
if (jspConfigDescriptor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<TaglibDescriptor> descriptors = jspConfigDescriptor.getTaglibs();
|
||||
for (TaglibDescriptor descriptor : descriptors) {
|
||||
String taglibURI = descriptor.getTaglibURI();
|
||||
String resourcePath = descriptor.getTaglibLocation();
|
||||
// Note: Whilst the Servlet 2.4 DTD implies that the location must
|
||||
// be a context-relative path starting with '/', JSP.7.3.6.1 states
|
||||
// explicitly how paths that do not start with '/' should be
|
||||
// handled.
|
||||
if (!resourcePath.startsWith("/")) {
|
||||
resourcePath = WEB_INF + resourcePath;
|
||||
}
|
||||
if (uriTldResourcePathMap.containsKey(taglibURI)) {
|
||||
log.warn(Localizer.getMessage(MSG + ".webxmlSkip",
|
||||
resourcePath,
|
||||
taglibURI));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(Localizer.getMessage(MSG + ".webxmlAdd",
|
||||
resourcePath,
|
||||
taglibURI));
|
||||
}
|
||||
|
||||
URL url = context.getResource(resourcePath);
|
||||
if (url != null) {
|
||||
TldResourcePath tldResourcePath;
|
||||
if (resourcePath.endsWith(".jar")) {
|
||||
// if the path points to a jar file, the TLD is presumed to be
|
||||
// inside at META-INF/taglib.tld
|
||||
tldResourcePath = new TldResourcePath(url, resourcePath, "META-INF/taglib.tld");
|
||||
} else {
|
||||
tldResourcePath = new TldResourcePath(url, resourcePath);
|
||||
}
|
||||
// parse TLD but store using the URI supplied in the descriptor
|
||||
TaglibXml tld = tldParser.parse(tldResourcePath);
|
||||
uriTldResourcePathMap.put(taglibURI, tldResourcePath);
|
||||
tldResourcePathTaglibXmlMap.put(tldResourcePath, tld);
|
||||
if (tld.getListeners() != null) {
|
||||
listeners.addAll(tld.getListeners());
|
||||
}
|
||||
} else {
|
||||
log.warn(Localizer.getMessage(MSG + ".webxmlFailPathDoesNotExist",
|
||||
resourcePath,
|
||||
taglibURI));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan web application resources for TLDs, recursively.
|
||||
*
|
||||
* @param startPath the directory resource to scan
|
||||
* @throws IOException if there was a problem scanning for or loading a TLD
|
||||
* @throws SAXException if there was a problem parsing a TLD
|
||||
*/
|
||||
protected void scanResourcePaths(String startPath)
|
||||
throws IOException, SAXException {
|
||||
|
||||
boolean found = false;
|
||||
Set<String> dirList = context.getResourcePaths(startPath);
|
||||
if (dirList != null) {
|
||||
for (String path : dirList) {
|
||||
if (path.startsWith("/WEB-INF/classes/")) {
|
||||
// Skip: JSP.7.3.1
|
||||
} else if (path.startsWith("/WEB-INF/lib/")) {
|
||||
// Skip: JSP.7.3.1
|
||||
} else if (path.endsWith("/")) {
|
||||
scanResourcePaths(path);
|
||||
} else if (path.startsWith("/WEB-INF/tags/")) {
|
||||
// JSP 7.3.1: in /WEB-INF/tags only consider implicit.tld
|
||||
if (path.endsWith("/implicit.tld")) {
|
||||
found = true;
|
||||
parseTld(path);
|
||||
}
|
||||
} else if (path.endsWith(TLD_EXT)) {
|
||||
found = true;
|
||||
parseTld(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(Localizer.getMessage("jsp.tldCache.tldInResourcePath", startPath));
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(Localizer.getMessage("jsp.tldCache.noTldInResourcePath", startPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for TLDs in JARs in /WEB-INF/lib.
|
||||
*/
|
||||
public void scanJars() {
|
||||
JarScanner scanner = JarScannerFactory.getJarScanner(context);
|
||||
TldScannerCallback callback = new TldScannerCallback();
|
||||
scanner.scan(JarScanType.TLD, context, callback);
|
||||
if (callback.scanFoundNoTLDs()) {
|
||||
log.info(Localizer.getMessage("jsp.tldCache.noTldSummary"));
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseTld(String resourcePath) throws IOException, SAXException {
|
||||
TldResourcePath tldResourcePath =
|
||||
new TldResourcePath(context.getResource(resourcePath), resourcePath);
|
||||
parseTld(tldResourcePath);
|
||||
}
|
||||
|
||||
protected void parseTld(TldResourcePath path) throws IOException, SAXException {
|
||||
if (tldResourcePathTaglibXmlMap.containsKey(path)) {
|
||||
// TLD has already been parsed as a result of processing web.xml
|
||||
return;
|
||||
}
|
||||
TaglibXml tld = tldParser.parse(path);
|
||||
String uri = tld.getUri();
|
||||
if (uri != null) {
|
||||
if (!uriTldResourcePathMap.containsKey(uri)) {
|
||||
uriTldResourcePathMap.put(uri, path);
|
||||
}
|
||||
}
|
||||
tldResourcePathTaglibXmlMap.put(path, tld);
|
||||
if (tld.getListeners() != null) {
|
||||
listeners.addAll(tld.getListeners());
|
||||
}
|
||||
}
|
||||
|
||||
class TldScannerCallback implements JarScannerCallback {
|
||||
private boolean foundJarWithoutTld = false;
|
||||
private boolean foundFileWithoutTld = false;
|
||||
|
||||
|
||||
@Override
|
||||
public void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException {
|
||||
boolean found = false;
|
||||
URL jarFileUrl = jar.getJarFileURL();
|
||||
jar.nextEntry();
|
||||
for (String entryName = jar.getEntryName();
|
||||
entryName != null;
|
||||
jar.nextEntry(), entryName = jar.getEntryName()) {
|
||||
if (!(entryName.startsWith("META-INF/") &&
|
||||
entryName.endsWith(TLD_EXT))) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
TldResourcePath tldResourcePath =
|
||||
new TldResourcePath(jarFileUrl, webappPath, entryName);
|
||||
try {
|
||||
parseTld(tldResourcePath);
|
||||
} catch (SAXException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(Localizer.getMessage("jsp.tldCache.tldInJar", jarFileUrl.toString()));
|
||||
}
|
||||
} else {
|
||||
foundJarWithoutTld = true;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(Localizer.getMessage(
|
||||
"jsp.tldCache.noTldInJar", jarFileUrl.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scan(File file, final String webappPath, boolean isWebapp)
|
||||
throws IOException {
|
||||
File metaInf = new File(file, "META-INF");
|
||||
if (!metaInf.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
foundFileWithoutTld = false;
|
||||
final Path filePath = file.toPath();
|
||||
Files.walkFileTree(metaInf.toPath(), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file,
|
||||
BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
Path fileName = file.getFileName();
|
||||
if (fileName == null || !fileName.toString().toLowerCase(
|
||||
Locale.ENGLISH).endsWith(TLD_EXT)) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
foundFileWithoutTld = true;
|
||||
String resourcePath;
|
||||
if (webappPath == null) {
|
||||
resourcePath = null;
|
||||
} else {
|
||||
String subPath = file.subpath(
|
||||
filePath.getNameCount(), file.getNameCount()).toString();
|
||||
if ('/' != File.separatorChar) {
|
||||
subPath = subPath.replace(File.separatorChar, '/');
|
||||
}
|
||||
resourcePath = webappPath + "/" + subPath;
|
||||
}
|
||||
|
||||
try {
|
||||
URL url = file.toUri().toURL();
|
||||
TldResourcePath path = new TldResourcePath(url, resourcePath);
|
||||
parseTld(path);
|
||||
} catch (SAXException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
if (foundFileWithoutTld) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(Localizer.getMessage("jsp.tldCache.tldInDir",
|
||||
file.getAbsolutePath()));
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(Localizer.getMessage("jsp.tldCache.noTldInDir",
|
||||
file.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanWebInfClasses() throws IOException {
|
||||
// This is used when scanAllDirectories is enabled and one or more
|
||||
// JARs have been unpacked into WEB-INF/classes as happens with some
|
||||
// IDEs.
|
||||
|
||||
Set<String> paths = context.getResourcePaths(WEB_INF + "classes/META-INF");
|
||||
if (paths == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String path : paths) {
|
||||
if (path.endsWith(TLD_EXT)) {
|
||||
try {
|
||||
parseTld(path);
|
||||
} catch (SAXException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean scanFoundNoTLDs() {
|
||||
return foundJarWithoutTld;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user