Thanks for the comments Kin-Man. Given those comments and a look at the jasper2 code I have come up with the patch below. After profiling jasper2 I saw that there was indeed a speed improvement over Jasper. However the excessive creation of File objects and getting files from URL's was still a problem. I did as you suggested and used the JspServletWrapper object to hold all of the required data. As well I set up this version to fall back to reading JSP's from a URL if reading the File object doesn't work. I found in my testing that the jasper engine used ~15% of the request time in loadJSP, jasper2 used ~12%, and my change uses ~1%. The only thing my code does not do is force a reload of the .class file if it is deleted. My code only causes a recompile if the jsp changes. I can look into modifying it to handle this situation as well if required. I look forward to your feedback.
Kin-Man Chung wrote: > Duncan, I looked at your patch carefully, looks like Remy's work on > jasper2 already addressed the issues that you are trying to solved. > In fact, Remy only creates a compiler for the container, whereas you > would have created a compiler for each jsp page. OTOH, you save the > modification date for each page in a hashtable, so is potentially > faster when checking whether a jsp needs to be send to the compiler. > I may incoporate your idea on this one later, maybe reusing the > JspServletWrapper class instead of using another hashtabl, since that > **IS** a per page object. > > Thanks again for the patch. If you wish, you should try running with > japser2, and compare its performance with the one you patched. > > > --- JspServlet.java.orig Mon Apr 29 20:50:33 2002 +++ JspServlet.java Mon Apr 29 17:34:28 2002 @@ -129,6 +129,11 @@ JspCompilationContext ctxt = null; String outDir = null; long available = 0L; + + File jspFile = null; + long nLastModified = 0; + boolean bCompiling = false; + boolean bInWar = false; JspServletWrapper(String jspUri, boolean isErrorPage) { this.jspUri = jspUri; @@ -276,6 +281,103 @@ if (theServlet != null) theServlet.destroy(); } + + /** + * This function takes the relative name of a jsp file and tries + * to determine whether it has changed since the last time this + * was called. + */ + private boolean hasJspChanged() { + try { + //get the last time the file was modified and the + //last time it was modified that we compiled + long nMod = getModifiedTime(); + + //if we have not compiled before, or the file has changed. + if (nMod == 0 || (nLastModified != nMod && !isCompiling(nMod))) { + return true; + } + + waitForCompile(); + + return false; + } catch(IOException ioEx) { + //ignore so that the normal logic will kick in. + } + + return true; + } + + /** + * Gets the time that a particular JSP file was last modified. + */ + private long getModifiedTime() + throws IOException { + if (bInWar) { + return nLastModified; + } + + if (jspFile == null) + { + //if is FAR faster to create a file object and get its + //last modified time than to open a URL connection to + //the file and get the last modified time that way. + String strPath = getServletConfig().getServletContext().getRealPath(jspUri); + jspFile = new File(strPath); + } + + try { + //this will return 0 if the file does not exist. + long nRet = jspFile.lastModified(); + + if (nRet != 0){ + return nRet; + } + } catch(Exception ex) { + //ignore and fall back. + } + + try { + URL jspUrl = ctxt.getResource(jspUri); + if (jspUrl != null) + { + bInWar = true; + return jspUrl.openConnection().getLastModified(); + } + } catch (Exception e) { + //ignore + } + + return 0; + } + + /** + * This function determines if the jsp this represents is + * already being compiled. If it is not then the new modification + * time is saved, and false is returned meaning the caller is + * allowed to compile the JSP. + */ + synchronized boolean isCompiling(long nNewModTime) { + if (bCompiling) { + return true; + } + + nLastModified = nNewModTime; + bCompiling = true; + + return false; + } + + void doneCompile() + { + bCompiling = false; + } + + void waitForCompile() + { + while (bCompiling) + Thread.yield(); + } } @@ -594,57 +696,68 @@ jsw.outDir, isErrorPage, options, req, res); } - JspCompilationContext ctxt = jsw.ctxt; - boolean outDated; - Compiler compiler = ctxt.createCompiler(); - - try { - synchronized(jsw) { - // Synchronizing on jsw enables simultaneous compilations of - // different pages, but not the same page. - outDated = compiler.isOutDated(); - if (outDated) - compiler.compile(); - } - } catch (FileNotFoundException ex) { - compiler.removeGeneratedFiles(); - throw ex; - } catch (JasperException ex) { - throw ex; - } catch (Exception ex) { - throw new JasperException(Constants.getString("jsp.error.unable.compile"), - ex); - } + + //this short circuits the need to create a copiler and do a test compile. + if (!jsw.hasJspChanged()) { + return false; + } + + try{ + JspCompilationContext ctxt = jsw.ctxt; + boolean outDated; + Compiler compiler = ctxt.createCompiler(); + + try { + synchronized(jsw) { + // Synchronizing on jsw enables simultaneous compilations of + // different pages, but not the same page. + outDated = compiler.compile(); + } + } catch (FileNotFoundException ex) { + compiler.removeGeneratedFiles(); + throw ex; + } catch (JasperException ex) { + throw ex; + } catch (Exception ex) { + throw new JasperException(Constants.getString("jsp.error.unable.compile"), + ex); + } - // Reload only if it's outdated - if ((jsw.servletClass == null) || outDated) { - try { - synchronized (jsw) { - if (jsw.servletClass == null || outDated) { - URL [] urls = new URL[1]; - File outputDir = new File(normalize(ctxt.getOutputDir())); - urls[0] = outputDir.toURL(); - jsw.loader = new JasperLoader( - urls,ctxt.getServletClassName(), - parentClassLoader, - permissionCollection, - codeSource); - jsw.servletClass = jsw.loader.loadClass( - Constants.JSP_PACKAGE_NAME + "." + - ctxt.getServletClassName()); + // Reload only if it's outdated + if ((jsw.servletClass == null) || outDated) { + try { + synchronized (jsw) { + if (jsw.servletClass == null || outDated) { + URL [] urls = new URL[1]; + File outputDir = new File(normalize(ctxt.getOutputDir())); + urls[0] = outputDir.toURL(); + + jsw.loader = new JasperLoader( + urls,ctxt.getServletClassName(), + parentClassLoader, + permissionCollection, + codeSource); + jsw.servletClass = jsw.loader.loadClass( + Constants.JSP_PACKAGE_NAME + "." + + ctxt.getServletClassName()); + } + } + } catch (ClassNotFoundException cex) { + System.out.println("Caught: " + cex); + throw new JasperException( + Constants.getString("jsp.error.unable.load"),cex); + } catch (MalformedURLException mue) { + System.out.println("Caught: " + mue); + throw new JasperException( + Constants.getString("jsp.error.unable.load"),mue); } + + } + + return outDated; + } finally { + jsw.doneCompile(); } - } catch (ClassNotFoundException cex) { - throw new JasperException( - Constants.getString("jsp.error.unable.load"),cex); - } catch (MalformedURLException mue) { - throw new JasperException( - Constants.getString("jsp.error.unable.load"),mue); - } - - } - - return outDated; } -- Duncan McLean Hummingbird Ltd. 613-548-4355 x1539 http://www.hummingbird.com -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>