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 }