« Return to Thread: Expanding enviornmental variables in properties files

Re: Expanding enviornmental variables in properties files

by csanders :: Rate this Message:

Reply to Author | View in Thread

And here is the patch :) .

csanders wrote:

> Attached is a patch for RuntimeInstance.java that adds two methods,
> private String replaceEnviornmentalVariable(String string) , private
> void replaceEnviormentalVariables(ExtendedProperties properties) ...
> and changes initializeProperties and setProperty() to replace them as
> they come in.
>
> Can someone review it and let me know what it needs to be included in
> Velocity ?
>
> Thanks!
> Charles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@...
> For additional commands, e-mail: dev-help@...
>

Index: /home/csanders/workspace/Velocity/src/java/org/apache/velocity/runtime/RuntimeInstance.java
===================================================================
--- /home/csanders/workspace/Velocity/src/java/org/apache/velocity/runtime/RuntimeInstance.java (revision 653607)
+++ /home/csanders/workspace/Velocity/src/java/org/apache/velocity/runtime/RuntimeInstance.java (working copy)
@@ -29,6 +29,7 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Properties;
 
@@ -64,23 +65,15 @@
 import org.apache.velocity.util.introspection.UberspectLoggable;
 
 /**
- * This is the Runtime system for Velocity. It is the
- * single access point for all functionality in Velocity.
- * It adheres to the mediator pattern and is the only
- * structure that developers need to be familiar with
- * in order to get Velocity to perform.
- *
- * The Runtime will also cooperate with external
- * systems like Turbine. Runtime properties can
- * set and then the Runtime is initialized.
- *
- * Turbine, for example, knows where the templates
- * are to be loaded from, and where the Velocity
- * log file should be placed.
- *
- * So in the case of Velocity cooperating with Turbine
- * the code might look something like the following:
- *
+ * This is the Runtime system for Velocity. It is the single access point for all functionality in Velocity. It adheres to the mediator pattern and is the only
+ * structure that developers need to be familiar with in order to get Velocity to perform.
+ *
+ * The Runtime will also cooperate with external systems like Turbine. Runtime properties can set and then the Runtime is initialized.
+ *
+ * Turbine, for example, knows where the templates are to be loaded from, and where the Velocity log file should be placed.
+ *
+ * So in the case of Velocity cooperating with Turbine the code might look something like the following:
+ *
  * <blockquote><code><pre>
  * ri.setProperty(Runtime.FILE_RESOURCE_LOADER_PATH, templatePath);
  * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog);
@@ -86,7 +79,7 @@
  * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog);
  * ri.init();
  * </pre></code></blockquote>
- *
+ *
  * <pre>
  * -----------------------------------------------------------------------
  * N O T E S  O N  R U N T I M E  I N I T I A L I Z A T I O N
@@ -92,7 +85,6 @@
  * N O T E S  O N  R U N T I M E  I N I T I A L I Z A T I O N
  * -----------------------------------------------------------------------
  * init()
- *
  * If init() is called by itself the RuntimeInstance will initialize
  * with a set of default values.
  * -----------------------------------------------------------------------
@@ -97,7 +89,6 @@
  * with a set of default values.
  * -----------------------------------------------------------------------
  * init(String/Properties)
- *
  * In this case the default velocity properties are layed down
  * first to provide a solid base, then any properties provided
  * in the given properties object will override the corresponding
@@ -104,7 +95,7 @@
  * default property.
  * -----------------------------------------------------------------------
  * </pre>
- *
+ *
  * @author <a href="mailto:jvanzyl@...">Jason van Zyl</a>
  * @author <a href="mailto:jlb@...">Jeff Bowden</a>
  * @author <a href="mailto:geirm@...">Geir Magusson Jr.</a>
@@ -110,1585 +101,1446 @@
  * @author <a href="mailto:geirm@...">Geir Magusson Jr.</a>
  * @version $Id$
  */
