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