001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: *
054: */
055:
056: package com.sun.portal.providers.jsp.jasper3.jasper.servlet;
057:
058: import javax.servlet.Servlet;
059: import javax.servlet.ServletContext;
060: import javax.servlet.ServletConfig;
061: import javax.servlet.ServletException;
062: import javax.servlet.SingleThreadModel;
063: import javax.servlet.http.HttpServlet;
064: import javax.servlet.http.HttpServletRequest;
065: import javax.servlet.http.HttpServletResponse;
066: import javax.servlet.jsp.HttpJspPage;
067:
068: // A@ changes for JSPProvider: we now import our own Jspfactory instead of
069: // the one from the JSP spec in order to have our own singleton
070: //import javax.servlet.jsp.JspFactory;
071: import com.sun.portal.providers.jsp.jasper3.jasper.JspFactory;
072:
073: import java.util.Hashtable;
074: import java.util.Enumeration;
075: import java.io.File;
076: import java.io.PrintWriter;
077: import java.io.IOException;
078: import java.io.FileNotFoundException;
079:
080: import com.sun.portal.providers.jsp.jasper3.jasper.JasperException;
081: import com.sun.portal.providers.jsp.jasper3.jasper.Constants;
082: import com.sun.portal.providers.jsp.jasper3.jasper.Options;
083: import com.sun.portal.providers.jsp.jasper3.jasper.EmbededServletOptions;
084: import com.sun.portal.providers.jsp.jasper3.jasper.JspCompilationContext;
085: import com.sun.portal.providers.jsp.jasper3.jasper.JspEngineContext;
086: import com.sun.portal.providers.jsp.jasper3.jasper.runtime.*;
087:
088: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.Compiler;
089: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.JspMangler;
090:
091: import com.sun.portal.providers.jsp.jasper3.tomcat.logging.Logger;
092:
093: /**
094: * The JSP engine (a.k.a Jasper)!
095: *
096: * @author Anil K. Vijendran
097: * @author Harish Prabandham
098: */
099: public class JspServlet extends HttpServlet {
100:
101: class JspServletWrapper {
102: Servlet theServlet = null;
103: String jspUri;
104: boolean isErrorPage;
105: // ServletWrapper will set this
106: Class servletClass;
107: int refCount = 0;
108: boolean markedForDestroy = false;
109: // A volatile on a long guarantees atomic read/write
110: volatile long lastCheckedTime = 0;
111:
112: File jspFile;
113: JasperException compileException = null;
114:
115: JspServletWrapper(String jspUri, String jspFileName,
116: boolean isErrorPage) {
117: this .jspUri = jspUri;
118: this .isErrorPage = isErrorPage;
119: if (!loadPrecompiledJsps) {
120: this .jspFile = new File(jspFileName);
121: }
122: }
123:
124: private boolean servletLoaded() {
125: return (theServlet != null);
126: }
127:
128: private boolean isValid() {
129: return (!markedForDestroy);
130: }
131:
132: private void loadAndInit(JspCompilationContext ctxt)
133: throws JasperException, ServletException {
134: // get the jsp servlet classname using mangler
135: JspMangler mangler = new JspMangler(ctxt.getJspFile(), ctxt
136: .getOutputDir());
137: String fullClassName = mangler.getPackageName() + "."
138: + mangler.getClassName();
139: try {
140: this .servletClass = loader.loadClass(fullClassName);
141: } catch (ClassNotFoundException cex) {
142: Constants.message("jsp.warning.load_failure",
143: new Object[] { fullClassName }, Logger.WARNING);
144: throw new JasperException(Constants
145: .getString("jsp.error.unable.load"), cex);
146: }
147: Constants.message("jsp.debug.load_success",
148: new Object[] { fullClassName }, Logger.DEBUG);
149:
150: // create a new servlet instance and call the init method
151: try {
152: theServlet = (Servlet) servletClass.newInstance();
153: } catch (Exception ex) {
154: throw new JasperException(ex);
155: }
156: theServlet.init(JspServlet.this .config);
157: if (theServlet instanceof HttpJspBase) {
158: HttpJspBase h = (HttpJspBase) theServlet;
159: h.setClassLoader(JspServlet.this .parentClassLoader);
160: }
161: }
162:
163: /* Check if we need to reload a JSP page.
164: *
165: * Side-effect: re-compile the JSP page.
166: *
167: * @param classpath explicitly set the JSP compilation path.
168: * @return true if JSP files is newer
169: */
170: private void loadJSP(JspCompilationContext ctxt)
171: throws JasperException, FileNotFoundException,
172: ServletException {
173: if (loadPrecompiledJsps) {
174: // No compilation required -- try to load the precompiled jsps
175: // from our classpath.
176: if (!servletLoaded()) {
177: try {
178: loadAndInit(ctxt);
179: return;
180: } catch (JasperException e) {
181: }
182: // if load fails, fall-thru and try to load conventional
183: // way.
184: // Allow ServletException to propogate to higher level
185:
186: } else {
187: // JSP already loaded. Dont check for outDated if JSP is
188: // precompiled.
189: Constants.message(
190: "jsp.debug.precompiled.no_reload",
191: Logger.DEBUG);
192: return;
193: }
194: }
195:
196: // if there was a compile exception during an earlier compile and
197: // the jsp file has not been modified, do not try to compile it,
198: // just rethrow the exception generated in the previous compile
199: if (compileException != null && !isJspFileModified())
200: throw compileException;
201:
202: try {
203: lastCheckedTime = System.currentTimeMillis();
204: Compiler compiler = ctxt.createCompiler();
205: compiler.compile();
206: loadAndInit(ctxt);
207: compileException = null;
208: } catch (FileNotFoundException ex) {
209: compileException = null;
210: throw ex;
211: } catch (JasperException ex) {
212: compileException = ex;
213: throw compileException;
214: } catch (Exception ex) {
215: compileException = new JasperException(Constants
216: .getString("jsp.error.unable.compile"), ex);
217: throw compileException;
218: }
219:
220: }
221:
222: boolean isJspFileModified() {
223: boolean res = false;
224: long currTime = System.currentTimeMillis();
225: if (currTime > (reloadInterval + lastCheckedTime)) {
226: long jspLastModTime = jspFile.lastModified();
227:
228: // jsp file does not exist or has been modified
229: if (jspLastModTime == 0L
230: || jspLastModTime > lastCheckedTime)
231: res = true;
232:
233: // update the time the jsp file was checked for being outdated
234: lastCheckedTime = currTime;
235:
236: }
237: return res;
238: }
239:
240: private void service(HttpServletRequest request,
241: HttpServletResponse response) throws ServletException,
242: IOException {
243: if (theServlet instanceof SingleThreadModel) {
244: synchronized (this ) {
245: theServlet.service(request, response);
246: }
247: } else {
248: theServlet.service(request, response);
249: }
250: }
251:
252: private synchronized void incrementRefCount() {
253: refCount++;
254: }
255:
256: private synchronized void decrementRefCount() {
257: refCount--;
258: if ((refCount == 0) && markedForDestroy) {
259: destroy();
260: }
261: }
262:
263: private void tryDestroy() {
264: if (refCount == 0)
265: destroy();
266: markedForDestroy = true;
267: }
268:
269: private void destroy() {
270: if (theServlet != null) {
271: theServlet.destroy();
272:
273: servletClass = null;
274: theServlet = null;
275: }
276: }
277:
278: } // end of JspServletWrapper
279:
280: protected ServletContext context = null;
281: protected Hashtable jsps = new Hashtable();
282: protected ServletConfig config;
283: protected JasperLoader loader;
284: protected Options options;
285: protected ClassLoader parentClassLoader;
286: protected String serverInfo;
287: protected boolean loadPrecompiledJsps = false;
288: // Time in millisecs to check for changes in jsps to force recompilation
289: protected long reloadInterval = 0;
290:
291: static boolean firstTime = true;
292: static boolean jdk12 = false;
293: static {
294: try {
295: Class.forName("java.security.PrivilegedAction");
296: jdk12 = true;
297: } catch (Exception ex) {
298: }
299: }
300:
301: public void init(ServletConfig config) throws ServletException {
302: super .init(config);
303: this .config = config;
304: this .context = config.getServletContext();
305: this .serverInfo = context.getServerInfo();
306: int reloadIntervalInSec = ((Integer) context
307: .getAttribute(Constants.JSP_RELOAD_INTERVAL))
308: .intValue();
309: if (reloadIntervalInSec > 0)
310: this .reloadInterval = reloadIntervalInSec * 1000;
311:
312: options = new EmbededServletOptions(config, context);
313:
314: parentClassLoader = (ClassLoader) context
315: .getAttribute(Constants.SERVLET_CLASS_LOADER);
316: if (parentClassLoader == null)
317: parentClassLoader = this .getClass().getClassLoader();
318:
319: // getClass().getClassLoader() returns null in JDK 1.1.6/1.1.8
320: if (parentClassLoader != null) {
321: Constants.message("jsp.message.parent_class_loader_is",
322: new Object[] { parentClassLoader.toString() },
323: Logger.DEBUG);
324: } else {
325: Constants.message("jsp.message.parent_class_loader_is",
326: new Object[] { "<none>" }, Logger.DEBUG);
327: }
328: if (loader == null) {
329: createJasperLoader();
330: }
331: if (firstTime) {
332: firstTime = false;
333: Constants
334: .message("jsp.message.scratch.dir.is",
335: new Object[] { options.getScratchDir()
336: .toString() }, Logger.INFORMATION);
337: Constants.message("jsp.message.dont.modify.servlets",
338: Logger.INFORMATION);
339: // A@ changes for JSPProvider: use setPsFactory() instead of setDefaultFactory
340: //JspFactory.setDefaultFactory(new JspFactoryImpl());
341: JspFactory.setPsFactory(new JspFactoryImpl());
342: }
343:
344: String usePrecompiled = config
345: .getInitParameter("use-precompiled");
346: if (usePrecompiled != null
347: && usePrecompiled.equalsIgnoreCase("true")) {
348: this .loadPrecompiledJsps = true;
349: Constants.message("jsp.message.precompiled_jsps",
350: Logger.INFORMATION);
351: }
352: }
353:
354: private void createJasperLoader() throws JasperException {
355: if (jdk12) {
356: try {
357: Class ld = Class
358: .forName("com.sun.portal.providers.jsp.jasper3.jasper.servlet.JasperLoader12");
359: loader = (JasperLoader) ld.newInstance();
360: } catch (Exception ex) {
361: throw new JasperException(ex);
362: }
363: }
364: if (loader == null)
365: loader = new com.sun.portal.providers.jsp.jasper3.jasper.servlet.JasperLoader();
366:
367: if (loader != null) {
368: loader.setParentClassLoader(parentClassLoader);
369: loader.setOptions(options);
370: Object pd = context
371: .getAttribute("com.sun.portal.providers.jsp.jasper3.tomcat.protection_domain");
372: loader.setProtectionDomain(pd);
373: }
374: }
375:
376: private void unloadJsps() {
377: Enumeration servlets = jsps.elements();
378: while (servlets.hasMoreElements())
379: ((JspServletWrapper) servlets.nextElement()).tryDestroy();
380: jsps.clear();
381: }
382:
383: // this needs a synchronized as multiple threads realize the need to reload the class-loader
384: // and toss out the jsp servlet objects.
385: private synchronized void reloadJsps() throws JasperException {
386: unloadJsps();
387: createJasperLoader();
388: }
389:
390: private JspServletWrapper getWrapper(String jspUri) {
391: JspServletWrapper wrapper = (JspServletWrapper) jsps
392: .get(jspUri);
393: if (wrapper != null) {
394: wrapper.incrementRefCount();
395: }
396: return wrapper;
397: }
398:
399: private void releaseWrapper(JspServletWrapper wrapper) {
400: if (wrapper != null) {
401: wrapper.decrementRefCount();
402: }
403: }
404:
405: private void serviceJspFile(HttpServletRequest request,
406: HttpServletResponse response, String jspUri,
407: Throwable exception, boolean precompile)
408: throws ServletException, IOException {
409: boolean isErrorPage = exception != null;
410:
411: // First try context attribute; if that fails then use the
412: // classpath init parameter.
413: String classpath = (String) context
414: .getAttribute(Constants.SERVLET_CLASSPATH);
415: String accordingto;
416:
417: if (classpath == null || classpath.equals("")) {
418: accordingto = "according to the init parameter";
419: classpath = options.getClassPath();
420: } else
421: accordingto = "according to the Servlet Engine";
422:
423: Constants.message("jsp.message.cp_is", new Object[] {
424: accordingto, classpath == null ? "" : classpath },
425: Logger.INFORMATION);
426: JspCompilationContext ctxt = new JspEngineContext(loader,
427: classpath, context, jspUri, isErrorPage, options,
428: request, response);
429: JspServletWrapper wrapper = getWrapper(jspUri);
430:
431: boolean error = false;
432: if (wrapper == null) {
433: synchronized (this ) {
434: wrapper = getWrapper(jspUri);
435: if (wrapper == null) {
436: String jspFileName = ctxt.getRealPath(ctxt
437: .getJspFile());
438: if (loadPrecompiledJsps || (jspFileName != null)) {
439: wrapper = new JspServletWrapper(jspUri,
440: jspFileName, isErrorPage);
441: wrapper.incrementRefCount();
442: jsps.put(jspUri, wrapper);
443: } else {
444: try {
445: response
446: .sendError(HttpServletResponse.SC_NOT_FOUND);
447: } catch (IllegalStateException ise) {
448: }
449: Constants.message("jsp.error.file.not.found",
450: new Object[] { ctxt.getJspFile(), },
451: Logger.ERROR);
452: error = true;
453: }
454: }
455: }
456: }
457:
458: if (error) {
459: return;
460: }
461:
462: try {
463: if (!wrapper.servletLoaded()) {
464: synchronized (wrapper) {
465: if (!wrapper.servletLoaded()) {
466: try {
467: wrapper.loadJSP(ctxt);
468: } catch (FileNotFoundException ex) {
469: try {
470: response
471: .sendError(HttpServletResponse.SC_NOT_FOUND);
472: } catch (IllegalStateException ise) {
473: }
474: Constants.jasperLog.log(Constants
475: .getString(
476: "jsp.error.file.not.found",
477: new Object[] { ex
478: .getMessage() }),
479: ex, Logger.ERROR);
480: error = true;
481: }
482: }
483: }
484: } else if (!loadPrecompiledJsps
485: && wrapper.isJspFileModified()) {
486: boolean doReload = false;
487:
488: // junk loader but use the wrapper found for this request
489: if (wrapper.isValid()) {
490: synchronized (wrapper) {
491: if (wrapper.isValid()) {
492: // mark the wrapper for deletion
493: wrapper.tryDestroy();
494:
495: doReload = true;
496: }
497: }
498: }
499:
500: if (doReload) {
501: reloadJsps();
502: }
503: }
504:
505: // Do not execute service if the request was only to precompile JSP
506: // and if there was a load/compile error
507: if (!precompile && !error) {
508: wrapper.service(request, response);
509: }
510: } finally {
511: releaseWrapper(wrapper);
512: wrapper = null;
513: }
514: }
515:
516: boolean preCompile(HttpServletRequest request)
517: throws ServletException {
518: boolean precompile = false;
519: String qString = request.getQueryString();
520:
521: // first check if query string contains jsp_precompile before
522: // doing a getParameter
523: if (qString != null
524: && (qString.startsWith(Constants.PRECOMPILE) || qString
525: .indexOf("&" + Constants.PRECOMPILE) != -1)) {
526: String precom = request.getParameter(Constants.PRECOMPILE);
527: if (precom != null) {
528: if (precom.equals("") || precom.equals("true")
529: || precom.equals("\"true\""))
530: precompile = true;
531: else if (precom.equals("false")
532: || precom.equals("\"false\""))
533: precompile = false;
534: else
535: // This is illegal.
536: throw new ServletException(
537: "Can't have request parameter "
538: + "jsp_precomile set to " + precom);
539: }
540: }
541:
542: return precompile;
543: }
544:
545: public void service(HttpServletRequest request,
546: HttpServletResponse response) throws ServletException,
547: IOException {
548: try {
549: String includeUri = (String) request
550: .getAttribute(Constants.INC_SERVLET_PATH);
551: String jspUri;
552:
553: if (includeUri == null)
554: jspUri = request.getServletPath();
555: else
556: jspUri = includeUri;
557:
558: // System.out.println("JspServletWrapper: " + includeUri + " " + jspUri +
559: // (String) request.getAttribute(Constants.INC_REQUEST_URI));
560:
561: boolean precompile = preCompile(request);
562:
563: Logger jasperLog = Constants.jasperLog;
564:
565: if (jasperLog != null
566: && jasperLog
567: .matchVerbosityLevel(Logger.INFORMATION)) {
568: jasperLog.log("JspEngine --> " + jspUri);
569: jasperLog.log("\t ServletPath: "
570: + request.getServletPath());
571: jasperLog.log("\t PathInfo: "
572: + request.getPathInfo());
573: jasperLog.log("\t RealPath: "
574: + getServletConfig().getServletContext()
575: .getRealPath(jspUri));
576: jasperLog.log("\t RequestURI: "
577: + request.getRequestURI());
578: jasperLog.log("\t QueryString: "
579: + request.getQueryString());
580: }
581: serviceJspFile(request, response, jspUri, null, precompile);
582: } catch (RuntimeException e) {
583: throw e;
584: } catch (ServletException e) {
585: throw e;
586: } catch (IOException e) {
587: throw e;
588: } catch (Exception e) {
589: throw new ServletException(e);
590: }
591:
592: // It's better to throw the exception - we don't
593: // know if we can deal with sendError ( buffer may be
594: // commited )
595: // catch (Throwable t) {
596: // unknownException(response, t);
597: // }
598: }
599:
600: public void destroy() {
601: if (Constants.jasperLog != null)
602: Constants.jasperLog.log("JspServlet.destroy()",
603: Logger.INFORMATION);
604: unloadJsps();
605: loader = null;
606: }
607: }
|