-public class RuntimeInstance implements RuntimeConstants, RuntimeServices
-{
-    /**
-     *  VelocimacroFactory object to manage VMs
-     */
-    private  VelocimacroFactory vmFactory = null;
+public class RuntimeInstance implements RuntimeConstants, RuntimeServices {
+ /**
+ * VelocimacroFactory object to manage VMs
+ */
+ private VelocimacroFactory vmFactory = null;
 
-    /**
-     * The Runtime logger.  We start with an instance of
-     * a 'primordial logger', which just collects log messages
-     * then, when the log system is initialized, all the
-     * messages get dumpted out of the primordial one into the real one.
-     */
-    private Log log = new Log();
+ /**
+ * The Runtime logger. We start with an instance of a 'primordial logger', which just collects log messages then, when the log system is initialized, all
+ * the messages get dumpted out of the primordial one into the real one.
+ */
+ private Log log = new Log();
 
-    /**
-     * The Runtime parser pool
-     */
-    private  ParserPool parserPool;
+ /**
+ * The Runtime parser pool
+ */
+ private ParserPool parserPool;
 
-    /**
-     * Indicate whether the Runtime is in the midst of initialization.
-     */
-    private boolean initializing = false;
+ /**
+ * Indicate whether the Runtime is in the midst of initialization.
+ */
+ private boolean initializing = false;
 
-    /**
-     * Indicate whether the Runtime has been fully initialized.
-     */
-    private boolean initialized = false;
+ /**
+ * Indicate whether the Runtime has been fully initialized.
+ */
+ private boolean initialized = false;
 
-    /**
-     * These are the properties that are laid down over top
-     * of the default properties when requested.
-     */
-    private  ExtendedProperties overridingProperties = null;
+ /**
+ * These are the properties that are laid down over top of the default properties when requested.
+ */
+ private ExtendedProperties overridingProperties = null;
 
-    /**
-     * This is a hashtable of initialized directives.
-     * The directives that populate this hashtable are
-     * taken from the RUNTIME_DEFAULT_DIRECTIVES
-     * property file. This hashtable is passed
-     * to each parser that is created.
-     */
-    private Hashtable runtimeDirectives;
+ /**
+ * This is a hashtable of initialized directives. The directives that populate this hashtable are taken from the RUNTIME_DEFAULT_DIRECTIVES property file.
+ * This hashtable is passed to each parser that is created.
+ */
+ private Hashtable runtimeDirectives;
 
-    /**
-     * Object that houses the configuration options for
-     * the velocity runtime. The ExtendedProperties object allows
-     * the convenient retrieval of a subset of properties.
-     * For example all the properties for a resource loader
-     * can be retrieved from the main ExtendedProperties object
-     * using something like the following:
-     *
-     * ExtendedProperties loaderConfiguration =
-     *         configuration.subset(loaderID);
-     *
-     * And a configuration is a lot more convenient to deal
-     * with then conventional properties objects, or Maps.
-     */
-    private  ExtendedProperties configuration = new ExtendedProperties();
+ /**
+ * Object that houses the configuration options for the velocity runtime. The ExtendedProperties object allows the convenient retrieval of a subset of
+ * properties. For example all the properties for a resource loader can be retrieved from the main ExtendedProperties object using something like the
+ * following:
+ *
+ * ExtendedProperties loaderConfiguration = configuration.subset(loaderID);
+ *
+ * And a configuration is a lot more convenient to deal with then conventional properties objects, or Maps.
+ */
+ private ExtendedProperties configuration = new ExtendedProperties();
 
-    private ResourceManager resourceManager = null;
+ private ResourceManager resourceManager = null;
 
-    /**
-     * This stores the engine-wide set of event handlers.  Event handlers for
-     * each specific merge are stored in the context.
-     */
-    private EventCartridge eventCartridge = null;
+ /**
+ * This stores the engine-wide set of event handlers. Event handlers for each specific merge are stored in the context.
+ */
+ private EventCartridge eventCartridge = null;
 
-    /*
-     *  Each runtime instance has it's own introspector
-     *  to ensure that each instance is completely separate.
-     */
-    private Introspector introspector = null;
+ /*
+ * Each runtime instance has it's own introspector to ensure that each instance is completely separate.
+ */
+ private Introspector introspector = null;
 
+ /*
+ * Opaque reference to something specificed by the application for use in application supplied/specified pluggable components
+ */
+ private Map applicationAttributes = null;
 
-    /*
-     *  Opaque reference to something specificed by the
-     *  application for use in application supplied/specified
-     *  pluggable components
-     */
-    private Map applicationAttributes = null;
+ private Uberspect uberSpect;
 
+ /**
+ * Creates a new RuntimeInstance object.
+ */
+ public RuntimeInstance() {
+ /*
+ * create a VM factory, introspector, and application attributes
+ */
+ vmFactory = new VelocimacroFactory(this);
 
-    private Uberspect uberSpect;
+ /*
+ * make a new introspector and initialize it
+ */
+ introspector = new Introspector(getLog());
 
-    /**
-     * Creates a new RuntimeInstance object.
-     */
-    public RuntimeInstance()
-    {
-        /*
-         *  create a VM factory, introspector, and application attributes
-         */
-        vmFactory = new VelocimacroFactory( this );
+ /*
+ * and a store for the application attributes
+ */
+ applicationAttributes = new HashMap();
+ }
 
-        /*
-         *  make a new introspector and initialize it
-         */
-        introspector = new Introspector(getLog());
+ /**
+ * This is the primary initialization method in the Velocity Runtime. The systems that are setup/initialized here are as follows:
+ *
+ * <ul>
+ * <li>Logging System</li>
+ * <li>ResourceManager</li>
+ * <li>EventHandler</li>
+ * <li>Parser Pool</li>
+ * <li>Global Cache</li>
+ * <li>Static Content Include System</li>
+ * <li>Velocimacro System</li>
+ * </ul>
+ *
+ * @throws Exception
+ *             When an error occured during initialization.
+ */
+ public synchronized void init() throws Exception {
+ if (!initialized && !initializing) {
+ initializing = true;
 
-        /*
-         * and a store for the application attributes
-         */
-        applicationAttributes = new HashMap();
-    }
+ log.trace("*******************************************************************");
+ log.debug("Starting Apache Velocity v@...@ (compiled: @build.time@)");
+ log.trace("RuntimeInstance initializing.");
 
-    /**
-     * This is the primary initialization method in the Velocity
-     * Runtime. The systems that are setup/initialized here are
-     * as follows:
-     *
-     * <ul>
-     *   <li>Logging System</li>
-     *   <li>ResourceManager</li>
-     *   <li>EventHandler</li>
-     *   <li>Parser Pool</li>
-     *   <li>Global Cache</li>
-     *   <li>Static Content Include System</li>
-     *   <li>Velocimacro System</li>
-     * </ul>
-     * @throws Exception When an error occured during initialization.
-     */
-    public synchronized void init()
-        throws Exception
-    {
-        if (!initialized && !initializing)
-        {
-            initializing = true;
+ initializeProperties();
+ initializeLog();
+ initializeResourceManager();
+ initializeDirectives();
+ initializeEventHandlers();
+ initializeParserPool();
 
-            log.trace("*******************************************************************");
-            log.debug("Starting Apache Velocity v@...@ (compiled: @build.time@)");
-            log.trace("RuntimeInstance initializing.");
+ initializeIntrospection();
+ /*
+ * initialize the VM Factory. It will use the properties accessable from Runtime, so keep this here at the end.
+ */
+ vmFactory.initVelocimacro();
 
-            initializeProperties();
-            initializeLog();
-            initializeResourceManager();
-            initializeDirectives();
-            initializeEventHandlers();
-            initializeParserPool();
+ log.trace("RuntimeInstance successfully initialized.");
 
-            initializeIntrospection();
-            /*
-             *  initialize the VM Factory.  It will use the properties
-             * accessable from Runtime, so keep this here at the end.
-             */
-            vmFactory.initVelocimacro();
+ initialized = true;
+ initializing = false;
+ }
+ }
 
-            log.trace("RuntimeInstance successfully initialized.");
+ /**
+ * Returns true if the RuntimeInstance has been successfully initialized.
+ *
+ * @return True if the RuntimeInstance has been successfully initialized.
+ */
+ public boolean isInitialized() {
+ return initialized;
+ }
 
-            initialized = true;
-            initializing = false;
-        }
-    }
+ /**
+ * Gets the classname for the Uberspect introspection package and instantiates an instance.
+ */
+ private void initializeIntrospection() throws Exception {
+ String rm = getString(RuntimeConstants.UBERSPECT_CLASSNAME);
 
-    /**
-     * Returns true if the RuntimeInstance has been successfully initialized.
-     * @return True if the RuntimeInstance has been successfully initialized.
-     */
-    public boolean isInitialized()
-    {
-        return initialized;
-    }
+ if (rm != null && rm.length() > 0) {
+ Object o = null;
 
-    /**
-     *  Gets the classname for the Uberspect introspection package and
-     *  instantiates an instance.
-     */
-    private void initializeIntrospection()
-        throws Exception
-    {
-        String rm = getString(RuntimeConstants.UBERSPECT_CLASSNAME);
+ try {
+ o = ClassUtils.getNewInstance(rm);
+ }
+ catch (ClassNotFoundException cnfe) {
+ String err = "The specified class for Uberspect (" + rm + ") does not exist or is not accessible to the current classloader.";
+ log.error(err);
+ throw new Exception(err);
+ }
 
-        if (rm != null && rm.length() > 0)
-        {
-            Object o = null;
+ if (!(o instanceof Uberspect)) {
+ String err = "The specified class for Uberspect (" + rm + ") does not implement " + Uberspect.class.getName()
+ + "; Velocity is not initialized correctly.";
 
-            try
-            {
-               o = ClassUtils.getNewInstance( rm );
-            }
-            catch (ClassNotFoundException cnfe)
-            {
-                String err = "The specified class for Uberspect (" + rm
-                    + ") does not exist or is not accessible to the current classloader.";
-                log.error(err);
-                throw new Exception(err);
-            }
+ log.error(err);
+ throw new Exception(err);
+ }
 
-            if (!(o instanceof Uberspect))
-            {
-                String err = "The specified class for Uberspect ("
-                    + rm + ") does not implement " + Uberspect.class.getName()
-                    + "; Velocity is not initialized correctly.";
+ uberSpect = (Uberspect) o;
 
-                log.error(err);
-                throw new Exception(err);
-            }
+ if (uberSpect instanceof UberspectLoggable) {
+ ((UberspectLoggable) uberSpect).setLog(getLog());
+ }
 
-            uberSpect = (Uberspect) o;
+ if (uberSpect instanceof RuntimeServicesAware) {
+ ((RuntimeServicesAware) uberSpect).setRuntimeServices(this);
+ }
 
-            if (uberSpect instanceof UberspectLoggable)
-            {
-                ((UberspectLoggable) uberSpect).setLog(getLog());
-            }
+ uberSpect.init();
+ }
+ else {
+ /*
+ * someone screwed up. Lets not fool around...
+ */
 
-            if (uberSpect instanceof RuntimeServicesAware)
-            {
-                ((RuntimeServicesAware) uberSpect).setRuntimeServices(this);
-            }
-            
-            uberSpect.init();
-         }
-         else
-         {
-            /*
-             *  someone screwed up.  Lets not fool around...
-             */
+ String err = "It appears that no class was specified as the" + " Uberspect.  Please ensure that all configuration" + " information is correct.";
 
-            String err = "It appears that no class was specified as the"
-            + " Uberspect.  Please ensure that all configuration"
-            + " information is correct.";
+ log.error(err);
+ throw new Exception(err);
+ }
+ }
 
-            log.error(err);
-            throw new Exception(err);
-        }
-    }
+ /**
+ * Initializes the Velocity Runtime with properties file. The properties file may be in the file system proper, or the properties file may be in the
+ * classpath.
+ */
+ private void setDefaultProperties() {
+ InputStream inputStream = null;
+ try {
+ inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_PROPERTIES);
 
-    /**
-     * Initializes the Velocity Runtime with properties file.
-     * The properties file may be in the file system proper,
-     * or the properties file may be in the classpath.
-     */
-    private void setDefaultProperties()
-    {
-        InputStream inputStream = null;
-        try
-        {
-            inputStream = getClass()
-                .getResourceAsStream('/' + DEFAULT_RUNTIME_PROPERTIES);
+ configuration.load(inputStream);
 
-            configuration.load( inputStream );
+ if (log.isDebugEnabled()) {
+ log.debug("Default Properties File: " + new File(DEFAULT_RUNTIME_PROPERTIES).getPath());
+ }
 
-            if (log.isDebugEnabled())
-            {
-                log.debug("Default Properties File: " +
-                    new File(DEFAULT_RUNTIME_PROPERTIES).getPath());
-            }
+ }
+ catch (IOException ioe) {
+ log.error("Cannot get Velocity Runtime default properties!", ioe);
+ }
+ finally {
+ try {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ catch (IOException ioe) {
+ log.error("Cannot close Velocity Runtime default properties!", ioe);
+ }
+ }
+ }
 
+ /**
+ * Allows an external system to set a property in the Velocity Runtime.
+ *
+ * @param key
+ *            property key
+ * @param value
+ *            property value
+ */
+ public void setProperty(String key, Object value) {
 
-        }
-        catch (IOException ioe)
-        {
-            log.error("Cannot get Velocity Runtime default properties!", ioe);
-        }
-        finally
-        {
-            try
-            {
-                if (inputStream != null)
-                {
-                    inputStream.close();
-                }
-            }
-            catch (IOException ioe)
-            {
-                log.error("Cannot close Velocity Runtime default properties!", ioe);
-            }
-        }
-    }
+ if (overridingProperties == null) {
+ overridingProperties = new ExtendedProperties();
+ }
 
-    /**
-     * Allows an external system to set a property in
-     * the Velocity Runtime.
-     *
-     * @param key property key
-     * @param  value property value
-     */
-    public void setProperty(String key, Object value)
-    {
-        if (overridingProperties == null)
-        {
-            overridingProperties = new ExtendedProperties();
-        }
+ if (value instanceof String)
+ {
+ String newValue = replaceEnviornmentalVariable((String) value);
+ overridingProperties.setProperty(key, newValue);
+ }
+ else if ( value instanceof String[] )
+ {
+ String[] stringArray = (String[])value;
+ String[] newStringArray = new String [ stringArray.length] ;
+ for ( int i = 0 ; i < stringArray.length;i ++ )
+ {
+ newStringArray[i] = replaceEnviornmentalVariable(stringArray[i]);
+ }
+
+ overridingProperties.setProperty(key, newStringArray);
+ }
+
+ }
 
-        overridingProperties.setProperty(key, value);
-    }
+ /**
+ * Allow an external system to set an ExtendedProperties object to use. This is useful where the external system also uses the ExtendedProperties class and
+ * the velocity configuration is a subset of parent application's configuration. This is the case with Turbine.
+ *
+ * @param configuration
+ */
+ public void setConfiguration(ExtendedProperties configuration) {
+ if (overridingProperties == null) {
+ overridingProperties = configuration;
+ }
+ else {
+ // Avoid possible ConcurrentModificationException
+ if (overridingProperties != configuration) {
+ overridingProperties.combine(configuration);
+ }
+ }
+ }
 
-    /**
-     * Allow an external system to set an ExtendedProperties
-     * object to use. This is useful where the external
-     * system also uses the ExtendedProperties class and
-     * the velocity configuration is a subset of
-     * parent application's configuration. This is
-     * the case with Turbine.
-     *
-     * @param  configuration
-     */
-    public void setConfiguration( ExtendedProperties configuration)
-    {
-        if (overridingProperties == null)
-        {
-            overridingProperties = configuration;
-        }
-        else
-        {
-            // Avoid possible ConcurrentModificationException
-            if (overridingProperties != configuration)
-            {
-                overridingProperties.combine(configuration);
-            }
-        }
-    }
+ /**
+ * Add a property to the configuration. If it already exists then the value stated here will be added to the configuration entry. For example, if
+ *
+ * resource.loader = file
+ *
+ * is already present in the configuration and you
+ *
+ * addProperty("resource.loader", "classpath")
+ *
+ * Then you will end up with a Vector like the following:
+ *
+ * ["file", "classpath"]
+ *
+ * @param key
+ * @param value
+ */
+ public void addProperty(String key, Object value) {
+ if (overridingProperties == null) {
+ overridingProperties = new ExtendedProperties();
+ }
 
-    /**
-     * Add a property to the configuration. If it already
-     * exists then the value stated here will be added
-     * to the configuration entry. For example, if
-     *
-     * resource.loader = file
-     *
-     * is already present in the configuration and you
-     *
-     * addProperty("resource.loader", "classpath")
-     *
-     * Then you will end up with a Vector like the
-     * following:
-     *
-     * ["file", "classpath"]
-     *
-     * @param  key
-     * @param  value
-     */
-    public void addProperty(String key, Object value)
-    {
-        if (overridingProperties == null)
-        {
-            overridingProperties = new ExtendedProperties();
-        }
+ overridingProperties.addProperty(key, value);
+ }
 
-        overridingProperties.addProperty(key, value);
-    }
+ /**
+ * Clear the values pertaining to a particular property.
+ *
+ * @param key
+ *            of property to clear
+ */
+ public void clearProperty(String key) {
+ if (overridingProperties != null) {
+ overridingProperties.clearProperty(key);
+ }
+ }
 
-    /**
-     * Clear the values pertaining to a particular
-     * property.
-     *
-     * @param key of property to clear
-     */
-    public void clearProperty(String key)
-    {
-        if (overridingProperties != null)
-        {
-            overridingProperties.clearProperty(key);
-        }
-    }
+ /**
+ * Allows an external caller to get a property. The calling routine is required to know the type, as this routine will return an Object, as that is what
+ * properties can be.
+ *
+ * @param key
+ *            property to return
+ * @return Value of the property or null if it does not exist.
+ */
+ public Object getProperty(String key) {
+ Object o = null;
 
-    /**
-     *  Allows an external caller to get a property.  The calling
-     *  routine is required to know the type, as this routine
-     *  will return an Object, as that is what properties can be.
-     *
-     *  @param key property to return
-     *  @return Value of the property or null if it does not exist.
-     */
-    public Object getProperty(String key)
-    {
-        Object o = null;
-        
-        /**
-         * Before initialization, check the user-entered properties first.
-         */
-        if (!initialized && !initializing && overridingProperties != null)
-        {
-            o = overridingProperties.get(key);
-        }
-        
-        /**
-         * After initialization, configuration will hold all properties.
-         */
-        if (o == null)
-        {
-            o = configuration.getProperty(key);
-        }
-        if (o instanceof String)
-        {
-            return StringUtils.nullTrim((String) o);
-        }
-        else
-        {
-            return o;
-        }
-    }
+ /**
+ * Before initialization, check the user-entered properties first.
+ */
+ if (!initialized && !initializing && overridingProperties != null) {
+ o = overridingProperties.get(key);
+ }
 
-    /**
-     * Initialize Velocity properties, if the default
-     * properties have not been laid down first then
-     * do so. Then proceed to process any overriding
-     * properties. Laying down the default properties
-     * gives a much greater chance of having a
-     * working system.
-     */
-    private void initializeProperties()
-    {
-        /*
-         * Always lay down the default properties first as
-         * to provide a solid base.
-         */
-        if (configuration.isInitialized() == false)
-        {
-            setDefaultProperties();
-        }
+ /**
+ * After initialization, configuration will hold all properties.
+ */
+ if (o == null) {
+ o = configuration.getProperty(key);
+ }
+ if (o instanceof String) {
+ return StringUtils.nullTrim((String) o);
+ }
+ else {
+ return o;
+ }
+ }
 
-        if( overridingProperties != null)
-        {
-            configuration.combine(overridingProperties);
-        }
-    }
+ /**
+ * Replace all environmental variables in the form of ${VELOCITY_HOME} with their
+ * expansions
+ * @param properties
+ */
+
+ private void replaceEnviormentalVariables(ExtendedProperties properties) {
 
-    /**
-     * Initialize the Velocity Runtime with a Properties
-     * object.
-     *
-     * @param p
-     * @throws Exception When an error occurs during initialization.
-     */
-    public void init(Properties p) throws Exception
-    {
-        overridingProperties = ExtendedProperties.convertProperties(p);
-        init();
-    }
+ Iterator iterator = properties.getKeys();
+ Map convertedKeys = new HashMap();
 
-    /**
-     * Initialize the Velocity Runtime with the name of
-     * ExtendedProperties object.
-     *
-     * @param configurationFile
-     * @throws Exception When an error occurs during initialization.
-     */
-    public void init(String configurationFile)
-        throws Exception
-    {
-        overridingProperties = new ExtendedProperties(configurationFile);
-        init();
-    }
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ Object value = properties.get(key);
 
-    private void initializeResourceManager()
-        throws Exception
-    {
-        /*
-         * Which resource manager?
-         */
+ if (value instanceof String) {
 
-        String rm = getString(RuntimeConstants.RESOURCE_MANAGER_CLASS);
+ String v = replaceEnviornmentalVariable((String) value);
+ convertedKeys.put(key, v);
 
-        if (rm != null && rm.length() > 0)
-        {
-            /*
-             *  if something was specified, then make one.
-             *  if that isn't a ResourceManager, consider
-             *  this a huge error and throw
-             */
+ }
+ else if (value instanceof String[]) {
+ String[] stringArray = (String[]) value;
+ for (int i = 0; i < stringArray.length; i++) {
+ String val = stringArray[i];
+ String v = replaceEnviornmentalVariable((String) val);
+ convertedKeys.put(key, v);
 
-            Object o = null;
+ }
+ }
 
-            try
-            {
-               o = ClassUtils.getNewInstance( rm );
-            }
-            catch (ClassNotFoundException cnfe )
-            {
-                String err = "The specified class for ResourceManager (" + rm
-                    + ") does not exist or is not accessible to the current classloader.";
-                log.error(err);
-                throw new Exception(err);
-            }
+ }
 
-            if (!(o instanceof ResourceManager))
-            {
-                String err = "The specified class for ResourceManager (" + rm
-                    + ") does not implement " + ResourceManager.class.getName()
-                    + "; Velocity is not initialized correctly.";
+ properties.putAll(convertedKeys);
 
-                log.error(err);
-                throw new Exception(err);
-            }
+ }
 
-            resourceManager = (ResourceManager) o;
+ /**
+ * Replace all occurrences of ${} in a string with its corresponding expanded environmental var's
+ * @param string
+ * @return
+ */
+
+ private String replaceEnviornmentalVariable(String string) {
 
-            resourceManager.initialize(this);
-         }
-         else
-         {
-            /*
-             *  someone screwed up.  Lets not fool around...
-             */
+ int start = string.indexOf("${");
 
-            String err = "It appears that no class was specified as the"
-            + " ResourceManager.  Please ensure that all configuration"
-            + " information is correct.";
+ if (start != -1) {
 
-            log.error(err);
-            throw new Exception( err );
-        }
-    }
+ int end = string.indexOf('}');
 
-    private void initializeEventHandlers()
-        throws Exception
-    {
+ if (end != -1) {
+ String env = string.substring(start + 2, end);
+ String regex = "\\$\\{" + env + "\\}";
+ String replacement = System.getenv(env);
+ if (replacement != null) {
+ return string.replaceAll(regex, replacement);
 
-        eventCartridge = new EventCartridge();
+ }
 
-        /**
-         * For each type of event handler, get the class name, instantiate it, and store it.
-         */
+ }
 
-        String[] referenceinsertion = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION);
-        if ( referenceinsertion != null )
-        {
-            for ( int i=0; i < referenceinsertion.length; i++ )
-            {
-                EventHandler ev = initializeSpecificEventHandler(referenceinsertion[i],RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION,ReferenceInsertionEventHandler.class);
-                if (ev != null)
-                    eventCartridge.addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev);
-            }
-        }
+ }
+ return string;
 
-        String[] nullset = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_NULLSET);
-        if ( nullset != null )
-        {
-            for ( int i=0; i < nullset.length; i++ )
-            {
-                EventHandler ev = initializeSpecificEventHandler(nullset[i],RuntimeConstants.EVENTHANDLER_NULLSET,NullSetEventHandler.class);
-                if (ev != null)
-                    eventCartridge.addNullSetEventHandler((NullSetEventHandler) ev);
-            }
-        }
+ }
 
