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