/* | |
* 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.struts2.jasper.servlet; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.apache.struts2.jasper.Constants; | |
import org.apache.struts2.jasper.EmbeddedServletOptions; | |
import org.apache.struts2.jasper.Options; | |
import org.apache.struts2.jasper.compiler.JspRuntimeContext; | |
import org.apache.struts2.jasper.compiler.Localizer; | |
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 java.io.IOException; | |
import java.lang.reflect.Constructor; | |
import java.util.Enumeration; | |
/** | |
* 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 { | |
// Logger | |
private Log log = LogFactory.getLog(JspServlet.class); | |
private ServletContext context; | |
private ServletConfig config; | |
private Options options; | |
private JspRuntimeContext rctxt; | |
/* | |
* Initializes this JspServlet. | |
*/ | |
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 (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) { | |
// 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 (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(); | |
} | |
/** | |
* <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 requset 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); | |
} | |
} | |
public void service (HttpServletRequest request, | |
HttpServletResponse response) | |
throws ServletException, IOException { | |
String jspUri = null; | |
String jspFile = (String) request.getAttribute(Constants.JSP_FILE); | |
if (jspFile != null) { | |
// JSP is specified via <jsp-file> in <servlet> declaration | |
jspUri = jspFile; | |
} else { | |
/* | |
* Check to see if the requested JSP has been the target of a | |
* RequestDispatcher.include() | |
*/ | |
jspUri = (String) request.getAttribute(Constants.INC_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( | |
"javax.servlet.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()); | |
log.debug("\t Request Params: "); | |
Enumeration e = request.getParameterNames(); | |
while (e.hasMoreElements()) { | |
String name = (String) e.nextElement(); | |
log.debug("\t\t " + name + " = " | |
+ request.getParameter(name)); | |
} | |
} | |
try { | |
boolean precompile = preCompile(request); | |
serviceJspFile(request, response, jspUri, null, precompile); | |
} catch (RuntimeException e) { | |
throw e; | |
} catch (ServletException e) { | |
throw e; | |
} catch (IOException e) { | |
throw e; | |
} catch (Throwable e) { | |
throw new ServletException(e); | |
} | |
} | |
public void destroy() { | |
if (log.isDebugEnabled()) { | |
log.debug("JspServlet.destroy()"); | |
} | |
rctxt.destroy(); | |
} | |
// -------------------------------------------------------- Private Methods | |
private void serviceJspFile(HttpServletRequest request, | |
HttpServletResponse response, String jspUri, | |
Throwable exception, boolean precompile) | |
throws ServletException, IOException { | |
JspServletWrapper wrapper = | |
(JspServletWrapper) rctxt.getWrapper(jspUri); | |
if (wrapper == null) { | |
synchronized(this) { | |
wrapper = (JspServletWrapper) 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)) { | |
String includeRequestUri = (String) | |
request.getAttribute("javax.servlet.include.request_uri"); | |
if (includeRequestUri != null) { | |
// This file was included. Throw an exception as | |
// a response.sendError() will be ignored | |
throw new ServletException(Localizer.getMessage( | |
"jsp.error.file.not.found",jspUri)); | |
} else { | |
try { | |
response.sendError(HttpServletResponse.SC_NOT_FOUND, | |
request.getRequestURI()); | |
} catch (IllegalStateException ise) { | |
log.error(Localizer.getMessage("jsp.error.file.not.found", | |
jspUri)); | |
} | |
} | |
return; | |
} | |
boolean isErrorPage = exception != null; | |
wrapper = new JspServletWrapper(config, options, jspUri, | |
isErrorPage, rctxt); | |
rctxt.addWrapper(jspUri,wrapper); | |
} | |
} | |
} | |
wrapper.service(request, response, precompile); | |
} | |
} |