-        String[] methodexception = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION);
-        if ( methodexception != null )
-        {
-            for ( int i=0; i < methodexception.length; i++ )
-            {
-                EventHandler ev = initializeSpecificEventHandler(methodexception[i],RuntimeConstants.EVENTHANDLER_METHODEXCEPTION,MethodExceptionEventHandler.class);
-                if (ev != null)
-                    eventCartridge.addMethodExceptionHandler((MethodExceptionEventHandler) ev);
-            }
-        }
+ /**
+ * Initialize Velocity properties, if the default properties have not been laid down first then do so. Then proceed to process any overriding properties.
+ * Laying down the default properties gives a much greater chance of having a working system.
+ */
+ private void initializeProperties() {
+ /*
+ * Always lay down the default properties first as to provide a solid base.
+ */
+ if (configuration.isInitialized() == false) {
+ setDefaultProperties();
+ replaceEnviormentalVariables(configuration);
+ }
 
-        String[] includeHandler = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INCLUDE);
-        if ( includeHandler != null )
-        {
-            for ( int i=0; i < includeHandler.length; i++ )
-            {
-                EventHandler ev = initializeSpecificEventHandler(includeHandler[i],RuntimeConstants.EVENTHANDLER_INCLUDE,IncludeEventHandler.class);
-                if (ev != null)
-                    eventCartridge.addIncludeEventHandler((IncludeEventHandler) ev);
-            }
-        }
+ if (overridingProperties != null) {
+ replaceEnviormentalVariables(overridingProperties);
+ configuration.combine(overridingProperties);
+ }
+ }
 
