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;
}
}

View File

@@ -0,0 +1,785 @@
/*
* 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.io.InputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import javax.servlet.descriptor.JspConfigDescriptor;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.runtime.ExceptionUtils;
import org.apache.tomcat.Jar;
import org.apache.tomcat.JarScanType;
import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
import org.apache.tomcat.util.descriptor.web.WebXml;
import org.apache.tomcat.util.descriptor.web.WebXmlParser;
import org.apache.tomcat.util.scan.JarFactory;
import org.apache.tomcat.util.scan.StandardJarScanFilter;
import org.apache.tomcat.util.scan.StandardJarScanner;
/**
* Simple <code>ServletContext</code> implementation without
* HTTP-specific methods.
*
* @author Peter Rossbach (pr@webapp.de)
*/
public class JspCServletContext implements ServletContext {
// ----------------------------------------------------- Instance Variables
/**
* Servlet context attributes.
*/
private final Map<String,Object> myAttributes;
/**
* Servlet context initialization parameters.
*/
private final ConcurrentMap<String,String> myParameters = new ConcurrentHashMap<>();
/**
* The log writer we will write log messages to.
*/
private final PrintWriter myLogWriter;
/**
* The base URL (document root) for this context.
*/
private final URL myResourceBaseURL;
/**
* Merged web.xml for the application.
*/
private WebXml webXml;
private List<URL> resourceJARs;
private JspConfigDescriptor jspConfigDescriptor;
/**
* Web application class loader.
*/
private final ClassLoader loader;
// ----------------------------------------------------------- Constructors
/**
* Create a new instance of this ServletContext implementation.
*
* @param aLogWriter PrintWriter which is used for <code>log()</code> calls
* @param aResourceBaseURL Resource base URL
* @param classLoader Class loader for this {@link ServletContext}
* @param validate Should a validating parser be used to parse web.xml?
* @param blockExternal Should external entities be blocked when parsing
* web.xml?
* @throws JasperException An error occurred building the merged web.xml
*/
public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL,
ClassLoader classLoader, boolean validate, boolean blockExternal)
throws JasperException {
myAttributes = new HashMap<>();
myParameters.put(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM,
String.valueOf(blockExternal));
myLogWriter = aLogWriter;
myResourceBaseURL = aResourceBaseURL;
this.loader = classLoader;
this.webXml = buildMergedWebXml(validate, blockExternal);
jspConfigDescriptor = webXml.getJspConfigDescriptor();
}
private WebXml buildMergedWebXml(boolean validate, boolean blockExternal)
throws JasperException {
WebXml webXml = new WebXml();
WebXmlParser webXmlParser = new WebXmlParser(validate, validate, blockExternal);
// Use this class's classloader as Ant will have set the TCCL to its own
webXmlParser.setClassLoader(getClass().getClassLoader());
try {
URL url = getResource(
org.apache.tomcat.util.descriptor.web.Constants.WEB_XML_LOCATION);
if (!webXmlParser.parseWebXml(url, webXml, false)) {
throw new JasperException(Localizer.getMessage("jspc.error.invalidWebXml"));
}
} catch (IOException e) {
throw new JasperException(e);
}
// if the application is metadata-complete then we can skip fragment processing
if (webXml.isMetadataComplete()) {
return webXml;
}
// If an empty absolute ordering element is present, fragment processing
// may be skipped.
Set<String> absoluteOrdering = webXml.getAbsoluteOrdering();
if (absoluteOrdering != null && absoluteOrdering.isEmpty()) {
return webXml;
}
Map<String, WebXml> fragments = scanForFragments(webXmlParser);
Set<WebXml> orderedFragments = WebXml.orderWebFragments(webXml, fragments, this);
// Find resource JARs
this.resourceJARs = scanForResourceJARs(orderedFragments, fragments.values());
// JspC is not affected by annotations so skip that processing, proceed to merge
webXml.merge(orderedFragments);
return webXml;
}
private List<URL> scanForResourceJARs(Set<WebXml> orderedFragments, Collection<WebXml> fragments)
throws JasperException {
List<URL> resourceJars = new ArrayList<>();
// Build list of potential resource JARs. Use same ordering as ContextConfig
Set<WebXml> resourceFragments = new LinkedHashSet<>();
for (WebXml fragment : orderedFragments) {
resourceFragments.add(fragment);
}
for (WebXml fragment : fragments) {
if (!resourceFragments.contains(fragment)) {
resourceFragments.add(fragment);
}
}
for (WebXml resourceFragment : resourceFragments) {
try (Jar jar = JarFactory.newInstance(resourceFragment.getURL())) {
if (jar.exists("META-INF/resources/")) {
// This is a resource JAR
resourceJars.add(resourceFragment.getURL());
}
jar.close();
} catch (IOException ioe) {
throw new JasperException(ioe);
}
}
return resourceJars;
}
private Map<String, WebXml> scanForFragments(WebXmlParser webXmlParser) throws JasperException {
StandardJarScanner scanner = new StandardJarScanner();
// TODO - enabling this means initializing the classloader first in JspC
scanner.setScanClassPath(false);
// TODO - configure filter rules from Ant rather then system properties
scanner.setJarScanFilter(new StandardJarScanFilter());
FragmentJarScannerCallback callback =
new FragmentJarScannerCallback(webXmlParser, false, true);
scanner.scan(JarScanType.PLUGGABILITY, this, callback);
if (!callback.isOk()) {
throw new JasperException(Localizer.getMessage("jspc.error.invalidFragment"));
}
return callback.getFragments();
}
// --------------------------------------------------------- Public Methods
/**
* Return the specified context attribute, if any.
*
* @param name Name of the requested attribute
*/
@Override
public Object getAttribute(String name) {
return myAttributes.get(name);
}
/**
* Return an enumeration of context attribute names.
*/
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(myAttributes.keySet());
}
/**
* Return the servlet context for the specified path.
*
* @param uripath Server-relative path starting with '/'
*/
@Override
public ServletContext getContext(String uripath) {
return null;
}
/**
* Return the context path.
*/
@Override
public String getContextPath() {
return null;
}
/**
* Return the specified context initialization parameter.
*
* @param name Name of the requested parameter
*/
@Override
public String getInitParameter(String name) {
return myParameters.get(name);
}
/**
* Return an enumeration of the names of context initialization
* parameters.
*/
@Override
public Enumeration<String> getInitParameterNames() {
return Collections.enumeration(myParameters.keySet());
}
/**
* Return the Servlet API major version number.
*/
@Override
public int getMajorVersion() {
return 3;
}
/**
* Return the MIME type for the specified filename.
*
* @param file Filename whose MIME type is requested
*/
@Override
public String getMimeType(String file) {
return null;
}
/**
* Return the Servlet API minor version number.
*/
@Override
public int getMinorVersion() {
return 1;
}
/**
* Return a request dispatcher for the specified servlet name.
*
* @param name Name of the requested servlet
*/
@Override
public RequestDispatcher getNamedDispatcher(String name) {
return null;
}
/**
* Return the real path for the specified context-relative
* virtual path.
*
* @param path The context-relative virtual path to resolve
*/
@Override
public String getRealPath(String path) {
if (!myResourceBaseURL.getProtocol().equals("file"))
return null;
if (!path.startsWith("/"))
return null;
try {
File f = new File(getResource(path).toURI());
return f.getAbsolutePath();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
return null;
}
}
/**
* Return a request dispatcher for the specified context-relative path.
*
* @param path Context-relative path for which to acquire a dispatcher
*/
@Override
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
/**
* Return a URL object of a resource that is mapped to the
* specified context-relative path.
*
* @param path Context-relative path of the desired resource
*
* @exception MalformedURLException if the resource path is
* not properly formed
*/
@Override
public URL getResource(String path) throws MalformedURLException {
if (!path.startsWith("/")) {
throw new MalformedURLException("Path '" + path + "' does not start with '/'");
}
// Strip leading '/'
path = path.substring(1);
URL url = new URL(myResourceBaseURL, path);
try (InputStream is = url.openStream()) {
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
url = null;
}
// During initialisation, getResource() is called before resourceJARs is
// initialised
if (url == null && resourceJARs != null) {
String jarPath = "META-INF/resources/" + path;
for (URL jarUrl : resourceJARs) {
try (Jar jar = JarFactory.newInstance(jarUrl)) {
if (jar.exists(jarPath)) {
return new URL(jar.getURL(jarPath));
}
} catch (IOException ioe) {
// Ignore
}
}
}
return url;
}
/**
* Return an InputStream allowing access to the resource at the
* specified context-relative path.
*
* @param path Context-relative path of the desired resource
*/
@Override
public InputStream getResourceAsStream(String path) {
try {
return getResource(path).openStream();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
return null;
}
}
/**
* Return the set of resource paths for the "directory" at the
* specified context path.
*
* @param path Context-relative base path
*/
@Override
public Set<String> getResourcePaths(String path) {
Set<String> thePaths = new HashSet<>();
if (!path.endsWith("/")) {
path += "/";
}
String basePath = getRealPath(path);
if (basePath != null) {
File theBaseDir = new File(basePath);
if (theBaseDir.isDirectory()) {
String theFiles[] = theBaseDir.list();
if (theFiles != null) {
for (int i = 0; i < theFiles.length; i++) {
File testFile = new File(basePath + File.separator + theFiles[i]);
if (testFile.isFile()) {
thePaths.add(path + theFiles[i]);
} else if (testFile.isDirectory()) {
thePaths.add(path + theFiles[i] + "/");
}
}
}
}
}
// During initialisation, getResourcePaths() is called before
// resourceJARs is initialised
if (resourceJARs != null) {
String jarPath = "META-INF/resources" + path;
for (URL jarUrl : resourceJARs) {
try (Jar jar = JarFactory.newInstance(jarUrl)) {
jar.nextEntry();
for (String entryName = jar.getEntryName();
entryName != null;
jar.nextEntry(), entryName = jar.getEntryName()) {
if (entryName.startsWith(jarPath) &&
entryName.length() > jarPath.length()) {
// Let the Set implementation handle duplicates
int sep = entryName.indexOf("/", jarPath.length());
if (sep < 0) {
// This is a file - strip leading "META-INF/resources"
thePaths.add(entryName.substring(18));
} else {
// This is a directory - strip leading "META-INF/resources"
thePaths.add(entryName.substring(18, sep + 1));
}
}
}
} catch (IOException e) {
log(e.getMessage(), e);
}
}
}
return thePaths;
}
/**
* Return descriptive information about this server.
*/
@Override
public String getServerInfo() {
return ("JspC/ApacheTomcat8");
}
/**
* Return a null reference for the specified servlet name.
*
* @param name Name of the requested servlet
*
* @deprecated This method has been deprecated with no replacement
*/
@Override
@Deprecated
public Servlet getServlet(String name) throws ServletException {
return null;
}
/**
* Return the name of this servlet context.
*/
@Override
public String getServletContextName() {
return getServerInfo();
}
/**
* Return an empty enumeration of servlet names.
*
* @deprecated This method has been deprecated with no replacement
*/
@Override
@Deprecated
public Enumeration<String> getServletNames() {
return new Vector<String>().elements();
}
/**
* Return an empty enumeration of servlets.
*
* @deprecated This method has been deprecated with no replacement
*/
@Override
@Deprecated
public Enumeration<Servlet> getServlets() {
return new Vector<Servlet>().elements();
}
/**
* Log the specified message.
*
* @param message The message to be logged
*/
@Override
public void log(String message) {
myLogWriter.println(message);
}
/**
* Log the specified message and exception.
*
* @param exception The exception to be logged
* @param message The message to be logged
*
* @deprecated Use log(String,Throwable) instead
*/
@Override
@Deprecated
public void log(Exception exception, String message) {
log(message, exception);
}
/**
* Log the specified message and exception.
*
* @param message The message to be logged
* @param exception The exception to be logged
*/
@Override
public void log(String message, Throwable exception) {
myLogWriter.println(message);
exception.printStackTrace(myLogWriter);
}
/**
* Remove the specified context attribute.
*
* @param name Name of the attribute to remove
*/
@Override
public void removeAttribute(String name) {
myAttributes.remove(name);
}
/**
* Set or replace the specified context attribute.
*
* @param name Name of the context attribute to set
* @param value Corresponding attribute value
*/
@Override
public void setAttribute(String name, Object value) {
myAttributes.put(name, value);
}
@Override
public FilterRegistration.Dynamic addFilter(String filterName,
String className) {
return null;
}
@Override
public ServletRegistration.Dynamic addServlet(String servletName,
String className) {
return null;
}
@Override
public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
return EnumSet.noneOf(SessionTrackingMode.class);
}
@Override
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
return EnumSet.noneOf(SessionTrackingMode.class);
}
@Override
public SessionCookieConfig getSessionCookieConfig() {
return null;
}
@Override
public void setSessionTrackingModes(
Set<SessionTrackingMode> sessionTrackingModes) {
// Do nothing
}
@Override
public Dynamic addFilter(String filterName, Filter filter) {
return null;
}
@Override
public Dynamic addFilter(String filterName,
Class<? extends Filter> filterClass) {
return null;
}
@Override
public ServletRegistration.Dynamic addServlet(String servletName,
Servlet servlet) {
return null;
}
@Override
public ServletRegistration.Dynamic addServlet(String servletName,
Class<? extends Servlet> servletClass) {
return null;
}
@Override
public <T extends Filter> T createFilter(Class<T> c)
throws ServletException {
return null;
}
@Override
public <T extends Servlet> T createServlet(Class<T> c)
throws ServletException {
return null;
}
@Override
public FilterRegistration getFilterRegistration(String filterName) {
return null;
}
@Override
public ServletRegistration getServletRegistration(String servletName) {
return null;
}
@Override
public boolean setInitParameter(String name, String value) {
return myParameters.putIfAbsent(name, value) == null;
}
@Override
public void addListener(Class<? extends EventListener> listenerClass) {
// NOOP
}
@Override
public void addListener(String className) {
// NOOP
}
@Override
public <T extends EventListener> void addListener(T t) {
// NOOP
}
@Override
public <T extends EventListener> T createListener(Class<T> c)
throws ServletException {
return null;
}
@Override
public void declareRoles(String... roleNames) {
// NOOP
}
@Override
public ClassLoader getClassLoader() {
return loader;
}
@Override
public int getEffectiveMajorVersion() {
return webXml.getMajorVersion();
}
@Override
public int getEffectiveMinorVersion() {
return webXml.getMinorVersion();
}
@Override
public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
return null;
}
@Override
public JspConfigDescriptor getJspConfigDescriptor() {
return jspConfigDescriptor;
}
@Override
public Map<String, ? extends ServletRegistration> getServletRegistrations() {
return null;
}
@Override
public String getVirtualServerName() {
return null;
}
}

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;
}
}

