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