-        String[] invalidReferenceSet = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES);
-        if ( invalidReferenceSet != null )
-        {
-            for ( int i=0; i < invalidReferenceSet.length; i++ )
-            {
-                EventHandler ev = initializeSpecificEventHandler(invalidReferenceSet[i],RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES,InvalidReferenceEventHandler.class);
-                if (ev != null)
-                {
-                    eventCartridge.addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev);
-                }
-            }
-        }
+ /**
+ * Initialize the Velocity Runtime with a Properties object.
+ *
+ * @param p
+ * @throws Exception
+ *             When an error occurs during initialization.
+ */
+ public void init(Properties p) throws Exception {
+ overridingProperties = ExtendedProperties.convertProperties(p);
+ init();
+ }
 
+ /**
+ * Initialize the Velocity Runtime with the name of ExtendedProperties object.
+ *
+ * @param configurationFile
+ * @throws Exception
+ *             When an error occurs during initialization.
+ */
+ public void init(String configurationFile) throws Exception {
+ overridingProperties = new ExtendedProperties(configurationFile);
+ init();
+ }
 
-    }
+ private void initializeResourceManager() throws Exception {
+ /*
+ * Which resource manager?
+ */
 
-    private EventHandler initializeSpecificEventHandler(String classname, String paramName, Class EventHandlerInterface)
-        throws Exception
-    {
-        if ( classname != null && classname.length() > 0)
-        {
-            Object o = null;
-            try {
-                o = ClassUtils.getNewInstance(classname);
-            }
-            catch (ClassNotFoundException cnfe )
-            {
-                String err = "The specified class for "
-                    + paramName + " (" + classname
-                    + ") does not exist or is not accessible to the current classloader.";
-                log.error(err);
-                throw new Exception(err);
-            }
+ String rm = getString(RuntimeConstants.RESOURCE_MANAGER_CLASS);
 
-            if (!EventHandlerInterface.isAssignableFrom(EventHandlerInterface))
-            {
-                String err = "The specified class for " + paramName + " ("
-                    + classname + ") does not implement "
-                    + EventHandlerInterface.getName()
-                    + "; Velocity is not initialized correctly.";
+ if (rm != null && rm.length() > 0) {
+ /*
+ * if something was specified, then make one. if that isn't a ResourceManager, consider this a huge error and throw
+ */
 
-                log.error(err);
-                throw new Exception(err);
-            }
+ Object o = null;
 
-            EventHandler ev = (EventHandler) o;
-            if ( ev instanceof RuntimeServicesAware )
-                ((RuntimeServicesAware) ev).setRuntimeServices(this);
-            return ev;
+ try {
+ o = ClassUtils.getNewInstance(rm);
+ }
+ catch (ClassNotFoundException cnfe) {
+ String err = "The specified class for ResourceManager (" + rm + ") does not exist or is not accessible to the current classloader.";
+ log.error(err);
+ throw new Exception(err);
+ }
 
-        } else
-            return null;
-    }
+ if (!(o instanceof ResourceManager)) {
+ String err = "The specified class for ResourceManager (" + rm + ") does not implement " + ResourceManager.class.getName()
+ + "; Velocity is not initialized correctly.";
 
-    /**
-     * Initialize the Velocity logging system.
-     *
-     * @throws Exception
-     */
-    private void initializeLog() throws Exception
-    {
-        // since the Log we started with was just placeholding,
-        // let's update it with the real LogChute settings.
-        LogManager.updateLog(this.log, this);
-    }
+ log.error(err);
+ throw new Exception(err);
+ }
 
+ resourceManager = (ResourceManager) o;
 
-    /**
-     * This methods initializes all the directives
-     * that are used by the Velocity Runtime. The
-     * directives to be initialized are listed in
-     * the RUNTIME_DEFAULT_DIRECTIVES properties
-     * file.
-     *
-     * @throws Exception
-     */
-    private void initializeDirectives()
-        throws Exception
-    {
-        /*
-         * Initialize the runtime directive table.
-         * This will be used for creating parsers.
-         */
-        runtimeDirectives = new Hashtable();
+ resourceManager.initialize(this);
+ }
+ else {
+ /*
+ * someone screwed up. Lets not fool around...
+ */
 
-        Properties directiveProperties = new Properties();
+ String err = "It appears that no class was specified as the" + " ResourceManager.  Please ensure that all configuration"
+ + " information is correct.";
 
-        /*
-         * Grab the properties file with the list of directives
-         * that we should initialize.
-         */
+ log.error(err);
+ throw new Exception(err);
+ }
+ }
 
-        InputStream inputStream = null;
+ private void initializeEventHandlers() throws Exception {
 
-        try
-        {
-            inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_DIRECTIVES);
+ eventCartridge = new EventCartridge();
 
-            if (inputStream == null)
-            {
-                throw new Exception("Error loading directive.properties! " +
-                                    "Something is very wrong if these properties " +
-                                    "aren't being located. Either your Velocity " +
-                                    "distribution is incomplete or your Velocity " +
-                                    "jar file is corrupted!");
-            }
+ /**
+ * For each type of event handler, get the class name, instantiate it, and store it.
+ */
 
-            directiveProperties.load(inputStream);
+ String[] referenceinsertion = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION);
+ if (referenceinsertion != null) {
+ for (int i = 0; i < referenceinsertion.length; i++) {
+ EventHandler ev = initializeSpecificEventHandler(referenceinsertion[i], RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION,
+ ReferenceInsertionEventHandler.class);
+ if (ev != null) eventCartridge.addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev);
+ }
+ }
 
