1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.jasper.servlet;
19
20 import org.apache.struts2.jasper.JasperException;
21 import org.apache.struts2.jasper.JspCompilationContext;
22 import org.apache.struts2.jasper.Options;
23 import org.apache.struts2.jasper.compiler.ErrorDispatcher;
24 import org.apache.struts2.jasper.compiler.JavacErrorDetail;
25 import org.apache.struts2.jasper.compiler.JspRuntimeContext;
26 import org.apache.struts2.jasper.compiler.Localizer;
27 import org.apache.struts2.jasper.runtime.JspSourceDependent;
28
29 import javax.servlet.*;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32 import javax.servlet.jsp.tagext.TagInfo;
33 import java.io.FileNotFoundException;
34 import java.io.IOException;
35 import java.net.URL;
36
37 /***
38 * The JSP engine (a.k.a Jasper).
39 * <p/>
40 * The servlet container is responsible for providing a
41 * URLClassLoader for the web application context Jasper
42 * is being used in. Jasper will try get the Tomcat
43 * ServletContext attribute for its ServletContext class
44 * loader, if that fails, it uses the parent class loader.
45 * In either case, it must be a URLClassLoader.
46 *
47 * @author Anil K. Vijendran
48 * @author Harish Prabandham
49 * @author Remy Maucherat
50 * @author Kin-man Chung
51 * @author Glenn Nielsen
52 * @author Tim Fennell
53 */
54
55 public class JspServletWrapper {
56
57 private Servlet theServlet;
58 private String jspUri;
59 private Class servletClass;
60 private Class tagHandlerClass;
61 private JspCompilationContext ctxt;
62 private long available = 0L;
63 private ServletConfig config;
64 private Options options;
65 private boolean firstTime = true;
66 private boolean reload = true;
67 private boolean isTagFile;
68 private int tripCount;
69 private JasperException compileException;
70 private long servletClassLastModifiedTime;
71 private long lastModificationTest = 0L;
72
73
74
75
76 JspServletWrapper(ServletConfig config, Options options, String jspUri,
77 boolean isErrorPage, JspRuntimeContext rctxt)
78 throws JasperException {
79
80 this.isTagFile = false;
81 this.config = config;
82 this.options = options;
83 this.jspUri = jspUri;
84 ctxt = new JspCompilationContext(jspUri, isErrorPage, options,
85 config.getServletContext(),
86 this, rctxt, null);
87 }
88
89
90
91
92 public JspServletWrapper(ServletContext servletContext,
93 Options options,
94 String tagFilePath,
95 TagInfo tagInfo,
96 JspRuntimeContext rctxt,
97 URL tagFileJarUrl)
98 throws JasperException {
99
100 this.isTagFile = true;
101 this.config = null;
102 this.options = options;
103 this.jspUri = tagFilePath;
104 this.tripCount = 0;
105 ctxt = new JspCompilationContext(jspUri, tagInfo, options,
106 servletContext, this, rctxt,
107 tagFileJarUrl);
108 }
109
110 public JspCompilationContext getJspEngineContext() {
111 return ctxt;
112 }
113
114 public void setReload(boolean reload) {
115 this.reload = reload;
116 }
117
118 public Servlet getServlet()
119 throws ServletException, IOException, FileNotFoundException {
120 if (reload) {
121 synchronized (this) {
122
123
124 if (reload) {
125
126 destroy();
127
128 Servlet servlet = null;
129 try {
130 servletClass = ctxt.load();
131 servlet = (Servlet) servletClass.newInstance();
132 } catch (IllegalAccessException ex1) {
133 throw new JasperException(ex1);
134 } catch (InstantiationException ex) {
135 throw new JasperException(ex);
136 }
137
138 servlet.init(config);
139
140 if (!firstTime) {
141 ctxt.getRuntimeContext().incrementJspReloadCount();
142 }
143
144 theServlet = servlet;
145 reload = false;
146 }
147 }
148 }
149 return theServlet;
150 }
151
152 public ServletContext getServletContext() {
153 return config.getServletContext();
154 }
155
156 /***
157 * Sets the compilation exception for this JspServletWrapper.
158 *
159 * @param je The compilation exception
160 */
161 public void setCompilationException(JasperException je) {
162 this.compileException = je;
163 }
164
165 /***
166 * Sets the last-modified time of the servlet class file associated with
167 * this JspServletWrapper.
168 *
169 * @param lastModified Last-modified time of servlet class
170 */
171 public void setServletClassLastModifiedTime(long lastModified) {
172 if (this.servletClassLastModifiedTime < lastModified) {
173 synchronized (this) {
174 if (this.servletClassLastModifiedTime < lastModified) {
175 this.servletClassLastModifiedTime = lastModified;
176 reload = true;
177 }
178 }
179 }
180 }
181
182 /***
183 * Compile (if needed) and load a tag file
184 */
185 public Class loadTagFile() throws JasperException {
186
187 try {
188 if (ctxt.isRemoved()) {
189 throw new FileNotFoundException(jspUri);
190 }
191 if (options.getDevelopment() || firstTime) {
192 synchronized (this) {
193 firstTime = false;
194 ctxt.compile();
195 }
196 } else {
197 if (compileException != null) {
198 throw compileException;
199 }
200 }
201
202 if (reload) {
203 tagHandlerClass = ctxt.load();
204 reload = false;
205 }
206 } catch (FileNotFoundException ex) {
207 throw new JasperException(ex);
208 }
209
210 return tagHandlerClass;
211 }
212
213 /***
214 * Compile and load a prototype for the Tag file. This is needed
215 * when compiling tag files with circular dependencies. A prototpe
216 * (skeleton) with no dependencies on other other tag files is
217 * generated and compiled.
218 */
219 public Class loadTagFilePrototype() throws JasperException {
220
221 ctxt.setPrototypeMode(true);
222 try {
223 return loadTagFile();
224 } finally {
225 ctxt.setPrototypeMode(false);
226 }
227 }
228
229 /***
230 * Get a list of files that the current page has source dependency on.
231 */
232 public java.util.List getDependants() {
233 try {
234 Object target;
235 if (isTagFile) {
236 if (reload) {
237 tagHandlerClass = ctxt.load();
238 reload = false;
239 }
240 target = tagHandlerClass.newInstance();
241 } else {
242 target = getServlet();
243 }
244 if (target != null && target instanceof JspSourceDependent) {
245 return ((java.util.List) ((JspSourceDependent) target).getDependants());
246 }
247 } catch (Throwable ex) {
248 }
249 return null;
250 }
251
252 public boolean isTagFile() {
253 return this.isTagFile;
254 }
255
256 public int incTripCount() {
257 return tripCount++;
258 }
259
260 public int decTripCount() {
261 return tripCount--;
262 }
263
264 public void service(HttpServletRequest request,
265 HttpServletResponse response,
266 boolean precompile)
267 throws ServletException, IOException, FileNotFoundException {
268 try {
269
270 if (ctxt.isRemoved()) {
271 throw new FileNotFoundException(jspUri);
272 }
273
274 if ((available > 0L) && (available < Long.MAX_VALUE)) {
275 if (available > System.currentTimeMillis()) {
276 response.setDateHeader("Retry-After", available);
277 response.sendError
278 (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
279 Localizer.getMessage("jsp.error.unavailable"));
280 return;
281 } else {
282
283 available = 0;
284 }
285 }
286
287
288
289
290 if (options.getDevelopment() || firstTime) {
291 synchronized (this) {
292 firstTime = false;
293
294
295 ctxt.compile();
296 }
297 } else {
298 if (compileException != null) {
299
300 throw compileException;
301 }
302 }
303
304
305
306
307 getServlet();
308
309
310 if (precompile) {
311 return;
312 }
313
314
315
316
317 if (theServlet instanceof SingleThreadModel) {
318
319
320 synchronized (this) {
321 theServlet.service(request, response);
322 }
323 } else {
324 theServlet.service(request, response);
325 }
326
327 } catch (UnavailableException ex) {
328 String includeRequestUri = (String)
329 request.getAttribute("javax.servlet.include.request_uri");
330 if (includeRequestUri != null) {
331
332
333
334 throw ex;
335 } else {
336 int unavailableSeconds = ex.getUnavailableSeconds();
337 if (unavailableSeconds <= 0) {
338 unavailableSeconds = 60;
339 }
340 available = System.currentTimeMillis() +
341 (unavailableSeconds * 1000L);
342 response.sendError
343 (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
344 ex.getMessage());
345 }
346 } catch (ServletException ex) {
347 if (options.getDevelopment()) {
348 throw handleJspException(ex);
349 } else {
350 throw ex;
351 }
352 } catch (IOException ex) {
353 if (options.getDevelopment()) {
354 throw handleJspException(ex);
355 } else {
356 throw ex;
357 }
358 } catch (IllegalStateException ex) {
359 if (options.getDevelopment()) {
360 throw handleJspException(ex);
361 } else {
362 throw ex;
363 }
364 } catch (Exception ex) {
365 if (options.getDevelopment()) {
366 throw handleJspException(ex);
367 } else {
368 throw new JasperException(ex);
369 }
370 }
371 }
372
373 public void destroy() {
374 if (theServlet != null) {
375 theServlet.destroy();
376 }
377 }
378
379 /***
380 * @return Returns the lastModificationTest.
381 */
382 public long getLastModificationTest() {
383 return lastModificationTest;
384 }
385
386 /***
387 * @param lastModificationTest The lastModificationTest to set.
388 */
389 public void setLastModificationTest(long lastModificationTest) {
390 this.lastModificationTest = lastModificationTest;
391 }
392
393 /***
394 * <p>Attempts to construct a JasperException that contains helpful information
395 * about what went wrong. Uses the JSP compiler system to translate the line
396 * number in the generated servlet that originated the exception to a line
397 * number in the JSP. Then constructs an exception containing that
398 * information, and a snippet of the JSP to help debugging.
399 * Please see http://issues.apache.org/bugzilla/show_bug.cgi?id=37062 and
400 * http://www.tfenne.com/jasper/ for more details.
401 * </p>
402 *
403 * @param ex the exception that was the cause of the problem.
404 * @return a JasperException with more detailed information
405 */
406 protected JasperException handleJspException(Exception ex) {
407 try {
408 Throwable realException = ex;
409 if (ex instanceof ServletException) {
410 realException = ((ServletException) ex).getRootCause();
411 }
412
413
414 StackTraceElement[] frames = realException.getStackTrace();
415 StackTraceElement jspFrame = null;
416
417 for (int i = 0; i < frames.length; ++i) {
418 if (frames[i].getClassName().equals(this.getServlet().getClass().getName())) {
419 jspFrame = frames[i];
420 break;
421 }
422 }
423
424 if (jspFrame == null) {
425
426
427 return new JasperException(ex);
428 } else {
429 int javaLineNumber = jspFrame.getLineNumber();
430 JavacErrorDetail detail = ErrorDispatcher.createJavacError(
431 jspFrame.getMethodName(),
432 this.ctxt.getCompiler().getPageNodes(),
433 null,
434 javaLineNumber,
435 this.ctxt);
436
437
438
439 int jspLineNumber = detail.getJspBeginLineNumber();
440 if (jspLineNumber < 1) {
441 throw new JasperException(ex);
442 }
443
444 return new JasperException("Exception in JSP: " +
445 detail.getJspFileName() + ":" + jspLineNumber + "\n\n" +
446 detail.getJspExtract() + "\n\nStacktrace:", ex);
447 }
448 } catch (Exception je) {
449
450 if (ex instanceof JasperException) {
451 return (JasperException) ex;
452 } else {
453 return new JasperException(ex);
454 }
455 }
456 }
457
458 }