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