-        }
-        catch (IOException ioe)
-        {
-            log.error("Error while loading directive properties!", ioe);
-        }
-        finally
-        {
-            try
-            {
-                if (inputStream != null)
-                {
-                    inputStream.close();
-                }
-            }
-            catch (IOException ioe)
-            {
-                log.error("Cannot close directive properties!", ioe);
-            }
-        }
+ String[] nullset = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_NULLSET);
+ if (nullset != null) {
+ for (int i = 0; i < nullset.length; i++) {
+ EventHandler ev = initializeSpecificEventHandler(nullset[i], RuntimeConstants.EVENTHANDLER_NULLSET, NullSetEventHandler.class);
+ if (ev != null) eventCartridge.addNullSetEventHandler((NullSetEventHandler) ev);
+ }
+ }
 
+ String[] methodexception = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION);
+ if (methodexception != null) {
+ for (int i = 0; i < methodexception.length; i++) {
+ EventHandler ev = initializeSpecificEventHandler(methodexception[i], RuntimeConstants.EVENTHANDLER_METHODEXCEPTION,
+ MethodExceptionEventHandler.class);
+ if (ev != null) eventCartridge.addMethodExceptionHandler((MethodExceptionEventHandler) ev);
+ }
+ }
 
-        /*
-         * Grab all the values of the properties. These
-         * are all class names for example:
-         *
-         * org.apache.velocity.runtime.directive.Foreach
-         */
-        Enumeration directiveClasses = directiveProperties.elements();
+ String[] includeHandler = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INCLUDE);
+ if (includeHandler != null) {
+ for (int i = 0; i < includeHandler.length; i++) {
+ EventHandler ev = initializeSpecificEventHandler(includeHandler[i], RuntimeConstants.EVENTHANDLER_INCLUDE, IncludeEventHandler.class);
+ if (ev != null) eventCartridge.addIncludeEventHandler((IncludeEventHandler) ev);
+ }
+ }
 
-        while (directiveClasses.hasMoreElements())
-        {
-            String directiveClass = (String) directiveClasses.nextElement();
-            loadDirective(directiveClass);
-            log.debug("Loaded System Directive: " + directiveClass);
-        }
+ String[] invalidReferenceSet = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES);
+ if (invalidReferenceSet != null) {
+ for (int i = 0; i < invalidReferenceSet.length; i++) {
+ EventHandler ev = initializeSpecificEventHandler(invalidReferenceSet[i], RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES,
+ InvalidReferenceEventHandler.class);
+ if (ev != null) {
+ eventCartridge.addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev);
+ }
+ }
+ }
 
-        /*
-         *  now the user's directives
-         */
+ }
 
-        String[] userdirective = configuration.getStringArray("userdirective");
+ private EventHandler initializeSpecificEventHandler(String classname, String paramName, Class EventHandlerInterface) throws Exception {
+ if (classname != null && classname.length() > 0) {
+ Object o = null;
+ try {
+ o = ClassUtils.getNewInstance(classname);
+ }
+ catch (ClassNotFoundException cnfe) {
+ String err = "The specified class for " + paramName + " (" + classname + ") does not exist or is not accessible to the current classloader.";
+ log.error(err);
+ throw new Exception(err);
+ }
 
-        for( int i = 0; i < userdirective.length; i++)
-        {
-            loadDirective(userdirective[i]);
-            if (log.isDebugEnabled())
-            {
-                log.debug("Loaded User Directive: " + userdirective[i]);
-            }
-        }
+ if (!EventHandlerInterface.isAssignableFrom(EventHandlerInterface)) {
+ String err = "The specified class for " + paramName + " (" + classname + ") does not implement " + EventHandlerInterface.getName()
+ + "; Velocity is not initialized correctly.";
 
-    }
+ log.error(err);
+ throw new Exception(err);
+ }
 
-    /**
-     * Programatically add a directive.
-     * @param directive
-     */
-    public void addDirective(Directive directive)
-    {
-        runtimeDirectives.put(directive.getName(), directive);
-    }
+ EventHandler ev = (EventHandler) o;
+ if (ev instanceof RuntimeServicesAware) ((RuntimeServicesAware) ev).setRuntimeServices(this);
+ return ev;
 
-    /**
-     * Retrieve a previously instantiated directive.
-     * @param name name of the directive
-     * @return
-     */
-    public Directive getDirective(String name)
-    {
-        return (Directive) runtimeDirectives.get(name);
-    }
+ }
+ else return null;
+ }
 
-    /**
-     * Remove a directive.
-     * @param name name of the directive.
-     */
-    public void removeDirective(String name)
-    {
-        runtimeDirectives.remove(name);
-    }
+ /**
+ * Initialize the Velocity logging system.
+ *
+ * @throws Exception
+ */
+ private void initializeLog() throws Exception {
+ // since the Log we started with was just placeholding,
+ // let's update it with the real LogChute settings.
+ LogManager.updateLog(this.log, this);
+ }
 
-    /**
-     *  instantiates and loads the directive with some basic checks
-     *
-     *  @param directiveClass classname of directive to load
-     */
-    private void loadDirective(String directiveClass)
-    {
-        try
-        {
-            Object o = ClassUtils.getNewInstance( directiveClass );
+ /**
+ * This methods initializes all the directives that are used by the Velocity Runtime. The directives to be initialized are listed in the
+ * RUNTIME_DEFAULT_DIRECTIVES properties file.
+ *
+ * @throws Exception
+ */
+ private void initializeDirectives() throws Exception {
+ /*
+ * Initialize the runtime directive table. This will be used for creating parsers.
+ */
+ runtimeDirectives = new Hashtable();
 
-            if (o instanceof Directive)
-            {
-                Directive directive = (Directive) o;
-                addDirective(directive);
-            }
-            else
-            {
-                log.error(directiveClass + " does not implement "
-                    + Directive.class.getName() + "; it cannot be loaded.");
-            }
-        }
-        // The ugly threesome:  ClassNotFoundException,
-        // IllegalAccessException, InstantiationException.
-        // Ignore Findbugs complaint for now.
-        catch (Exception e)
-        {
-            log.error("Failed to load Directive: " + directiveClass, e);
-        }
-    }
+ Properties directiveProperties = new Properties();
 
+ /*
+ * Grab the properties file with the list of directives that we should initialize.
+ */
 
-    /**
-     * Initializes the Velocity parser pool.
-     */
-    private void initializeParserPool() throws Exception
-    {
-        /*
-         * Which parser pool?
-         */
-        String pp = getString(RuntimeConstants.PARSER_POOL_CLASS);
+ InputStream inputStream = null;
 
-        if (pp != null && pp.length() > 0)
-        {
-            /*
-             *  if something was specified, then make one.
-             *  if that isn't a ParserPool, consider
-             *  this a huge error and throw
-             */
+ try {
+ inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_DIRECTIVES);
 
-            Object o = null;
+ if (inputStream == null) { throw new Exception("Error loading directive.properties! " + "Something is very wrong if these properties "
+ + "aren't being located. Either your Velocity " + "distribution is incomplete or your Velocity " + "jar file is corrupted!"); }
 
-            try
-            {
-                o = ClassUtils.getNewInstance( pp );
-            }
-            catch (ClassNotFoundException cnfe )
-            {
-                String err = "The specified class for ParserPool ("
-                    + pp
-                    + ") does not exist (or is not accessible to the current classloader.";
-                log.error(err);
-                throw new Exception(err);
-            }
+ directiveProperties.load(inputStream);
 
-            if (!(o instanceof ParserPool))
-            {
-                String err = "The specified class for ParserPool ("
-                    + pp + ") does not implement " + ParserPool.class
-                    + " Velocity not initialized correctly.";
+ }
+ catch (IOException ioe) {
+ log.error("Error while loading directive properties!", ioe);
+ }
+ finally {
+ try {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ catch (IOException ioe) {
+ log.error("Cannot close directive properties!", ioe);
+ }
+ }
 
-                log.error(err);
-                throw new Exception(err);
-            }
+ /*
+ * Grab all the values of the properties. These are all class names for example:
+ *
+ * org.apache.velocity.runtime.directive.Foreach
+ */
+ Enumeration directiveClasses = directiveProperties.elements();
 
-            parserPool = (ParserPool) o;
+ while (directiveClasses.hasMoreElements()) {
+ String directiveClass = (String) directiveClasses.nextElement();
+ loadDirective(directiveClass);
+ log.debug("Loaded System Directive: " + directiveClass);
+ }
 
-            parserPool.initialize(this);
-        }
-        else
-        {
-            /*
-             *  someone screwed up.  Lets not fool around...
-             */
+ /*
+ * now the user's directives
+ */
 
-            String err = "It appears that no class was specified as the"
-                + " ParserPool.  Please ensure that all configuration"
-                + " information is correct.";
+ String[] userdirective = configuration.getStringArray("userdirective");
 
-            log.error(err);
-            throw new Exception( err );
-        }
+ for (int i = 0; i < userdirective.length; i++) {
+ loadDirective(userdirective[i]);
+ if (log.isDebugEnabled()) {
+ log.debug("Loaded User Directive: " + userdirective[i]);
+ }
+ }
 
-    }
+ }
 
