Author: nbubna
Date: Fri Jul 25 15:49:47 2008
New Revision: 679916

URL: http://svn.apache.org/viewvc?rev=679916&view=rev
Log:
VELOCITY-595 drop synchronization to clear performance bottleneck (thanks to 
Jarkko Viinamaki)

Modified:
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/Resource.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/Resource.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/Resource.java?rev=679916&r1=679915&r2=679916&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/Resource.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/Resource.java
 Fri Jul 25 15:49:47 2008
@@ -85,6 +85,11 @@
     protected Object data = null;
 
     /**
+     *  Resource type (RESOURCE_TEMPLATE or RESOURCE_CONTENT)
+     */
+    protected int type;
+
+    /**
      *  Default constructor
      */
     public Resource()
@@ -267,4 +272,20 @@
     {
         return data;
     }
+    
+    /**
+     * Sets the type of this Resource (RESOURCE_TEMPLATE or RESOURCE_CONTENT)
+     */
+    public void setType(int type)
+    {
+        this.type = type;
+    }
+    
+    /**
+     * @return type code of the Resource
+     */
+    public int getType()
+    {
+        return type;
+    }
 }

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java?rev=679916&r1=679915&r2=679916&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java
 Fri Jul 25 15:49:47 2008
@@ -99,11 +99,11 @@
     public synchronized void initialize(final RuntimeServices rsvc)
         throws Exception
     {
-       if (isInit)
-       {
-           log.error("Re-initialization of ResourceLoader attempted!");
-           return;
-       }
+        if (isInit)
+        {
+            log.error("Re-initialization of ResourceLoader attempted!");
+            return;
+        }
        
         ResourceLoader resourceLoader = null;
 
@@ -259,6 +259,9 @@
      * Gets the named resource. Returned class type corresponds to specified 
type (i.e. <code>Template</code> to <code>
      * RESOURCE_TEMPLATE</code>).
      *
+     * This method is now unsynchronized which requires that ResourceCache
+     * implementations be thread safe (as the default is).
+     *
      * @param  resourceName  The name of the resource to retrieve.
      * @param  resourceType  The type of resource 
(<code>RESOURCE_TEMPLATE</code>, <code>RESOURCE_CONTENT</code>, etc.).
      * @param  encoding  The character encoding to use.
@@ -269,7 +272,7 @@
      * @throws  ParseErrorException  if template cannot be parsed due to 
syntax (or other) error.
      * @throws  Exception  if a problem in parse
      */
-    public synchronized Resource getResource(final String resourceName, final 
int resourceType, final String encoding)
+    public Resource getResource(final String resourceName, final int 
resourceType, final String encoding)
         throws ResourceNotFoundException,
             ParseErrorException,
             Exception
@@ -289,13 +292,29 @@
 
         if (resource != null)
         {
-            /*
-             *  refresh the resource
-             */
-
             try
             {
-                refreshResource(resource, encoding);
+                // avoids additional method call to refreshResource
+                if (resource.requiresChecking())
+                {
+                    /*
+                     * both loadResource() and refreshResource() now return
+                     * a new Resource instance when they are called
+                     * (put in the cache when appropriate) in order to allow
+                     * several threads to parse the same template 
simultaneously.
+                     * It is redundant work and will cause more garbage 
collection but the
+                     * benefit is that it allows concurrent parsing and 
processing
+                     * without race conditions when multiple requests try to
+                     * refresh/load the same template at the same time.
+                     *
+                     * Another alternative is to limit template 
parsing/retrieval
+                     * so that only one thread can parse each template at a 
time
+                     * but that creates a scalability bottleneck.
+                     *
+                     * See VELOCITY-606, VELOCITY-595 and VELOCITY-24
+                     */
+                    resource = refreshResource(resource, encoding);
+                }
             }
             catch (ResourceNotFoundException rnfe)
             {
@@ -316,7 +335,7 @@
             }
             catch (RuntimeException re)
             {
-               throw re;
+                   throw re;
             }
             catch (Exception e)
             {
@@ -330,8 +349,7 @@
             {
                 /*
                  *  it's not in the cache, so load it.
-                 */
-
+                 */    
                 resource = loadResource(resourceName, resourceType, encoding);
 
                 if (resource.getResourceLoader().isCachingOn())
@@ -352,7 +370,7 @@
             }
             catch (RuntimeException re)
             {
-               throw re;
+                   throw re;
             }
             catch (Exception e)
             {
@@ -383,9 +401,7 @@
             Exception
     {
         Resource resource = ResourceFactory.getResource(resourceName, 
resourceType);
-
         resource.setRuntimeServices(rsvc);
-
         resource.setName(resourceName);
         resource.setEncoding(encoding);
 
@@ -475,12 +491,11 @@
      * @throws  ParseErrorException  if template cannot be parsed due to 
syntax (or other) error.
      * @throws  Exception  if a problem in parse
      */
-    protected void refreshResource(final Resource resource, final String 
encoding)
+    protected Resource refreshResource(Resource resource, final String 
encoding)
         throws ResourceNotFoundException,
             ParseErrorException,
             Exception
     {
-
         /*
          * The resource knows whether it needs to be checked
          * or not, and the resource's loader can check to
@@ -489,52 +504,58 @@
          * the input stream and parse it to make a new
          * AST for the resource.
          */
-        if (resource.requiresChecking())
+            
+        /*
+         *  touch() the resource to reset the counters
+         */
+        resource.touch();
+
+        if (resource.isSourceModified())
         {
             /*
-             *  touch() the resource to reset the counters
+             *  now check encoding info.  It's possible that the newly declared
+             *  encoding is different than the encoding already in the resource
+             *  this strikes me as bad...
              */
 
-            resource.touch();
-
-            if (resource.isSourceModified())
+            if 
(!org.apache.commons.lang.StringUtils.equals(resource.getEncoding(), encoding))
             {
-                /*
-                 *  now check encoding info.  It's possible that the newly 
declared
-                 *  encoding is different than the encoding already in the 
resource
-                 *  this strikes me as bad...
-                 */
-
-                if 
(!org.apache.commons.lang.StringUtils.equals(resource.getEncoding(), encoding))
-                {
-                    log.warn("Declared encoding for template '" +
+                log.warn("Declared encoding for template '" +
                              resource.getName() +
                              "' is different on reload. Old = '" +
                              resource.getEncoding() + "' New = '" + encoding);
 
-                    resource.setEncoding(encoding);
-                }
-
-                /*
-                 *  read how old the resource is _before_
-                 *  processing (=>reading) it
-                 */
-                long howOldItWas = 
resource.getResourceLoader().getLastModified(resource);
+                resource.setEncoding(encoding);
+            }
 
-                /*
-                 *  read in the fresh stream and parse
-                 */
+            /*
+             *  read how old the resource is _before_
+             *  processing (=>reading) it
+             */
+            long howOldItWas = 
resource.getResourceLoader().getLastModified(resource);
 
-                resource.process();
+            String resourceKey = resource.getType() + resource.getName();
 
-                /*
-                 *  now set the modification info and reset
-                 *  the modification check counters
-                 */
+            /* 
+             * we create a copy to avoid partially overwriting a
+             * template which may be in use in another thread
+             */ 
+   
+            Resource newResource = 
+                ResourceFactory.getResource(resource.getName(), 
resource.getType());
+
+            newResource.setRuntimeServices(rsvc);
+            newResource.setName(resource.getName());
+            newResource.setEncoding(resource.getEncoding());
+            newResource.setResourceLoader(resource.getResourceLoader());
+
+            newResource.process();
+            newResource.setLastModified(howOldItWas);
+            resource = newResource;
 
-                resource.setLastModified(howOldItWas);
-            }
+            globalCache.put(resourceKey, newResource);
         }
+        return resource;
     }
 
     /**


Reply via email to