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