View File

@@ -0,0 +1,637 @@
/*
* 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.util.HashMap;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.tagext.TagInfo;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.Options;
import org.apache.jasper.compiler.ErrorDispatcher;
import org.apache.jasper.compiler.JavacErrorDetail;
import org.apache.jasper.compiler.JspRuntimeContext;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.runtime.ExceptionUtils;
import org.apache.jasper.runtime.InstanceManagerFactory;
import org.apache.jasper.runtime.JspSourceDependent;
import org.apache.jasper.util.FastRemovalDequeue;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.Jar;
/**
* 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
* @author Tim Fennell
*/
@SuppressWarnings("deprecation") // Have to support SingleThreadModel
public class JspServletWrapper {
private static final Map<String,Long> ALWAYS_OUTDATED_DEPENDENCIES =
new HashMap<>();
static {
// If this is missing,
ALWAYS_OUTDATED_DEPENDENCIES.put("/WEB-INF/web.xml", Long.valueOf(-1));
}
// Logger
private final Log log = LogFactory.getLog(JspServletWrapper.class); // must not be static
private volatile Servlet theServlet;
private final String jspUri;
private volatile Class<?> tagHandlerClass;
private final JspCompilationContext ctxt;
private long available = 0L;
private final ServletConfig config;
private final Options options;
/*
* The servlet / tag file needs a compilation check on first access. Use a
* separate flag (rather then theServlet == null / tagHandlerClass == null
* as it avoids the potentially expensive isOutDated() calls in
* ctxt.compile() if there are multiple concurrent requests for the servlet
* / tag before the class has been loaded.
*/
private volatile boolean mustCompile = true;
/* Whether the servlet/tag file needs reloading on next access */
private volatile boolean reload = true;
private final boolean isTagFile;
private int tripCount;
private JasperException compileException;
/* Timestamp of last time servlet resource was modified */
private volatile long servletClassLastModifiedTime;
private long lastModificationTest = 0L;
private long lastUsageTime = System.currentTimeMillis();
private FastRemovalDequeue<JspServletWrapper>.Entry unloadHandle;
private final boolean unloadAllowed;
private final boolean unloadByCount;
private final boolean unloadByIdle;
/*
* JspServletWrapper for JSP pages.
*/
public JspServletWrapper(ServletConfig config, Options options,
String jspUri, JspRuntimeContext rctxt) {
this.isTagFile = false;
this.config = config;
this.options = options;
this.jspUri = jspUri;
unloadByCount = options.getMaxLoadedJsps() > 0 ? true : false;
unloadByIdle = options.getJspIdleTimeout() > 0 ? true : false;
unloadAllowed = unloadByCount || unloadByIdle ? true : false;
ctxt = new JspCompilationContext(jspUri, options,
config.getServletContext(),
this, rctxt);
}
/*
* JspServletWrapper for tag files.
*/
public JspServletWrapper(ServletContext servletContext,
Options options,
String tagFilePath,
TagInfo tagInfo,
JspRuntimeContext rctxt,
Jar tagJar) {
this.isTagFile = true;
this.config = null; // not used
this.options = options;
this.jspUri = tagFilePath;
this.tripCount = 0;
unloadByCount = options.getMaxLoadedJsps() > 0 ? true : false;
unloadByIdle = options.getJspIdleTimeout() > 0 ? true : false;
unloadAllowed = unloadByCount || unloadByIdle ? true : false;
ctxt = new JspCompilationContext(jspUri, tagInfo, options,
servletContext, this, rctxt,
tagJar);
}
public JspCompilationContext getJspEngineContext() {
return ctxt;
}
public void setReload(boolean reload) {
this.reload = reload;
}
public boolean getReload() {
return reload;
}
private boolean getReloadInternal() {
return reload && !ctxt.getRuntimeContext().isCompileCheckInProgress();
}
public Servlet getServlet() throws ServletException {
/*
* DCL on 'reload' requires that 'reload' be volatile
* (this also forces a read memory barrier, ensuring the new servlet
* object is read consistently).
*
* When running in non development mode with a checkInterval it is
* possible (see BZ 62603) for a race condition to cause failures
* if a Servlet or tag is reloaded while a compile check is running
*/
if (getReloadInternal() || theServlet == null) {
synchronized (this) {
// Synchronizing on jsw enables simultaneous loading
// of different pages, but not the same page.
if (getReloadInternal() || theServlet == null) {
// This is to maintain the original protocol.
destroy();
final Servlet servlet;
try {
InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config);
servlet = (Servlet) instanceManager.newInstance(ctxt.getFQCN(), ctxt.getJspLoader());
} catch (Exception e) {
Throwable t = ExceptionUtils
.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(t);
throw new JasperException(t);
}
servlet.init(config);
if (theServlet != null) {
ctxt.getRuntimeContext().incrementJspReloadCount();
}
theServlet = servlet;
reload = false;
// Volatile 'reload' forces in order write of 'theServlet' and new servlet object
}
}
}
return theServlet;
}
public ServletContext getServletContext() {
return ctxt.getServletContext();
}
/**
* Sets the compilation exception for this JspServletWrapper.
*
* @param je The compilation exception
*/
public void setCompilationException(JasperException je) {
this.compileException = je;
}
/**
* Sets the last-modified time of the servlet class file associated with
* this JspServletWrapper.
*
* @param lastModified Last-modified time of servlet class
*/
public void setServletClassLastModifiedTime(long lastModified) {
// DCL requires servletClassLastModifiedTime be volatile
// to force read and write barriers on access/set
// (and to get atomic write of long)
if (this.servletClassLastModifiedTime < lastModified) {
synchronized (this) {
if (this.servletClassLastModifiedTime < lastModified) {
this.servletClassLastModifiedTime = lastModified;
reload = true;
// Really need to unload the old class but can't do that. Do
// the next best thing which is throw away the JspLoader so
// a new loader will be created which will load the new
// class.
// TODO Are there inefficiencies between reload and the
// isOutDated() check?
ctxt.clearJspLoader();
}
}
}
}
/**
* Compile (if needed) and load a tag file.
* @return the loaded class
* @throws JasperException Error compiling or loading tag file
*/
public Class<?> loadTagFile() throws JasperException {
try {
if (ctxt.isRemoved()) {
throw new FileNotFoundException(jspUri);
}
if (options.getDevelopment() || mustCompile) {
synchronized (this) {
if (options.getDevelopment() || mustCompile) {
ctxt.compile();
mustCompile = false;
}
}
} else {
if (compileException != null) {
throw compileException;
}
}
if (getReloadInternal() || tagHandlerClass == null) {
synchronized (this) {
if (getReloadInternal() || tagHandlerClass == null) {
tagHandlerClass = ctxt.load();
// Volatile 'reload' forces in order write of 'tagHandlerClass'
reload = false;
}
}
}
} catch (FileNotFoundException ex) {
throw new JasperException(ex);
}
return tagHandlerClass;
}
/**
* Compile and load a prototype for the Tag file. This is needed
* when compiling tag files with circular dependencies. A prototype
* (skeleton) with no dependencies on other other tag files is
* generated and compiled.
* @return the loaded class
* @throws JasperException Error compiling or loading tag file
*/
public Class<?> loadTagFilePrototype() throws JasperException {
ctxt.setPrototypeMode(true);
try {
return loadTagFile();
} finally {
ctxt.setPrototypeMode(false);
}
}
/**
* Get a list of files that the current page has source dependency on.
* @return the map of dependent resources
*/
public java.util.Map<String,Long> getDependants() {
try {
Object target;
if (isTagFile) {
if (reload) {
synchronized (this) {
if (reload) {
tagHandlerClass = ctxt.load();
reload = false;
}
}
}
target = tagHandlerClass.getConstructor().newInstance();
} else {
target = getServlet();
}
if (target instanceof JspSourceDependent) {
return ((JspSourceDependent) target).getDependants();
}
} catch (AbstractMethodError ame) {
// Almost certainly a pre Tomcat 7.0.17 compiled JSP using the old
// version of the interface. Force a re-compile.
return ALWAYS_OUTDATED_DEPENDENCIES;
} catch (Throwable ex) {
ExceptionUtils.handleThrowable(ex);
}
return null;
}
public boolean isTagFile() {
return this.isTagFile;
}
public int incTripCount() {
return tripCount++;
}
public int decTripCount() {
return tripCount--;
}
public String getJspUri() {
return jspUri;
}
public FastRemovalDequeue<JspServletWrapper>.Entry getUnloadHandle() {
return unloadHandle;
}
public void service(HttpServletRequest request,
HttpServletResponse response,
boolean precompile)
throws ServletException, IOException, FileNotFoundException {
Servlet servlet;
try {
if (ctxt.isRemoved()) {
throw new FileNotFoundException(jspUri);
}
if ((available > 0L) && (available < Long.MAX_VALUE)) {
if (available > System.currentTimeMillis()) {
response.setDateHeader("Retry-After", available);
response.sendError
(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
Localizer.getMessage("jsp.error.unavailable"));
return;
}
// Wait period has expired. Reset.
available = 0;
}
/*
* (1) Compile
*/
if (options.getDevelopment() || mustCompile) {
synchronized (this) {
if (options.getDevelopment() || mustCompile) {
// The following sets reload to true, if necessary
ctxt.compile();
mustCompile = false;
}
}
} else {
if (compileException != null) {
// Throw cached compilation exception
throw compileException;
}
}
/*
* (2) (Re)load servlet class file
*/
servlet = getServlet();
// If a page is to be precompiled only, return.
if (precompile) {
return;
}
} catch (ServletException ex) {
if (options.getDevelopment()) {
throw handleJspException(ex);
}
throw ex;
} catch (FileNotFoundException fnfe) {
// File has been removed. Let caller handle this.
throw fnfe;
} catch (IOException ex) {
if (options.getDevelopment()) {
throw handleJspException(ex);
}
throw ex;
} catch (IllegalStateException ex) {
if (options.getDevelopment()) {
throw handleJspException(ex);
}
throw ex;
} catch (Exception ex) {
if (options.getDevelopment()) {
throw handleJspException(ex);
}
throw new JasperException(ex);
}
try {
/*
* (3) Handle limitation of number of loaded Jsps
*/
if (unloadAllowed) {
synchronized(this) {
if (unloadByCount) {
if (unloadHandle == null) {
unloadHandle = ctxt.getRuntimeContext().push(this);
} else if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
ctxt.getRuntimeContext().makeYoungest(unloadHandle);
lastUsageTime = System.currentTimeMillis();
}
} else {
if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
lastUsageTime = System.currentTimeMillis();
}
}
}
}
/*
* (4) Service request
*/
if (servlet instanceof SingleThreadModel) {
// sync on the wrapper so that the freshness
// of the page is determined right before servicing
synchronized (this) {
servlet.service(request, response);
}
} else {
servlet.service(request, response);
}
} catch (UnavailableException ex) {
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 by the
// servlet engine.
throw ex;
}
int unavailableSeconds = ex.getUnavailableSeconds();
if (unavailableSeconds <= 0) {
unavailableSeconds = 60; // Arbitrary default
}
available = System.currentTimeMillis() +
(unavailableSeconds * 1000L);
response.sendError
(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
ex.getMessage());
} catch (ServletException ex) {
if(options.getDevelopment()) {
throw handleJspException(ex);
}
throw ex;
} catch (IOException ex) {
if (options.getDevelopment()) {
throw new IOException(handleJspException(ex).getMessage(), ex);
}
throw ex;
} catch (IllegalStateException ex) {
if(options.getDevelopment()) {
throw handleJspException(ex);
}
throw ex;
} catch (Exception ex) {
if(options.getDevelopment()) {
throw handleJspException(ex);
}
throw new JasperException(ex);
}
}
public void destroy() {
if (theServlet != null) {
try {
theServlet.destroy();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(Localizer.getMessage("jsp.error.servlet.destroy.failed"), t);
}
InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config);
try {
instanceManager.destroyInstance(theServlet);
} catch (Exception e) {
Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(t);
// Log any exception, since it can't be passed along
log.error(Localizer.getMessage("jsp.error.file.not.found",
e.getMessage()), t);
}
}
}
/**
* @return Returns the lastModificationTest.
*/
public long getLastModificationTest() {
return lastModificationTest;
}
/**
* @param lastModificationTest The lastModificationTest to set.
*/
public void setLastModificationTest(long lastModificationTest) {
this.lastModificationTest = lastModificationTest;
}
/**
* @return the lastUsageTime.
*/
public long getLastUsageTime() {
return lastUsageTime;
}
/**
* <p>Attempts to construct a JasperException that contains helpful information
* about what went wrong. Uses the JSP compiler system to translate the line
* number in the generated servlet that originated the exception to a line
* number in the JSP. Then constructs an exception containing that
* information, and a snippet of the JSP to help debugging.
* Please see https://bz.apache.org/bugzilla/show_bug.cgi?id=37062 and
* http://www.tfenne.com/jasper/ for more details.
*</p>
*
* @param ex the exception that was the cause of the problem.
* @return a JasperException with more detailed information
*/
protected JasperException handleJspException(Exception ex) {
try {
Throwable realException = ex;
if (ex instanceof ServletException) {
realException = ((ServletException) ex).getRootCause();
}
// First identify the stack frame in the trace that represents the JSP
StackTraceElement[] frames = realException.getStackTrace();
StackTraceElement jspFrame = null;
for (int i=0; i<frames.length; ++i) {
if ( frames[i].getClassName().equals(this.getServlet().getClass().getName()) ) {
jspFrame = frames[i];
break;
}
}
if (jspFrame == null ||
this.ctxt.getCompiler().getPageNodes() == null) {
// If we couldn't find a frame in the stack trace corresponding
// to the generated servlet class or we don't have a copy of the
// parsed JSP to hand, we can't really add anything
return new JasperException(ex);
}
int javaLineNumber = jspFrame.getLineNumber();
JavacErrorDetail detail = ErrorDispatcher.createJavacError(
jspFrame.getMethodName(),
this.ctxt.getCompiler().getPageNodes(),
null,
javaLineNumber,
ctxt);
// If the line number is less than one we couldn't find out
// where in the JSP things went wrong
int jspLineNumber = detail.getJspBeginLineNumber();
if (jspLineNumber < 1) {
throw new JasperException(ex);
}
if (options.getDisplaySourceFragment()) {
return new JasperException(Localizer.getMessage
("jsp.exception", detail.getJspFileName(),
"" + jspLineNumber) + System.lineSeparator() +
System.lineSeparator() + detail.getJspExtract() +
System.lineSeparator() + System.lineSeparator() +
"Stacktrace:", ex);
}
return new JasperException(Localizer.getMessage
("jsp.exception", detail.getJspFileName(),
"" + jspLineNumber), ex);
} catch (Exception je) {
// If anything goes wrong, just revert to the original behaviour
if (ex instanceof JasperException) {
return (JasperException) ex;
}
return new JasperException(ex);
}
}
}

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>