001: /*
002: * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
003: * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to license terms.
004: */
005: package com.sun.portal.providers.jsp;
006:
007: import java.io.FileNotFoundException;
008: import java.io.IOException;
009: import java.io.File;
010: import java.io.FileOutputStream;
011: import java.nio.channels.FileChannel;
012: import java.nio.channels.FileLock;
013: import java.util.logging.Logger;
014: import java.util.logging.Level;
015: import java.util.logging.LogRecord;
016:
017: import javax.servlet.ServletException;
018: import javax.servlet.SingleThreadModel;
019:
020: import javax.servlet.http.HttpServletResponse;
021: import javax.servlet.http.HttpServletRequest;
022:
023: import javax.servlet.jsp.HttpJspPage;
024:
025: import com.sun.portal.providers.jsp.jasper3.jasper.EmbededServletOptions;
026: import com.sun.portal.providers.jsp.jasper3.jasper.JspEngineContext;
027: import com.sun.portal.providers.jsp.jasper3.jasper.JasperException;
028:
029: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.JspMangler;
030: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.Mangler;
031: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.SunJavaCompiler;
032: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.JspCompiler;
033: import com.sun.portal.providers.jsp.jasper3.jasper.servlet.JasperLoader;
034:
035: import com.sun.portal.desktop.context.ClassInfoCache;
036: import com.sun.portal.desktop.context.ProviderClassLoader;
037: import com.sun.portal.log.common.PortalLogger;
038:
039: /**
040: * JspServletWrapper is a wrapper for the servlet that is produced
041: * from a JSP file. It is responsible for compiling and loading the
042: * JSP if necessary and calling the init and service methods on the
043: * JSP servlet.
044: */
045: class JspServletWrapper {
046: HttpJspPage theServlet;
047: String jspPath;
048: String uri;
049: JasperLoader loader;
050: EmbededServletOptions options;
051: JspServletEnvironment config;
052: SunJavaCompiler javaCompiler;
053: Mangler mangler;
054: ProviderClassLoader pcl;
055: File jspFile;
056: File classFile;
057: long expireTime = 0;
058: int scanInterval = 0;
059: long loadtime = 0;
060: private static Logger logger = PortalLogger
061: .getLogger(JspServletWrapper.class);
062:
063: JspServletWrapper(String uri, String jspPath, String fullJspPath,
064: String mostSpecificJspPath, JspServletEnvironment cfg,
065: int scanInterval, ProviderClassLoader pcl) {
066: this .uri = uri;
067: this .jspPath = jspPath;
068: this .options = cfg.getOptions();
069: this .config = cfg;
070: this .scanInterval = scanInterval * 1000;
071: this .pcl = pcl;
072:
073: theServlet = null;
074: //mjm -must set the parent and options on this.
075: loader = new JasperLoader();
076: loader.setParentClassLoader(pcl);
077: loader.setOptions(options);
078: javaCompiler = new SunJavaCompiler();
079: mangler = new JspMangler(mostSpecificJspPath, options
080: .getScratchDir().getPath());
081: jspFile = new File(fullJspPath);
082: classFile = new File(mangler.getClassFileName());
083:
084: }
085:
086: private synchronized void loadIfNecessary(HttpServletRequest req,
087: HttpServletResponse res) throws JasperException,
088: ServletException, FileNotFoundException {
089: boolean outDated = false;
090: if (!expired()) {
091: return;
092: }
093: File lockFile = new File(classFile.toString() + ".lck");
094: FileOutputStream fo = null;
095: FileChannel fc = null;
096: FileLock fl = null;
097: try {
098: // A check is done to verify if the JSP needs to be recompiled.
099: // This can be done by making a call to the JspCompiler.isOutdated()
100: // or the JspCompiler.compile() methods. But to rely on the JspCompiler
101: // to do this check will require the JspServletEnviroment, JspCompiler
102: // objects to be created for every call to loadIfNecessary(). To avoid
103: // the above senario we are checking for the last modified time of
104: // the Jsp's class file and the JSP file. This is what is exactly done
105: // by the call to compiler.isOutdated().
106: outDated = classFile.lastModified() < jspFile
107: .lastModified();
108: if (outDated) {
109: boolean createDirs = makeOutputDirectory(classFile
110: .toString());
111: //Do not proceed if you could not create directories.
112: if (!createDirs)
113: throw new Exception(
114: "Could not create jsp compile output directories.");
115: try {
116: fo = new FileOutputStream(lockFile.toString());
117: fc = fo.getChannel();
118: //wait for the lock
119: fl = fc.lock();
120: } catch (IOException e) {
121: if (logger.isLoggable(Level.FINEST)) {
122: LogRecord record = new LogRecord(Level.FINEST,
123: "PSDT_CSPPJ0021");
124: record.setLoggerName(logger.getName());
125: record
126: .setParameters(new Object[] {
127: lockFile.toString(),
128: jspFile.toString() });
129: record.setThrown(e);
130: logger.log(record);
131: }
132: //You must have got 'java.io.IOException: Bad file number';
133: //does not matter, since this happens when another JVM process tries
134: //to open the stream on the same file and get file channel on it.
135: //Anyway, though you did not get the lock, the job is already done.
136: }
137: }
138: //Check again if classfile is outdated; It is possible that when
139: //you waited for lock, other process has compiled the jsp
140: outDated = classFile.lastModified() < jspFile
141: .lastModified();
142:
143: if (outDated && fl != null) { //check if compilation required
144:
145: String optionsClassPath = ClassInfoCache
146: .getProviderClasspath()
147: + File.pathSeparatorChar
148: + options.getClassPath();
149:
150: JspEngineContext ctxt = new JspEngineContext(loader,
151: optionsClassPath, config, jspPath, false,
152: options, req, res);
153: JspCompiler compiler = new JspCompiler(ctxt);
154: compiler.setMangler(mangler);
155:
156: // Creating the directory for output files.
157: // This is not necessary for Desktop6.0, but needed for Forte
158: // tool environment. Apparently, the jsp compiler in the
159: // Forte tool does not create the diretory before compilation.
160: // Until a better solution is available, this is the best
161: // we can do at this moment.
162: // See bug 4679958
163:
164: String javaFileName = mangler.getJavaFileName();
165: int index = javaFileName
166: .lastIndexOf(File.separatorChar);
167:
168: if (index != -1) {
169: String outputDir = javaFileName.substring(0, index);
170:
171: File f = new File(outputDir);
172: if ((f.exists()) == false) {
173: f.mkdirs();
174: }
175: }
176:
177: compiler.setJavaCompiler(javaCompiler);
178: compiler.compile();
179: }
180: } catch (FileNotFoundException ex) {
181: if (logger.isLoggable(Level.SEVERE)) {
182: LogRecord record = new LogRecord(Level.SEVERE,
183: "PSDT_CSPPJ0003");
184: record.setLoggerName(logger.getName());
185: record
186: .setParameters(new Object[] { jspFile
187: .toString() });
188: record.setThrown(ex);
189: logger.log(record);
190: }
191: throw ex;
192: } catch (JasperException ex) {
193: if (logger.isLoggable(Level.SEVERE)) {
194: LogRecord record = new LogRecord(Level.SEVERE,
195: "PSDT_CSPPJ0003");
196: record.setLoggerName(logger.getName());
197: record
198: .setParameters(new Object[] { jspFile
199: .toString() });
200: record.setThrown(ex);
201: logger.log(record);
202: }
203: throw ex;
204: } catch (Exception ex) {
205: if (logger.isLoggable(Level.SEVERE)) {
206: LogRecord record = new LogRecord(Level.SEVERE,
207: "PSDT_CSPPJ0003");
208: record.setLoggerName(logger.getName());
209: record
210: .setParameters(new Object[] { jspFile
211: .toString() });
212: record.setThrown(ex);
213: logger.log(record);
214: }
215: throw new JasperException("Unable to compile JSP", ex);
216: } finally {
217: if (fl != null) {
218: try {
219: fo.close();
220: fl.release(); //release the lock
221: lockFile.delete(); //cleanup
222: } catch (IOException e) {
223: //drop through; you really did not open the stream
224: }
225: }
226: }
227: // if we have a new class file and the servlet is already loaded,
228: // or classfile timestamp is greater than that when it was last loaded
229: // then create a new class loader to reload the class
230: if ((outDated && theServlet != null)
231: || loadtime < classFile.lastModified()) {
232: loader = new JasperLoader();
233: loader.setParentClassLoader(pcl);
234: loader.setOptions(options);
235: destroy(); // this makes theServlet == null
236: }
237:
238: if (theServlet == null) {
239: try {
240: loadtime = classFile.lastModified();
241: String fullClassName = mangler.getPackageName() + "."
242: + mangler.getClassName();
243: Class jspClass = loader.loadClass(fullClassName);
244: theServlet = (HttpJspPage) jspClass.newInstance();
245: } catch (Exception ex) {
246: if (logger.isLoggable(Level.SEVERE)) {
247: LogRecord record = new LogRecord(Level.SEVERE,
248: "PSDT_CSPPJ0022");
249: record.setLoggerName(logger.getName());
250: record.setParameters(new Object[] { jspFile
251: .toString() });
252: record.setThrown(ex);
253: logger.log(record);
254: }
255: throw new JasperException("Unable to load JSP", ex);
256: }
257: theServlet.init(config);
258: }
259: resetExpireTime();
260: }
261:
262: public void service(HttpServletRequest request,
263: HttpServletResponse response) throws ServletException,
264: IOException {
265: try {
266: loadIfNecessary(request, response);
267: if (theServlet instanceof SingleThreadModel) {
268: // sync on the wrapper so that the freshness
269: // of the page is determined right before servicing
270: synchronized (this ) {
271: theServlet.service(request, response);
272: }
273: } else {
274: theServlet.service(request, response);
275: }
276:
277: } catch (FileNotFoundException ex) {
278: if (logger.isLoggable(Level.FINEST)) {
279: LogRecord record = new LogRecord(Level.FINEST,
280: "PSDT_CSPPJ0022");
281: record.setLoggerName(logger.getName());
282: record
283: .setParameters(new Object[] { jspFile
284: .toString() });
285: record.setThrown(ex);
286: logger.log(record);
287: }
288: response.sendError(HttpServletResponse.SC_NOT_FOUND, ex
289: .getMessage());
290: throw new ServletException(ex);
291:
292: } catch (ServletException e) {
293: if (logger.isLoggable(Level.FINEST)) {
294: LogRecord record = new LogRecord(Level.FINEST,
295: "PSDT_CSPPJ0007");
296: record.setLoggerName(logger.getName());
297: record
298: .setParameters(new Object[] { jspFile
299: .toString() });
300: record.setThrown(e);
301: logger.log(record);
302: }
303: response.sendError(HttpServletResponse.SC_NOT_FOUND, e
304: .getMessage());
305: throw e;
306:
307: } catch (Exception e) {
308: if (logger.isLoggable(Level.FINEST)) {
309: LogRecord record = new LogRecord(Level.FINEST,
310: "PSDT_CSPPJ0007");
311: record.setLoggerName(logger.getName());
312: record
313: .setParameters(new Object[] { jspFile
314: .toString() });
315: record.setThrown(e);
316: logger.log(record);
317: }
318: response.sendError(HttpServletResponse.SC_NOT_FOUND, e
319: .getMessage());
320: throw new ServletException(e);
321: }
322: }
323:
324: void setFullJspFile(String fullJspPath) {
325: File currentFullJspPath = new File(fullJspPath);
326: if (!jspFile.equals(currentFullJspPath)) {
327: jspFile = currentFullJspPath;
328:
329: // jsp file has changed, remove the compiled java and class file
330: // to make sure it get re-compiled.
331: File jspJavaFile = new File(mangler.getJavaFileName());
332:
333: jspJavaFile.delete();
334: classFile.delete();
335: }
336: }
337:
338: public void destroy() {
339: if (theServlet != null) {
340: theServlet.destroy();
341: theServlet = null;
342: }
343: }
344:
345: String getURI() {
346: return uri;
347: }
348:
349: boolean expired() {
350: return System.currentTimeMillis() > expireTime;
351: }
352:
353: void resetExpireTime() {
354: expireTime = System.currentTimeMillis() + scanInterval;
355: }
356:
357: ProviderClassLoader getProviderClassLoader() {
358: return pcl;
359: }
360:
361: private boolean makeOutputDirectory(String javaFileName)
362: throws Exception {
363: boolean res = false;
364: int index = javaFileName.lastIndexOf(File.separatorChar);
365: if (index != -1) {
366: String outputDir = javaFileName.substring(0, index);
367: index = outputDir.indexOf(File.separatorChar);
368: //Easier way is to use 'mkdirs()'; But it sometimes returns false
369: //when many threads/processes are trying to do the same.
370: while (index != -1) {
371: File f = new File(outputDir.substring(0, index));
372: if ((res = f.exists()) == false)
373: res = f.mkdir();
374: index = outputDir
375: .indexOf(File.separatorChar, index + 1);
376: }
377: new File(outputDir).mkdir();
378: }
379: return res;
380: }
381: }
|