Source code: org/apache/jasper/servlet/JspServlet.java
1 /*
2 * Copyright 1999,2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.jasper.servlet;
18
19 import java.io.IOException;
20 import java.lang.reflect.Constructor;
21 import java.util.Enumeration;
22
23 import javax.servlet.ServletConfig;
24 import javax.servlet.ServletContext;
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServlet;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32
33 import org.apache.jasper.Constants;
34 import org.apache.jasper.EmbeddedServletOptions;
35 import org.apache.jasper.Options;
36 import org.apache.jasper.compiler.JspRuntimeContext;
37 import org.apache.jasper.compiler.Localizer;
38
39 /**
40 * The JSP engine (a.k.a Jasper).
41 *
42 * The servlet container is responsible for providing a
43 * URLClassLoader for the web application context Jasper
44 * is being used in. Jasper will try get the Tomcat
45 * ServletContext attribute for its ServletContext class
46 * loader, if that fails, it uses the parent class loader.
47 * In either case, it must be a URLClassLoader.
48 *
49 * @author Anil K. Vijendran
50 * @author Harish Prabandham
51 * @author Remy Maucherat
52 * @author Kin-man Chung
53 * @author Glenn Nielsen
54 */
55 public class JspServlet extends HttpServlet {
56
57 // Logger
58 private Log log = LogFactory.getLog(JspServlet.class);
59
60 private ServletContext context;
61 private ServletConfig config;
62 private Options options;
63 private JspRuntimeContext rctxt;
64
65
66 /*
67 * Initializes this JspServlet.
68 */
69 public void init(ServletConfig config) throws ServletException {
70
71 super.init(config);
72 this.config = config;
73 this.context = config.getServletContext();
74
75 // Initialize the JSP Runtime Context
76 // Check for a custom Options implementation
77 String engineOptionsName =
78 config.getInitParameter("engineOptionsClass");
79 if (engineOptionsName != null) {
80 // Instantiate the indicated Options implementation
81 try {
82 ClassLoader loader = Thread.currentThread()
83 .getContextClassLoader();
84 Class engineOptionsClass = loader.loadClass(engineOptionsName);
85 Class[] ctorSig = { ServletConfig.class, ServletContext.class };
86 Constructor ctor = engineOptionsClass.getConstructor(ctorSig);
87 Object[] args = { config, context };
88 options = (Options) ctor.newInstance(args);
89 } catch (Throwable e) {
90 // Need to localize this.
91 log.warn("Failed to load engineOptionsClass", e);
92 // Use the default Options implementation
93 options = new EmbeddedServletOptions(config, context);
94 }
95 } else {
96 // Use the default Options implementation
97 options = new EmbeddedServletOptions(config, context);
98 }
99 rctxt = new JspRuntimeContext(context, options);
100
101 if (log.isDebugEnabled()) {
102 log.debug(Localizer.getMessage("jsp.message.scratch.dir.is",
103 options.getScratchDir().toString()));
104 log.debug(Localizer.getMessage("jsp.message.dont.modify.servlets"));
105 }
106 }
107
108
109 /**
110 * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
111 * the number of JSPs that have been loaded into the webapp with which
112 * this JspServlet is associated.
113 *
114 * <p>This info may be used for monitoring purposes.
115 *
116 * @return The number of JSPs that have been loaded into the webapp with
117 * which this JspServlet is associated
118 */
119 public int getJspCount() {
120 return this.rctxt.getJspCount();
121 }
122
123
124 /**
125 * Resets the JSP reload counter.
126 *
127 * @param count Value to which to reset the JSP reload counter
128 */
129 public void setJspReloadCount(int count) {
130 this.rctxt.setJspReloadCount(count);
131 }
132
133
134 /**
135 * Gets the number of JSPs that have been reloaded.
136 *
137 * <p>This info may be used for monitoring purposes.
138 *
139 * @return The number of JSPs (in the webapp with which this JspServlet is
140 * associated) that have been reloaded
141 */
142 public int getJspReloadCount() {
143 return this.rctxt.getJspReloadCount();
144 }
145
146
147 /**
148 * <p>Look for a <em>precompilation request</em> as described in
149 * Section 8.4.2 of the JSP 1.2 Specification. <strong>WARNING</strong> -
150 * we cannot use <code>request.getParameter()</code> for this, because
151 * that will trigger parsing all of the request parameters, and not give
152 * a servlet the opportunity to call
153 * <code>request.setCharacterEncoding()</code> first.</p>
154 *
155 * @param request The servlet requset we are processing
156 *
157 * @exception ServletException if an invalid parameter value for the
158 * <code>jsp_precompile</code> parameter name is specified
159 */
160 boolean preCompile(HttpServletRequest request) throws ServletException {
161
162 String queryString = request.getQueryString();
163 if (queryString == null) {
164 return (false);
165 }
166 int start = queryString.indexOf(Constants.PRECOMPILE);
167 if (start < 0) {
168 return (false);
169 }
170 queryString =
171 queryString.substring(start + Constants.PRECOMPILE.length());
172 if (queryString.length() == 0) {
173 return (true); // ?jsp_precompile
174 }
175 if (queryString.startsWith("&")) {
176 return (true); // ?jsp_precompile&foo=bar...
177 }
178 if (!queryString.startsWith("=")) {
179 return (false); // part of some other name or value
180 }
181 int limit = queryString.length();
182 int ampersand = queryString.indexOf("&");
183 if (ampersand > 0) {
184 limit = ampersand;
185 }
186 String value = queryString.substring(1, limit);
187 if (value.equals("true")) {
188 return (true); // ?jsp_precompile=true
189 } else if (value.equals("false")) {
190 // Spec says if jsp_precompile=false, the request should not
191 // be delivered to the JSP page; the easiest way to implement
192 // this is to set the flag to true, and precompile the page anyway.
193 // This still conforms to the spec, since it says the
194 // precompilation request can be ignored.
195 return (true); // ?jsp_precompile=false
196 } else {
197 throw new ServletException("Cannot have request parameter " +
198 Constants.PRECOMPILE + " set to " +
199 value);
200 }
201
202 }
203
204
205 public void service (HttpServletRequest request,
206 HttpServletResponse response)
207 throws ServletException, IOException {
208
209 String jspUri = null;
210
211 String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
212 if (jspFile != null) {
213 // JSP is specified via <jsp-file> in <servlet> declaration
214 jspUri = jspFile;
215 } else {
216 /*
217 * Check to see if the requested JSP has been the target of a
218 * RequestDispatcher.include()
219 */
220 jspUri = (String) request.getAttribute(Constants.INC_SERVLET_PATH);
221 if (jspUri != null) {
222 /*
223 * Requested JSP has been target of
224 * RequestDispatcher.include(). Its path is assembled from the
225 * relevant javax.servlet.include.* request attributes
226 */
227 String pathInfo = (String) request.getAttribute(
228 "javax.servlet.include.path_info");
229 if (pathInfo != null) {
230 jspUri += pathInfo;
231 }
232 } else {
233 /*
234 * Requested JSP has not been the target of a
235 * RequestDispatcher.include(). Reconstruct its path from the
236 * request's getServletPath() and getPathInfo()
237 */
238 jspUri = request.getServletPath();
239 String pathInfo = request.getPathInfo();
240 if (pathInfo != null) {
241 jspUri += pathInfo;
242 }
243 }
244 }
245
246 if (log.isDebugEnabled()) {
247 log.debug("JspEngine --> " + jspUri);
248 log.debug("\t ServletPath: " + request.getServletPath());
249 log.debug("\t PathInfo: " + request.getPathInfo());
250 log.debug("\t RealPath: " + context.getRealPath(jspUri));
251 log.debug("\t RequestURI: " + request.getRequestURI());
252 log.debug("\t QueryString: " + request.getQueryString());
253 log.debug("\t Request Params: ");
254 Enumeration e = request.getParameterNames();
255 while (e.hasMoreElements()) {
256 String name = (String) e.nextElement();
257 log.debug("\t\t " + name + " = "
258 + request.getParameter(name));
259 }
260 }
261
262 try {
263 boolean precompile = preCompile(request);
264 serviceJspFile(request, response, jspUri, null, precompile);
265 } catch (RuntimeException e) {
266 throw e;
267 } catch (ServletException e) {
268 throw e;
269 } catch (IOException e) {
270 throw e;
271 } catch (Throwable e) {
272 throw new ServletException(e);
273 }
274
275 }
276
277 public void destroy() {
278 if (log.isDebugEnabled()) {
279 log.debug("JspServlet.destroy()");
280 }
281
282 rctxt.destroy();
283 }
284
285
286 // -------------------------------------------------------- Private Methods
287
288 private void serviceJspFile(HttpServletRequest request,
289 HttpServletResponse response, String jspUri,
290 Throwable exception, boolean precompile)
291 throws ServletException, IOException {
292
293 JspServletWrapper wrapper =
294 (JspServletWrapper) rctxt.getWrapper(jspUri);
295 if (wrapper == null) {
296 synchronized(this) {
297 wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
298 if (wrapper == null) {
299 // Check if the requested JSP page exists, to avoid
300 // creating unnecessary directories and files.
301 if (null == context.getResource(jspUri)) {
302 response.sendError(HttpServletResponse.SC_NOT_FOUND,
303 jspUri);
304 return;
305 }
306 boolean isErrorPage = exception != null;
307 wrapper = new JspServletWrapper(config, options, jspUri,
308 isErrorPage, rctxt);
309 rctxt.addWrapper(jspUri,wrapper);
310 }
311 }
312 }
313
314 wrapper.service(request, response, precompile);
315
316 }
317
318 }