I'm new to this list (and the jasper code), so please let me know if I've
submitted this incorrectly.
        I was doing some profiling of the server side environment that I work on
and noticed that quite a bit of time was being used in the JSP engine. After
further investigation I noticed that each JSP request that comes into the jasper
engine generates a new compiler object and attempt at compilation. As much as 15%
of the time of a JSP request is wasted this way.
        The change I made attempts to read the last modified date from the JSP
file being request. A comparison is made against known last modified time
for the current compiled version, if they are different then the code flows
as it used to. If the times are the same then the rest of loadJSP is skipped.
        In my test application which relies on calling ~10 jsp files to generate
a single page I found a speed improvement of ~25-50ms (on a PIII 650).
        There may be better ways to do this. I concentrated my fix here because
it is where the profiler said the problem was. One drawback of the code I'm
submitting is that if there are a large number of JSP's the Hashtable I use to
store information may get large. However I can't see that being a problem until
a site has >10,000 jsp's.
        The change I'm submitting may not be the correct way to approach the
problem. Either way I think this is an issue that deserves some attention so
if someone can suggest a better fix please do.

- * The JSP engine (a.k.a Jasper).
+ * The JSP engine (a.k.a Jasper)!
   *
   * The servlet container is responsible for providing a
   * URLClassLoader for the web application context Jasper
@@ -231,6 +232,32 @@
                theServlet.destroy();
        }
      }
+
+ 
/**
+ 
  * This class stores data on the last time a jsp was modified, and
+ 
  * whether it is currently being compiled.
+ 
  */
