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