Expanding enviornmental variables in properties files

View: New views
3 Messages — Rating Filter:   Alert me  

Expanding enviornmental variables in properties files

by csanders :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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@...


Re: Expanding enviornmental variables in properties files

by csanders :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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
+ *