001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.jasper.servlet;
019:
020: import java.io.IOException;
021: import java.lang.reflect.Constructor;
022: import java.util.Enumeration;
023:
024: import javax.servlet.ServletConfig;
025: import javax.servlet.ServletContext;
026: import javax.servlet.ServletException;
027: import javax.servlet.http.HttpServlet;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import org.apache.PeriodicEventListener;
032:
033: import org.apache.jasper.Constants;
034: import org.apache.jasper.EmbeddedServletOptions;
035: import org.apache.jasper.Options;
036: import org.apache.jasper.compiler.JspRuntimeContext;
037: import org.apache.jasper.compiler.Localizer;
038: import org.apache.juli.logging.Log;
039: import org.apache.juli.logging.LogFactory;
040:
041: /**
042: * The JSP engine (a.k.a Jasper).
043: *
044: * The servlet container is responsible for providing a
045: * URLClassLoader for the web application context Jasper
046: * is being used in. Jasper will try get the Tomcat
047: * ServletContext attribute for its ServletContext class
048: * loader, if that fails, it uses the parent class loader.
049: * In either case, it must be a URLClassLoader.
050: *
051: * @author Anil K. Vijendran
052: * @author Harish Prabandham
053: * @author Remy Maucherat
054: * @author Kin-man Chung
055: * @author Glenn Nielsen
056: */
057: public class JspServlet extends HttpServlet implements
058: PeriodicEventListener {
059:
060: // Logger
061: private Log log = LogFactory.getLog(JspServlet.class);
062:
063: private ServletContext context;
064: private ServletConfig config;
065: private Options options;
066: private JspRuntimeContext rctxt;
067:
068: /*
069: * Initializes this JspServlet.
070: */
071: public void init(ServletConfig config) throws ServletException {
072:
073: super .init(config);
074: this .config = config;
075: this .context = config.getServletContext();
076:
077: // Initialize the JSP Runtime Context
078: // Check for a custom Options implementation
079: String engineOptionsName = config
080: .getInitParameter("engineOptionsClass");
081: if (engineOptionsName != null) {
082: // Instantiate the indicated Options implementation
083: try {
084: ClassLoader loader = Thread.currentThread()
085: .getContextClassLoader();
086: Class engineOptionsClass = loader
087: .loadClass(engineOptionsName);
088: Class[] ctorSig = { ServletConfig.class,
089: ServletContext.class };
090: Constructor ctor = engineOptionsClass
091: .getConstructor(ctorSig);
092: Object[] args = { config, context };
093: options = (Options) ctor.newInstance(args);
094: } catch (Throwable e) {
095: // Need to localize this.
096: log.warn("Failed to load engineOptionsClass", e);
097: // Use the default Options implementation
098: options = new EmbeddedServletOptions(config, context);
099: }
100: } else {
101: // Use the default Options implementation
102: options = new EmbeddedServletOptions(config, context);
103: }
104: rctxt = new JspRuntimeContext(context, options);
105:
106: if (log.isDebugEnabled()) {
107: log.debug(Localizer.getMessage(
108: "jsp.message.scratch.dir.is", options
109: .getScratchDir().toString()));
110: log.debug(Localizer
111: .getMessage("jsp.message.dont.modify.servlets"));
112: }
113: }
114:
115: /**
116: * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
117: * the number of JSPs that have been loaded into the webapp with which
118: * this JspServlet is associated.
119: *
120: * <p>This info may be used for monitoring purposes.
121: *
122: * @return The number of JSPs that have been loaded into the webapp with
123: * which this JspServlet is associated
124: */
125: public int getJspCount() {
126: return this .rctxt.getJspCount();
127: }
128:
129: /**
130: * Resets the JSP reload counter.
131: *
132: * @param count Value to which to reset the JSP reload counter
133: */
134: public void setJspReloadCount(int count) {
135: this .rctxt.setJspReloadCount(count);
136: }
137:
138: /**
139: * Gets the number of JSPs that have been reloaded.
140: *
141: * <p>This info may be used for monitoring purposes.
142: *
143: * @return The number of JSPs (in the webapp with which this JspServlet is
144: * associated) that have been reloaded
145: */
146: public int getJspReloadCount() {
147: return this .rctxt.getJspReloadCount();
148: }
149:
150: /**
151: * <p>Look for a <em>precompilation request</em> as described in
152: * Section 8.4.2 of the JSP 1.2 Specification. <strong>WARNING</strong> -
153: * we cannot use <code>request.getParameter()</code> for this, because
154: * that will trigger parsing all of the request parameters, and not give
155: * a servlet the opportunity to call
156: * <code>request.setCharacterEncoding()</code> first.</p>
157: *
158: * @param request The servlet requset we are processing
159: *
160: * @exception ServletException if an invalid parameter value for the
161: * <code>jsp_precompile</code> parameter name is specified
162: */
163: boolean preCompile(HttpServletRequest request)
164: throws ServletException {
165:
166: String queryString = request.getQueryString();
167: if (queryString == null) {
168: return (false);
169: }
170: int start = queryString.indexOf(Constants.PRECOMPILE);
171: if (start < 0) {
172: return (false);
173: }
174: queryString = queryString.substring(start
175: + Constants.PRECOMPILE.length());
176: if (queryString.length() == 0) {
177: return (true); // ?jsp_precompile
178: }
179: if (queryString.startsWith("&")) {
180: return (true); // ?jsp_precompile&foo=bar...
181: }
182: if (!queryString.startsWith("=")) {
183: return (false); // part of some other name or value
184: }
185: int limit = queryString.length();
186: int ampersand = queryString.indexOf("&");
187: if (ampersand > 0) {
188: limit = ampersand;
189: }
190: String value = queryString.substring(1, limit);
191: if (value.equals("true")) {
192: return (true); // ?jsp_precompile=true
193: } else if (value.equals("false")) {
194: // Spec says if jsp_precompile=false, the request should not
195: // be delivered to the JSP page; the easiest way to implement
196: // this is to set the flag to true, and precompile the page anyway.
197: // This still conforms to the spec, since it says the
198: // precompilation request can be ignored.
199: return (true); // ?jsp_precompile=false
200: } else {
201: throw new ServletException("Cannot have request parameter "
202: + Constants.PRECOMPILE + " set to " + value);
203: }
204:
205: }
206:
207: public void service(HttpServletRequest request,
208: HttpServletResponse response) throws ServletException,
209: IOException {
210:
211: String jspUri = null;
212:
213: String jspFile = (String) request
214: .getAttribute(Constants.JSP_FILE);
215: if (jspFile != null) {
216: // JSP is specified via <jsp-file> in <servlet> declaration
217: jspUri = jspFile;
218: } else {
219: /*
220: * Check to see if the requested JSP has been the target of a
221: * RequestDispatcher.include()
222: */
223: jspUri = (String) request
224: .getAttribute(Constants.INC_SERVLET_PATH);
225: if (jspUri != null) {
226: /*
227: * Requested JSP has been target of
228: * RequestDispatcher.include(). Its path is assembled from the
229: * relevant javax.servlet.include.* request attributes
230: */
231: String pathInfo = (String) request
232: .getAttribute("javax.servlet.include.path_info");
233: if (pathInfo != null) {
234: jspUri += pathInfo;
235: }
236: } else {
237: /*
238: * Requested JSP has not been the target of a
239: * RequestDispatcher.include(). Reconstruct its path from the
240: * request's getServletPath() and getPathInfo()
241: */
242: jspUri = request.getServletPath();
243: String pathInfo = request.getPathInfo();
244: if (pathInfo != null) {
245: jspUri += pathInfo;
246: }
247: }
248: }
249:
250: if (log.isDebugEnabled()) {
251: log.debug("JspEngine --> " + jspUri);
252: log
253: .debug("\t ServletPath: "
254: + request.getServletPath());
255: log.debug("\t PathInfo: " + request.getPathInfo());
256: log.debug("\t RealPath: "
257: + context.getRealPath(jspUri));
258: log.debug("\t RequestURI: " + request.getRequestURI());
259: log
260: .debug("\t QueryString: "
261: + request.getQueryString());
262: log.debug("\t Request Params: ");
263: Enumeration e = request.getParameterNames();
264: while (e.hasMoreElements()) {
265: String name = (String) e.nextElement();
266: log.debug("\t\t " + name + " = "
267: + request.getParameter(name));
268: }
269: }
270:
271: try {
272: boolean precompile = preCompile(request);
273: serviceJspFile(request, response, jspUri, null, precompile);
274: } catch (RuntimeException e) {
275: throw e;
276: } catch (ServletException e) {
277: throw e;
278: } catch (IOException e) {
279: throw e;
280: } catch (Throwable e) {
281: throw new ServletException(e);
282: }
283:
284: }
285:
286: public void destroy() {
287: if (log.isDebugEnabled()) {
288: log.debug("JspServlet.destroy()");
289: }
290:
291: rctxt.destroy();
292: }
293:
294: public void periodicEvent() {
295: rctxt.checkCompile();
296: }
297:
298: // -------------------------------------------------------- Private Methods
299:
300: private void serviceJspFile(HttpServletRequest request,
301: HttpServletResponse response, String jspUri,
302: Throwable exception, boolean precompile)
303: throws ServletException, IOException {
304:
305: JspServletWrapper wrapper = (JspServletWrapper) rctxt
306: .getWrapper(jspUri);
307: if (wrapper == null) {
308: synchronized (this ) {
309: wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
310: if (wrapper == null) {
311: // Check if the requested JSP page exists, to avoid
312: // creating unnecessary directories and files.
313: if (null == context.getResource(jspUri)) {
314: response.sendError(
315: HttpServletResponse.SC_NOT_FOUND,
316: request.getRequestURI());
317: return;
318: }
319: boolean isErrorPage = exception != null;
320: wrapper = new JspServletWrapper(config, options,
321: jspUri, isErrorPage, rctxt);
322: rctxt.addWrapper(jspUri, wrapper);
323: }
324: }
325: }
326:
327: wrapper.service(request, response, precompile);
328:
329: }
330:
331: }
|