001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jasper.servlet;
018:
019: import java.io.FileNotFoundException;
020: import java.io.IOException;
021: import java.net.URL;
022:
023: import javax.servlet.Servlet;
024: import javax.servlet.ServletConfig;
025: import javax.servlet.ServletContext;
026: import javax.servlet.ServletException;
027: import javax.servlet.SingleThreadModel;
028: import javax.servlet.UnavailableException;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpServletResponse;
031: import javax.servlet.jsp.tagext.TagInfo;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.apache.jasper.JasperException;
036: import org.apache.jasper.JspCompilationContext;
037: import org.apache.jasper.Options;
038: import org.apache.jasper.compiler.JspRuntimeContext;
039: import org.apache.jasper.compiler.Localizer;
040: import org.apache.jasper.runtime.JspSourceDependent;
041:
042: /**
043: * The JSP engine (a.k.a Jasper).
044: *
045: * The servlet container is responsible for providing a
046: * URLClassLoader for the web application context Jasper
047: * is being used in. Jasper will try get the Tomcat
048: * ServletContext attribute for its ServletContext class
049: * loader, if that fails, it uses the parent class loader.
050: * In either case, it must be a URLClassLoader.
051: *
052: * @author Anil K. Vijendran
053: * @author Harish Prabandham
054: * @author Remy Maucherat
055: * @author Kin-man Chung
056: * @author Glenn Nielsen
057: */
058:
059: public class JspServletWrapper {
060:
061: // Logger
062: private static Log log = LogFactory.getLog(JspServletWrapper.class);
063:
064: private Servlet theServlet;
065: private String jspUri;
066: private Class servletClass;
067: private Class tagHandlerClass;
068: private JspCompilationContext ctxt;
069: private long available = 0L;
070: private ServletConfig config;
071: private Options options;
072: private boolean firstTime = true;
073: private boolean reload = true;
074: private boolean isTagFile;
075: private int tripCount;
076: private JasperException compileException;
077: private long servletClassLastModifiedTime;
078:
079: /*
080: * JspServletWrapper for JSP pages.
081: */
082: JspServletWrapper(ServletConfig config, Options options,
083: String jspUri, boolean isErrorPage, JspRuntimeContext rctxt)
084: throws JasperException {
085:
086: this .isTagFile = false;
087: this .config = config;
088: this .options = options;
089: this .jspUri = jspUri;
090: ctxt = new JspCompilationContext(jspUri, isErrorPage, options,
091: config.getServletContext(), this , rctxt);
092: }
093:
094: /*
095: * JspServletWrapper for tag files.
096: */
097: public JspServletWrapper(ServletContext servletContext,
098: Options options, String tagFilePath, TagInfo tagInfo,
099: JspRuntimeContext rctxt, URL tagFileJarUrl)
100: throws JasperException {
101:
102: this .isTagFile = true;
103: this .config = null; // not used
104: this .options = options;
105: this .jspUri = tagFilePath;
106: this .tripCount = 0;
107: ctxt = new JspCompilationContext(jspUri, tagInfo, options,
108: servletContext, this , rctxt, tagFileJarUrl);
109: }
110:
111: public JspCompilationContext getJspEngineContext() {
112: return ctxt;
113: }
114:
115: public void setReload(boolean reload) {
116: this .reload = reload;
117: }
118:
119: public Servlet getServlet() throws ServletException, IOException,
120: FileNotFoundException {
121: if (reload) {
122: synchronized (this ) {
123: // Synchronizing on jsw enables simultaneous loading
124: // of different pages, but not the same page.
125: if (reload) {
126: // This is to maintain the original protocol.
127: destroy();
128:
129: try {
130: servletClass = ctxt.load();
131: theServlet = (Servlet) servletClass
132: .newInstance();
133: } catch (IllegalAccessException ex1) {
134: throw new JasperException(ex1);
135: } catch (InstantiationException ex) {
136: throw new JasperException(ex);
137: }
138:
139: theServlet.init(config);
140:
141: if (!firstTime) {
142: ctxt.getRuntimeContext()
143: .incrementJspReloadCount();
144: }
145:
146: reload = false;
147: }
148: }
149: }
150: return theServlet;
151: }
152:
153: public ServletContext getServletContext() {
154: return config.getServletContext();
155: }
156:
157: /**
158: * Sets the compilation exception for this JspServletWrapper.
159: *
160: * @param je The compilation exception
161: */
162: public void setCompilationException(JasperException je) {
163: this .compileException = je;
164: }
165:
166: /**
167: * Sets the last-modified time of the servlet class file associated with
168: * this JspServletWrapper.
169: *
170: * @param lastModified Last-modified time of servlet class
171: */
172: public void setServletClassLastModifiedTime(long lastModified) {
173: if (this .servletClassLastModifiedTime < lastModified) {
174: synchronized (this ) {
175: if (this .servletClassLastModifiedTime < lastModified) {
176: this .servletClassLastModifiedTime = lastModified;
177: reload = true;
178: }
179: }
180: }
181: }
182:
183: /**
184: * Compile (if needed) and load a tag file
185: */
186: public Class loadTagFile() throws JasperException {
187:
188: try {
189: if (ctxt.isRemoved()) {
190: throw new FileNotFoundException(jspUri);
191: }
192: if (options.getDevelopment() || firstTime) {
193: synchronized (this ) {
194: if (firstTime) {
195: firstTime = false;
196: }
197: ctxt.compile();
198: }
199: } else {
200: if (compileException != null) {
201: throw compileException;
202: }
203: }
204:
205: if (reload) {
206: tagHandlerClass = ctxt.load();
207: }
208: } catch (FileNotFoundException ex) {
209: throw new JasperException(ex);
210: }
211:
212: return tagHandlerClass;
213: }
214:
215: /**
216: * Compile and load a prototype for the Tag file. This is needed
217: * when compiling tag files with circular dependencies. A prototpe
218: * (skeleton) with no dependencies on other other tag files is
219: * generated and compiled.
220: */
221: public Class loadTagFilePrototype() throws JasperException {
222:
223: ctxt.setPrototypeMode(true);
224: try {
225: return loadTagFile();
226: } finally {
227: ctxt.setPrototypeMode(false);
228: }
229: }
230:
231: /**
232: * Get a list of files that the current page has source dependency on.
233: */
234: public java.util.List getDependants() {
235: try {
236: Object target;
237: if (isTagFile) {
238: if (reload) {
239: tagHandlerClass = ctxt.load();
240: }
241: target = tagHandlerClass.newInstance();
242: } else {
243: target = getServlet();
244: }
245: if (target != null && target instanceof JspSourceDependent) {
246: return ((JspSourceDependent) target).getDependants();
247: }
248: } catch (Throwable ex) {
249: }
250: return null;
251: }
252:
253: public boolean isTagFile() {
254: return this .isTagFile;
255: }
256:
257: public int incTripCount() {
258: return tripCount++;
259: }
260:
261: public int decTripCount() {
262: return tripCount--;
263: }
264:
265: public void service(HttpServletRequest request,
266: HttpServletResponse response, 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: response.setDateHeader("Retry-After", available);
276: response.sendError(
277: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
278: Localizer.getMessage("jsp.error.unavailable"));
279: }
280:
281: /*
282: * (1) Compile
283: */
284: if (options.getDevelopment() || firstTime) {
285: synchronized (this ) {
286: if (firstTime) {
287: firstTime = false;
288: }
289: // The following sets reload to true, if necessary
290: ctxt.compile();
291: }
292: } else {
293: if (compileException != null) {
294: // Throw cached compilation exception
295: throw compileException;
296: }
297: }
298:
299: /*
300: * (2) (Re)load servlet class file
301: */
302: getServlet();
303:
304: // If a page is to be precompiled only, return.
305: if (precompile) {
306: return;
307: }
308:
309: /*
310: * (3) Service request
311: */
312: if (theServlet instanceof SingleThreadModel) {
313: // sync on the wrapper so that the freshness
314: // of the page is determined right before servicing
315: synchronized (this ) {
316: theServlet.service(request, response);
317: }
318: } else {
319: theServlet.service(request, response);
320: }
321:
322: } catch (UnavailableException ex) {
323: String includeRequestUri = (String) request
324: .getAttribute("javax.servlet.include.request_uri");
325: if (includeRequestUri != null) {
326: // This file was included. Throw an exception as
327: // a response.sendError() will be ignored by the
328: // servlet engine.
329: throw ex;
330: } else {
331: int unavailableSeconds = ex.getUnavailableSeconds();
332: if (unavailableSeconds <= 0) {
333: unavailableSeconds = 60; // Arbitrary default
334: }
335: available = System.currentTimeMillis()
336: + (unavailableSeconds * 1000L);
337: response.sendError(
338: HttpServletResponse.SC_SERVICE_UNAVAILABLE, ex
339: .getMessage());
340: }
341: } catch (FileNotFoundException ex) {
342: ctxt.incrementRemoved();
343: String includeRequestUri = (String) request
344: .getAttribute("javax.servlet.include.request_uri");
345: if (includeRequestUri != null) {
346: // This file was included. Throw an exception as
347: // a response.sendError() will be ignored by the
348: // servlet engine.
349: throw new ServletException(ex);
350: } else {
351: try {
352: response.sendError(
353: HttpServletResponse.SC_NOT_FOUND, ex
354: .getMessage());
355: } catch (IllegalStateException ise) {
356: log.error(Localizer
357: .getMessage("jsp.error.file.not.found", ex
358: .getMessage()), ex);
359: }
360: }
361: } catch (ServletException ex) {
362: throw ex;
363: } catch (IOException ex) {
364: throw ex;
365: } catch (IllegalStateException ex) {
366: throw ex;
367: } catch (Exception ex) {
368: throw new JasperException(ex);
369: }
370: }
371:
372: public void destroy() {
373: if (theServlet != null) {
374: theServlet.destroy();
375: }
376: }
377:
378: }
|