+ 
class JspModfificationData {
+ 
        long    nLastModified   = 0;
+ 
        boolean bCompiling              = false;
+
+ 
        /**
+ 
         * 
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;
+ 
        }
+ 
}
        
        
      protected ServletContext context = null;
@@ -242,6 +269,7 @@
      protected String serverInfo;
      private PermissionCollection permissionCollection = null;
      private CodeSource codeSource = null;
+    private Hashtable htModificationData = new Hashtable();

      static boolean firstTime = true;

@@ -473,6 +501,9 @@
              serviceJspFile(request, response, jspUri, null, precompile);
        } catch (RuntimeException e) {
            throw e;
+        } catch (JasperError ex) {
+            response.setContentType("text/html");
+            response.getWriter().print(ex.getMessage());
        } catch (ServletException e) {
            throw e;
        } catch (IOException e) {
@@ -509,86 +540,172 @@
      boolean loadJSP(String jspUri, String classpath,
        boolean isErrorPage, HttpServletRequest req, HttpServletResponse res)
        throws JasperException, FileNotFoundException
-    {
- 
// First check if the requested JSP page exists, to avoid creating
- 
// unnecessary directories and files.
- 
if (context.getResourceAsStream(jspUri) == null)
- 
     throw new FileNotFoundException(jspUri);
-
- 
JspServletWrapper jsw=(JspServletWrapper) jsps.get(jspUri);
- 
if( jsw==null ) {
- 
     throw new JasperException("Can't happen - JspServletWrapper=null");
- 
}
-        File outDir = null;
-        try {
-            URL outURL = options.getScratchDir().toURL();
-            String outURI = outURL.toString();
-            if( outURI.endsWith("/") )
-                outURI = outURI + jspUri.substring(1,jspUri.lastIndexOf("/")+1);
-            else
-                outURI = outURI + jspUri.substring(0,jspUri.lastIndexOf("/")+1);;
-            outURL = new URL(outURI);
-            outDir = new File(outURL.getFile());
-            if( !outDir.exists() ) {
-                outDir.mkdirs();
-            }
-        } catch(Exception e) {
-            throw new JasperException("No output directory: " + e.getMessage());
-        }
- 
boolean firstTime = jsw.servletClass == null;
-        JspCompilationContext ctxt = new JspEngineContext(parentClassLoader, 
classpath,
-                                                     context, jspUri, 
outDir.toString() + File.separator,
-                                                     isErrorPage, options,
-                                                     req, res);
- 
boolean outDated = false;
+ 
{
+ 
        // First check if the requested JSP page exists, to avoid creating
+ 
        // unnecessary directories and files.
+ 
        if (context.getResourceAsStream(jspUri) == null)
+ 
        throw new FileNotFoundException(jspUri);
+
+ 
        JspServletWrapper jsw=(JspServletWrapper) jsps.get(jspUri);
+ 
        if( jsw==null ) {
+ 
                throw new JasperException("Can't happen - JspServletWrapper=null");
+ 
        }

-        Compiler compiler = ctxt.createCompiler();
-
-        try {
-            outDated = compiler.compile();
-            if ( (jsw.servletClass == null) || (compiler.isOutDated()) ) {
-                synchronized ( this ) {
-                    if ((jsw.servletClass == null) ||
- 
                (compiler.isOutDated() ))  {
-                        outDated = compiler.compile();
-                    }
+ 
        //this short circuits the need to create a copiler and do a test compile.
+ 
        if (!hasJspChanged(jspUri))     {
+ 
                JspModfificationData data = getModificationData(jspUri);
+
+ 
                while (data.bCompiling) {
+ 
                        Thread.yield();
+ 
                }
+
+ 
                return false;
+ 
        }
+
+ 
        try {
+ 
                File outDir = null;
+ 
                try {
+ 
                        URL outURL = options.getScratchDir().toURL();
+ 
                        String outURI = outURL.toString();
+ 
                        if( outURI.endsWith("/") )
+ 
                                outURI = outURI + 
jspUri.substring(1,jspUri.lastIndexOf("/")+1);
+ 
                        else
+ 
                                outURI = outURI + 
jspUri.substring(0,jspUri.lastIndexOf("/")+1);;
+ 
        
+ 
                        outURL = new URL(outURI);
+ 
                        outDir = new File(outURL.getFile());
+ 
                        if( !outDir.exists() ) {
+ 
                                outDir.mkdirs();
+ 
                        }
+ 
                } catch(Exception e) {
+ 
                        throw new JasperException("No output directory: " + 
e.getMessage());
+ 
                }
+ 
                
+ 
                boolean firstTime = jsw.servletClass == null;
+ 
                JspCompilationContext ctxt = new JspEngineContext(parentClassLoader, 
classpath,
+ 
                                context, jspUri, outDir.toString() + File.separator,
+ 
                                isErrorPage, options,
+ 
                                req, res);
+ 
                boolean outDated = false;
+
+ 
                Compiler compiler = ctxt.createCompiler();
+
+ 
                try {
+ 
                        outDated = compiler.compile();
+ 
                        if ( (jsw.servletClass == null) || (compiler.isOutDated()) ) {
+ 
                                synchronized ( this ) {
+ 
                                        if ((jsw.servletClass == null) ||
+ 
                                                        (compiler.isOutDated() )) {
+ 
                                                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 {
+ 
                                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) {
+ 
                                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;
+ 
        }
+ 
        finally {
+ 
                getModificationData(jspUri).bCompiling = false;
                }
-            }
-        } 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 {
- 
        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) {
- 
        throw new JasperException(
- 
            Constants.getString("jsp.error.unable.load"),cex);
- 
     } catch (MalformedURLException mue) {
-                throw new JasperException(
- 
            Constants.getString("jsp.error.unable.load"),mue);
- 
     }
- 

+ 
/**
+ 
  * 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.
+ 
  * The previous method involved attempting to compile the jsp
+ 
  * file which was EXTREMELY expensive. On a PIII 650 this method
+ 
  * saves over 50ms per JSP call!
+ 
  */
+ 
private boolean hasJspChanged(String strFile) {
+ 
        try     {
+ 
                //get the last time the file was modified and the
+ 
                //last time it was modified that we compiled
+ 
                long                                    nMod = 
getModifiedTime(strFile);
+ 
                JspModfificationData    data = getModificationData(strFile);
+ 
                
+ 
                //if we have not compiled before, or the file has changed.
+ 
                if (nMod != 0 && data.nLastModified != nMod &&
+ 
                                !data.isCompiling(nMod)) {
+ 
                        return true;
+ 
                }
+
+ 
                return false;
+ 
        } catch(MalformedURLException muEx)     {
+ 
                //ignore so that the normal logic will kick in.
+ 
        } catch(IOException ioEx) {
+ 
                //ignore so that the normal logic will kick in.
+ 
        }
+ 
        
+ 
        return true;
        }
        
- 
return outDated;
-    }
+ 
/**
+ 
  * Grab the copy the mofification data for a particular JSP.
+ 
  **/
+ 
private JspModfificationData getModificationData(String strJsp) {
+ 
        JspModfificationData data = 
(JspModfificationData)htModificationData.get(strJsp);
+ 
        
+ 
        //if there is no data then try to create it.
+ 
        if (data == null) {
+ 
                synchronized(htModificationData) {
+ 
                        data = (JspModfificationData)htModificationData.get(strJsp);
+ 
                        
+ 
                        if (data == null) {
+ 
                                data = new JspModfificationData();
+ 
                                htModificationData.put(strJsp, data);
+ 
                        }
+ 
                }
+ 
        }
+ 
        
+ 
        return data;
+ 
}

+
+ 
/**
+ 
  * Gets the time that a particular JSP file was last modified.
+ 
  */    
+ 
private long getModifiedTime(String strFile)
+ 
        throws MalformedURLException, IOException {
+ 
        //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(strFile);
+ 
        File    file    = new File(strPath);
+
+ 
        //this will return 0 if the file does not exist.
+ 
        return file.lastModified();
+ 
}

      /**
       * Determines whether the current JSP class is older than the JSP file


--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to