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