-    /**
-     * Returns a JavaCC generated Parser.
-     *
-     * @return Parser javacc generated parser
-     */
-    public Parser createNewParser()
-    {
-        /* must be initialized before we use runtimeDirectives */
-        if (!initialized && !initializing)
-        {
-            log.debug("Velocity was not initialized! Calling init()...");
-            try
-            {
-                init();
-            }
-            catch (Exception e)
-            {
-                getLog().error("Could not auto-initialize Velocity", e);
-                throw new IllegalStateException("Velocity could not be initialized!");
-            }
-        }
+ /**
+ * Programatically add a directive.
+ *
+ * @param directive
+ */
+ public void addDirective(Directive directive) {
+ runtimeDirectives.put(directive.getName(), directive);
+ }
 
-        Parser parser = new Parser(this);
-        parser.setDirectives(runtimeDirectives);
-        return parser;
-    }
+ /**
+ * Retrieve a previously instantiated directive.
+ *
+ * @param name
+ *            name of the directive
+ * @return
+ */
+ public Directive getDirective(String name) {
+ return (Directive) runtimeDirectives.get(name);
+ }
 
-    /**
-     * Parse the input and return the root of
-     * AST node structure.
-     * <br><br>
-     *  In the event that it runs out of parsers in the
-     *  pool, it will create and let them be GC'd
-     *  dynamically, logging that it has to do that.  This
-     *  is considered an exceptional condition.  It is
-     *  expected that the user will set the
-     *  PARSER_POOL_SIZE property appropriately for their
-     *  application.  We will revisit this.
-     *
-     * @param reader Reader retrieved by a resource loader
-     * @param templateName name of the template being parsed
-     * @return A root node representing the template as an AST tree.
-     * @throws ParseException When the template could not be parsed.
-     */
-    public SimpleNode parse(Reader reader, String templateName)
-        throws ParseException
-    {
-        /*
-         *  do it and dump the VM namespace for this template
-         */
-        return parse(reader, templateName, true);
-    }
+ /**
+ * Remove a directive.
+ *
+ * @param name
+ *            name of the directive.
+ */
+ public void removeDirective(String name) {
+ runtimeDirectives.remove(name);
+ }
 
-    /**
-     *  Parse the input and return the root of the AST node structure.
-     *
-     * @param reader Reader retrieved by a resource loader
-     * @param templateName name of the template being parsed
-     * @param dumpNamespace flag to dump the Velocimacro namespace for this template
-     * @return A root node representing the template as an AST tree.
-     * @throws ParseException When the template could not be parsed.
-     */
-    public SimpleNode parse(Reader reader, String templateName, boolean dumpNamespace)
-        throws ParseException
-    {
-        /* must be initialized before using parserPool */
-        if (!initialized && !initializing)
-        {
-            log.debug("Velocity was not initialized! Calling init()...");
-            try
-            {
-                init();
-            }
-            catch (Exception e)
-            {
-                getLog().error("Could not auto-initialize Velocity", e);
-                throw new IllegalStateException("Velocity could not be initialized!");
-            }
-        }
+ /**
+ * instantiates and loads the directive with some basic checks
+ *
+ * @param directiveClass
+ *            classname of directive to load
+ */
+ private void loadDirective(String directiveClass) {
+ try {
+ Object o = ClassUtils.getNewInstance(directiveClass);
 
-        SimpleNode ast = null;
-        Parser parser = (Parser) parserPool.get();
+ if (o instanceof Directive) {
+ Directive directive = (Directive) o;
+ addDirective(directive);
+ }
+ else {
+ log.error(directiveClass + " does not implement " + Directive.class.getName() + "; it cannot be loaded.");
+ }
+ }
+ // The ugly threesome: ClassNotFoundException,
+ // IllegalAccessException, InstantiationException.
+ // Ignore Findbugs complaint for now.
+ catch (Exception e) {
+ log.error("Failed to load Directive: " + directiveClass, e);
+ }
+ }
 
-        if (parser == null)
-        {
-            /*
-             *  if we couldn't get a parser from the pool
-             *  make one and log it.
-             */
+ /**
+ * Initializes the Velocity parser pool.
+ */
+ private void initializeParserPool() throws Exception {
+ /*
+ * Which parser pool?
+ */
+ String pp = getString(RuntimeConstants.PARSER_POOL_CLASS);
 
-            if (log.isInfoEnabled())
-            {
-                log.info("Runtime : ran out of parsers. Creating a new one. "
-                      + " Please increment the parser.pool.size property."
-                      + " The current value is too small.");
-            }
+ if (pp != null && pp.length() > 0) {
+ /*
+ * if something was specified, then make one. if that isn't a ParserPool, consider this a huge error and throw
+ */
 
-            parser = createNewParser();
+ Object o = null;
 
-        }
+ try {
+ o = ClassUtils.getNewInstance(pp);
+ }
+ catch (ClassNotFoundException cnfe) {
+ String err = "The specified class for ParserPool (" + pp + ") does not exist (or is not accessible to the current classloader.";
+ log.error(err);
+ throw new Exception(err);
+ }
 
-        /*
-         *  now, if we have a parser
-         */
+ if (!(o instanceof ParserPool)) {
+ String err = "The specified class for ParserPool (" + pp + ") does not implement " + ParserPool.class + " Velocity not initialized correctly.";
 
-        if (parser != null)
-        {
-            try
-            {
-                /*
-                 *  dump namespace if we are told to.  Generally, you want to
-                 *  do this - you don't in special circumstances, such as
-                 *  when a VM is getting init()-ed & parsed
-                 */
+ log.error(err);
+ throw new Exception(err);
+ }
 
-                if (dumpNamespace)
-                {
-                    dumpVMNamespace(templateName);
-                }
+ parserPool = (ParserPool) o;
 
-                ast = parser.parse(reader, templateName);
-            }
-            finally
-            {
-                /*
-                 *  put it back
-                 */
-                parserPool.put(parser);
+ parserPool.initialize(this);
+ }
+ else {
+ /*
+ * someone screwed up. Lets not fool around...
+ */
 
-            }
-        }
-        else
-        {
-            log.error("Runtime : ran out of parsers and unable to create more.");
-        }
-        return ast;
-    }
+ String err = "It appears that no class was specified as the" + " ParserPool.  Please ensure that all configuration" + " information is correct.";
 
-    /**
-     * Renders the input string using the context into the output writer.
-     * To be used when a template is dynamically constructed, or want to use
-     * Velocity as a token replacer.
-     *
-     * @param context context to use in rendering input string
-     * @param out  Writer in which to render the output
-     * @param logTag  string to be used as the template name for log
-     *                messages in case of error
-     * @param instring input string containing the VTL to be rendered
-     *
-     * @return true if successful, false otherwise.  If false, see
-     *              Velocity runtime log
-     * @throws ParseErrorException The template could not be parsed.
-     * @throws MethodInvocationException A method on a context object could not be invoked.
-     * @throws ResourceNotFoundException A referenced resource could not be loaded.
-     * @throws IOException While rendering to the writer, an I/O problem occured.
-     * @since Velocity 1.6
-     */
-    public boolean evaluate(Context context,  Writer out,
-                            String logTag, String instring) throws IOException
-    {
-        return evaluate(context, out, logTag,
-                        new BufferedReader(new StringReader(instring)));
-    }
+ log.error(err);
+ throw new Exception(err);
+ }
 
-    /**
-     * Renders the input reader using the context into the output writer.
-     * To be used when a template is dynamically constructed, or want to
-     * use Velocity as a token replacer.
-     *
-     * @param context context to use in rendering input string
-     * @param writer  Writer in which to render the output
-     * @param logTag  string to be used as the template name for log messages
-     *                in case of error
-     * @param reader Reader containing the VTL to be rendered
-     *
-     * @return true if successful, false otherwise.  If false, see
-     *              Velocity runtime log
-     * @throws ParseErrorException The template could not be parsed.
-     * @throws MethodInvocationException A method on a context object could not be invoked.
-     * @throws ResourceNotFoundException A referenced resource could not be loaded.
-     * @throws IOException While reading from the reader or rendering to the writer,
-     *                     an I/O problem occured.
-     * @since Velocity 1.6
-     */
-    public boolean evaluate(Context context, Writer writer,
-                            String logTag, Reader reader) throws IOException
-    {
-        if (logTag == null)
-        {
-            throw new NullPointerException("logTag (i.e. template name) cannot be null, you must provide an identifier for the content being evaluated");
-        }
+ }
 
-        SimpleNode nodeTree = null;
-        try
-        {
-            nodeTree = parse(reader, logTag);
-        }
-        catch (ParseException pex)
-        {
-            throw new ParseErrorException(pex);
-        }
-        catch (TemplateInitException pex)
-        {
-            throw new ParseErrorException(pex);
-        }
+ /**
+ * Returns a JavaCC generated Parser.
+ *
+ * @return Parser javacc generated parser
+ */
+ public Parser createNewParser() {
+ /* must be initialized before we use runtimeDirectives */
+ if (!initialized && !initializing) {
+ log.debug("Velocity was not initialized! Calling init()...");
+ try {
+ init();
+ }
+ catch (Exception e) {
+ getLog().error("Could not auto-initialize Velocity", e);
+ throw new IllegalStateException("Velocity could not be initialized!");
+ }
+ }
 
-        if (nodeTree == null)
-        {
-            return false;
-        }
-        else
-        {
-            return render(context, writer, logTag, nodeTree);
-        }
-    }
+ Parser parser = new Parser(this);
+ parser.setDirectives(runtimeDirectives);
+ return parser;
+ }
 
