[prev in list] [next in list] [prev in thread] [next in thread] 

List:       velocity-dev
Subject:    Re: Expanding enviornmental variables in properties files
From:       csanders <csanders () hoovers ! com>
Date:       2008-05-06 14:39:28
Message-ID: 48206DA0.5070009 () hoovers ! com
[Download RAW message or body]

Argh, sorry to spam, ignore this patch please - there is something else 
that needs updating.

Thanks,
Charlie

csanders wrote:
> 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@velocity.apache.org
> > For additional commands, e-mail: dev-help@velocity.apache.org
> > 
> 
> ------------------------------------------------------------------------
> 
> 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@apache.org">Jason van Zyl</a>
> * @author <a href="mailto:jlb@houseofdistraction.com">Jeff Bowden</a>
> * @author <a href="mailto:geirm@optonline.net">Geir Magusson Jr.</a>
> @@ -110,1585 +101,1446 @@
> * @author <a href="mailto:geirm@optonline.net">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@build.version@ (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@build.version@ (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 let any exceptions fly
> +			 */
> +			nodeTree.render(ica, writer);
> +		}
> +		finally {
> +			ica.popCurrentTemplateName();
> +		}
> 
> -        return resourceManager.getLoaderNameForResource(resourceName);
> -    }
> +		return true;
> +	}
> 
> -    /**
> -     * Returns a convenient Log instance that wraps the current LogChute.
> -     * Use this to log error messages. It has the usual methods.
> -     *
> -     * @return A convenience Log instance that wraps the current LogChute.
> -     */
> -    public Log getLog()
> -    {
> -        return log;
> -    }
> +	/**
> +	 * 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;
> +		}
> 
> -    /**
> -     * @deprecated Use getLog() and call warn() on it.
> -     * @see Log#warn(Object)
> -     * @param message The message to log.
> -     */
> -    public void warn(Object message)
> -    {
> -        getLog().warn(message);
> -    }
> +		/* handle easily corrected parameters */
> +		if (logTag == null) {
> +			logTag = vmName;
> +		}
> +		if (params == null) {
> +			params = new String[0];
> +		}
> 
> -    /**
> -     * @deprecated Use getLog() and call info() on it.
> -     * @see Log#info(Object)
> -     * @param message The message to log.
> -     */
> -    public void info(Object message)
> -    {
> -        getLog().info(message);
> -    }
> +		/* does the VM exist? */
> +		if (!isVelocimacro(vmName, logTag)) {
> +			getLog().error("RuntimeInstance.invokeVelocimacro() : VM '" + vmName + "' is \
> not registered."); +			return false;
> +		}
> 
> -    /**
> -     * @deprecated Use getLog() and call error() on it.
> -     * @see Log#error(Object)
> -     * @param message The message to log.
> -     */
> -    public void error(Object message)
> -    {
> -        getLog().error(message);
> -    }
> +		/* 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(" )");
> 
> -    /**
> -     * @deprecated Use getLog() and call debug() on it.
> -     * @see Log#debug(Object)
> -     * @param message The message to log.
> -     */
> -    public void debug(Object message)
> -    {
> -        getLog().debug(message);
> -    }
> +		return evaluate(context, writer, logTag, template.toString());
> +	}
> 
> -    /**
> -     * String property accessor method with default to hide the
> -     * configuration implementation.
> -     *
> -     * @param key property key
> -     * @param defaultValue  default value to return if key not
> -     *               found in resource manager.
> -     * @return value of key or default
> -     */
> -    public String getString( String key, String defaultValue)
> -    {
> -        return configuration.getString(key, defaultValue);
> -    }
> +	/**
> +	 * 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)); +	}
> 
> -    /**
> -     * Returns the appropriate VelocimacroProxy object if strVMname
> -     * is a valid current Velocimacro.
> -     *
> -     * @param vmName Name of velocimacro requested
> -     * @param templateName Name of the template that contains the velocimacro.
> -     * @return The requested VelocimacroProxy.
> -     */
> -    public Directive getVelocimacro(String vmName, String templateName)
> -    {
> -        return vmFactory.getVelocimacro( vmName, templateName );
> -    }
> +	/**
> +	 * 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();
> +		}
> 
> -   /**
> -    * Adds a new Velocimacro. Usually called by Macro only while parsing.
> -    *
> -    * @param name Name of velocimacro
> -    * @param macro String form of macro body
> -    * @param argArray Array of strings, containing the
> -    *                         #macro() arguments.  the 0th is the name.
> -    * @param sourceTemplate Name of the template that contains the velocimacro.
> -    * @return True if added, false if rejected for some
> -    *                  reason (either parameters or permission settings)
> -    */
> -    public boolean addVelocimacro( String name,
> -                                          String macro,
> -                                          String argArray[],
> -                                          String sourceTemplate )
> -    {
> -        return vmFactory.addVelocimacro(name, macro,  argArray,  sourceTemplate);
> -    }
> +		return (Template) resourceManager.getResource(name, \
> ResourceManager.RESOURCE_TEMPLATE, encoding); +	}
> 
> -    /**
> -     *  Checks to see if a VM exists
> -     *
> -     * @param vmName Name of the Velocimacro.
> -     * @param templateName Template on which to look for the Macro.
> -     * @return True if VM by that name exists, false if not
> -     */
> -    public boolean isVelocimacro( String vmName, String templateName )
> -    {
> -        return vmFactory.isVelocimacro(vmName, templateName);
> -    }
> +	/**
> +	 * 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 +		 */
> 
> -    /**
> -     * tells the vmFactory to dump the specified namespace.  This is to support
> -     * clearing the VM list when in inline-VM-local-scope mode
> -     * @param namespace Namespace to dump.
> -     * @return True if namespace was dumped successfully.
> -     */
> -    public boolean dumpVMNamespace(String namespace)
> -    {
> -        return vmFactory.dumpVMNamespace( namespace );
> -    }
> +		return getContent(name, getString(INPUT_ENCODING, ENCODING_DEFAULT));
> +	}
> 
> -    /* --------------------------------------------------------------------
> -     * R U N T I M E  A C C E S S O R  M E T H O D S
> -     * --------------------------------------------------------------------
> -     * These are the getXXX() methods that are a simple wrapper
> -     * around the configuration object. This is an attempt
> -     * to make a the Velocity Runtime the single access point
> -     * for all things Velocity, and allow the Runtime to
> -     * adhere as closely as possible the the Mediator pattern
> -     * which is the ultimate goal.
> -     * --------------------------------------------------------------------
> -     */
> +	/**
> +	 * 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();
> +		}
> 
> -    /**
> -     * String property accessor method to hide the configuration implementation
> -     * @param key  property key
> -     * @return   value of key or null
> -     */
> -    public String getString(String key)
> -    {
> -        return StringUtils.nullTrim(configuration.getString(key));
> -    }
> +		return (ContentResource) resourceManager.getResource(name, \
> ResourceManager.RESOURCE_CONTENT, encoding); +	}
> 
> -    /**
> -     * Int property accessor method to hide the configuration implementation.
> -     *
> -     * @param key Property key
> -     * @return value
> -     */
> -    public int getInt(String key)
> -    {
> -        return configuration.getInt(key);
> -    }
> +	/**
> +	 * 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!");
> +			}
> +		}
> 
> -    /**
> -     * Int property accessor method to hide the configuration implementation.
> -     *
> -     * @param key  property key
> -     * @param defaultValue The default value.
> -     * @return value
> -     */
> -    public int getInt(String key, int defaultValue)
> -    {
> -        return configuration.getInt(key, defaultValue);
> -    }
> +		return resourceManager.getLoaderNameForResource(resourceName);
> +	}
> 
> -    /**
> -     * Boolean property accessor method to hide the configuration implementation.
> -     *
> -     * @param key property key
> -     * @param def The default value if property not found.
> -     * @return value of key or default value
> -     */
> -    public boolean getBoolean(String key, boolean def)
> -    {
> -        return configuration.getBoolean(key, def);
> -    }
> +	/**
> +	 * Returns a convenient Log instance that wraps the current LogChute. Use this to \
> log error messages. It has the usual methods. +	 * 
> +	 * @return A convenience Log instance that wraps the current LogChute.
> +	 */
> +	public Log getLog() {
> +		return log;
> +	}
> 
> -    /**
> -     * Return the velocity runtime configuration object.
> -     *
> -     * @return Configuration object which houses the Velocity runtime
> -     * properties.
> -     */
> -    public ExtendedProperties getConfiguration()
> -    {
> -        return configuration;
> -    }
> +	/**
> +	 * @deprecated Use getLog() and call warn() on it.
> +	 * @see Log#warn(Object)
> +	 * @param message
> +	 *            The message to log.
> +	 */
> +	public void warn(Object message) {
> +		getLog().warn(message);
> +	}
> 
> -    /**
> -     *  Return the Introspector for this instance
> -     * @return The Introspector for this instance
> -     */
> -    public Introspector getIntrospector()
> -    {
> -        return introspector;
> -    }
> +	/**
> +	 * @deprecated Use getLog() and call info() on it.
> +	 * @see Log#info(Object)
> +	 * @param message
> +	 *            The message to log.
> +	 */
> +	public void info(Object message) {
> +		getLog().info(message);
> +	}
> 
> -    /**
> -     * Returns the event handlers for the application.
> -     * @return The event handlers for the application.
> -     */
> -    public EventCartridge getApplicationEventCartridge()
> -    {
> -        return eventCartridge;
> -    }
> +	/**
> +	 * @deprecated Use getLog() and call error() on it.
> +	 * @see Log#error(Object)
> +	 * @param message
> +	 *            The message to log.
> +	 */
> +	public void error(Object message) {
> +		getLog().error(message);
> +	}
> 
> +	/**
> +	 * @deprecated Use getLog() and call debug() on it.
> +	 * @see Log#debug(Object)
> +	 * @param message
> +	 *            The message to log.
> +	 */
> +	public void debug(Object message) {
> +		getLog().debug(message);
> +	}
> 
> -    /**
> -     *  Gets the application attribute for the given key
> -     *
> -     * @param key
> -     * @return The application attribute for the given key.
> -     */
> -    public Object getApplicationAttribute(Object key)
> -    {
> -        return applicationAttributes.get(key);
> -    }
> +	/**
> +	 * String property accessor method with default to hide the configuration \
> implementation. +	 * 
> +	 * @param key
> +	 *            property key
> +	 * @param defaultValue
> +	 *            default value to return if key not found in resource manager.
> +	 * @return value of key or default
> +	 */
> +	public String getString(String key, String defaultValue) {
> +		return configuration.getString(key, defaultValue);
> +	}
> 
> -    /**
> -     *   Sets the application attribute for the given key
> -     *
> -     * @param key
> -     * @param o The new application attribute.
> -     * @return The old value of this attribute or null if it hasn't been set \
>                 before.
> -     */
> -    public Object setApplicationAttribute(Object key, Object o)
> -    {
> -        return applicationAttributes.put(key, o);
> -    }
> +	/**
> +	 * Returns the appropriate VelocimacroProxy object if strVMname is a valid \
> current Velocimacro. +	 * 
> +	 * @param vmName
> +	 *            Name of velocimacro requested
> +	 * @param templateName
> +	 *            Name of the template that contains the velocimacro.
> +	 * @return The requested VelocimacroProxy.
> +	 */
> +	public Directive getVelocimacro(String vmName, String templateName) {
> +		return vmFactory.getVelocimacro(vmName, templateName);
> +	}
> 
> -    /**
> -     * Returns the Uberspect object for this Instance.
> -     *
> -     * @return The Uberspect object for this Instance.
> -     */
> -    public Uberspect getUberspect()
> -    {
> -        return uberSpect;
> -    }
> +	/**
> +	 * Adds a new Velocimacro. Usually called by Macro only while parsing.
> +	 * 
> +	 * @param name
> +	 *            Name of velocimacro
> +	 * @param macro
> +	 *            String form of macro body
> +	 * @param argArray
> +	 *            Array of strings, containing the #macro() arguments. the 0th is the \
> name. +	 * @param sourceTemplate
> +	 *            Name of the template that contains the velocimacro.
> +	 * @return True if added, false if rejected for some reason (either parameters or \
> permission settings) +	 */
> +	public boolean addVelocimacro(String name, String macro, String argArray[], \
> String sourceTemplate) { +		return vmFactory.addVelocimacro(name, macro, argArray, \
> sourceTemplate); +	}
> 
> -    /**
> -     * This method is called before a macro is rendered. This method
> -     * checks whether a macro call is within the allowed calling depth.
> -     * If the macro call exceeds the allowed calling depth it will throw
> -     * an exception.
> -     *
> -     * @param macroName name of the macro
> -     * @param templateName name of the template file containing the macro
> -     * @throws MacroOverflowException if the number of macro calls 
> -     * exceeds the specified value
> -     */
> -    public void startMacroRendering(String macroName, String templateName) throws \
>                 MacroOverflowException
> -    {
> -        vmFactory.startMacroRendering(macroName, templateName);
> -    }
> +	/**
> +	 * Checks to see if a VM exists
> +	 * 
> +	 * @param vmName
> +	 *            Name of the Velocimacro.
> +	 * @param templateName
> +	 *            Template on which to look for the Macro.
> +	 * @return True if VM by that name exists, false if not
> +	 */
> +	public boolean isVelocimacro(String vmName, String templateName) {
> +		return vmFactory.isVelocimacro(vmName, templateName);
> +	}
> 
> -    /**
> -     * This method is called after a macro rendering is finished. It cleasr
> -     * all the state saved in the VelociMacroFactory about this particular
> -     * macro.
> -     * @param macroName name of the macro
> -     * @param templateName template name that contains the macro
> -     */
> -    public void endMacroRendering(String macroName, String templateName)
> -    {
> -        vmFactory.endMacroRendering(macroName, templateName);
> -    }
> +	/**
> +	 * tells the vmFactory to dump the specified namespace. This is to support \
> clearing the VM list when in inline-VM-local-scope mode +	 * 
> +	 * @param namespace
> +	 *            Namespace to dump.
> +	 * @return True if namespace was dumped successfully.
> +	 */
> +	public boolean dumpVMNamespace(String namespace) {
> +		return vmFactory.dumpVMNamespace(namespace);
> +	}
> +
> +	/*
> +	 * -------------------------------------------------------------------- R U N T I \
> M E A C C E S S O R M E T H O D S +	 * \
> -------------------------------------------------------------------- These are the \
> getXXX() methods that are a simple wrapper around the configuration +	 * object. \
> This is an attempt to make a the Velocity Runtime the single access point for all \
> things Velocity, and allow the Runtime to adhere as closely as +	 * possible the \
> the Mediator pattern which is the ultimate goal. \
> -------------------------------------------------------------------- +	 */
> +
> +	/**
> +	 * String property accessor method to hide the configuration implementation
> +	 * 
> +	 * @param key
> +	 *            property key
> +	 * @return value of key or null
> +	 */
> +	public String getString(String key) {
> +		return StringUtils.nullTrim(configuration.getString(key));
> +	}
> +
> +	/**
> +	 * Int property accessor method to hide the configuration implementation.
> +	 * 
> +	 * @param key
> +	 *            Property key
> +	 * @return value
> +	 */
> +	public int getInt(String key) {
> +		return configuration.getInt(key);
> +	}
> +
> +	/**
> +	 * Int property accessor method to hide the configuration implementation.
> +	 * 
> +	 * @param key
> +	 *            property key
> +	 * @param defaultValue
> +	 *            The default value.
> +	 * @return value
> +	 */
> +	public int getInt(String key, int defaultValue) {
> +		return configuration.getInt(key, defaultValue);
> +	}
> +
> +	/**
> +	 * Boolean property accessor method to hide the configuration implementation.
> +	 * 
> +	 * @param key
> +	 *            property key
> +	 * @param def
> +	 *            The default value if property not found.
> +	 * @return value of key or default value
> +	 */
> +	public boolean getBoolean(String key, boolean def) {
> +		return configuration.getBoolean(key, def);
> +	}
> +
> +	/**
> +	 * Return the velocity runtime configuration object.
> +	 * 
> +	 * @return Configuration object which houses the Velocity runtime properties.
> +	 */
> +	public ExtendedProperties getConfiguration() {
> +		return configuration;
> +	}
> +
> +	/**
> +	 * Return the Introspector for this instance
> +	 * 
> +	 * @return The Introspector for this instance
> +	 */
> +	public Introspector getIntrospector() {
> +		return introspector;
> +	}
> +
> +	/**
> +	 * Returns the event handlers for the application.
> +	 * 
> +	 * @return The event handlers for the application.
> +	 */
> +	public EventCartridge getApplicationEventCartridge() {
> +		return eventCartridge;
> +	}
> +
> +	/**
> +	 * Gets the application attribute for the given key
> +	 * 
> +	 * @param key
> +	 * @return The application attribute for the given key.
> +	 */
> +	public Object getApplicationAttribute(Object key) {
> +		return applicationAttributes.get(key);
> +	}
> +
> +	/**
> +	 * Sets the application attribute for the given key
> +	 * 
> +	 * @param key
> +	 * @param o
> +	 *            The new application attribute.
> +	 * @return The old value of this attribute or null if it hasn't been set before.
> +	 */
> +	public Object setApplicationAttribute(Object key, Object o) {
> +		return applicationAttributes.put(key, o);
> +	}
> +
> +	/**
> +	 * Returns the Uberspect object for this Instance.
> +	 * 
> +	 * @return The Uberspect object for this Instance.
> +	 */
> +	public Uberspect getUberspect() {
> +		return uberSpect;
> +	}
> +
> +	/**
> +	 * This method is called before a macro is rendered. This method checks whether a \
> macro call is within the allowed calling depth. If the macro call exceeds +	 * the \
> allowed calling depth it will throw an exception. +	 * 
> +	 * @param macroName
> +	 *            name of the macro
> +	 * @param templateName
> +	 *            name of the template file containing the macro
> +	 * @throws MacroOverflowException
> +	 *             if the number of macro calls exceeds the specified value
> +	 */
> +	public void startMacroRendering(String macroName, String templateName) throws \
> MacroOverflowException { +		vmFactory.startMacroRendering(macroName, templateName);
> +	}
> +
> +	/**
> +	 * This method is called after a macro rendering is finished. It cleasr all the \
> state saved in the VelociMacroFactory about this particular macro. +	 * 
> +	 * @param macroName
> +	 *            name of the macro
> +	 * @param templateName
> +	 *            template name that contains the macro
> +	 */
> +	public void endMacroRendering(String macroName, String templateName) {
> +		vmFactory.endMacroRendering(macroName, templateName);
> +	}
> }
> 
> 
> ------------------------------------------------------------------------
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
> For additional commands, e-mail: dev-help@velocity.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic