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