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