+ /**
+ * Parse the input and return the root of AST node structure. <br>
+ * <br>
+ * In the event that it runs out of parsers in the pool, it will create and let them be GC'd dynamically, logging that it has to do that. This is considered
+ * an exceptional condition. It is expected that the user will set the PARSER_POOL_SIZE property appropriately for their application. We will revisit this.
+ *
+ * @param reader
+ *            Reader retrieved by a resource loader
+ * @param templateName
+ *            name of the template being parsed
+ * @return A root node representing the template as an AST tree.
+ * @throws ParseException
+ *             When the template could not be parsed.
+ */
+ public SimpleNode parse(Reader reader, String templateName) throws ParseException {
+ /*
+ * do it and dump the VM namespace for this template
+ */
+ return parse(reader, templateName, true);
+ }
 
-    /**
-     * Initializes and renders the AST {@link SimpleNode} using the context
-     * into the output writer.
-     *
-     * @param context context to use in rendering input string
-     * @param writer  Writer in which to render the output
-     * @param logTag  string to be used as the template name for log messages
-     *                in case of error
-     * @param nodeTree SimpleNode which is the root of the AST to be rendered
-     *
-     * @return true if successful, false otherwise.  If false, see
-     *              Velocity runtime log for errors
-     * @throws ParseErrorException The template could not be parsed.
-     * @throws MethodInvocationException A method on a context object could not be invoked.
-     * @throws ResourceNotFoundException A referenced resource could not be loaded.
-     * @throws IOException While rendering to the writer, an I/O problem occured.
-     * @since Velocity 1.6
-     */
-    public boolean render(Context context, Writer writer,
-                          String logTag, SimpleNode nodeTree) throws IOException
-    {
-        /*
-         * we want to init then render
-         */
-        InternalContextAdapterImpl ica =
-            new InternalContextAdapterImpl(context);
+ /**
+ * Parse the input and return the root of the AST node structure.
+ *
+ * @param reader
+ *            Reader retrieved by a resource loader
+ * @param templateName
+ *            name of the template being parsed
+ * @param dumpNamespace
+ *            flag to dump the Velocimacro namespace for this template
+ * @return A root node representing the template as an AST tree.
+ * @throws ParseException
+ *             When the template could not be parsed.
+ */
+ public SimpleNode parse(Reader reader, String templateName, boolean dumpNamespace) throws ParseException {
+ /* must be initialized before using parserPool */
+ if (!initialized && !initializing) {
+ log.debug("Velocity was not initialized! Calling init()...");
+ try {
+ init();
+ }
+ catch (Exception e) {
+ getLog().error("Could not auto-initialize Velocity", e);
+ throw new IllegalStateException("Velocity could not be initialized!");
+ }
+ }
 
-        ica.pushCurrentTemplateName(logTag);
+ SimpleNode ast = null;
+ Parser parser = (Parser) parserPool.get();
 
-        try
-        {
-            try
-            {
-                nodeTree.init(ica, this);
-            }
-            catch (TemplateInitException pex)
-            {
-                throw new ParseErrorException(pex);
-            }
-            /**
-             * pass through application level runtime exceptions
-             */
-            catch(RuntimeException e)
-            {
-                throw e;
-            }
-            catch(Exception e)
-            {
-                getLog().error("RuntimeInstance.render(): init exception for tag = "
-                               + logTag, e);
-            }
+ if (parser == null) {
+ /*
+ * if we couldn't get a parser from the pool make one and log it.
+ */
 
-            /*
-             *  now render, and let any exceptions fly
-             */
-            nodeTree.render(ica, writer);
-        }
-        finally
-        {
-            ica.popCurrentTemplateName();
-        }
+ if (log.isInfoEnabled()) {
+ log.info("Runtime : ran out of parsers. Creating a new one. " + " Please increment the parser.pool.size property."
+ + " The current value is too small.");
+ }
 
-        return true;
-    }
+ parser = createNewParser();
 
-    /**
-     * Invokes a currently registered Velocimacro with the params provided
-     * and places the rendered stream into the writer.
-     * <br>
-     * Note : currently only accepts args to the VM if they are in the context.
-     *
-     * @param vmName name of Velocimacro to call
-     * @param logTag string to be used for template name in case of error. if null,
-     *               the vmName will be used
-     * @param params keys for args used to invoke Velocimacro, in java format
-     *               rather than VTL (eg  "foo" or "bar" rather than "$foo" or "$bar")
-     * @param context Context object containing data/objects used for rendering.
-     * @param writer  Writer for output stream
-     * @return true if Velocimacro exists and successfully invoked, false otherwise.
-     * @throws IOException While rendering to the writer, an I/O problem occured.
-     */
-    public boolean invokeVelocimacro(final String vmName, String logTag,
-                                     String[] params, final Context context,
-                                     final Writer writer)
-        throws IOException
-    {
-        /* check necessary parameters */
-        if (vmName == null || context == null || writer == null)
-        {
-            getLog().error("RuntimeInstance.invokeVelocimacro() : invalid call : vmName, context, and writer must not be null");
-            return false;
-        }
+ }
 
-        /* handle easily corrected parameters */
-        if (logTag == null)
-        {
-            logTag = vmName;
-        }
-        if (params == null)
-        {
-            params = new String[0];
-        }
+ /*
+ * now, if we have a parser
+ */
 
-        /* does the VM exist? */
-        if (!isVelocimacro(vmName, logTag))
-        {
-            getLog().error("RuntimeInstance.invokeVelocimacro() : VM '" + vmName
-                           + "' is not registered.");
-            return false;
-        }
+ if (parser != null) {
+ try {
+ /*
+ * dump namespace if we are told to. Generally, you want to do this - you don't in special circumstances, such as when a VM is getting init()-ed &
+ * parsed
+ */
 
-        /* now just create the VM call, and use evaluate */
-        StringBuffer template = new StringBuffer("#");
-        template.append(vmName);
-        template.append("(");
-        for( int i = 0; i < params.length; i++)
-        {
-            template.append(" $");
-            template.append(params[i]);
-        }
-        template.append(" )");
+ if (dumpNamespace) {
+ dumpVMNamespace(templateName);
+ }
 
-        return evaluate(context, writer, logTag, template.toString());
-    }
+ ast = parser.parse(reader, templateName);
+ }
+ finally {
+ /*
+ * put it back
+ */
+ parserPool.put(parser);
 
-    /**
-     * Returns a <code>Template</code> from the resource manager.
-     * This method assumes that the character encoding of the
-     * template is set by the <code>input.encoding</code>
-     * property.  The default is "ISO-8859-1"
-     *
-     * @param name The file name of the desired template.
-     * @return     The template.
-     * @throws ResourceNotFoundException if template not found
-     *          from any available source.
-     * @throws ParseErrorException if template cannot be parsed due
-     *          to syntax (or other) error.
-     * @throws Exception if an error occurs in template initialization
-     */
-    public Template getTemplate(String name)
-        throws ResourceNotFoundException, ParseErrorException, Exception
-    {
-        return getTemplate(name, getString( INPUT_ENCODING, ENCODING_DEFAULT));
-    }
+ }
+ }
+ else {
+ log.error("Runtime : ran out of parsers and unable to create more.");
+ }
+ return ast;
+ }
 
