1
17
18 package org.apache.jasper.servlet;
19
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import javax.servlet.RequestDispatcher;
26 import javax.servlet.Servlet;
27 import javax.servlet.ServletConfig;
28 import javax.servlet.ServletContext;
29 import javax.servlet.ServletException;
30 import javax.servlet.SingleThreadModel;
31 import javax.servlet.UnavailableException;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34 import javax.servlet.jsp.tagext.TagInfo;
35
36 import org.apache.jasper.JasperException;
37 import org.apache.jasper.JspCompilationContext;
38 import org.apache.jasper.Options;
39 import org.apache.jasper.compiler.JavacErrorDetail;
40 import org.apache.jasper.compiler.JspRuntimeContext;
41 import org.apache.jasper.compiler.Localizer;
42 import org.apache.jasper.compiler.SmapInput;
43 import org.apache.jasper.compiler.SmapStratum;
44 import org.apache.jasper.runtime.ExceptionUtils;
45 import org.apache.jasper.runtime.InstanceManagerFactory;
46 import org.apache.jasper.runtime.JspSourceDependent;
47 import org.apache.jasper.util.FastRemovalDequeue;
48 import org.apache.juli.logging.Log;
49 import org.apache.juli.logging.LogFactory;
50 import org.apache.tomcat.InstanceManager;
51 import org.apache.tomcat.Jar;
52
53
70
71 @SuppressWarnings("deprecation")
72 public class JspServletWrapper {
73
74 private static final Map<String,Long> ALWAYS_OUTDATED_DEPENDENCIES =
75 new HashMap<>();
76
77 static {
78
79 ALWAYS_OUTDATED_DEPENDENCIES.put("/WEB-INF/web.xml", Long.valueOf(-1));
80 }
81
82
83 private final Log log = LogFactory.getLog(JspServletWrapper.class);
84
85 private volatile Servlet theServlet;
86 private final String jspUri;
87 private volatile Class<?> tagHandlerClass;
88 private final JspCompilationContext ctxt;
89 private long available = 0L;
90 private final ServletConfig config;
91 private final Options options;
92
99 private volatile boolean mustCompile = true;
100
101 private volatile boolean reload = true;
102 private final boolean isTagFile;
103 private int tripCount;
104 private JasperException compileException;
105
106 private volatile long servletClassLastModifiedTime;
107 private long lastModificationTest = 0L;
108 private long lastUsageTime = System.currentTimeMillis();
109 private FastRemovalDequeue<JspServletWrapper>.Entry unloadHandle;
110 private final boolean unloadAllowed;
111 private final boolean unloadByCount;
112 private final boolean unloadByIdle;
113
114
117 public JspServletWrapper(ServletConfig config, Options options,
118 String jspUri, JspRuntimeContext rctxt) {
119
120 this.isTagFile = false;
121 this.config = config;
122 this.options = options;
123 this.jspUri = jspUri;
124 unloadByCount = options.getMaxLoadedJsps() > 0 ? true : false;
125 unloadByIdle = options.getJspIdleTimeout() > 0 ? true : false;
126 unloadAllowed = unloadByCount || unloadByIdle ? true : false;
127 ctxt = new JspCompilationContext(jspUri, options,
128 config.getServletContext(),
129 this, rctxt);
130 }
131
132
135 public JspServletWrapper(ServletContext servletContext,
136 Options options,
137 String tagFilePath,
138 TagInfo tagInfo,
139 JspRuntimeContext rctxt,
140 Jar tagJar) {
141
142 this.isTagFile = true;
143 this.config = null;
144 this.options = options;
145 this.jspUri = tagFilePath;
146 this.tripCount = 0;
147 unloadByCount = options.getMaxLoadedJsps() > 0 ? true : false;
148 unloadByIdle = options.getJspIdleTimeout() > 0 ? true : false;
149 unloadAllowed = unloadByCount || unloadByIdle ? true : false;
150 ctxt = new JspCompilationContext(jspUri, tagInfo, options,
151 servletContext, this, rctxt,
152 tagJar);
153 }
154
155 public JspCompilationContext getJspEngineContext() {
156 return ctxt;
157 }
158
159 public void setReload(boolean reload) {
160 this.reload = reload;
161 }
162
163 public boolean getReload() {
164 return reload;
165 }
166
167 private boolean getReloadInternal() {
168 return reload && !ctxt.getRuntimeContext().isCompileCheckInProgress();
169 }
170
171 public Servlet getServlet() throws ServletException {
172
181 if (getReloadInternal() || theServlet == null) {
182 synchronized (this) {
183
184
185 if (getReloadInternal() || theServlet == null) {
186
187 destroy();
188
189 final Servlet servlet;
190
191 try {
192 InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config);
193 servlet = (Servlet) instanceManager.newInstance(ctxt.getFQCN(), ctxt.getJspLoader());
194 } catch (Exception e) {
195 Throwable t = ExceptionUtils
196 .unwrapInvocationTargetException(e);
197 ExceptionUtils.handleThrowable(t);
198 throw new JasperException(t);
199 }
200
201 servlet.init(config);
202
203 if (theServlet != null) {
204 ctxt.getRuntimeContext().incrementJspReloadCount();
205 }
206
207 theServlet = servlet;
208 reload = false;
209
210 }
211 }
212 }
213 return theServlet;
214 }
215
216 public ServletContext getServletContext() {
217 return ctxt.getServletContext();
218 }
219
220
225 public void setCompilationException(JasperException je) {
226 this.compileException = je;
227 }
228
229
235 public void setServletClassLastModifiedTime(long lastModified) {
236
237
238
239 if (this.servletClassLastModifiedTime < lastModified) {
240 synchronized (this) {
241 if (this.servletClassLastModifiedTime < lastModified) {
242 this.servletClassLastModifiedTime = lastModified;
243 reload = true;
244
245
246
247
248
249
250 ctxt.clearJspLoader();
251 }
252 }
253 }
254 }
255
256
261 public Class<?> loadTagFile() throws JasperException {
262
263 try {
264 if (ctxt.isRemoved()) {
265 throw new FileNotFoundException(jspUri);
266 }
267 if (options.getDevelopment() || mustCompile) {
268 synchronized (this) {
269 if (options.getDevelopment() || mustCompile) {
270 ctxt.compile();
271 mustCompile = false;
272 }
273 }
274 } else {
275 if (compileException != null) {
276 throw compileException;
277 }
278 }
279
280 if (getReloadInternal() || tagHandlerClass == null) {
281 synchronized (this) {
282 if (getReloadInternal() || tagHandlerClass == null) {
283 tagHandlerClass = ctxt.load();
284
285 reload = false;
286 }
287 }
288 }
289 } catch (FileNotFoundException ex) {
290 throw new JasperException(ex);
291 }
292
293 return tagHandlerClass;
294 }
295
296
304 public Class<?> loadTagFilePrototype() throws JasperException {
305
306 ctxt.setPrototypeMode(true);
307 try {
308 return loadTagFile();
309 } finally {
310 ctxt.setPrototypeMode(false);
311 }
312 }
313
314
318 public java.util.Map<String,Long> getDependants() {
319 try {
320 Object target;
321 if (isTagFile) {
322 if (reload) {
323 synchronized (this) {
324 if (reload) {
325 tagHandlerClass = ctxt.load();
326 reload = false;
327 }
328 }
329 }
330 target = tagHandlerClass.getConstructor().newInstance();
331 } else {
332 target = getServlet();
333 }
334 if (target instanceof JspSourceDependent) {
335 return ((JspSourceDependent) target).getDependants();
336 }
337 } catch (AbstractMethodError ame) {
338
339
340 return ALWAYS_OUTDATED_DEPENDENCIES;
341 } catch (Throwable ex) {
342 ExceptionUtils.handleThrowable(ex);
343 }
344 return null;
345 }
346
347 public boolean isTagFile() {
348 return this.isTagFile;
349 }
350
351 public int incTripCount() {
352 return tripCount++;
353 }
354
355 public int decTripCount() {
356 return tripCount--;
357 }
358
359 public String getJspUri() {
360 return jspUri;
361 }
362
363 public FastRemovalDequeue<JspServletWrapper>.Entry getUnloadHandle() {
364 return unloadHandle;
365 }
366
367 public void service(HttpServletRequest request,
368 HttpServletResponse response,
369 boolean precompile)
370 throws ServletException, IOException, FileNotFoundException {
371
372 Servlet servlet;
373
374 try {
375
376 if (ctxt.isRemoved()) {
377 throw new FileNotFoundException(jspUri);
378 }
379
380 if ((available > 0L) && (available < Long.MAX_VALUE)) {
381 if (available > System.currentTimeMillis()) {
382 response.setDateHeader("Retry-After", available);
383 response.sendError
384 (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
385 Localizer.getMessage("jsp.error.unavailable"));
386 return;
387 }
388
389
390 available = 0;
391 }
392
393
396 if (options.getDevelopment() || mustCompile) {
397 synchronized (this) {
398 if (options.getDevelopment() || mustCompile) {
399
400 ctxt.compile();
401 mustCompile = false;
402 }
403 }
404 } else {
405 if (compileException != null) {
406
407 throw compileException;
408 }
409 }
410
411
414 servlet = getServlet();
415
416
417 if (precompile) {
418 return;
419 }
420
421 } catch (ServletException ex) {
422 if (options.getDevelopment()) {
423 throw handleJspException(ex);
424 }
425 throw ex;
426 } catch (FileNotFoundException fnfe) {
427
428 throw fnfe;
429 } catch (IOException ex) {
430 if (options.getDevelopment()) {
431 throw handleJspException(ex);
432 }
433 throw ex;
434 } catch (IllegalStateException ex) {
435 if (options.getDevelopment()) {
436 throw handleJspException(ex);
437 }
438 throw ex;
439 } catch (Exception ex) {
440 if (options.getDevelopment()) {
441 throw handleJspException(ex);
442 }
443 throw new JasperException(ex);
444 }
445
446 try {
447
450 if (unloadAllowed) {
451 synchronized(this) {
452 if (unloadByCount) {
453 if (unloadHandle == null) {
454 unloadHandle = ctxt.getRuntimeContext().push(this);
455 } else if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
456 ctxt.getRuntimeContext().makeYoungest(unloadHandle);
457 lastUsageTime = System.currentTimeMillis();
458 }
459 } else {
460 if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
461 lastUsageTime = System.currentTimeMillis();
462 }
463 }
464 }
465 }
466
467
470 if (servlet instanceof SingleThreadModel) {
471
472
473 synchronized (this) {
474 servlet.service(request, response);
475 }
476 } else {
477 servlet.service(request, response);
478 }
479 } catch (UnavailableException ex) {
480 String includeRequestUri = (String)
481 request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
482 if (includeRequestUri != null) {
483
484
485
486 throw ex;
487 }
488
489 int unavailableSeconds = ex.getUnavailableSeconds();
490 if (unavailableSeconds <= 0) {
491 unavailableSeconds = 60;
492 }
493 available = System.currentTimeMillis() +
494 (unavailableSeconds * 1000L);
495 response.sendError
496 (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
497 ex.getMessage());
498 } catch (ServletException ex) {
499 if(options.getDevelopment()) {
500 throw handleJspException(ex);
501 }
502 throw ex;
503 } catch (IOException ex) {
504 if (options.getDevelopment()) {
505 throw new IOException(handleJspException(ex).getMessage(), ex);
506 }
507 throw ex;
508 } catch (IllegalStateException ex) {
509 if(options.getDevelopment()) {
510 throw handleJspException(ex);
511 }
512 throw ex;
513 } catch (Exception ex) {
514 if(options.getDevelopment()) {
515 throw handleJspException(ex);
516 }
517 throw new JasperException(ex);
518 }
519 }
520
521 public void destroy() {
522 if (theServlet != null) {
523 try {
524 theServlet.destroy();
525 } catch (Throwable t) {
526 ExceptionUtils.handleThrowable(t);
527 log.error(Localizer.getMessage("jsp.error.servlet.destroy.failed"), t);
528 }
529 InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config);
530 try {
531 instanceManager.destroyInstance(theServlet);
532 } catch (Exception e) {
533 Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
534 ExceptionUtils.handleThrowable(t);
535
536 log.error(Localizer.getMessage("jsp.error.file.not.found",
537 e.getMessage()), t);
538 }
539 }
540 }
541
542
545 public long getLastModificationTest() {
546 return lastModificationTest;
547 }
548
551 public void setLastModificationTest(long lastModificationTest) {
552 this.lastModificationTest = lastModificationTest;
553 }
554
555
558 public long getLastUsageTime() {
559 return lastUsageTime;
560 }
561
562
575 protected JasperException handleJspException(Exception ex) {
576 try {
577 Throwable realException = ex;
578 if (ex instanceof ServletException) {
579 realException = ((ServletException) ex).getRootCause();
580 }
581
582
583
584 StackTraceElement[] frames = realException.getStackTrace();
585 StackTraceElement jspFrame = null;
586
587 String servletPackageName = ctxt.getBasePackageName();
588 for (StackTraceElement frame : frames) {
589 if (frame.getClassName().startsWith(servletPackageName)) {
590 jspFrame = frame;
591 break;
592 }
593 }
594
595 SmapStratum smap = null;
596
597 if (jspFrame != null) {
598 smap = ctxt.getCompiler().getSmap(jspFrame.getClassName());
599 }
600
601 if (smap == null) {
602
603
604
605 return new JasperException(ex);
606 }
607
608 @SuppressWarnings("null")
609 int javaLineNumber = jspFrame.getLineNumber();
610 SmapInput source = smap.getInputLineNumber(javaLineNumber);
611
612
613
614 if (source.getLineNumber() < 1) {
615 throw new JasperException(ex);
616 }
617
618 JavacErrorDetail detail = new JavacErrorDetail(jspFrame.getMethodName(), javaLineNumber,
619 source.getFileName(), source.getLineNumber(), null, ctxt);
620
621 if (options.getDisplaySourceFragment()) {
622 return new JasperException(Localizer.getMessage
623 ("jsp.exception", detail.getJspFileName(),
624 "" + source.getLineNumber()) + System.lineSeparator() +
625 System.lineSeparator() + detail.getJspExtract() +
626 System.lineSeparator() + System.lineSeparator() +
627 "Stacktrace:", ex);
628
629 }
630
631 return new JasperException(Localizer.getMessage
632 ("jsp.exception", detail.getJspFileName(),
633 "" + source.getLineNumber()), ex);
634 } catch (Exception je) {
635
636 if (ex instanceof JasperException) {
637 return (JasperException) ex;
638 }
639 return new JasperException(ex);
640 }
641 }
642 }
643