Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/jasper/servlet/JspServletWrapper.java


1   /*
2    * Copyright 1999,2004-2005 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.BufferedReader;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileNotFoundException;
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.io.IOException;
26  import java.net.URL;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import javax.servlet.Servlet;
31  import javax.servlet.ServletConfig;
32  import javax.servlet.ServletContext;
33  import javax.servlet.ServletException;
34  import javax.servlet.SingleThreadModel;
35  import javax.servlet.UnavailableException;
36  import javax.servlet.http.HttpServletRequest;
37  import javax.servlet.http.HttpServletResponse;
38  import javax.servlet.jsp.tagext.TagInfo;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.jasper.JasperException;
43  import org.apache.jasper.JspCompilationContext;
44  import org.apache.jasper.Options;
45  import org.apache.jasper.compiler.ErrorDispatcher;
46  import org.apache.jasper.compiler.JavacErrorDetail;
47  import org.apache.jasper.compiler.JspRuntimeContext;
48  import org.apache.jasper.compiler.Localizer;
49  import org.apache.jasper.runtime.JspSourceDependent;
50  
51  /**
52   * The JSP engine (a.k.a Jasper).
53   *
54   * The servlet container is responsible for providing a
55   * URLClassLoader for the web application context Jasper
56   * is being used in. Jasper will try get the Tomcat
57   * ServletContext attribute for its ServletContext class
58   * loader, if that fails, it uses the parent class loader.
59   * In either case, it must be a URLClassLoader.
60   *
61   * @author Anil K. Vijendran
62   * @author Harish Prabandham
63   * @author Remy Maucherat
64   * @author Kin-man Chung
65   * @author Glenn Nielsen
66   * @author Tim Fennell
67   */
68  
69  public class JspServletWrapper {
70  
71      // Logger
72      private Log log = LogFactory.getLog(JspServletWrapper.class);
73  
74      private Servlet theServlet;
75      private String jspUri;
76      private Class servletClass;
77      private Class tagHandlerClass;
78      private JspCompilationContext ctxt;
79      private long available = 0L;
80      private ServletConfig config;
81      private Options options;
82      private boolean firstTime = true;
83      private boolean reload = true;
84      private boolean isTagFile;
85      private int tripCount;
86      private JasperException compileException;
87      private long servletClassLastModifiedTime;
88      private long lastModificationTest = 0L;
89  
90      /*
91       * JspServletWrapper for JSP pages.
92       */
93      JspServletWrapper(ServletConfig config, Options options, String jspUri,
94                        boolean isErrorPage, JspRuntimeContext rctxt)
95              throws JasperException {
96  
97    this.isTagFile = false;
98          this.config = config;
99          this.options = options;
100         this.jspUri = jspUri;
101         ctxt = new JspCompilationContext(jspUri, isErrorPage, options,
102            config.getServletContext(),
103            this, rctxt);
104     }
105 
106     /*
107      * JspServletWrapper for tag files.
108      */
109     public JspServletWrapper(ServletContext servletContext,
110            Options options,
111            String tagFilePath,
112            TagInfo tagInfo,
113            JspRuntimeContext rctxt,
114            URL tagFileJarUrl)
115       throws JasperException {
116 
117   this.isTagFile = true;
118         this.config = null;  // not used
119         this.options = options;
120   this.jspUri = tagFilePath;
121   this.tripCount = 0;
122         ctxt = new JspCompilationContext(jspUri, tagInfo, options,
123            servletContext, this, rctxt,
124            tagFileJarUrl);
125     }
126 
127     public JspCompilationContext getJspEngineContext() {
128         return ctxt;
129     }
130 
131     public void setReload(boolean reload) {
132         this.reload = reload;
133     }
134 
135     public Servlet getServlet()
136         throws ServletException, IOException, FileNotFoundException
137     {
138         if (reload) {
139             synchronized (this) {
140                 // Synchronizing on jsw enables simultaneous loading
141                 // of different pages, but not the same page.
142                 if (reload) {
143                     // This is to maintain the original protocol.
144                     destroy();
145                     
146                     try {
147                         servletClass = ctxt.load();
148                         theServlet = (Servlet) servletClass.newInstance();
149                     } catch( IllegalAccessException ex1 ) {
150                         throw new JasperException( ex1 );
151                     } catch( InstantiationException ex ) {
152                         throw new JasperException( ex );
153                     }
154                     
155                     theServlet.init(config);
156 
157                     if (!firstTime) {
158                         ctxt.getRuntimeContext().incrementJspReloadCount();
159                     }
160 
161                     reload = false;
162                 }
163             }    
164         }
165         return theServlet;
166     }
167 
168     public ServletContext getServletContext() {
169         return config.getServletContext();
170     }
171 
172     /**
173      * Sets the compilation exception for this JspServletWrapper.
174      *
175      * @param je The compilation exception
176      */
177     public void setCompilationException(JasperException je) {
178         this.compileException = je;
179     }
180 
181     /**
182      * Sets the last-modified time of the servlet class file associated with
183      * this JspServletWrapper.
184      *
185      * @param lastModified Last-modified time of servlet class
186      */
187     public void setServletClassLastModifiedTime(long lastModified) {
188         if (this.servletClassLastModifiedTime < lastModified) {
189             synchronized (this) {
190                 if (this.servletClassLastModifiedTime < lastModified) {
191                     this.servletClassLastModifiedTime = lastModified;
192                     reload = true;
193                 }
194             }
195         }
196     }
197 
198     /**
199      * Compile (if needed) and load a tag file
200      */
201     public Class loadTagFile() throws JasperException {
202 
203         try {
204             if (ctxt.isRemoved()) {
205                 throw new FileNotFoundException(jspUri);
206             }
207             if (options.getDevelopment() || firstTime ) {
208                 synchronized (this) {
209                     firstTime = false;
210                     ctxt.compile();
211                 }
212             } else {
213                 if (compileException != null) {
214                     throw compileException;
215                 }
216             }
217 
218             if (reload) {
219                 tagHandlerClass = ctxt.load();
220             }
221         } catch (FileNotFoundException ex) {
222             throw new JasperException(ex);
223   }
224 
225   return tagHandlerClass;
226     }
227 
228     /**
229      * Compile and load a prototype for the Tag file.  This is needed
230      * when compiling tag files with circular dependencies.  A prototpe
231      * (skeleton) with no dependencies on other other tag files is
232      * generated and compiled.
233      */
234     public Class loadTagFilePrototype() throws JasperException {
235 
236   ctxt.setPrototypeMode(true);
237   try {
238       return loadTagFile();
239   } finally {
240       ctxt.setPrototypeMode(false);
241   }
242     }
243 
244     /**
245      * Get a list of files that the current page has source dependency on.
246      */
247     public java.util.List getDependants() {
248   try {
249       Object target;
250       if (isTagFile) {
251                 if (reload) {
252                     tagHandlerClass = ctxt.load();
253                 }
254     target = tagHandlerClass.newInstance();
255       } else {
256     target = getServlet();
257       }
258       if (target != null && target instanceof JspSourceDependent) {
259             return ((java.util.List) ((JspSourceDependent) target).getDependants());
260       }
261   } catch (Throwable ex) {
262   }
263   return null;
264     }
265 
266     public boolean isTagFile() {
267   return this.isTagFile;
268     }
269 
270     public int incTripCount() {
271   return tripCount++;
272     }
273 
274     public int decTripCount() {
275   return tripCount--;
276     }
277 
278     public void service(HttpServletRequest request, 
279                         HttpServletResponse response,
280                         boolean precompile)
281       throws ServletException, IOException, FileNotFoundException {
282         try {
283 
284             if (ctxt.isRemoved()) {
285                 throw new FileNotFoundException(jspUri);
286             }
287 
288             if ((available > 0L) && (available < Long.MAX_VALUE)) {
289                 response.setDateHeader("Retry-After", available);
290                 response.sendError
291                     (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
292                      Localizer.getMessage("jsp.error.unavailable"));
293             }
294 
295             /*
296              * (1) Compile
297              */
298             if (options.getDevelopment() || firstTime ) {
299                 synchronized (this) {
300                     firstTime = false;
301 
302                     // The following sets reload to true, if necessary
303                     ctxt.compile();
304                 }
305             } else {
306                 if (compileException != null) {
307                     // Throw cached compilation exception
308                     throw compileException;
309                 }
310             }
311 
312             /*
313              * (2) (Re)load servlet class file
314              */
315             getServlet();
316 
317             // If a page is to be precompiled only, return.
318             if (precompile) {
319                 return;
320             }
321 
322             /*
323              * (3) Service request
324              */
325             if (theServlet instanceof SingleThreadModel) {
326                // sync on the wrapper so that the freshness
327                // of the page is determined right before servicing
328                synchronized (this) {
329                    theServlet.service(request, response);
330                 }
331             } else {
332                 theServlet.service(request, response);
333             }
334 
335         } catch (UnavailableException ex) {
336             String includeRequestUri = (String)
337                 request.getAttribute("javax.servlet.include.request_uri");
338             if (includeRequestUri != null) {
339                 // This file was included. Throw an exception as
340                 // a response.sendError() will be ignored by the
341                 // servlet engine.
342                 throw ex;
343             } else {
344                 int unavailableSeconds = ex.getUnavailableSeconds();
345                 if (unavailableSeconds <= 0) {
346                     unavailableSeconds = 60;        // Arbitrary default
347                 }
348                 available = System.currentTimeMillis() +
349                     (unavailableSeconds * 1000L);
350                 response.sendError
351                     (HttpServletResponse.SC_SERVICE_UNAVAILABLE, 
352                      ex.getMessage());
353             }
354         } catch (FileNotFoundException ex) {
355             ctxt.incrementRemoved();
356             String includeRequestUri = (String)
357                 request.getAttribute("javax.servlet.include.request_uri");
358             if (includeRequestUri != null) {
359                 // This file was included. Throw an exception as
360                 // a response.sendError() will be ignored by the
361                 // servlet engine.
362                 throw new ServletException(ex);
363             } else {
364                 try {
365                     response.sendError(HttpServletResponse.SC_NOT_FOUND, 
366                                       ex.getMessage());
367                 } catch (IllegalStateException ise) {
368                     log.error(Localizer.getMessage("jsp.error.file.not.found",
369                ex.getMessage()),
370             ex);
371                 }
372             }
373         } catch (ServletException ex) {
374             if(options.getDevelopment()) {
375                 throw handleJspException(ex);
376             } else {
377                 throw ex;
378             }
379         } catch (IOException ex) {
380             if(options.getDevelopment()) {
381                 throw handleJspException(ex);
382             } else {
383                 throw ex;
384             }
385         } catch (IllegalStateException ex) {
386             if(options.getDevelopment()) {
387                 throw handleJspException(ex);
388             } else {
389                 throw ex;
390             }
391         } catch (Exception ex) {
392             if(options.getDevelopment()) {
393                 throw handleJspException(ex);
394             } else {
395                 throw new JasperException(ex);
396             }
397         }
398     }
399 
400     public void destroy() {
401         if (theServlet != null) {
402             theServlet.destroy();
403         }
404     }
405 
406     /**
407      * @return Returns the lastModificationTest.
408      */
409     public long getLastModificationTest() {
410         return lastModificationTest;
411     }
412     /**
413      * @param lastModificationTest The lastModificationTest to set.
414      */
415     public void setLastModificationTest(long lastModificationTest) {
416         this.lastModificationTest = lastModificationTest;
417     }
418 
419     /**
420      * <p>Attempts to construct a JasperException that contains helpful information
421      * about what went wrong. Uses the JSP compiler system to translate the line
422      * number in the generated servlet that originated the exception to a line
423      * number in the JSP.  Then constructs an exception containing that
424      * information, and a snippet of the JSP to help debugging.
425      * Please see http://issues.apache.org/bugzilla/show_bug.cgi?id=37062 and
426      * http://www.tfenne.com/jasper/ for more details.
427      *</p>
428      *
429      * @param ex the exception that was the cause of the problem.
430      * @return a JasperException with more detailed information
431      */
432     protected JasperException handleJspException(Exception ex) {
433         try {
434             Throwable realException = ex;
435             if (ex instanceof ServletException) {
436                 realException = ((ServletException) ex).getRootCause();
437             }
438 
439             // First identify the stack frame in the trace that represents the JSP
440             StackTraceElement[] frames = realException.getStackTrace();
441             StackTraceElement jspFrame = null;
442 
443             for (int i=0; i<frames.length; ++i) {
444                 if ( frames[i].getClassName().equals(this.getServlet().getClass().getName()) ) {
445                     jspFrame = frames[i];
446                     break;
447                 }
448             }
449 
450             if (jspFrame == null) {
451                 // If we couldn't find a frame in the stack trace corresponding
452                 // to the generated servlet class, we can't really add anything
453                 return new JasperException(ex);
454             }
455             else {
456                 int javaLineNumber = jspFrame.getLineNumber();
457                 JavacErrorDetail detail = ErrorDispatcher.createJavacError(
458                                                                            jspFrame.getMethodName(),
459                                                                            this.ctxt.getCompiler().getPageNodes(),
460                                                                            null,
461                                                                            javaLineNumber);
462 
463                 // If the line number is less than one we couldn't find out
464                 // where in the JSP things went wrong
465                 int jspLineNumber = detail.getJspBeginLineNumber();
466                 if (jspLineNumber < 1) {
467                     throw new JasperException(ex);
468                 }
469 
470                 // Read both files in, so we can inspect them
471                 String[] jspLines = readFile
472                     (this.ctxt.getResourceAsStream(this.ctxt.getJspFile()));
473 
474                 String[] javaLines = readFile
475                     (new FileInputStream(this.ctxt.getServletJavaFileName()));
476 
477                 // If the line contains the opening of a multi-line scriptlet
478                 // block, then the JSP line number we got back is probably
479                 // faulty.  Scan forward to match the java line...
480                 if (jspLines[jspLineNumber-1].lastIndexOf("<%") >
481                     jspLines[jspLineNumber-1].lastIndexOf("%>")) {
482                     String javaLine = javaLines[javaLineNumber-1].trim();
483 
484                     for (int i=jspLineNumber-1; i<jspLines.length; i++) {
485                         if (jspLines[i].indexOf(javaLine) != -1) {
486                             jspLineNumber = i+1;
487                             break;
488                         }
489                     }
490                 }
491 
492                 // copy out a fragment of JSP to display to the user
493                 StringBuffer buffer = new StringBuffer(1024);
494                 int startIndex = Math.max(0, jspLineNumber-1-3);
495                 int endIndex = Math.min(jspLines.length-1, jspLineNumber-1+3);
496 
497                 for (int i=startIndex;i<=endIndex; ++i) {
498                     buffer.append(i+1);
499                     buffer.append(": ");
500                     buffer.append(jspLines[i]);
501                     buffer.append("\n");
502                 }
503 
504                 return new JasperException(
505                                            "Exception in JSP: " + detail.getJspFileName() + ":" +
506                                            jspLineNumber + "\n\n" + buffer + "\n\nStacktrace:", ex);
507             }
508         } catch (Exception je) {
509             // If anything goes wrong, just revert to the original behaviour
510             return new JasperException(ex);
511         }
512     }
513 
514     /**
515      * Reads a text file from an input stream into a String[]. Used to read in
516      * the JSP and generated Java file when generating error messages.
517      */
518     private String[] readFile(InputStream s) throws IOException {
519         BufferedReader reader = new BufferedReader(new InputStreamReader(s));
520         List lines = new ArrayList();
521         String line;
522 
523         while ( (line = reader.readLine()) != null ) {
524             lines.add(line);
525         }
526 
527         return (String[]) lines.toArray( new String[lines.size()] );
528     }
529 
530 }