Added support for 'autoreload' feature for global VM libraries
PR:
Obtained from:
Submitted by:
Reviewed by:


git-svn-id: https://svn.apache.org/repos/asf/jakarta/velocity/trunk@75198 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/velocity/runtime/RuntimeConstants.java b/src/java/org/apache/velocity/runtime/RuntimeConstants.java
index dfcf4f2..d4c35ab 100644
--- a/src/java/org/apache/velocity/runtime/RuntimeConstants.java
+++ b/src/java/org/apache/velocity/runtime/RuntimeConstants.java
@@ -62,7 +62,7 @@
  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
- * @version $Id: RuntimeConstants.java,v 1.26 2001/05/20 21:40:40 geirm Exp $
+ * @version $Id: RuntimeConstants.java,v 1.27 2001/08/13 13:58:34 geirm Exp $
  */
 public interface RuntimeConstants
 {
@@ -295,6 +295,12 @@
     public static final String VM_LIBRARY  = "velocimacro.library";
 
     /** 
+     * switch for autoloading library-sourced VMs (for development) 
+     */
+    public final static String VM_LIBRARY_AUTORELOAD = 
+        "velocimacro.library.autoreload";
+
+    /** 
      * boolean (true/false) default true : allow 
      * inline (in-template) macro definitions 
      */
@@ -324,7 +330,7 @@
     */
     public final static String VM_CONTEXT_LOCALSCOPE = 
         "velocimacro.context.localscope";
-
+    
     /*
      * ----------------------------------------------------------------------
      * G E N E R A L  R U N T I M E  C O N F I G U R A T I O N
diff --git a/src/java/org/apache/velocity/runtime/VelocimacroFactory.java b/src/java/org/apache/velocity/runtime/VelocimacroFactory.java
index fcc1a41..5ceede8 100644
--- a/src/java/org/apache/velocity/runtime/VelocimacroFactory.java
+++ b/src/java/org/apache/velocity/runtime/VelocimacroFactory.java
@@ -60,6 +60,8 @@
 import org.apache.velocity.runtime.parser.node.SimpleNode;
 
 import java.util.Vector;
+import java.util.Map;
+import java.util.HashMap;
 
 /**
  *  VelocimacroFactory.java
@@ -67,27 +69,82 @@
  *   manages the set of VMs in a running Velocity engine.
  *
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
- * @version $Id: VelocimacroFactory.java,v 1.14 2001/08/07 22:06:49 geirm Exp $ 
+ * @version $Id: VelocimacroFactory.java,v 1.15 2001/08/13 13:58:34 geirm Exp $ 
  */
 public class VelocimacroFactory
 {
+    /**
+     *  runtime services for this instance
+     */
     private RuntimeServices rsvc = null;
 
+    /**
+     *  VMManager : deal with namespace management
+     *  and actually keeps all the VM definitions
+     */
     private VelocimacroManager vmManager = null;
 
+    /**
+     *  determines if replacement of global VMs are allowed
+     *  controlled by  VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL
+     */
     private boolean replaceAllowed = false;
+
+    /**
+     *  controls if new VMs can be added.  Set by
+     *  VM_PERM_ALLOW_INLINE  Note the assumption that only
+     *  through inline defs can this happen.
+     *  additions through autoloaded VMs is allowed
+     */
     private boolean addNewAllowed = true;
+
+    /**
+     *  sets if template-local namespace in used
+     */
     private boolean templateLocal = false;
+
+    /**
+     *  controls log output
+     */
     private boolean blather = false;
 
+    /**
+     *  determines if the libraries are auto-loaded
+     *  when they change
+     */
+    private boolean autoReloadLibrary = false;
+
+    /**
+     *  vector of the library names
+     */
+    private Vector macroLibVec = null;
+
+    /**
+     *  map of the library Template objects
+     *  used for reload determination
+     */
+    private Map libModMap;
+
+    /**
+     *  CTOR : requires a runtime services from now
+     *  on
+     */
     public VelocimacroFactory( RuntimeServices rs )
     {
         this.rsvc = rs;
+
+        /*
+         *  we always access in a synchronized(), so we 
+         *  can use an unsynchronized hashmap
+         */
+        libModMap = new HashMap();
+
         vmManager = new VelocimacroManager( rsvc );
     }
 
     /**
-     *    setup
+     *  initialize the factory - setup all permissions
+     *  load all global libraries.
      */
     public void initVelocimacro()
     {
@@ -119,21 +176,20 @@
              Object libfiles = rsvc.getProperty( RuntimeConstants.VM_LIBRARY );
            
              if( libfiles != null)
-             {
-                 Vector v = new Vector();
-          
+             {         
                  if (libfiles instanceof Vector)
                  {
-                     v = (Vector) libfiles;
+                     macroLibVec = (Vector) libfiles;
                  }
                  else if (libfiles instanceof String)
                  { 
-                     v.addElement( libfiles );
+                     macroLibVec = new Vector();
+                     macroLibVec.addElement( libfiles );
                  }
                  
-                 for( int i = 0; i < v.size(); i++)
+                 for( int i = 0; i < macroLibVec.size(); i++)
                  {
-                     String lib = (String) v.elementAt(i);
+                     String lib = (String) macroLibVec.elementAt(i);
                  
                      /*
                       * only if it's a non-empty string do we bother
@@ -141,21 +197,38 @@
 
                      if (lib != null && !lib.equals(""))
                      {
+                         /*
+                          *  let the VMManager know that the following is coming
+                          *  from libraries - need to know for auto-load
+                          */
+
+                         vmManager.setRegisterFromLib( true );
+
+                         logVMMessageInfo("Velocimacro : adding VMs from " +
+                             "VM library template : " + lib  );
+
                          try 
                          {
-                             logVMMessageInfo("Velocimacro : adding VMs from " +
-                                              "VM library template : " + lib  );
-                             
-                             Template template = rsvc.getTemplate( lib );   
-                             
-                             logVMMessageInfo("Velocimacro :  VM library template " +
-                                              "macro registration complete." );
+                             Template template = rsvc.getTemplate( lib );
+
+                             /*
+                              *  save the template.  This depends on the assumption
+                              *  that the Template object won't change - currently
+                              *  this is how the Resource manager works
+                              */
+
+                             libModMap.put( lib, template );
                          } 
                          catch (Exception e)
                          {
                              logVMMessageInfo("Velocimacro : error using  VM " +
                                               "library template " + lib + " : " + e );
                          }
+
+                         logVMMessageInfo("Velocimacro :  VM library template " +
+                                 "macro registration complete." );
+            
+                         vmManager.setRegisterFromLib( false );
                      }
                  }
              }
@@ -244,7 +317,23 @@
             }
             else
             {
-                rsvc.info("Velocimacro : messages off : VM system will be quiet");
+                logVMMessageInfo("Velocimacro : messages off : VM system will be quiet");
+            }
+
+            /*
+             *  autoload VM libraries
+             */
+            setAutoload( rsvc.getBoolean( RuntimeConstants.VM_LIBRARY_AUTORELOAD, false ));
+        
+            if (getAutoload())
+            {
+                logVMMessageInfo("Velocimacro : autoload on  : VM system " +
+                                 "will automatically reload global library macros");
+            }
+            else
+            {
+                logVMMessageInfo("Velocimacro : autoload off  : VM system " +
+                                 "will not automatically reload global library macros");
             }
 
             rsvc.info("Velocimacro : initialization complete.");
@@ -275,6 +364,79 @@
         }
         
         /*
+         *  see if the current ruleset allows this addition
+         */
+
+        if (!canAddVelocimacro( name, sourceTemplate ))
+        {
+            return false;
+        }
+
+        /*
+         *  seems like all is good.  Lets do it.
+         */
+        synchronized( this ) 
+        {
+            vmManager.addVM( name, macroBody, argArray, sourceTemplate );
+        }
+
+        /*
+         *  if we are to blather, blather...
+         */
+        if ( blather)
+        {
+            String s = "#" +  argArray[0];
+            s += "(";
+        
+            for( int i=1; i < argArray.length; i++)
+            {
+                s += " ";
+                s += argArray[i];
+            }
+
+            s += " ) : source = ";
+            s += sourceTemplate;
+            
+           logVMMessageInfo( "Velocimacro : added new VM : " + s );
+        }
+
+        return true;
+    }
+
+    /**
+     *  determines if a given macro/namespace (name, source) combo is allowed
+     *  to be added
+     *
+     *  @param name Name of VM to add
+     *  @param sourceTemplate Source template that contains the defintion of the VM
+     *  @return true if it is allowed to be added, false otherwise
+     */
+    private boolean canAddVelocimacro( String name, String sourceTemplate)
+    {
+        /*
+         *  short circuit and do it if autoloader is on, and the
+         *  template is one of the library templates
+         */
+        
+        if ( getAutoload() )
+        {
+            /*
+             *  see if this is a library template
+             */
+
+            for( int i = 0; i < macroLibVec.size(); i++)
+            {
+                String lib = (String) macroLibVec.elementAt(i);
+
+                if (lib.equals( sourceTemplate ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+           
+        /*
          * maybe the rules should be in manager?  I dunno. It's to manage 
          * the namespace issues first, are we allowed to add VMs at all? 
          * This trumps all.
@@ -307,34 +469,7 @@
                 return false;
             }
         }
-
-        /*
-         *  seems like all is good.  Lets do it.
-         */
-        synchronized( this ) 
-        {
-            vmManager.addVM( name, macroBody, argArray, sourceTemplate );
-        }
-
-        /*
-         *  if we are to blather, blather...
-         */
-        if ( blather)
-        {
-            String s = "#" +  argArray[0];
-            s += "(";
         
-            for( int i=1; i < argArray.length; i++)
-                {
-                    s += " ";
-                    s += argArray[i];
-                }
-            s += " ) : source = ";
-            s += sourceTemplate;
-            
-           logVMMessageInfo( "Velocimacro : added new VM : " + s );
-        }
-
         return true;
     }
 
@@ -380,18 +515,78 @@
      */
     public Directive getVelocimacro( String vmName, String sourceTemplate )
     {
+        VelocimacroProxy vp = null;
+
         synchronized( this ) 
         {
-            if ( isVelocimacro( vmName, sourceTemplate ) ) 
+            /*
+             *  don't ask - do
+             */
+
+            vp = vmManager.get( vmName, sourceTemplate);
+
+            /*
+             *  if this exists, and autoload is on, we need to check
+             *  where this VM came from
+             */
+
+            if ( vp != null && getAutoload() ) 
             {    
-                return  vmManager.get( vmName, sourceTemplate );
+                /*
+                 *  see if this VM came from a library.  Need to pass sourceTemplate
+                 *  in the event namespaces are set, as it could be masked by local
+                 */
+                
+                String lib = vmManager.getLibraryName( vmName, sourceTemplate );
+
+                if (lib != null)
+                {
+                    try 
+                    {
+                        /*
+                         *  get the template from our map
+                         */
+
+                        Template template = (Template) libModMap.get(lib );
+
+                        if (template != null)
+                        {
+                            /*
+                             *  now, compare the last modified time of the resource
+                             *  with the last modified time of the template
+                             *  if the file has changed, then reload. Otherwise, we should
+                             *  be ok.
+                             */
+
+                            long tt = template.getLastModified();
+                            long ft = template.getResourceLoader().getLastModified( template );
+
+                            if ( ft > tt )
+                            {
+                                logVMMessageInfo("Velocimacro : autoload reload for VMs from " +
+                                                 "VM library template : " + lib  );
+
+                                template = rsvc.getTemplate( lib );
+                                libModMap.put( lib, template );
+                            }
+                         } 
+                    }
+                    catch (Exception e)
+                    {
+                        logVMMessageInfo("Velocimacro : error using  VM " +
+                                         "library template " + lib + " : " + e );
+                    }
+
+                    /*
+                     *  and get again
+                     */
+
+                    vp = vmManager.get( vmName, sourceTemplate);
+                }
             }
         }
-
-        /*
-         *  wasn't a VM.  Sorry...
-         */
-        return null;
+        
+        return vp;
     }
 
     /**
@@ -455,6 +650,25 @@
     {
         return blather;
     }
+
+    /**
+     *  set the switch for automatic reloading of
+     *  global library-based VMs
+     */
+    private void setAutoload( boolean b)
+    {
+        autoReloadLibrary = b;
+    }
+    
+    /**
+     *  get the switch for automatic reloading of
+     *  global library-based VMs
+     */
+    private boolean getAutoload()
+    {
+        return autoReloadLibrary;
+    }
+
 }
 
 
diff --git a/src/java/org/apache/velocity/runtime/VelocimacroManager.java b/src/java/org/apache/velocity/runtime/VelocimacroManager.java
index 72e5b36..ffb2caa 100644
--- a/src/java/org/apache/velocity/runtime/VelocimacroManager.java
+++ b/src/java/org/apache/velocity/runtime/VelocimacroManager.java
@@ -83,14 +83,15 @@
  *
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  * @author <a href="mailto:JFernandez@viquity.com">Jose Alberto Fernandez</a>
- * @version $Id: VelocimacroManager.java,v 1.12 2001/08/07 22:06:49 geirm Exp $ 
+ * @version $Id: VelocimacroManager.java,v 1.13 2001/08/13 13:58:35 geirm Exp $ 
  */
 public class VelocimacroManager
 {
     private RuntimeServices rsvc = null;
-
     private static String GLOBAL_NAMESPACE = "";
 
+    private boolean registerFromLib = false;
+
     /*  hash of namespace hashes */
     private Hashtable namespaceHash = new Hashtable();
 
@@ -123,6 +124,8 @@
     {
         MacroEntry me = new MacroEntry( this, vmName,  macroBody,  argArray,  namespace );
 
+        me.setFromLibrary(registerFromLib);
+    
         if ( usingNamespaces( namespace ) )
         {
             /*
@@ -138,7 +141,19 @@
         else
         {
             /*
-             * otherwise, add to global template
+             *  otherwise, add to global template.  First, check if we already have it
+             *  to preserve some of the autoload information
+             */
+
+            MacroEntry exist = (MacroEntry) (getNamespace( GLOBAL_NAMESPACE )).get( vmName );
+            
+            if (exist != null)
+            {
+                me.setFromLibrary( exist.getFromLibrary());
+            }
+
+            /*
+             *  now add it
              */
 
             (getNamespace( GLOBAL_NAMESPACE )).put( vmName, me );
@@ -227,6 +242,12 @@
         return;
     }
 
+    public void setRegisterFromLib( boolean b )
+    {
+        registerFromLib = b;
+        return;
+    }
+
     public void setTemplateLocalInlineVM( boolean b )
     {
         inlineLocalMode = b;
@@ -318,6 +339,45 @@
         return false;
     }
 
+    public String getLibraryName( String vmName, String namespace )
+    {
+        if ( usingNamespaces( namespace ) )
+        {
+            Hashtable local =  getNamespace( namespace, false );
+         
+            /*
+             *  if we have this macro defined in this namespace, then
+             *  it is masking the global, library-based one, so 
+             *  just return null
+             */
+
+            if ( local != null)
+            {
+                MacroEntry me = (MacroEntry) local.get( vmName );
+               
+                if (me != null)
+                {
+                    return null;
+                }
+            }
+        }
+
+        /*
+         * if we didn't return from there, we need to simply see 
+         * if it's in the global namespace
+         */
+        
+        MacroEntry me = (MacroEntry) (getNamespace( GLOBAL_NAMESPACE )).get( vmName );
+        
+        if (me != null)
+        {
+            return me.getSourceTemplate();
+        }
+ 
+        return null;
+    }
+
+
     /**
      *  wrapper class for holding VM information
      */
@@ -329,6 +389,7 @@
         String sourcetemplate;
         SimpleNode nodeTree = null;
         VelocimacroManager manager = null;
+        boolean fromLibrary = false;
 
         MacroEntry(VelocimacroManager vmm, String vmName, String macroBody, String argArray[],  String sourceTemplate)
         {
@@ -339,11 +400,26 @@
             this.manager = vmm;
         }
 
+        public void setFromLibrary( boolean b )
+        {
+            fromLibrary = b;
+        }
+        
+        public boolean getFromLibrary()
+        {
+            return fromLibrary;
+        }
+
         public SimpleNode getNodeTree()
         {
             return nodeTree;
         }
 
+        public String getSourceTemplate()
+        {
+            return sourcetemplate;
+        }
+
         VelocimacroProxy createVelocimacro( String namespace )
         {
             VelocimacroProxy vp = new VelocimacroProxy();