This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
/*
* 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.IOException;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.jsp.JspFactory;
import org.apache.jasper.Constants;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.compiler.TldCache;
import org.apache.jasper.runtime.JspFactoryImpl;
import org.apache.jasper.security.SecurityClassLoad;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.SimpleInstanceManager;
import org.xml.sax.SAXException;
/**
* Initializer for the Jasper JSP Engine.
*/
public class JasperInitializer implements ServletContainerInitializer {
private static final String MSG = "org.apache.jasper.servlet.JasperInitializer";
private final Log log = LogFactory.getLog(JasperInitializer.class); // must not be static
/**
* Preload classes required at runtime by a JSP servlet so that
* we don't get a defineClassInPackage security exception.
*/
static {
JspFactoryImpl factory = new JspFactoryImpl();
SecurityClassLoad.securityClassLoad(factory.getClass().getClassLoader());
if( System.getSecurityManager() != null ) {
String basePackage = "org.apache.jasper.";
try {
factory.getClass().getClassLoader().loadClass( basePackage +
"runtime.JspFactoryImpl$PrivilegedGetPageContext");
factory.getClass().getClassLoader().loadClass( basePackage +
"runtime.JspFactoryImpl$PrivilegedReleasePageContext");
factory.getClass().getClassLoader().loadClass( basePackage +
"runtime.JspRuntimeLibrary");
factory.getClass().getClassLoader().loadClass( basePackage +
"runtime.ServletResponseWrapperInclude");
factory.getClass().getClassLoader().loadClass( basePackage +
"servlet.JspServletWrapper");
} catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
}
}
if (JspFactory.getDefaultFactory() == null) {
JspFactory.setDefaultFactory(factory);
}
}
@Override
public void onStartup(Set<Class<?>> types, ServletContext context) throws ServletException {
if (log.isDebugEnabled()) {
log.debug(Localizer.getMessage(MSG + ".onStartup", context.getServletContextName()));
}
// Setup a simple default Instance Manager
if (context.getAttribute(InstanceManager.class.getName())==null) {
context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
}
boolean validate = Boolean.parseBoolean(
context.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
String blockExternalString = context.getInitParameter(
Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
boolean blockExternal;
if (blockExternalString == null) {
blockExternal = true;
} else {
blockExternal = Boolean.parseBoolean(blockExternalString);
}
// scan the application for TLDs
TldScanner scanner = newTldScanner(context, true, validate, blockExternal);
try {
scanner.scan();
} catch (IOException | SAXException e) {
throw new ServletException(e);
}
// add any listeners defined in TLDs
for (String listener : scanner.getListeners()) {
context.addListener(listener);
}
context.setAttribute(TldCache.SERVLET_CONTEXT_ATTRIBUTE_NAME,
new TldCache(context, scanner.getUriTldResourcePathMap(),
scanner.getTldResourcePathTaglibXmlMap()));
}
protected TldScanner newTldScanner(ServletContext context, boolean namespaceAware,
boolean validate, boolean blockExternal) {
return new TldScanner(context, namespaceAware, validate, blockExternal);
}
}

View File

@@ -0,0 +1,168 @@
/*
* 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.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.PermissionCollection;
import org.apache.jasper.Constants;
/**
* Class loader for loading servlet class files (corresponding to JSP files)
* and tag handler class files (corresponding to tag files).
*
* @author Anil K. Vijendran
* @author Harish Prabandham
*/
public class JasperLoader extends URLClassLoader {
private final PermissionCollection permissionCollection;
private final SecurityManager securityManager;
public JasperLoader(URL[] urls, ClassLoader parent,
PermissionCollection permissionCollection) {
super(urls, parent);
this.permissionCollection = permissionCollection;
this.securityManager = System.getSecurityManager();
}
/**
* Load the class with the specified name. This method searches for
* classes in the same manner as <code>loadClass(String, boolean)</code>
* with <code>false</code> as the second argument.
*
* @param name Name of the class to be loaded
*
* @exception ClassNotFoundException if the class was not found
*/
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* Load the class with the specified name, searching using the following
* algorithm until it finds and returns the class. If the class cannot
* be found, returns <code>ClassNotFoundException</code>.
* <ul>
* <li>Call <code>findLoadedClass(String)</code> to check if the
* class has already been loaded. If it has, the same
* <code>Class</code> object is returned.</li>
* <li>If the <code>delegate</code> property is set to <code>true</code>,
* call the <code>loadClass()</code> method of the parent class
* loader, if any.</li>
* <li>Call <code>findClass()</code> to find this class in our locally
* defined repositories.</li>
* <li>Call the <code>loadClass()</code> method of our parent
* class loader, if any.</li>
* </ul>
* If the class was found using the above steps, and the
* <code>resolve</code> flag is <code>true</code>, this method will then
* call <code>resolveClass(Class)</code> on the resulting Class object.
*
* @param name Name of the class to be loaded
* @param resolve If <code>true</code> then resolve the class
*
* @exception ClassNotFoundException if the class was not found
*/
@Override
public synchronized Class<?> loadClass(final String name, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz = null;
// (0) Check our previously loaded class cache
clazz = findLoadedClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return clazz;
}
// (.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int dot = name.lastIndexOf('.');
if (dot >= 0) {
try {
// Do not call the security manager since by default, we grant that package.
if (!"org.apache.jasper.runtime".equalsIgnoreCase(name.substring(0,dot))){
securityManager.checkPackageAccess(name.substring(0,dot));
}
} catch (SecurityException se) {
String error = "Security Violation, attempt to use " +
"Restricted Class: " + name;
se.printStackTrace();
throw new ClassNotFoundException(error);
}
}
}
if( !name.startsWith(Constants.JSP_PACKAGE_NAME + '.') ) {
// Class is not in org.apache.jsp, therefore, have our
// parent load it
clazz = getParent().loadClass(name);
if( resolve )
resolveClass(clazz);
return clazz;
}
return findClass(name);
}
/**
* Delegate to parent
*
* @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
*/
@Override
public InputStream getResourceAsStream(String name) {
InputStream is = getParent().getResourceAsStream(name);
if (is == null) {
URL url = findResource(name);
if (url != null) {
try {
is = url.openStream();
} catch (IOException e) {
// Ignore
}
}
}
return is;
}
/**
* Get the Permissions for a CodeSource.
*
* Since this ClassLoader is only used for a JSP page in
* a web application context, we just return our preset
* PermissionCollection for the web app context.
*
* @param codeSource Code source where the code was loaded from
* @return PermissionCollection for CodeSource
*/
@Override
public final PermissionCollection getPermissions(CodeSource codeSource) {
return permissionCollection;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,422 @@
/*
* 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.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.jasper.Constants;
import org.apache.jasper.EmbeddedServletOptions;
import org.apache.jasper.Options;
import org.apache.jasper.compiler.JspRuntimeContext;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.runtime.ExceptionUtils;
import org.apache.jasper.security.SecurityUtil;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.PeriodicEventListener;
import org.apache.tomcat.util.security.Escape;
/**
* The JSP engine (a.k.a Jasper).
*
* The servlet container is responsible for providing a
* URLClassLoader for the web application context Jasper
* is being used in. Jasper will try get the Tomcat
* ServletContext attribute for its ServletContext class
* loader, if that fails, it uses the parent class loader.
* In either case, it must be a URLClassLoader.
*
* @author Anil K. Vijendran
* @author Harish Prabandham
* @author Remy Maucherat
* @author Kin-man Chung
* @author Glenn Nielsen
*/
public class JspServlet extends HttpServlet implements PeriodicEventListener {
private static final long serialVersionUID = 1L;
// Logger
private final transient Log log = LogFactory.getLog(JspServlet.class);
private transient ServletContext context;
private ServletConfig config;
private transient Options options;
private transient JspRuntimeContext rctxt;
// jspFile for a jsp configured explicitly as a servlet, in environments where this
// configuration is translated into an init-param for this servlet.
private String jspFile;
/*
* Initializes this JspServlet.
*/
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.config = config;
this.context = config.getServletContext();
// Initialize the JSP Runtime Context
// Check for a custom Options implementation
String engineOptionsName = config.getInitParameter("engineOptionsClass");
if (Constants.IS_SECURITY_ENABLED && engineOptionsName != null) {
log.info(Localizer.getMessage(
"jsp.info.ignoreSetting", "engineOptionsClass", engineOptionsName));
engineOptionsName = null;
}
if (engineOptionsName != null) {
// Instantiate the indicated Options implementation
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> engineOptionsClass = loader.loadClass(engineOptionsName);
Class<?>[] ctorSig = { ServletConfig.class, ServletContext.class };
Constructor<?> ctor = engineOptionsClass.getConstructor(ctorSig);
Object[] args = { config, context };
options = (Options) ctor.newInstance(args);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
// Need to localize this.
log.warn("Failed to load engineOptionsClass", e);
// Use the default Options implementation
options = new EmbeddedServletOptions(config, context);
}
} else {
// Use the default Options implementation
options = new EmbeddedServletOptions(config, context);
}
rctxt = new JspRuntimeContext(context, options);
if (config.getInitParameter("jspFile") != null) {
jspFile = config.getInitParameter("jspFile");
try {
if (null == context.getResource(jspFile)) {
return;
}
} catch (MalformedURLException e) {
throw new ServletException("cannot locate jsp file", e);
}
try {
if (SecurityUtil.isPackageProtectionEnabled()){
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>(){
@Override
public Object run() throws IOException, ServletException {
serviceJspFile(null, null, jspFile, true);
return null;
}
});
} else {
serviceJspFile(null, null, jspFile, true);
}
} catch (IOException e) {
throw new ServletException("Could not precompile jsp: " + jspFile, e);
} catch (PrivilegedActionException e) {
Throwable t = e.getCause();
if (t instanceof ServletException) throw (ServletException)t;
throw new ServletException("Could not precompile jsp: " + jspFile, e);
}
}
if (log.isDebugEnabled()) {
log.debug(Localizer.getMessage("jsp.message.scratch.dir.is",
options.getScratchDir().toString()));
log.debug(Localizer.getMessage("jsp.message.dont.modify.servlets"));
}
}
/**
* Returns the number of JSPs for which JspServletWrappers exist, i.e.,
* the number of JSPs that have been loaded into the webapp with which
* this JspServlet is associated.
*
* <p>This info may be used for monitoring purposes.
*
* @return The number of JSPs that have been loaded into the webapp with
* which this JspServlet is associated
*/
public int getJspCount() {
return this.rctxt.getJspCount();
}
/**
* Resets the JSP reload counter.
*
* @param count Value to which to reset the JSP reload counter
*/
public void setJspReloadCount(int count) {
this.rctxt.setJspReloadCount(count);
}
/**
* Gets the number of JSPs that have been reloaded.
*
* <p>This info may be used for monitoring purposes.
*
* @return The number of JSPs (in the webapp with which this JspServlet is
* associated) that have been reloaded
*/
public int getJspReloadCount() {
return this.rctxt.getJspReloadCount();
}
/**
* Gets the number of JSPs that are in the JSP limiter queue
*
* <p>This info may be used for monitoring purposes.
*
* @return The number of JSPs (in the webapp with which this JspServlet is
* associated) that are in the JSP limiter queue
*/
public int getJspQueueLength() {
return this.rctxt.getJspQueueLength();
}
/**
* Gets the number of JSPs that have been unloaded.
*
* <p>This info may be used for monitoring purposes.
*
* @return The number of JSPs (in the webapp with which this JspServlet is
* associated) that have been unloaded
*/
public int getJspUnloadCount() {
return this.rctxt.getJspUnloadCount();
}
/**
* <p>Look for a <em>precompilation request</em> as described in
* Section 8.4.2 of the JSP 1.2 Specification. <strong>WARNING</strong> -
* we cannot use <code>request.getParameter()</code> for this, because
* that will trigger parsing all of the request parameters, and not give
* a servlet the opportunity to call
* <code>request.setCharacterEncoding()</code> first.</p>
*
* @param request The servlet request we are processing
*
* @exception ServletException if an invalid parameter value for the
* <code>jsp_precompile</code> parameter name is specified
*/
boolean preCompile(HttpServletRequest request) throws ServletException {
String queryString = request.getQueryString();
if (queryString == null) {
return false;
}
int start = queryString.indexOf(Constants.PRECOMPILE);
if (start < 0) {
return false;
}
queryString =
queryString.substring(start + Constants.PRECOMPILE.length());
if (queryString.length() == 0) {
return true; // ?jsp_precompile
}
if (queryString.startsWith("&")) {
return true; // ?jsp_precompile&foo=bar...
}
if (!queryString.startsWith("=")) {
return false; // part of some other name or value
}
int limit = queryString.length();
int ampersand = queryString.indexOf('&');
if (ampersand > 0) {
limit = ampersand;
}
String value = queryString.substring(1, limit);
if (value.equals("true")) {
return true; // ?jsp_precompile=true
} else if (value.equals("false")) {
// Spec says if jsp_precompile=false, the request should not
// be delivered to the JSP page; the easiest way to implement
// this is to set the flag to true, and precompile the page anyway.
// This still conforms to the spec, since it says the
// precompilation request can be ignored.
return true; // ?jsp_precompile=false
} else {
throw new ServletException("Cannot have request parameter " +
Constants.PRECOMPILE + " set to " +
value);
}
}
@Override
public void service (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// jspFile may be configured as an init-param for this servlet instance
String jspUri = jspFile;
if (jspUri == null) {
/*
* Check to see if the requested JSP has been the target of a
* RequestDispatcher.include()
*/
jspUri = (String) request.getAttribute(
RequestDispatcher.INCLUDE_SERVLET_PATH);
if (jspUri != null) {
/*
* Requested JSP has been target of
* RequestDispatcher.include(). Its path is assembled from the
* relevant javax.servlet.include.* request attributes
*/
String pathInfo = (String) request.getAttribute(
RequestDispatcher.INCLUDE_PATH_INFO);
if (pathInfo != null) {
jspUri += pathInfo;
}
} else {
/*
* Requested JSP has not been the target of a
* RequestDispatcher.include(). Reconstruct its path from the
* request's getServletPath() and getPathInfo()
*/
jspUri = request.getServletPath();
String pathInfo = request.getPathInfo();
if (pathInfo != null) {
jspUri += pathInfo;
}
}
}
if (log.isDebugEnabled()) {
log.debug("JspEngine --> " + jspUri);
log.debug("\t ServletPath: " + request.getServletPath());
log.debug("\t PathInfo: " + request.getPathInfo());
log.debug("\t RealPath: " + context.getRealPath(jspUri));
log.debug("\t RequestURI: " + request.getRequestURI());
log.debug("\t QueryString: " + request.getQueryString());
}
try {
boolean precompile = preCompile(request);
serviceJspFile(request, response, jspUri, precompile);
} catch (RuntimeException e) {
throw e;
} catch (ServletException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(e);
}
}
@Override
public void destroy() {
if (log.isDebugEnabled()) {
log.debug("JspServlet.destroy()");
}
rctxt.destroy();
}
@Override
public void periodicEvent() {
rctxt.checkUnload();
rctxt.checkCompile();
}
// -------------------------------------------------------- Private Methods
private void serviceJspFile(HttpServletRequest request,
HttpServletResponse response, String jspUri,
boolean precompile)
throws ServletException, IOException {
JspServletWrapper wrapper = rctxt.getWrapper(jspUri);
if (wrapper == null) {
synchronized(this) {
wrapper = rctxt.getWrapper(jspUri);
if (wrapper == null) {
// Check if the requested JSP page exists, to avoid
// creating unnecessary directories and files.
if (null == context.getResource(jspUri)) {
handleMissingResource(request, response, jspUri);
return;
}
wrapper = new JspServletWrapper(config, options, jspUri,
rctxt);
rctxt.addWrapper(jspUri,wrapper);
}
}
}
try {
wrapper.service(request, response, precompile);
} catch (FileNotFoundException fnfe) {
handleMissingResource(request, response, jspUri);
}
}
private void handleMissingResource(HttpServletRequest request,
HttpServletResponse response, String jspUri)
throws ServletException, IOException {
String includeRequestUri =
(String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
if (includeRequestUri != null) {
// This file was included. Throw an exception as
// a response.sendError() will be ignored
String msg =
Localizer.getMessage("jsp.error.file.not.found",jspUri);
// Strictly, filtering this is an application
// responsibility but just in case...
throw new ServletException(Escape.htmlElementContent(msg));
} else {
try {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
request.getRequestURI());
} catch (IllegalStateException ise) {
log.error(Localizer.getMessage("jsp.error.file.not.found",
jspUri));
}
}
return;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
/*
* 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.net.URL;
import java.util.Collection;
import javax.servlet.ServletContext;
import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
public class TldPreScanned extends TldScanner {
private final Collection<URL> preScannedURLs;
public TldPreScanned (ServletContext context, boolean namespaceAware, boolean validation,
boolean blockExternal, Collection<URL> preScannedTlds) {
super(context, namespaceAware, validation, blockExternal);
preScannedURLs = preScannedTlds;
}
@Override
public void scanJars() {
for (URL url : preScannedURLs){
String str = url.toExternalForm();
int a = str.indexOf("jar:");
int b = str.indexOf("!/");
if (a >= 0 && b> 0) {
String fileUrl = str.substring(a + 4, b);
String path = str.substring(b + 2);
try {
parseTld(new TldResourcePath(new URL(fileUrl), null, path));
} catch (Exception e) {
throw new IllegalStateException(e);
}
} else {
throw new IllegalStateException("Bad tld url: "+str);
}
}
}
}

View 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 &lt;jsp-config&gt; 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 &lt;jsp-config&gt;.
* @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;
}
}
}

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<mbeans-descriptors>
<mbean name="JspMonitor"
description="JSP Monitoring"
domain="Catalina"
group="Monitoring"
type="org.apache.jasper.servlet.JspServlet">
<attribute name="jspCount"
description="The number of JSPs that have been loaded into a webapp"
type="int"
writeable="false"/>
<attribute name="jspReloadCount"
description="The number of JSPs that have been reloaded"
type="int"/>
<attribute name="jspUnloadCount"
description="The number of JSPs that have been unloaded"
type="int"/>
<attribute name="jspQueueLength"
description="The length of the JSP queue (if enabled via maxLoadedJsps)"
type="int"/>
</mbean>
</mbeans-descriptors>