View Javadoc

1   package org.apache.velocity.runtime;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.Reader;
26  import java.io.StringReader;
27  import java.io.Writer;
28  import java.util.Enumeration;
29  import java.util.HashMap;
30  import java.util.Hashtable;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Properties;
34  
35  import org.apache.commons.collections.ExtendedProperties;
36  import org.apache.velocity.Template;
37  import org.apache.velocity.app.event.EventCartridge;
38  import org.apache.velocity.app.event.EventHandler;
39  import org.apache.velocity.app.event.IncludeEventHandler;
40  import org.apache.velocity.app.event.InvalidReferenceEventHandler;
41  import org.apache.velocity.app.event.MethodExceptionEventHandler;
42  import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
43  import org.apache.velocity.context.Context;
44  import org.apache.velocity.context.InternalContextAdapterImpl;
45  import org.apache.velocity.exception.MethodInvocationException;
46  import org.apache.velocity.exception.ParseErrorException;
47  import org.apache.velocity.exception.ResourceNotFoundException;
48  import org.apache.velocity.exception.TemplateInitException;
49  import org.apache.velocity.exception.VelocityException;
50  import org.apache.velocity.runtime.directive.Directive;
51  import org.apache.velocity.runtime.directive.Macro;
52  import org.apache.velocity.runtime.directive.VelocimacroProxy;
53  import org.apache.velocity.runtime.directive.Scope;
54  import org.apache.velocity.runtime.directive.StopCommand;
55  import org.apache.velocity.runtime.log.Log;
56  import org.apache.velocity.runtime.log.LogManager;
57  import org.apache.velocity.runtime.parser.ParseException;
58  import org.apache.velocity.runtime.parser.Parser;
59  import org.apache.velocity.runtime.parser.node.Node;
60  import org.apache.velocity.runtime.parser.node.SimpleNode;
61  import org.apache.velocity.runtime.resource.ContentResource;
62  import org.apache.velocity.runtime.resource.ResourceManager;
63  import org.apache.velocity.util.ClassUtils;
64  import org.apache.velocity.util.RuntimeServicesAware;
65  import org.apache.velocity.util.StringUtils;
66  import org.apache.velocity.util.introspection.ChainableUberspector;
67  import org.apache.velocity.util.introspection.Introspector;
68  import org.apache.velocity.util.introspection.LinkingUberspector;
69  import org.apache.velocity.util.introspection.Uberspect;
70  import org.apache.velocity.util.introspection.UberspectLoggable;
71  
72  /**
73   * This is the Runtime system for Velocity. It is the
74   * single access point for all functionality in Velocity.
75   * It adheres to the mediator pattern and is the only
76   * structure that developers need to be familiar with
77   * in order to get Velocity to perform.
78   *
79   * The Runtime will also cooperate with external
80   * systems like Turbine. Runtime properties can
81   * set and then the Runtime is initialized.
82   *
83   * Turbine, for example, knows where the templates
84   * are to be loaded from, and where the Velocity
85   * log file should be placed.
86   *
87   * So in the case of Velocity cooperating with Turbine
88   * the code might look something like the following:
89   *
90   * <blockquote><code><pre>
91   * ri.setProperty(Runtime.FILE_RESOURCE_LOADER_PATH, templatePath);
92   * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog);
93   * ri.init();
94   * </pre></code></blockquote>
95   *
96   * <pre>
97   * -----------------------------------------------------------------------
98   * 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
99   * -----------------------------------------------------------------------
100  * init()
101  *
102  * If init() is called by itself the RuntimeInstance will initialize
103  * with a set of default values.
104  * -----------------------------------------------------------------------
105  * init(String/Properties)
106  *
107  * In this case the default velocity properties are layed down
108  * first to provide a solid base, then any properties provided
109  * in the given properties object will override the corresponding
110  * default property.
111  * -----------------------------------------------------------------------
112  * </pre>
113  *
114  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
115  * @author <a href="mailto:jlb@houseofdistraction.com">Jeff Bowden</a>
116  * @author <a href="mailto:geirm@optonline.net">Geir Magusson Jr.</a>
117  * @version $Id: RuntimeInstance.java 1040976 2010-12-01 12:15:39Z apetrelli $
118  */
119 public class RuntimeInstance implements RuntimeConstants, RuntimeServices
120 {
121     /**
122      *  VelocimacroFactory object to manage VMs
123      */
124     private  VelocimacroFactory vmFactory = null;
125 
126     /**
127      * The Runtime logger.  We start with an instance of
128      * a 'primordial logger', which just collects log messages
129      * then, when the log system is initialized, all the
130      * messages get dumpted out of the primordial one into the real one.
131      */
132     private Log log = new Log();
133 
134     /**
135      * The Runtime parser pool
136      */
137     private  ParserPool parserPool;
138 
139     /**
140      * Indicate whether the Runtime is in the midst of initialization.
141      */
142     private boolean initializing = false;
143 
144     /**
145      * Indicate whether the Runtime has been fully initialized.
146      */
147     private volatile boolean initialized = false;
148 
149     /**
150      * These are the properties that are laid down over top
151      * of the default properties when requested.
152      */
153     private  ExtendedProperties overridingProperties = null;
154 
155     /**
156      * This is a hashtable of initialized directives.
157      * The directives that populate this hashtable are
158      * taken from the RUNTIME_DEFAULT_DIRECTIVES
159      * property file.
160      */
161     private Map runtimeDirectives = new Hashtable();
162     /**
163      * Copy of the actual runtimeDirectives that is shared between
164      * parsers. Whenever directives are updated, the synchronized
165      * runtimeDirectives is first updated and then an unsynchronized
166      * copy of it is passed to parsers.
167      */
168     private Map runtimeDirectivesShared;
169 
170     /**
171      * Object that houses the configuration options for
172      * the velocity runtime. The ExtendedProperties object allows
173      * the convenient retrieval of a subset of properties.
174      * For example all the properties for a resource loader
175      * can be retrieved from the main ExtendedProperties object
176      * using something like the following:
177      *
178      * ExtendedProperties loaderConfiguration =
179      *         configuration.subset(loaderID);
180      *
181      * And a configuration is a lot more convenient to deal
182      * with then conventional properties objects, or Maps.
183      */
184     private  ExtendedProperties configuration = new ExtendedProperties();
185 
186     private ResourceManager resourceManager = null;
187 
188     /**
189      * This stores the engine-wide set of event handlers.  Event handlers for
190      * each specific merge are stored in the context.
191      */
192     private EventCartridge eventCartridge = null;
193 
194     /*
195      *  Each runtime instance has it's own introspector
196      *  to ensure that each instance is completely separate.
197      */
198     private Introspector introspector = null;
199 
200     /*
201      * Settings for provision of root scope for evaluate(...) calls.
202      */
203     private String evaluateScopeName = "evaluate";
204     private boolean provideEvaluateScope = false;
205 
206     /*
207      *  Opaque reference to something specificed by the
208      *  application for use in application supplied/specified
209      *  pluggable components
210      */
211     private Map applicationAttributes = null;
212     private Uberspect uberSpect;
213     private String encoding;
214 
215     /**
216      * Creates a new RuntimeInstance object.
217      */
218     public RuntimeInstance()
219     {
220         reset();
221     }
222 
223     /**
224      * This is the primary initialization method in the Velocity
225      * Runtime. The systems that are setup/initialized here are
226      * as follows:
227      *
228      * <ul>
229      *   <li>Logging System</li>
230      *   <li>ResourceManager</li>
231      *   <li>EventHandler</li>
232      *   <li>Parser Pool</li>
233      *   <li>Global Cache</li>
234      *   <li>Static Content Include System</li>
235      *   <li>Velocimacro System</li>
236      * </ul>
237      */
238     public synchronized void init()
239     {
240         if (!initialized && !initializing)
241         {
242             log.debug("Initializing Velocity, Calling init()...");
243             initializing = true;
244 
245             log.trace("*******************************************************************");
246             log.debug("Starting Apache Velocity v2.0-dev (compiled: 2009-02-05 07:40:05)");
247             log.trace("RuntimeInstance initializing.");
248 
249             initializeProperties();
250             initializeLog();
251             initializeResourceManager();
252             initializeDirectives();
253             initializeEventHandlers();
254             initializeParserPool();
255 
256             initializeIntrospection();
257             initializeEvaluateScopeSettings();
258             /*
259              *  initialize the VM Factory.  It will use the properties
260              * accessable from Runtime, so keep this here at the end.
261              */
262             vmFactory.initVelocimacro();
263 
264             log.trace("RuntimeInstance successfully initialized.");
265 
266             initialized = true;
267             initializing = false;
268         }
269     }
270 
271     /**
272      * Resets the instance, so Velocity can be re-initialized again.
273      *
274      * @since 2.0.0
275      */
276     public synchronized void reset()
277     {
278         this.configuration = new ExtendedProperties();
279         this.encoding = null;
280         this.evaluateScopeName = "evaluate";
281         this.eventCartridge = null;
282         this.initialized = false;
283         this.initializing = false;
284         this.log = new Log();
285         this.overridingProperties = null;
286         this.parserPool = null;
287         this.provideEvaluateScope = false;
288         this.resourceManager = null;
289         this.runtimeDirectives = new Hashtable();
290         this.runtimeDirectivesShared = null;
291         this.uberSpect = null;
292 
293         /*
294          *  create a VM factory, introspector, and application attributes
295          */
296         vmFactory = new VelocimacroFactory( this );
297 
298         /*
299          *  make a new introspector and initialize it
300          */
301         introspector = new Introspector(getLog());
302 
303         /*
304          * and a store for the application attributes
305          */
306         applicationAttributes = new HashMap();
307     }
308 
309     /**
310      * Returns true if the RuntimeInstance has been successfully initialized.
311      * @return True if the RuntimeInstance has been successfully initialized.
312      * @since 1.5
313      */
314     public boolean isInitialized()
315     {
316         return initialized;
317     }
318 
319     /**
320      * Init or die! (with some log help, of course)
321      */
322     private void requireInitialization()
323     {
324         if (!initialized)
325         {
326             try
327             {
328                 init();
329             }
330             catch (Exception e)
331             {
332                 getLog().error("Could not auto-initialize Velocity", e);
333                 throw new RuntimeException("Velocity could not be initialized!", e);
334             }
335         }
336     }
337 
338     /**
339      *  Gets the classname for the Uberspect introspection package and
340      *  instantiates an instance.
341      */
342     private void initializeIntrospection()
343     {
344         String[] uberspectors = configuration.getStringArray(RuntimeConstants.UBERSPECT_CLASSNAME);
345         for (int i=0; i <uberspectors.length;i++)
346         {
347             String rm = uberspectors[i];
348             Object o = null;
349 
350             try
351             {
352                o = ClassUtils.getNewInstance( rm );
353             }
354             catch (ClassNotFoundException cnfe)
355             {
356                 String err = "The specified class for Uberspect (" + rm
357                     + ") does not exist or is not accessible to the current classloader.";
358                 log.error(err);
359                 throw new VelocityException(err, cnfe);
360             }
361             catch (InstantiationException ie)
362             {
363               throw new VelocityException("Could not instantiate class '" + rm + "'", ie);
364             }
365             catch (IllegalAccessException ae)
366             {
367               throw new VelocityException("Cannot access class '" + rm + "'", ae);
368             }
369 
370             if (!(o instanceof Uberspect))
371             {
372                 String err = "The specified class for Uberspect ("
373                     + rm + ") does not implement " + Uberspect.class.getName()
374                     + "; Velocity is not initialized correctly.";
375 
376                 log.error(err);
377                 throw new VelocityException(err);
378             }
379 
380             Uberspect u = (Uberspect)o;
381 
382             if (u instanceof UberspectLoggable)
383             {
384                 ((UberspectLoggable)u).setLog(getLog());
385             }
386 
387             if (u instanceof RuntimeServicesAware)
388             {
389                 ((RuntimeServicesAware)u).setRuntimeServices(this);
390             }
391 
392             if (uberSpect == null)
393             {
394                 uberSpect = u;
395             }
396             else
397             {
398                 if (u instanceof ChainableUberspector)
399                 {
400                     ((ChainableUberspector)u).wrap(uberSpect);
401                     uberSpect = u;
402                 }
403                 else
404                 {
405                     uberSpect = new LinkingUberspector(uberSpect,u);
406                 }
407             }
408         }
409 
410         if(uberSpect != null)
411         {
412             uberSpect.init();
413         }
414         else
415         {
416            /*
417             *  someone screwed up.  Lets not fool around...
418             */
419 
420            String err = "It appears that no class was specified as the"
421            + " Uberspect.  Please ensure that all configuration"
422            + " information is correct.";
423 
424            log.error(err);
425            throw new VelocityException(err);
426         }
427     }
428 
429     /**
430      * Initializes the Velocity Runtime with properties file.
431      * The properties file may be in the file system proper,
432      * or the properties file may be in the classpath.
433      */
434     private void setDefaultProperties()
435     {
436         InputStream inputStream = null;
437         try
438         {
439             inputStream = getClass()
440                 .getResourceAsStream('/' + DEFAULT_RUNTIME_PROPERTIES);
441 
442             configuration.load( inputStream );
443 
444             if (log.isDebugEnabled())
445             {
446                 log.debug("Default Properties File: " +
447                     new File(DEFAULT_RUNTIME_PROPERTIES).getPath());
448             }
449 
450 
451         }
452         catch (IOException ioe)
453         {
454             String msg = "Cannot get Velocity Runtime default properties!";
455             log.error(msg, ioe);
456             throw new RuntimeException(msg, ioe);
457         }
458         finally
459         {
460             try
461             {
462                 if (inputStream != null)
463                 {
464                     inputStream.close();
465                 }
466             }
467             catch (IOException ioe)
468             {
469                 String msg = "Cannot close Velocity Runtime default properties!";
470                 log.error(msg, ioe);
471                 throw new RuntimeException(msg, ioe);
472             }
473         }
474     }
475 
476     /**
477      * Allows an external system to set a property in
478      * the Velocity Runtime.
479      *
480      * @param key property key
481      * @param  value property value
482      */
483     public void setProperty(String key, Object value)
484     {
485         if (overridingProperties == null)
486         {
487             overridingProperties = new ExtendedProperties();
488         }
489 
490         overridingProperties.setProperty(key, value);
491     }
492 
493 
494     /**
495      * Add all properties contained in the file fileName to the RuntimeInstance properties
496      */
497     public void setProperties(String fileName)
498     {
499         ExtendedProperties props = null;
500         try
501         {
502               props = new ExtendedProperties(fileName);
503         }
504         catch (IOException e)
505         {
506               throw new VelocityException("Error reading properties from '"
507                 + fileName + "'", e);
508         }
509 
510         Enumeration en = props.keys();
511         while (en.hasMoreElements())
512         {
513             String key = en.nextElement().toString();
514             setProperty(key, props.get(key));
515         }
516     }
517 
518 
519     /**
520      * Add all the properties in props to the RuntimeInstance properties
521      */
522     public void setProperties(Properties props)
523     {
524         Enumeration en = props.keys();
525         while (en.hasMoreElements())
526         {
527             String key = en.nextElement().toString();
528             setProperty(key, props.get(key));
529         }
530     }
531 
532     /**
533      * Allow an external system to set an ExtendedProperties
534      * object to use. This is useful where the external
535      * system also uses the ExtendedProperties class and
536      * the velocity configuration is a subset of
537      * parent application's configuration. This is
538      * the case with Turbine.
539      *
540      * @param  configuration
541      */
542     public void setConfiguration( ExtendedProperties configuration)
543     {
544         if (overridingProperties == null)
545         {
546             overridingProperties = configuration;
547         }
548         else
549         {
550             // Avoid possible ConcurrentModificationException
551             if (overridingProperties != configuration)
552             {
553                 overridingProperties.combine(configuration);
554             }
555         }
556     }
557 
558     /**
559      * Add a property to the configuration. If it already
560      * exists then the value stated here will be added
561      * to the configuration entry. For example, if
562      *
563      * resource.loader = file
564      *
565      * is already present in the configuration and you
566      *
567      * addProperty("resource.loader", "classpath")
568      *
569      * Then you will end up with a Vector like the
570      * following:
571      *
572      * ["file", "classpath"]
573      *
574      * @param  key
575      * @param  value
576      */
577     public void addProperty(String key, Object value)
578     {
579         if (overridingProperties == null)
580         {
581             overridingProperties = new ExtendedProperties();
582         }
583 
584         overridingProperties.addProperty(key, value);
585     }
586 
587     /**
588      * Clear the values pertaining to a particular
589      * property.
590      *
591      * @param key of property to clear
592      */
593     public void clearProperty(String key)
594     {
595         if (overridingProperties != null)
596         {
597             overridingProperties.clearProperty(key);
598         }
599     }
600 
601     /**
602      *  Allows an external caller to get a property.  The calling
603      *  routine is required to know the type, as this routine
604      *  will return an Object, as that is what properties can be.
605      *
606      *  @param key property to return
607      *  @return Value of the property or null if it does not exist.
608      */
609     public Object getProperty(String key)
610     {
611         Object o = null;
612 
613         /**
614          * Before initialization, check the user-entered properties first.
615          */
616         if (!initialized && overridingProperties != null)
617         {
618             o = overridingProperties.get(key);
619         }
620 
621         /**
622          * After initialization, configuration will hold all properties.
623          */
624         if (o == null)
625         {
626             o = configuration.getProperty(key);
627         }
628         if (o instanceof String)
629         {
630             return StringUtils.nullTrim((String) o);
631         }
632         else
633         {
634             return o;
635         }
636     }
637 
638     /**
639      * Initialize Velocity properties, if the default
640      * properties have not been laid down first then
641      * do so. Then proceed to process any overriding
642      * properties. Laying down the default properties
643      * gives a much greater chance of having a
644      * working system.
645      */
646     private void initializeProperties()
647     {
648         /*
649          * Always lay down the default properties first as
650          * to provide a solid base.
651          */
652         if (configuration.isInitialized() == false)
653         {
654             setDefaultProperties();
655         }
656 
657         if( overridingProperties != null)
658         {
659             configuration.combine(overridingProperties);
660         }
661     }
662 
663     /**
664      * Initialize the Velocity Runtime with a Properties
665      * object.
666      *
667      * @param p Velocity properties for initialization
668      */
669     public void init(Properties p)
670     {
671         setProperties(ExtendedProperties.convertProperties(p));
672         init();
673     }
674 
675     private void setProperties(ExtendedProperties p)
676     {
677         if (overridingProperties == null)
678         {
679             overridingProperties = p;
680         }
681         else
682         {
683             overridingProperties.combine(p);
684         }
685     }
686 
687     /**
688      * Initialize the Velocity Runtime with the name of
689      * ExtendedProperties object.
690      *
691      * @param configurationFile
692      */
693     public void init(String configurationFile)
694     {
695         try
696         {
697             setProperties(new ExtendedProperties(configurationFile));
698         }
699         catch (IOException e)
700         {
701             throw new VelocityException("Error reading properties from '"
702                 + configurationFile + "'", e);
703         }
704         init();
705     }
706 
707     private void initializeResourceManager()
708     {
709         /*
710          * Which resource manager?
711          */
712         String rm = getString(RuntimeConstants.RESOURCE_MANAGER_CLASS);
713 
714         if (rm != null && rm.length() > 0)
715         {
716             /*
717              *  if something was specified, then make one.
718              *  if that isn't a ResourceManager, consider
719              *  this a huge error and throw
720              */
721 
722             Object o = null;
723 
724             try
725             {
726                o = ClassUtils.getNewInstance( rm );
727             }
728             catch (ClassNotFoundException cnfe )
729             {
730                 String err = "The specified class for ResourceManager (" + rm
731                     + ") does not exist or is not accessible to the current classloader.";
732                 log.error(err);
733                 throw new VelocityException(err, cnfe);
734             }
735             catch (InstantiationException ie)
736             {
737               throw new VelocityException("Could not instantiate class '" + rm + "'", ie);
738             }
739             catch (IllegalAccessException ae)
740             {
741               throw new VelocityException("Cannot access class '" + rm + "'", ae);
742             }
743 
744             if (!(o instanceof ResourceManager))
745             {
746                 String err = "The specified class for ResourceManager (" + rm
747                     + ") does not implement " + ResourceManager.class.getName()
748                     + "; Velocity is not initialized correctly.";
749 
750                 log.error(err);
751                 throw new VelocityException(err);
752             }
753 
754             resourceManager = (ResourceManager) o;
755 
756             resourceManager.initialize(this);
757          }
758          else
759          {
760             /*
761              *  someone screwed up.  Lets not fool around...
762              */
763 
764             String err = "It appears that no class was specified as the"
765             + " ResourceManager.  Please ensure that all configuration"
766             + " information is correct.";
767 
768             log.error(err);
769             throw new VelocityException( err );
770         }
771     }
772 
773     private void initializeEventHandlers()
774     {
775 
776         eventCartridge = new EventCartridge();
777 
778         /**
779          * For each type of event handler, get the class name, instantiate it, and store it.
780          */
781 
782         String[] referenceinsertion = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION);
783         if ( referenceinsertion != null )
784         {
785             for ( int i=0; i < referenceinsertion.length; i++ )
786             {
787                 EventHandler ev = initializeSpecificEventHandler(referenceinsertion[i],RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION,ReferenceInsertionEventHandler.class);
788                 if (ev != null)
789                     eventCartridge.addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev);
790             }
791         }
792 
793         String[] methodexception = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION);
794         if ( methodexception != null )
795         {
796             for ( int i=0; i < methodexception.length; i++ )
797             {
798                 EventHandler ev = initializeSpecificEventHandler(methodexception[i],RuntimeConstants.EVENTHANDLER_METHODEXCEPTION,MethodExceptionEventHandler.class);
799                 if (ev != null)
800                     eventCartridge.addMethodExceptionHandler((MethodExceptionEventHandler) ev);
801             }
802         }
803 
804         String[] includeHandler = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INCLUDE);
805         if ( includeHandler != null )
806         {
807             for ( int i=0; i < includeHandler.length; i++ )
808             {
809                 EventHandler ev = initializeSpecificEventHandler(includeHandler[i],RuntimeConstants.EVENTHANDLER_INCLUDE,IncludeEventHandler.class);
810                 if (ev != null)
811                     eventCartridge.addIncludeEventHandler((IncludeEventHandler) ev);
812             }
813         }
814 
815         String[] invalidReferenceSet = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES);
816         if ( invalidReferenceSet != null )
817         {
818             for ( int i=0; i < invalidReferenceSet.length; i++ )
819             {
820                 EventHandler ev = initializeSpecificEventHandler(invalidReferenceSet[i],RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES,InvalidReferenceEventHandler.class);
821                 if (ev != null)
822                 {
823                     eventCartridge.addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev);
824                 }
825             }
826         }
827 
828 
829     }
830 
831     private EventHandler initializeSpecificEventHandler(String classname, String paramName, Class EventHandlerInterface)
832     {
833         if ( classname != null && classname.length() > 0)
834         {
835             Object o = null;
836             try
837             {
838                 o = ClassUtils.getNewInstance(classname);
839             }
840             catch (ClassNotFoundException cnfe )
841             {
842                 String err = "The specified class for "
843                     + paramName + " (" + classname
844                     + ") does not exist or is not accessible to the current classloader.";
845                 log.error(err);
846                 throw new VelocityException(err, cnfe);
847             }
848             catch (InstantiationException ie)
849             {
850               throw new VelocityException("Could not instantiate class '" + classname + "'", ie);
851             }
852             catch (IllegalAccessException ae)
853             {
854               throw new VelocityException("Cannot access class '" + classname + "'", ae);
855             }
856 
857             if (!EventHandlerInterface.isAssignableFrom(EventHandlerInterface))
858             {
859                 String err = "The specified class for " + paramName + " ("
860                     + classname + ") does not implement "
861                     + EventHandlerInterface.getName()
862                     + "; Velocity is not initialized correctly.";
863 
864                 log.error(err);
865                 throw new VelocityException(err);
866             }
867 
868             EventHandler ev = (EventHandler) o;
869             if ( ev instanceof RuntimeServicesAware )
870                 ((RuntimeServicesAware) ev).setRuntimeServices(this);
871             return ev;
872 
873         } else
874             return null;
875     }
876 
877     /**
878      * Initialize the Velocity logging system.
879      */
880     private void initializeLog()
881     {
882         // since the Log we started with was just placeholding,
883         // let's update it with the real LogChute settings.
884         try
885         {
886             LogManager.updateLog(this.log, this);
887         }
888         catch (Exception e)
889         {
890             throw new VelocityException("Error initializing log: " + e.getMessage(), e);
891         }
892     }
893 
894 
895     /**
896      * This methods initializes all the directives
897      * that are used by the Velocity Runtime. The
898      * directives to be initialized are listed in
899      * the RUNTIME_DEFAULT_DIRECTIVES properties
900      * file.
901      */
902     private void initializeDirectives()
903     {
904         Properties directiveProperties = new Properties();
905 
906         /*
907          * Grab the properties file with the list of directives
908          * that we should initialize.
909          */
910 
911         InputStream inputStream = null;
912 
913         try
914         {
915             inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_DIRECTIVES);
916 
917             if (inputStream == null)
918             {
919                 throw new VelocityException("Error loading directive.properties! " +
920                                     "Something is very wrong if these properties " +
921                                     "aren't being located. Either your Velocity " +
922                                     "distribution is incomplete or your Velocity " +
923                                     "jar file is corrupted!");
924             }
925 
926             directiveProperties.load(inputStream);
927 
928         }
929         catch (IOException ioe)
930         {
931             String msg = "Error while loading directive properties!";
932             log.error(msg, ioe);
933             throw new RuntimeException(msg, ioe);
934         }
935         finally
936         {
937             try
938             {
939                 if (inputStream != null)
940                 {
941                     inputStream.close();
942                 }
943             }
944             catch (IOException ioe)
945             {
946                 String msg = "Cannot close directive properties!";
947                 log.error(msg, ioe);
948                 throw new RuntimeException(msg, ioe);
949             }
950         }
951 
952 
953         /*
954          * Grab all the values of the properties. These
955          * are all class names for example:
956          *
957          * org.apache.velocity.runtime.directive.Foreach
958          */
959         Enumeration directiveClasses = directiveProperties.elements();
960 
961         while (directiveClasses.hasMoreElements())
962         {
963             String directiveClass = (String) directiveClasses.nextElement();
964             loadDirective(directiveClass);
965             log.debug("Loaded System Directive: " + directiveClass);
966         }
967 
968         /*
969          *  now the user's directives
970          */
971 
972         String[] userdirective = configuration.getStringArray("userdirective");
973 
974         for( int i = 0; i < userdirective.length; i++)
975         {
976             loadDirective(userdirective[i]);
977             if (log.isDebugEnabled())
978             {
979                 log.debug("Loaded User Directive: " + userdirective[i]);
980             }
981         }
982 
983     }
984 
985     /**
986      * Programatically add a directive.
987      * @param directive
988      */
989     public synchronized void addDirective(Directive directive)
990     {
991         runtimeDirectives.put(directive.getName(), directive);
992         updateSharedDirectivesMap();
993     }
994 
995     /**
996      * Retrieve a previously instantiated directive.
997      * @param name name of the directive
998      * @return the {@link Directive} for that name
999      */
1000     public Directive getDirective(String name)
1001     {
1002         return (Directive) runtimeDirectivesShared.get(name);
1003     }
1004 
1005     /**
1006      * Remove a directive.
1007      * @param name name of the directive.
1008      */
1009     public synchronized void removeDirective(String name)
1010     {
1011         runtimeDirectives.remove(name);
1012         updateSharedDirectivesMap();
1013     }
1014 
1015     /**
1016      * Makes an unsynchronized copy of the directives map
1017      * that is used for Directive lookups by all parsers.
1018      *
1019      * This follows Copy-on-Write pattern. The cost of creating
1020      * a new map is acceptable since directives are typically
1021      * set and modified only during Velocity setup phase.
1022      */
1023     private void updateSharedDirectivesMap()
1024     {
1025         Map tmp = new HashMap(runtimeDirectives);
1026         runtimeDirectivesShared = tmp;
1027     }
1028 
1029     /**
1030      *  instantiates and loads the directive with some basic checks
1031      *
1032      *  @param directiveClass classname of directive to load
1033      */
1034     public void loadDirective(String directiveClass)
1035     {
1036         try
1037         {
1038             Object o = ClassUtils.getNewInstance( directiveClass );
1039 
1040             if (o instanceof Directive)
1041             {
1042                 Directive directive = (Directive) o;
1043                 addDirective(directive);
1044             }
1045             else
1046             {
1047                 String msg = directiveClass + " does not implement "
1048                     + Directive.class.getName() + "; it cannot be loaded.";
1049                 log.error(msg);
1050                 throw new VelocityException(msg);
1051             }
1052         }
1053         // The ugly threesome:  ClassNotFoundException,
1054         // IllegalAccessException, InstantiationException.
1055         // Ignore Findbugs complaint for now.
1056         catch (Exception e)
1057         {
1058             String msg = "Failed to load Directive: " + directiveClass;
1059             log.error(msg, e);
1060             throw new VelocityException(msg, e);
1061         }
1062     }
1063 
1064 
1065     /**
1066      * Initializes the Velocity parser pool.
1067      */
1068     private void initializeParserPool()
1069     {
1070         /*
1071          * Which parser pool?
1072          */
1073         String pp = getString(RuntimeConstants.PARSER_POOL_CLASS);
1074 
1075         if (pp != null && pp.length() > 0)
1076         {
1077             /*
1078              *  if something was specified, then make one.
1079              *  if that isn't a ParserPool, consider
1080              *  this a huge error and throw
1081              */
1082 
1083             Object o = null;
1084 
1085             try
1086             {
1087                 o = ClassUtils.getNewInstance( pp );
1088             }
1089             catch (ClassNotFoundException cnfe )
1090             {
1091                 String err = "The specified class for ParserPool ("
1092                     + pp
1093                     + ") does not exist (or is not accessible to the current classloader.";
1094                 log.error(err);
1095                 throw new VelocityException(err, cnfe);
1096             }
1097             catch (InstantiationException ie)
1098             {
1099               throw new VelocityException("Could not instantiate class '" + pp + "'", ie);
1100             }
1101             catch (IllegalAccessException ae)
1102             {
1103               throw new VelocityException("Cannot access class '" + pp + "'", ae);
1104             }
1105 
1106             if (!(o instanceof ParserPool))
1107             {
1108                 String err = "The specified class for ParserPool ("
1109                     + pp + ") does not implement " + ParserPool.class
1110                     + " Velocity not initialized correctly.";
1111 
1112                 log.error(err);
1113                 throw new VelocityException(err);
1114             }
1115 
1116             parserPool = (ParserPool) o;
1117 
1118             parserPool.initialize(this);
1119         }
1120         else
1121         {
1122             /*
1123              *  someone screwed up.  Lets not fool around...
1124              */
1125 
1126             String err = "It appears that no class was specified as the"
1127                 + " ParserPool.  Please ensure that all configuration"
1128                 + " information is correct.";
1129 
1130             log.error(err);
1131             throw new VelocityException( err );
1132         }
1133 
1134     }
1135 
1136     /**
1137      * Returns a JavaCC generated Parser.
1138      *
1139      * @return Parser javacc generated parser
1140      */
1141     public Parser createNewParser()
1142     {
1143         requireInitialization();
1144 
1145         Parser parser = new Parser(this);
1146         return parser;
1147     }
1148 
1149     /**
1150      * Parse the input and return the root of
1151      * AST node structure.
1152      * <br><br>
1153      *  In the event that it runs out of parsers in the
1154      *  pool, it will create and let them be GC'd
1155      *  dynamically, logging that it has to do that.  This
1156      *  is considered an exceptional condition.  It is
1157      *  expected that the user will set the
1158      *  PARSER_POOL_SIZE property appropriately for their
1159      *  application.  We will revisit this.
1160      *
1161      * @param string String to be parsed
1162      * @param templateName name of the template being parsed
1163      * @return A root node representing the template as an AST tree.
1164      * @throws ParseException When the string could not be parsed as a template.
1165      * @since 1.6
1166      */
1167     public SimpleNode parse(String string, String templateName)
1168         throws ParseException
1169     {
1170         return parse(new StringReader(string), templateName);
1171     }
1172 
1173     /**
1174      * Parse the input and return the root of
1175      * AST node structure.
1176      * <br><br>
1177      *  In the event that it runs out of parsers in the
1178      *  pool, it will create and let them be GC'd
1179      *  dynamically, logging that it has to do that.  This
1180      *  is considered an exceptional condition.  It is
1181      *  expected that the user will set the
1182      *  PARSER_POOL_SIZE property appropriately for their
1183      *  application.  We will revisit this.
1184      *
1185      * @param reader Reader retrieved by a resource loader
1186      * @param templateName name of the template being parsed
1187      * @return A root node representing the template as an AST tree.
1188      * @throws ParseException When the template could not be parsed.
1189      */
1190     public SimpleNode parse(Reader reader, String templateName)
1191         throws ParseException
1192     {
1193         /*
1194          *  do it and dump the VM namespace for this template
1195          */
1196         return parse(reader, templateName, true);
1197     }
1198 
1199     /**
1200      *  Parse the input and return the root of the AST node structure.
1201      *
1202      * @param reader Reader retrieved by a resource loader
1203      * @param templateName name of the template being parsed
1204      * @param dumpNamespace flag to dump the Velocimacro namespace for this template
1205      * @return A root node representing the template as an AST tree.
1206      * @throws ParseException When the template could not be parsed.
1207      */
1208     public SimpleNode parse(Reader reader, String templateName, boolean dumpNamespace)
1209         throws ParseException
1210     {
1211         requireInitialization();
1212 
1213         Parser parser = (Parser) parserPool.get();
1214         boolean keepParser = true;
1215         if (parser == null)
1216         {
1217             /*
1218              *  if we couldn't get a parser from the pool make one and log it.
1219              */
1220             if (log.isInfoEnabled())
1221             {
1222                 log.info("Runtime : ran out of parsers. Creating a new one. "
1223                       + " Please increment the parser.pool.size property."
1224                       + " The current value is too small.");
1225             }
1226             parser = createNewParser();
1227             keepParser = false;
1228         }
1229 
1230         try
1231         {
1232             /*
1233              *  dump namespace if we are told to.  Generally, you want to
1234              *  do this - you don't in special circumstances, such as
1235              *  when a VM is getting init()-ed & parsed
1236              */
1237             if (dumpNamespace)
1238             {
1239                 dumpVMNamespace(templateName);
1240             }
1241             return parser.parse(reader, templateName);
1242         }
1243         finally
1244         {
1245             if (keepParser)
1246             {
1247                 parserPool.put(parser);
1248             }
1249 
1250         }
1251     }
1252 
1253     private void initializeEvaluateScopeSettings()
1254     {
1255         String property = evaluateScopeName+'.'+PROVIDE_SCOPE_CONTROL;
1256         provideEvaluateScope = getBoolean(property, provideEvaluateScope);
1257     }
1258 
1259     /**
1260      * Renders the input string using the context into the output writer.
1261      * To be used when a template is dynamically constructed, or want to use
1262      * Velocity as a token replacer.
1263      *
1264      * @param context context to use in rendering input string
1265      * @param out  Writer in which to render the output
1266      * @param logTag  string to be used as the template name for log
1267      *                messages in case of error
1268      * @param instring input string containing the VTL to be rendered
1269      *
1270      * @return true if successful, false otherwise.  If false, see
1271      *              Velocity runtime log
1272      * @throws ParseErrorException The template could not be parsed.
1273      * @throws MethodInvocationException A method on a context object could not be invoked.
1274      * @throws ResourceNotFoundException A referenced resource could not be loaded.
1275      * @since Velocity 1.6
1276      */
1277     public boolean evaluate(Context context,  Writer out,
1278                             String logTag, String instring)
1279     {
1280         return evaluate(context, out, logTag, new StringReader(instring));
1281     }
1282 
1283     /**
1284      * Renders the input reader using the context into the output writer.
1285      * To be used when a template is dynamically constructed, or want to
1286      * use Velocity as a token replacer.
1287      *
1288      * @param context context to use in rendering input string
1289      * @param writer  Writer in which to render the output
1290      * @param logTag  string to be used as the template name for log messages
1291      *                in case of error
1292      * @param reader Reader containing the VTL to be rendered
1293      *
1294      * @return true if successful, false otherwise.  If false, see
1295      *              Velocity runtime log
1296      * @throws ParseErrorException The template could not be parsed.
1297      * @throws MethodInvocationException A method on a context object could not be invoked.
1298      * @throws ResourceNotFoundException A referenced resource could not be loaded.
1299      * @since Velocity 1.6
1300      */
1301     public boolean evaluate(Context context, Writer writer,
1302                             String logTag, Reader reader)
1303     {
1304         if (logTag == null)
1305         {
1306             throw new NullPointerException("logTag (i.e. template name) cannot be null, you must provide an identifier for the content being evaluated");
1307         }
1308 
1309         SimpleNode nodeTree = null;
1310         try
1311         {
1312             nodeTree = parse(reader, logTag);
1313         }
1314         catch (ParseException pex)
1315         {
1316             throw new ParseErrorException(pex, null);
1317         }
1318         catch (TemplateInitException pex)
1319         {
1320             throw new ParseErrorException(pex, null);
1321         }
1322 
1323         if (nodeTree == null)
1324         {
1325             return false;
1326         }
1327         else
1328         {
1329             return render(context, writer, logTag, nodeTree);
1330         }
1331     }
1332 
1333 
1334     /**
1335      * Initializes and renders the AST {@link SimpleNode} using the context
1336      * into the output writer.
1337      *
1338      * @param context context to use in rendering input string
1339      * @param writer  Writer in which to render the output
1340      * @param logTag  string to be used as the template name for log messages
1341      *                in case of error
1342      * @param nodeTree SimpleNode which is the root of the AST to be rendered
1343      *
1344      * @return true if successful, false otherwise.  If false, see
1345      *              Velocity runtime log for errors
1346      * @throws ParseErrorException The template could not be parsed.
1347      * @throws MethodInvocationException A method on a context object could not be invoked.
1348      * @throws ResourceNotFoundException A referenced resource could not be loaded.
1349      * @since Velocity 1.6
1350      */
1351     public boolean render(Context context, Writer writer,
1352                           String logTag, SimpleNode nodeTree)
1353     {
1354         /*
1355          * we want to init then render
1356          */
1357         InternalContextAdapterImpl ica =
1358             new InternalContextAdapterImpl(context);
1359 
1360         ica.pushCurrentTemplateName(logTag);
1361 
1362         try
1363         {
1364             try
1365             {
1366                 nodeTree.init(ica, this);
1367             }
1368             catch (TemplateInitException pex)
1369             {
1370                 throw new ParseErrorException(pex, null);
1371             }
1372             /**
1373              * pass through application level runtime exceptions
1374              */
1375             catch(RuntimeException e)
1376             {
1377                 throw e;
1378             }
1379             catch(Exception e)
1380             {
1381                 String msg = "RuntimeInstance.render(): init exception for tag = "+logTag;
1382                 getLog().error(msg, e);
1383                 throw new VelocityException(msg, e);
1384             }
1385 
1386             try
1387             {
1388                 if (provideEvaluateScope)
1389                 {
1390                     Object previous = ica.get(evaluateScopeName);
1391                     context.put(evaluateScopeName, new Scope(this, previous));
1392                 }
1393                 nodeTree.render(ica, writer);
1394             }
1395             catch (StopCommand stop)
1396             {
1397                 if (!stop.isFor(this))
1398                 {
1399                     throw stop;
1400                 }
1401                 else if (getLog().isDebugEnabled())
1402                 {
1403                     getLog().debug(stop.getMessage());
1404                 }
1405             }
1406             catch (IOException e)
1407             {
1408                 throw new VelocityException("IO Error in writer: " + e.getMessage(), e);
1409             }
1410         }
1411         finally
1412         {
1413             ica.popCurrentTemplateName();
1414             if (provideEvaluateScope)
1415             {
1416                 Object obj = ica.get(evaluateScopeName);
1417                 if (obj instanceof Scope)
1418                 {
1419                     Scope scope = (Scope)obj;
1420                     if (scope.getParent() != null)
1421                     {
1422                         ica.put(evaluateScopeName, scope.getParent());
1423                     }
1424                     else if (scope.getReplaced() != null)
1425                     {
1426                         ica.put(evaluateScopeName, scope.getReplaced());
1427                     }
1428                     else
1429                     {
1430                         ica.remove(evaluateScopeName);
1431                     }
1432                 }
1433             }
1434         }
1435 
1436         return true;
1437     }
1438 
1439     /**
1440      * Invokes a currently registered Velocimacro with the params provided
1441      * and places the rendered stream into the writer.
1442      * <br>
1443      * Note : currently only accepts args to the VM if they are in the context.
1444      *
1445      * @param vmName name of Velocimacro to call
1446      * @param logTag string to be used for template name in case of error. if null,
1447      *               the vmName will be used
1448      * @param params keys for args used to invoke Velocimacro, in java format
1449      *               rather than VTL (eg  "foo" or "bar" rather than "$foo" or "$bar")
1450      * @param context Context object containing data/objects used for rendering.
1451      * @param writer  Writer for output stream
1452      * @return true if Velocimacro exists and successfully invoked, false otherwise.
1453      * @since 1.6
1454      */
1455     public boolean invokeVelocimacro(final String vmName, String logTag,
1456                                      String[] params, final Context context,
1457                                      final Writer writer)
1458      {
1459         /* check necessary parameters */
1460         if (vmName == null || context == null || writer == null)
1461         {
1462             String msg = "RuntimeInstance.invokeVelocimacro() : invalid call : vmName, context, and writer must not be null";
1463             getLog().error(msg);
1464             throw new NullPointerException(msg);
1465         }
1466 
1467         /* handle easily corrected parameters */
1468         if (logTag == null)
1469         {
1470             logTag = vmName;
1471         }
1472         if (params == null)
1473         {
1474             params = new String[0];
1475         }
1476 
1477         /* does the VM exist? */
1478         if (!isVelocimacro(vmName, logTag))
1479         {
1480             String msg = "RuntimeInstance.invokeVelocimacro() : VM '" + vmName
1481                          + "' is not registered.";
1482             getLog().error(msg);
1483             throw new VelocityException(msg);
1484         }
1485 
1486         /* now just create the VM call, and use evaluate */
1487         StringBuilder template = new StringBuilder("#");
1488         template.append(vmName);
1489         template.append("(");
1490         for( int i = 0; i < params.length; i++)
1491         {
1492             template.append(" $");
1493             template.append(params[i]);
1494         }
1495         template.append(" )");
1496 
1497         return evaluate(context, writer, logTag, template.toString());
1498     }
1499 
1500     /**
1501      * Retrieves and caches the configured default encoding
1502      * for better performance. (VELOCITY-606)
1503      */
1504     private String getDefaultEncoding()
1505     {
1506         if (encoding == null)
1507         {
1508             encoding = getString(INPUT_ENCODING, ENCODING_DEFAULT);
1509         }
1510         return encoding;
1511     }
1512 
1513     /**
1514      * Returns a <code>Template</code> from the resource manager.
1515      * This method assumes that the character encoding of the
1516      * template is set by the <code>input.encoding</code>
1517      * property.  The default is "ISO-8859-1"
1518      *
1519      * @param name The file name of the desired template.
1520      * @return     The template.
1521      * @throws ResourceNotFoundException if template not found
1522      *          from any available source.
1523      * @throws ParseErrorException if template cannot be parsed due
1524      *          to syntax (or other) error.
1525      */
1526     public Template getTemplate(String name)
1527         throws ResourceNotFoundException, ParseErrorException
1528     {
1529         return getTemplate(name, getDefaultEncoding());
1530     }
1531 
1532     /**
1533      * Returns a <code>Template</code> from the resource manager
1534      *
1535      * @param name The  name of the desired template.
1536      * @param encoding Character encoding of the template
1537      * @return     The template.
1538      * @throws ResourceNotFoundException if template not found
1539      *          from any available source.
1540      * @throws ParseErrorException if template cannot be parsed due
1541      *          to syntax (or other) error.
1542      */
1543     public Template getTemplate(String name, String  encoding)
1544         throws ResourceNotFoundException, ParseErrorException
1545     {
1546         requireInitialization();
1547 
1548         return (Template)
1549                 resourceManager.getResource(name,
1550                     ResourceManager.RESOURCE_TEMPLATE, encoding);
1551     }
1552 
1553     /**
1554      * Returns a static content resource from the
1555      * resource manager.  Uses the current value
1556      * if INPUT_ENCODING as the character encoding.
1557      *
1558      * @param name Name of content resource to get
1559      * @return parsed ContentResource object ready for use
1560      * @throws ResourceNotFoundException if template not found
1561      *          from any available source.
1562      * @throws ParseErrorException When the template could not be parsed.
1563      */
1564     public ContentResource getContent(String name)
1565         throws ResourceNotFoundException, ParseErrorException
1566     {
1567         /*
1568          *  the encoding is irrelvant as we don't do any converstion
1569          *  the bytestream should be dumped to the output stream
1570          */
1571 
1572         return getContent(name, getDefaultEncoding());
1573     }
1574 
1575     /**
1576      * Returns a static content resource from the
1577      * resource manager.
1578      *
1579      * @param name Name of content resource to get
1580      * @param encoding Character encoding to use
1581      * @return parsed ContentResource object ready for use
1582      * @throws ResourceNotFoundException if template not found
1583      *          from any available source.
1584      * @throws ParseErrorException When the template could not be parsed.
1585      */
1586     public ContentResource getContent(String name, String encoding)
1587         throws ResourceNotFoundException, ParseErrorException
1588     {
1589         requireInitialization();
1590 
1591         return (ContentResource)
1592                 resourceManager.getResource(name,
1593                         ResourceManager.RESOURCE_CONTENT, encoding);
1594     }
1595 
1596 
1597     /**
1598      *  Determines if a template exists and returns name of the loader that
1599      *  provides it.  This is a slightly less hokey way to support
1600      *  the Velocity.resourceExists() utility method, which was broken
1601      *  when per-template encoding was introduced.  We can revisit this.
1602      *
1603      *  @param resourceName Name of template or content resource
1604      *  @return class name of loader than can provide it
1605      */
1606     public String getLoaderNameForResource(String resourceName)
1607     {
1608         requireInitialization();
1609 
1610         return resourceManager.getLoaderNameForResource(resourceName);
1611     }
1612 
1613     /**
1614      * Returns a convenient Log instance that wraps the current LogChute.
1615      * Use this to log error messages. It has the usual methods.
1616      *
1617      * @return A convenience Log instance that wraps the current LogChute.
1618      * @since 1.5
1619      */
1620     public Log getLog()
1621     {
1622         return log;
1623     }
1624 
1625     /**
1626      * String property accessor method with default to hide the
1627      * configuration implementation.
1628      *
1629      * @param key property key
1630      * @param defaultValue  default value to return if key not
1631      *               found in resource manager.
1632      * @return value of key or default
1633      */
1634     public String getString( String key, String defaultValue)
1635     {
1636         return configuration.getString(key, defaultValue);
1637     }
1638 
1639     /**
1640      * Returns the appropriate VelocimacroProxy object if vmName
1641      * is a valid current Velocimacro.
1642      *
1643      * @param vmName Name of velocimacro requested
1644      * @param templateName Name of the template that contains the velocimacro.
1645      * @return The requested VelocimacroProxy.
1646      * @since 1.6
1647      */
1648     public Directive getVelocimacro(String vmName, String templateName)
1649     {
1650         return vmFactory.getVelocimacro( vmName, templateName );
1651     }
1652 
1653     /**
1654      * Returns the appropriate VelocimacroProxy object if vmName
1655      * is a valid current Velocimacro.
1656      *
1657      * @param vmName  Name of velocimacro requested
1658      * @param templateName Name of the namespace.
1659      * @param renderingTemplate Name of the template we are currently rendering. This
1660      *    information is needed when VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL setting is true
1661      *    and template contains a macro with the same name as the global macro library.
1662      *
1663      * @since Velocity 1.6
1664      *
1665      * @return VelocimacroProxy
1666      */
1667     public Directive getVelocimacro(String vmName, String templateName, String renderingTemplate)
1668     {
1669         return vmFactory.getVelocimacro( vmName, templateName, renderingTemplate );
1670     }
1671 
1672     /**
1673      * Return a list of VelocimacroProxies that are defined by the given
1674      * template name.
1675      */
1676     public List<VelocimacroProxy> getVelocimacros(String templateName)
1677     {
1678         return vmFactory.getVelocimacros(templateName);
1679     }
1680 
1681 
1682     /**
1683      * Adds a new Velocimacro. Usually called by Macro only while parsing.
1684      *
1685      * Called by org.apache.velocity.runtime.directive.processAndRegister
1686      *
1687      * @param name  Name of velocimacro
1688      * @param macro  root AST node of the parsed macro
1689      * @param macroArgs  Array of macro arguments, containing the
1690      *        #macro() arguments and default values.  the 0th is the name.
1691      * @param sourceTemplate
1692      *
1693      * @since Velocity 1.6
1694      *
1695      * @return boolean  True if added, false if rejected for some
1696      *                  reason (either parameters or permission settings)
1697      */
1698     public boolean addVelocimacro( String name,
1699                                           Node macro,
1700                                           List<Macro.MacroArg> macroArgs,
1701                                           String sourceTemplate )
1702     {
1703         return vmFactory.addVelocimacro(name.intern(), macro,  macroArgs,  sourceTemplate);
1704     }
1705 
1706 
1707     /**
1708      *  Checks to see if a VM exists
1709      *
1710      * @param vmName Name of the Velocimacro.
1711      * @param templateName Template on which to look for the Macro.
1712      * @return True if VM by that name exists, false if not
1713      */
1714     public boolean isVelocimacro( String vmName, String templateName )
1715     {
1716         return vmFactory.isVelocimacro(vmName.intern(), templateName);
1717     }
1718 
1719     /**
1720      * tells the vmFactory to dump the specified namespace.  This is to support
1721      * clearing the VM list when in inline-VM-local-scope mode
1722      * @param namespace Namespace to dump.
1723      * @return True if namespace was dumped successfully.
1724      */
1725     public boolean dumpVMNamespace(String namespace)
1726     {
1727         return vmFactory.dumpVMNamespace( namespace );
1728     }
1729 
1730     /* --------------------------------------------------------------------
1731      * R U N T I M E  A C C E S S O R  M E T H O D S
1732      * --------------------------------------------------------------------
1733      * These are the getXXX() methods that are a simple wrapper
1734      * around the configuration object. This is an attempt
1735      * to make a the Velocity Runtime the single access point
1736      * for all things Velocity, and allow the Runtime to
1737      * adhere as closely as possible the the Mediator pattern
1738      * which is the ultimate goal.
1739      * --------------------------------------------------------------------
1740      */
1741 
1742     /**
1743      * String property accessor method to hide the configuration implementation
1744      * @param key  property key
1745      * @return   value of key or null
1746      */
1747     public String getString(String key)
1748     {
1749         return StringUtils.nullTrim(configuration.getString(key));
1750     }
1751 
1752     /**
1753      * Int property accessor method to hide the configuration implementation.
1754      *
1755      * @param key Property key
1756      * @return value
1757      */
1758     public int getInt(String key)
1759     {
1760         return configuration.getInt(key);
1761     }
1762 
1763     /**
1764      * Int property accessor method to hide the configuration implementation.
1765      *
1766      * @param key  property key
1767      * @param defaultValue The default value.
1768      * @return value
1769      */
1770     public int getInt(String key, int defaultValue)
1771     {
1772         return configuration.getInt(key, defaultValue);
1773     }
1774 
1775     /**
1776      * Boolean property accessor method to hide the configuration implementation.
1777      *
1778      * @param key property key
1779      * @param def The default value if property not found.
1780      * @return value of key or default value
1781      */
1782     public boolean getBoolean(String key, boolean def)
1783     {
1784         return configuration.getBoolean(key, def);
1785     }
1786 
1787     /**
1788      * Return the velocity runtime configuration object.
1789      *
1790      * @return Configuration object which houses the Velocity runtime
1791      * properties.
1792      */
1793     public ExtendedProperties getConfiguration()
1794     {
1795         return configuration;
1796     }
1797 
1798     /**
1799      *  Return the Introspector for this instance
1800      * @return The Introspector for this instance
1801      */
1802     public Introspector getIntrospector()
1803     {
1804         return introspector;
1805     }
1806 
1807     /**
1808      * Returns the event handlers for the application.
1809      * @return The event handlers for the application.
1810      * @since 1.5
1811      */
1812     public EventCartridge getApplicationEventCartridge()
1813     {
1814         return eventCartridge;
1815     }
1816 
1817 
1818     /**
1819      *  Gets the application attribute for the given key
1820      *
1821      * @param key
1822      * @return The application attribute for the given key.
1823      */
1824     public Object getApplicationAttribute(Object key)
1825     {
1826         return applicationAttributes.get(key);
1827     }
1828 
1829     /**
1830      *   Sets the application attribute for the given key
1831      *
1832      * @param key
1833      * @param o The new application attribute.
1834      * @return The old value of this attribute or null if it hasn't been set before.
1835      */
1836     public Object setApplicationAttribute(Object key, Object o)
1837     {
1838         return applicationAttributes.put(key, o);
1839     }
1840 
1841     /**
1842      * Returns the Uberspect object for this Instance.
1843      *
1844      * @return The Uberspect object for this Instance.
1845      */
1846     public Uberspect getUberspect()
1847     {
1848         return uberSpect;
1849     }
1850 
1851 }