-    /**
-     * Returns a <code>Template</code> from the resource manager
-     *
-     * @param name The  name of the desired template.
-     * @param encoding Character encoding of the template
-     * @return     The template.
-     * @throws ResourceNotFoundException if template not found
-     *          from any available source.
-     * @throws ParseErrorException if template cannot be parsed due
-     *          to syntax (or other) error.
-     * @throws Exception if an error occurs in template initialization
-     */
-    public Template getTemplate(String name, String  encoding)
-        throws ResourceNotFoundException, ParseErrorException, Exception
-    {
-        /* must be initialized before using resourceManager */
-        if (!initialized && !initializing)
-        {
-            log.debug("Velocity not initialized yet. Calling init()...");
-            init();
-        }
+ /**
+ * Renders the input string using the context into the output writer. To be used when a template is dynamically constructed, or want to use Velocity as a
+ * token replacer.
+ *
+ * @param context
+ *            context to use in rendering input string
+ * @param out
+ *            Writer in which to render the output
+ * @param logTag
+ *            string to be used as the template name for log messages in case of error
+ * @param instring
+ *            input string containing the VTL to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see Velocity runtime log
+ * @throws ParseErrorException
+ *             The template could not be parsed.
+ * @throws MethodInvocationException
+ *             A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException
+ *             A referenced resource could not be loaded.
+ * @throws IOException
+ *             While rendering to the writer, an I/O problem occured.
+ * @since Velocity 1.6
+ */
+ public boolean evaluate(Context context, Writer out, String logTag, String instring) throws IOException {
+ return evaluate(context, out, logTag, new BufferedReader(new StringReader(instring)));
+ }
 
-        return (Template)
-                resourceManager.getResource(name,
-                    ResourceManager.RESOURCE_TEMPLATE, encoding);
-    }
+ /**
+ * Renders the input reader using the context into the output writer. To be used when a template is dynamically constructed, or want to use Velocity as a
+ * token replacer.
+ *
+ * @param context
+ *            context to use in rendering input string
+ * @param writer
+ *            Writer in which to render the output
+ * @param logTag
+ *            string to be used as the template name for log messages in case of error
+ * @param reader
+ *            Reader containing the VTL to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see Velocity runtime log
+ * @throws ParseErrorException
+ *             The template could not be parsed.
+ * @throws MethodInvocationException
+ *             A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException
+ *             A referenced resource could not be loaded.
+ * @throws IOException
+ *             While reading from the reader or rendering to the writer, an I/O problem occured.
+ * @since Velocity 1.6
+ */
+ public boolean evaluate(Context context, Writer writer, String logTag, Reader reader) throws IOException {
+ if (logTag == null) { throw new NullPointerException(
+ "logTag (i.e. template name) cannot be null, you must provide an identifier for the content being evaluated"); }
 
-    /**
-     * Returns a static content resource from the
-     * resource manager.  Uses the current value
-     * if INPUT_ENCODING as the character encoding.
-     *
-     * @param name Name of content resource to get
-     * @return parsed ContentResource object ready for use
-     * @throws ResourceNotFoundException if template not found
-     *          from any available source.
-     * @throws ParseErrorException When the template could not be parsed.
-     * @throws Exception Any other error.
-     */
-    public ContentResource getContent(String name)
-        throws ResourceNotFoundException, ParseErrorException, Exception
-    {
-        /*
-         *  the encoding is irrelvant as we don't do any converstion
-         *  the bytestream should be dumped to the output stream
-         */
+ SimpleNode nodeTree = null;
+ try {
+ nodeTree = parse(reader, logTag);
+ }
+ catch (ParseException pex) {
+ throw new ParseErrorException(pex);
+ }
+ catch (TemplateInitException pex) {
+ throw new ParseErrorException(pex);
+ }
 
-        return getContent(name, getString( INPUT_ENCODING, ENCODING_DEFAULT));
-    }
+ if (nodeTree == null) {
+ return false;
+ }
+ else {
+ return render(context, writer, logTag, nodeTree);
+ }
+ }
 
-    /**
-     * Returns a static content resource from the
-     * resource manager.
-     *
-     * @param name Name of content resource to get
-     * @param encoding Character encoding to use
-     * @return parsed ContentResource object ready for use
-     * @throws ResourceNotFoundException if template not found
-     *          from any available source.
-     * @throws ParseErrorException When the template could not be parsed.
-     * @throws Exception Any other error.
-     */
-    public ContentResource getContent(String name, String encoding)
-        throws ResourceNotFoundException, ParseErrorException, Exception
-    {
-        /* must be initialized before using resourceManager */
-        if (!initialized && !initializing)
-        {
-            log.debug("Velocity not initialized yet. Calling init()...");
-            init();
-        }
+ /**
+ * Initializes and renders the AST {@link SimpleNode} using the context into the output writer.
+ *
+ * @param context
+ *            context to use in rendering input string
+ * @param writer
+ *            Writer in which to render the output
+ * @param logTag
+ *            string to be used as the template name for log messages in case of error
+ * @param nodeTree
+ *            SimpleNode which is the root of the AST to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see Velocity runtime log for errors
+ * @throws ParseErrorException
+ *             The template could not be parsed.
+ * @throws MethodInvocationException
+ *             A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException
+ *             A referenced resource could not be loaded.
+ * @throws IOException
+ *             While rendering to the writer, an I/O problem occured.
+ * @since Velocity 1.6
+ */
+ public boolean render(Context context, Writer writer, String logTag, SimpleNode nodeTree) throws IOException {
+ /*
+ * we want to init then render
+ */
+ InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
 
-        return (ContentResource)
-                resourceManager.getResource(name,
-                        ResourceManager.RESOURCE_CONTENT, encoding);
-    }
+ ica.pushCurrentTemplateName(logTag);
 
+ try {
+ try {
+ nodeTree.init(ica, this);
+ }
+ catch (TemplateInitException pex) {
+ throw new ParseErrorException(pex);
+ }
+ /**
+ * pass through application level runtime exceptions
+ */
+ catch (RuntimeException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ getLog().error("RuntimeInstance.render(): init exception for tag = " + logTag, e);
+ }
 
-    /**
-     *  Determines if a template exists and returns name of the loader that
-     *  provides it.  This is a slightly less hokey way to support
-     *  the Velocity.resourceExists() utility method, which was broken
-     *  when per-template encoding was introduced.  We can revisit this.
-     *
-     *  @param resourceName Name of template or content resource
-     *  @return class name of loader than can provide it
-     */
-    public String getLoaderNameForResource(String resourceName)
-    {
-        /* must be initialized before using resourceManager */
-        if (!initialized && !initializing)
-        {
-            log.debug("Velocity was not initialized! Calling init()...");
-            try
-            {
-                init();
-            }
-            catch (Exception e)
-            {
-                getLog().error("Could not initialize Velocity", e);
-                throw new IllegalStateException("Velocity could not be initialized!");
-            }
-        }
+ /*
+ * now render, and