src/content/engine/1.6.2/xref/org/apache/velocity/runtime/VelocimacroFactory.html [1:749]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.StringReader; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.Map; 26 import java.util.Vector; 27 import java.util.ArrayList; 28 29 import org.apache.commons.lang.StringUtils; 30 import org.apache.velocity.Template; 31 import org.apache.velocity.exception.VelocityException; 32 import org.apache.velocity.runtime.directive.Directive; 33 import org.apache.velocity.runtime.directive.Macro; 34 import org.apache.velocity.runtime.directive.VelocimacroProxy; 35 import org.apache.velocity.runtime.log.LogDisplayWrapper; 36 import org.apache.velocity.runtime.parser.ParseException; 37 import org.apache.velocity.runtime.parser.node.Node; 38 39 /** 40 * VelocimacroFactory.java 41 * 42 * manages the set of VMs in a running Velocity engine. 43 * 44 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> 45 * @version $Id: VelocimacroFactory.java 718442 2008-11-18 00:01:17Z nbubna $ 46 */ 47 public class VelocimacroFactory 48 { 49 /** 50 * runtime services for this instance 51 */ 52 private final RuntimeServices rsvc; 53 54 /** 55 * the log for this instance 56 */ 57 private final LogDisplayWrapper log; 58 59 /** 60 * VMManager : deal with namespace management 61 * and actually keeps all the VM definitions 62 */ 63 private VelocimacroManager vmManager = null; 64 65 /** 66 * determines if replacement of global VMs are allowed 67 * controlled by VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL 68 */ 69 private boolean replaceAllowed = false; 70 71 /** 72 * controls if new VMs can be added. Set by 73 * VM_PERM_ALLOW_INLINE Note the assumption that only 74 * through inline defs can this happen. 75 * additions through autoloaded VMs is allowed 76 */ 77 private boolean addNewAllowed = true; 78 79 /** 80 * sets if template-local namespace in used 81 */ 82 private boolean templateLocal = false; 83 84 /** 85 * determines if the libraries are auto-loaded 86 * when they change 87 */ 88 private boolean autoReloadLibrary = false; 89 90 /** 91 * vector of the library names 92 */ 93 private List macroLibVec = null; 94 95 /** 96 * map of the library Template objects 97 * used for reload determination 98 */ 99 private Map libModMap; 100 101 /** 102 * C'tor for the VelociMacro factory. 103 * 104 * @param rsvc Reference to a runtime services object. 105 */ 106 public VelocimacroFactory(final RuntimeServices rsvc) 107 { 108 this.rsvc = rsvc; 109 this.log = new LogDisplayWrapper(rsvc.getLog(), "Velocimacro : ", 110 rsvc.getBoolean(RuntimeConstants.VM_MESSAGES_ON, true)); 111 112 /* 113 * we always access in a synchronized(), so we 114 * can use an unsynchronized hashmap 115 */ 116 libModMap = new HashMap(); 117 vmManager = new VelocimacroManager(rsvc); 118 } 119 120 /** 121 * initialize the factory - setup all permissions 122 * load all global libraries. 123 */ 124 public void initVelocimacro() 125 { 126 /* 127 * maybe I'm just paranoid... 128 */ 129 synchronized(this) 130 { 131 log.trace("initialization starting."); 132 133 /* 134 * allow replacements while we add the libraries, if exist 135 */ 136 setReplacementPermission(true); 137 138 /* 139 * add all library macros to the global namespace 140 */ 141 142 vmManager.setNamespaceUsage(false); 143 144 /* 145 * now, if there is a global or local libraries specified, use them. 146 * All we have to do is get the template. The template will be parsed; 147 * VM's are added during the parse phase 148 */ 149 150 Object libfiles = rsvc.getProperty(RuntimeConstants.VM_LIBRARY); 151 152 if (libfiles == null) 153 { 154 log.debug("\"" + RuntimeConstants.VM_LIBRARY + 155 "\" is not set. Trying default library: " + 156 RuntimeConstants.VM_LIBRARY_DEFAULT); 157 158 // try the default library. 159 if (rsvc.getLoaderNameForResource(RuntimeConstants.VM_LIBRARY_DEFAULT) != null) 160 { 161 libfiles = RuntimeConstants.VM_LIBRARY_DEFAULT; 162 } 163 else 164 { 165 log.debug("Default library not found."); 166 } 167 } 168 169 if(libfiles != null) 170 { 171 macroLibVec = new ArrayList(); 172 if (libfiles instanceof Vector) 173 { 174 macroLibVec.addAll((Vector)libfiles); 175 } 176 else if (libfiles instanceof String) 177 { 178 macroLibVec.add(libfiles); 179 } 180 181 for(int i = 0, is = macroLibVec.size(); i < is; i++) 182 { 183 String lib = (String) macroLibVec.get(i); 184 185 /* 186 * only if it's a non-empty string do we bother 187 */ 188 189 if (StringUtils.isNotEmpty(lib)) 190 { 191 /* 192 * let the VMManager know that the following is coming 193 * from libraries - need to know for auto-load 194 */ 195 196 vmManager.setRegisterFromLib(true); 197 198 log.debug("adding VMs from VM library : " + lib); 199 200 try 201 { 202 Template template = rsvc.getTemplate(lib); 203 204 /* 205 * save the template. This depends on the assumption 206 * that the Template object won't change - currently 207 * this is how the Resource manager works 208 */ 209 210 Twonk twonk = new Twonk(); 211 twonk.template = template; 212 twonk.modificationTime = template.getLastModified(); 213 libModMap.put(lib, twonk); 214 } 215 catch (Exception e) 216 { 217 String msg = "Velocimacro : Error using VM library : " + lib; 218 log.error(true, msg, e); 219 throw new VelocityException(msg, e); 220 } 221 222 log.trace("VM library registration complete."); 223 224 vmManager.setRegisterFromLib(false); 225 } 226 } 227 } 228 229 /* 230 * now, the permissions 231 */ 232 233 234 /* 235 * allowinline : anything after this will be an inline macro, I think 236 * there is the question if a #include is an inline, and I think so 237 * 238 * default = true 239 */ 240 setAddMacroPermission(true); 241 242 if (!rsvc.getBoolean( RuntimeConstants.VM_PERM_ALLOW_INLINE, true)) 243 { 244 setAddMacroPermission(false); 245 246 log.debug("allowInline = false : VMs can NOT be defined inline in templates"); 247 } 248 else 249 { 250 log.debug("allowInline = true : VMs can be defined inline in templates"); 251 } 252 253 /* 254 * allowInlineToReplaceGlobal : allows an inline VM , if allowed at all, 255 * to replace an existing global VM 256 * 257 * default = false 258 */ 259 setReplacementPermission(false); 260 261 if (rsvc.getBoolean( 262 RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, false)) 263 { 264 setReplacementPermission(true); 265 266 log.debug("allowInlineToOverride = true : VMs " + 267 "defined inline may replace previous VM definitions"); 268 } 269 else 270 { 271 log.debug("allowInlineToOverride = false : VMs " + 272 "defined inline may NOT replace previous VM definitions"); 273 } 274 275 /* 276 * now turn on namespace handling as far as permissions allow in the 277 * manager, and also set it here for gating purposes 278 */ 279 vmManager.setNamespaceUsage(true); 280 281 /* 282 * template-local inline VM mode : default is off 283 */ 284 setTemplateLocalInline(rsvc.getBoolean( 285 RuntimeConstants.VM_PERM_INLINE_LOCAL, false)); 286 287 if (getTemplateLocalInline()) 288 { 289 log.debug("allowInlineLocal = true : VMs " + 290 "defined inline will be local to their defining template only."); 291 } 292 else 293 { 294 log.debug("allowInlineLocal = false : VMs " + 295 "defined inline will be global in scope if allowed."); 296 } 297 298 vmManager.setTemplateLocalInlineVM(getTemplateLocalInline()); 299 300 /* 301 * autoload VM libraries 302 */ 303 setAutoload(rsvc.getBoolean(RuntimeConstants.VM_LIBRARY_AUTORELOAD, false)); 304 305 if (getAutoload()) 306 { 307 log.debug("autoload on : VM system " + 308 "will automatically reload global library macros"); 309 } 310 else 311 { 312 log.debug("autoload off : VM system " + 313 "will not automatically reload global library macros"); 314 } 315 316 log.trace("Velocimacro : initialization complete."); 317 } 318 } 319 320 /** 321 * Adds a macro to the factory. 322 * 323 * addVelocimacro(String, Node, String[] argArray, String) should be used internally 324 * instead of this. 325 * 326 * @param name Name of the Macro to add. 327 * @param macroBody String representation of the macro. 328 * @param argArray Macro arguments. First element is the macro name. 329 * @param sourceTemplate Source template from which the macro gets registered. 330 * 331 * @return true if Macro was registered successfully. 332 */ 333 public boolean addVelocimacro(String name, String macroBody, 334 String argArray[], String sourceTemplate) 335 { 336 /* 337 * maybe we should throw an exception, maybe just tell 338 * the caller like this... 339 * 340 * I hate this : maybe exceptions are in order here... 341 * They definitely would be if this was only called by directly 342 * by users, but Velocity calls this internally. 343 */ 344 if (name == null || macroBody == null || argArray == null || 345 sourceTemplate == null) 346 { 347 String msg = "VM '"+name+"' addition rejected : "; 348 if (name == null) 349 { 350 msg += "name"; 351 } 352 else if (macroBody == null) 353 { 354 msg += "macroBody"; 355 } 356 else if (argArray == null) 357 { 358 msg += "argArray"; 359 } 360 else 361 { 362 msg += "sourceTemplate"; 363 } 364 msg += " argument was null"; 365 log.error(msg); 366 throw new NullPointerException(msg); 367 } 368 369 /* 370 * see if the current ruleset allows this addition 371 */ 372 373 if (!canAddVelocimacro(name, sourceTemplate)) 374 { 375 return false; 376 } 377 378 synchronized (this) 379 { 380 try 381 { 382 Node macroRootNode = rsvc.parse(new StringReader(macroBody), sourceTemplate); 383 384 vmManager.addVM(name, macroRootNode, argArray, sourceTemplate, replaceAllowed); 385 } 386 catch (ParseException ex) 387 { 388 // to keep things 1.3 compatible call toString() here 389 throw new RuntimeException(ex.toString()); 390 } 391 } 392 393 if (log.isDebugEnabled()) 394 { 395 StringBuffer msg = new StringBuffer("added "); 396 Macro.macroToString(msg, argArray); 397 msg.append(" : source = ").append(sourceTemplate); 398 log.debug(msg.toString()); 399 } 400 401 return true; 402 } 403 404 /** 405 * Adds a macro to the factory. 406 * 407 * @param name Name of the Macro to add. 408 * @param macroBody root node of the parsed macro AST 409 * @param argArray Name of the macro arguments. First element is the macro name. 410 * @param sourceTemplate Source template from which the macro gets registered. 411 * @return true if Macro was registered successfully. 412 * @since 1.6 413 */ 414 public boolean addVelocimacro(String name, Node macroBody, 415 String argArray[], String sourceTemplate) 416 { 417 // Called by RuntimeInstance.addVelocimacro 418 419 /* 420 * maybe we should throw an exception, maybe just tell 421 * the caller like this... 422 * 423 * I hate this : maybe exceptions are in order here... 424 * They definitely would be if this was only called by directly 425 * by users, but Velocity calls this internally. 426 */ 427 if (name == null || macroBody == null || argArray == null || 428 sourceTemplate == null) 429 { 430 String msg = "VM '"+name+"' addition rejected : "; 431 if (name == null) 432 { 433 msg += "name"; 434 } 435 else if (macroBody == null) 436 { 437 msg += "macroBody"; 438 } 439 else if (argArray == null) 440 { 441 msg += "argArray"; 442 } 443 else 444 { 445 msg += "sourceTemplate"; 446 } 447 msg += " argument was null"; 448 log.error(msg); 449 throw new NullPointerException(msg); 450 } 451 452 /* 453 * see if the current ruleset allows this addition 454 */ 455 456 if (!canAddVelocimacro(name, sourceTemplate)) 457 { 458 return false; 459 } 460 461 synchronized(this) 462 { 463 vmManager.addVM(name, macroBody, argArray, sourceTemplate, replaceAllowed); 464 } 465 return(true); 466 } 467 468 469 /** 470 * determines if a given macro/namespace (name, source) combo is allowed 471 * to be added 472 * 473 * @param name Name of VM to add 474 * @param sourceTemplate Source template that contains the defintion of the VM 475 * @return true if it is allowed to be added, false otherwise 476 */ 477 private synchronized boolean canAddVelocimacro(String name, String sourceTemplate) 478 { 479 /* 480 * short circuit and do it if autoloader is on, and the 481 * template is one of the library templates 482 */ 483 484 if (autoReloadLibrary && (macroLibVec != null)) 485 { 486 if( macroLibVec.contains(sourceTemplate) ) 487 return true; 488 } 489 490 491 /* 492 * maybe the rules should be in manager? I dunno. It's to manage 493 * the namespace issues first, are we allowed to add VMs at all? 494 * This trumps all. 495 */ 496 if (!addNewAllowed) 497 { 498 log.warn("VM addition rejected : "+name+" : inline VMs not allowed."); 499 return false; 500 } 501 502 /* 503 * are they local in scope? Then it is ok to add. 504 */ 505 if (!templateLocal) 506 { 507 /* 508 * otherwise, if we have it already in global namespace, and they can't replace 509 * since local templates are not allowed, the global namespace is implied. 510 * remember, we don't know anything about namespace managment here, so lets 511 * note do anything fancy like trying to give it the global namespace here 512 * 513 * so if we have it, and we aren't allowed to replace, bail 514 */ 515 if (!replaceAllowed && isVelocimacro(name, sourceTemplate)) 516 { 517 /* 518 * Concurrency fix: the log entry was changed to debug scope because it 519 * causes false alarms when several concurrent threads simultaneously (re)parse 520 * some macro 521 */ 522 if (log.isDebugEnabled()) 523 log.debug("VM addition rejected : "+name+" : inline not allowed to replace existing VM"); 524 return false; 525 } 526 } 527 528 return true; 529 } 530 531 /** 532 * Tells the world if a given directive string is a Velocimacro 533 * @param vm Name of the Macro. 534 * @param sourceTemplate Source template from which the macro should be loaded. 535 * @return True if the given name is a macro. 536 */ 537 public boolean isVelocimacro(String vm, String sourceTemplate) 538 { 539 // synchronization removed 540 return(vmManager.get(vm, sourceTemplate) != null); 541 } 542 543 /** 544 * actual factory : creates a Directive that will 545 * behave correctly wrt getting the framework to 546 * dig out the correct # of args 547 * @param vmName Name of the Macro. 548 * @param sourceTemplate Source template from which the macro should be loaded. 549 * @return A directive representing the Macro. 550 */ 551 public Directive getVelocimacro(String vmName, String sourceTemplate) 552 { 553 return(getVelocimacro(vmName, sourceTemplate, null)); 554 } 555 556 /** 557 * @since 1.6 558 */ 559 public Directive getVelocimacro(String vmName, String sourceTemplate, String renderingTemplate) 560 { 561 VelocimacroProxy vp = null; 562 563 vp = vmManager.get(vmName, sourceTemplate, renderingTemplate); 564 565 /* 566 * if this exists, and autoload is on, we need to check where this VM came from 567 */ 568 569 if (vp != null && autoReloadLibrary ) 570 { 571 synchronized (this) 572 { 573 /* 574 * see if this VM came from a library. Need to pass sourceTemplate in the event 575 * namespaces are set, as it could be masked by local 576 */ 577 578 String lib = vmManager.getLibraryName(vmName, sourceTemplate); 579 580 if (lib != null) 581 { 582 try 583 { 584 /* 585 * get the template from our map 586 */ 587 588 Twonk tw = (Twonk) libModMap.get(lib); 589 590 if (tw != null) 591 { 592 Template template = tw.template; 593 594 /* 595 * now, compare the last modified time of the resource with the last 596 * modified time of the template if the file has changed, then reload. 597 * Otherwise, we should be ok. 598 */ 599 600 long tt = tw.modificationTime; 601 long ft = template.getResourceLoader().getLastModified(template); 602 603 if (ft > tt) 604 { 605 log.debug("auto-reloading VMs from VM library : " + lib); 606 607 /* 608 * when there are VMs in a library that invoke each other, there are 609 * calls into getVelocimacro() from the init() process of the VM 610 * directive. To stop the infinite loop we save the current time 611 * reported by the resource loader and then be honest when the 612 * reload is complete 613 */ 614 615 tw.modificationTime = ft; 616 617 template = rsvc.getTemplate(lib); 618 619 /* 620 * and now we be honest 621 */ 622 623 tw.template = template; 624 tw.modificationTime = template.getLastModified(); 625 626 /* 627 * note that we don't need to put this twonk 628 * back into the map, as we can just use the 629 * same reference and this block is synchronized 630 */ 631 } 632 } 633 } 634 catch (Exception e) 635 { 636 String msg = "Velocimacro : Error using VM library : " + lib; 637 log.error(true, msg, e); 638 throw new VelocityException(msg, e); 639 } 640 641 vp = vmManager.get(vmName, sourceTemplate, renderingTemplate); 642 } 643 } 644 } 645 646 return vp; 647 } 648 649 /** 650 * tells the vmManager to dump the specified namespace 651 * 652 * @param namespace Namespace to dump. 653 * @return True if namespace has been dumped successfully. 654 */ 655 public boolean dumpVMNamespace(String namespace) 656 { 657 return vmManager.dumpNamespace(namespace); 658 } 659 660 /** 661 * sets permission to have VMs local in scope to their declaring template note that this is 662 * really taken care of in the VMManager class, but we need it here for gating purposes in addVM 663 * eventually, I will slide this all into the manager, maybe. 664 */ 665 private void setTemplateLocalInline(boolean b) 666 { 667 templateLocal = b; 668 } 669 670 private boolean getTemplateLocalInline() 671 { 672 return templateLocal; 673 } 674 675 /** 676 * sets the permission to add new macros 677 */ 678 private boolean setAddMacroPermission(final boolean addNewAllowed) 679 { 680 boolean b = this.addNewAllowed; 681 this.addNewAllowed = addNewAllowed; 682 return b; 683 } 684 685 /** 686 * sets the permission for allowing addMacro() calls to replace existing VM's 687 */ 688 private boolean setReplacementPermission(boolean arg) 689 { 690 boolean b = replaceAllowed; 691 replaceAllowed = arg; 692 vmManager.setInlineReplacesGlobal(arg); 693 return b; 694 } 695 696 /** 697 * set the switch for automatic reloading of 698 * global library-based VMs 699 */ 700 private void setAutoload(boolean b) 701 { 702 autoReloadLibrary = b; 703 } 704 705 /** 706 * get the switch for automatic reloading of 707 * global library-based VMs 708 */ 709 private boolean getAutoload() 710 { 711 return autoReloadLibrary; 712 } 713 714 /** 715 * small container class to hold the tuple 716 * of a template and modification time. 717 * We keep the modification time so we can 718 * 'override' it on a reload to prevent 719 * recursive reload due to inter-calling 720 * VMs in a library 721 */ 722 private static class Twonk 723 { 724 /** Template kept in this container. */ 725 public Template template; 726 727 /** modification time of the template. */ 728 public long modificationTime; 729 } 730 } 731 732 733 734 735 736 737
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.StringReader; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.Map; 26 import java.util.Vector; 27 import java.util.ArrayList; 28 29 import org.apache.commons.lang.StringUtils; 30 import org.apache.velocity.Template; 31 import org.apache.velocity.exception.VelocityException; 32 import org.apache.velocity.runtime.directive.Directive; 33 import org.apache.velocity.runtime.directive.Macro; 34 import org.apache.velocity.runtime.directive.VelocimacroProxy; 35 import org.apache.velocity.runtime.log.LogDisplayWrapper; 36 import org.apache.velocity.runtime.parser.ParseException; 37 import org.apache.velocity.runtime.parser.node.Node; 38 39 /** 40 * VelocimacroFactory.java 41 * 42 * manages the set of VMs in a running Velocity engine. 43 * 44 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> 45 * @version $Id: VelocimacroFactory.java 718442 2008-11-18 00:01:17Z nbubna $ 46 */ 47 public class VelocimacroFactory 48 { 49 /** 50 * runtime services for this instance 51 */ 52 private final RuntimeServices rsvc; 53 54 /** 55 * the log for this instance 56 */ 57 private final LogDisplayWrapper log; 58 59 /** 60 * VMManager : deal with namespace management 61 * and actually keeps all the VM definitions 62 */ 63 private VelocimacroManager vmManager = null; 64 65 /** 66 * determines if replacement of global VMs are allowed 67 * controlled by VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL 68 */ 69 private boolean replaceAllowed = false; 70 71 /** 72 * controls if new VMs can be added. Set by 73 * VM_PERM_ALLOW_INLINE Note the assumption that only 74 * through inline defs can this happen. 75 * additions through autoloaded VMs is allowed 76 */ 77 private boolean addNewAllowed = true; 78 79 /** 80 * sets if template-local namespace in used 81 */ 82 private boolean templateLocal = false; 83 84 /** 85 * determines if the libraries are auto-loaded 86 * when they change 87 */ 88 private boolean autoReloadLibrary = false; 89 90 /** 91 * vector of the library names 92 */ 93 private List macroLibVec = null; 94 95 /** 96 * map of the library Template objects 97 * used for reload determination 98 */ 99 private Map libModMap; 100 101 /** 102 * C'tor for the VelociMacro factory. 103 * 104 * @param rsvc Reference to a runtime services object. 105 */ 106 public VelocimacroFactory(final RuntimeServices rsvc) 107 { 108 this.rsvc = rsvc; 109 this.log = new LogDisplayWrapper(rsvc.getLog(), "Velocimacro : ", 110 rsvc.getBoolean(RuntimeConstants.VM_MESSAGES_ON, true)); 111 112 /* 113 * we always access in a synchronized(), so we 114 * can use an unsynchronized hashmap 115 */ 116 libModMap = new HashMap(); 117 vmManager = new VelocimacroManager(rsvc); 118 } 119 120 /** 121 * initialize the factory - setup all permissions 122 * load all global libraries. 123 */ 124 public void initVelocimacro() 125 { 126 /* 127 * maybe I'm just paranoid... 128 */ 129 synchronized(this) 130 { 131 log.trace("initialization starting."); 132 133 /* 134 * allow replacements while we add the libraries, if exist 135 */ 136 setReplacementPermission(true); 137 138 /* 139 * add all library macros to the global namespace 140 */ 141 142 vmManager.setNamespaceUsage(false); 143 144 /* 145 * now, if there is a global or local libraries specified, use them. 146 * All we have to do is get the template. The template will be parsed; 147 * VM's are added during the parse phase 148 */ 149 150 Object libfiles = rsvc.getProperty(RuntimeConstants.VM_LIBRARY); 151 152 if (libfiles == null) 153 { 154 log.debug("\"" + RuntimeConstants.VM_LIBRARY + 155 "\" is not set. Trying default library: " + 156 RuntimeConstants.VM_LIBRARY_DEFAULT); 157 158 // try the default library. 159 if (rsvc.getLoaderNameForResource(RuntimeConstants.VM_LIBRARY_DEFAULT) != null) 160 { 161 libfiles = RuntimeConstants.VM_LIBRARY_DEFAULT; 162 } 163 else 164 { 165 log.debug("Default library not found."); 166 } 167 } 168 169 if(libfiles != null) 170 { 171 macroLibVec = new ArrayList(); 172 if (libfiles instanceof Vector) 173 { 174 macroLibVec.addAll((Vector)libfiles); 175 } 176 else if (libfiles instanceof String) 177 { 178 macroLibVec.add(libfiles); 179 } 180 181 for(int i = 0, is = macroLibVec.size(); i < is; i++) 182 { 183 String lib = (String) macroLibVec.get(i); 184 185 /* 186 * only if it's a non-empty string do we bother 187 */ 188 189 if (StringUtils.isNotEmpty(lib)) 190 { 191 /* 192 * let the VMManager know that the following is coming 193 * from libraries - need to know for auto-load 194 */ 195 196 vmManager.setRegisterFromLib(true); 197 198 log.debug("adding VMs from VM library : " + lib); 199 200 try 201 { 202 Template template = rsvc.getTemplate(lib); 203 204 /* 205 * save the template. This depends on the assumption 206 * that the Template object won't change - currently 207 * this is how the Resource manager works 208 */ 209 210 Twonk twonk = new Twonk(); 211 twonk.template = template; 212 twonk.modificationTime = template.getLastModified(); 213 libModMap.put(lib, twonk); 214 } 215 catch (Exception e) 216 { 217 String msg = "Velocimacro : Error using VM library : " + lib; 218 log.error(true, msg, e); 219 throw new VelocityException(msg, e); 220 } 221 222 log.trace("VM library registration complete."); 223 224 vmManager.setRegisterFromLib(false); 225 } 226 } 227 } 228 229 /* 230 * now, the permissions 231 */ 232 233 234 /* 235 * allowinline : anything after this will be an inline macro, I think 236 * there is the question if a #include is an inline, and I think so 237 * 238 * default = true 239 */ 240 setAddMacroPermission(true); 241 242 if (!rsvc.getBoolean( RuntimeConstants.VM_PERM_ALLOW_INLINE, true)) 243 { 244 setAddMacroPermission(false); 245 246 log.debug("allowInline = false : VMs can NOT be defined inline in templates"); 247 } 248 else 249 { 250 log.debug("allowInline = true : VMs can be defined inline in templates"); 251 } 252 253 /* 254 * allowInlineToReplaceGlobal : allows an inline VM , if allowed at all, 255 * to replace an existing global VM 256 * 257 * default = false 258 */ 259 setReplacementPermission(false); 260 261 if (rsvc.getBoolean( 262 RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, false)) 263 { 264 setReplacementPermission(true); 265 266 log.debug("allowInlineToOverride = true : VMs " + 267 "defined inline may replace previous VM definitions"); 268 } 269 else 270 { 271 log.debug("allowInlineToOverride = false : VMs " + 272 "defined inline may NOT replace previous VM definitions"); 273 } 274 275 /* 276 * now turn on namespace handling as far as permissions allow in the 277 * manager, and also set it here for gating purposes 278 */ 279 vmManager.setNamespaceUsage(true); 280 281 /* 282 * template-local inline VM mode : default is off 283 */ 284 setTemplateLocalInline(rsvc.getBoolean( 285 RuntimeConstants.VM_PERM_INLINE_LOCAL, false)); 286 287 if (getTemplateLocalInline()) 288 { 289 log.debug("allowInlineLocal = true : VMs " + 290 "defined inline will be local to their defining template only."); 291 } 292 else 293 { 294 log.debug("allowInlineLocal = false : VMs " + 295 "defined inline will be global in scope if allowed."); 296 } 297 298 vmManager.setTemplateLocalInlineVM(getTemplateLocalInline()); 299 300 /* 301 * autoload VM libraries 302 */ 303 setAutoload(rsvc.getBoolean(RuntimeConstants.VM_LIBRARY_AUTORELOAD, false)); 304 305 if (getAutoload()) 306 { 307 log.debug("autoload on : VM system " + 308 "will automatically reload global library macros"); 309 } 310 else 311 { 312 log.debug("autoload off : VM system " + 313 "will not automatically reload global library macros"); 314 } 315 316 log.trace("Velocimacro : initialization complete."); 317 } 318 } 319 320 /** 321 * Adds a macro to the factory. 322 * 323 * addVelocimacro(String, Node, String[] argArray, String) should be used internally 324 * instead of this. 325 * 326 * @param name Name of the Macro to add. 327 * @param macroBody String representation of the macro. 328 * @param argArray Macro arguments. First element is the macro name. 329 * @param sourceTemplate Source template from which the macro gets registered. 330 * 331 * @return true if Macro was registered successfully. 332 */ 333 public boolean addVelocimacro(String name, String macroBody, 334 String argArray[], String sourceTemplate) 335 { 336 /* 337 * maybe we should throw an exception, maybe just tell 338 * the caller like this... 339 * 340 * I hate this : maybe exceptions are in order here... 341 * They definitely would be if this was only called by directly 342 * by users, but Velocity calls this internally. 343 */ 344 if (name == null || macroBody == null || argArray == null || 345 sourceTemplate == null) 346 { 347 String msg = "VM '"+name+"' addition rejected : "; 348 if (name == null) 349 { 350 msg += "name"; 351 } 352 else if (macroBody == null) 353 { 354 msg += "macroBody"; 355 } 356 else if (argArray == null) 357 { 358 msg += "argArray"; 359 } 360 else 361 { 362 msg += "sourceTemplate"; 363 } 364 msg += " argument was null"; 365 log.error(msg); 366 throw new NullPointerException(msg); 367 } 368 369 /* 370 * see if the current ruleset allows this addition 371 */ 372 373 if (!canAddVelocimacro(name, sourceTemplate)) 374 { 375 return false; 376 } 377 378 synchronized (this) 379 { 380 try 381 { 382 Node macroRootNode = rsvc.parse(new StringReader(macroBody), sourceTemplate); 383 384 vmManager.addVM(name, macroRootNode, argArray, sourceTemplate, replaceAllowed); 385 } 386 catch (ParseException ex) 387 { 388 // to keep things 1.3 compatible call toString() here 389 throw new RuntimeException(ex.toString()); 390 } 391 } 392 393 if (log.isDebugEnabled()) 394 { 395 StringBuffer msg = new StringBuffer("added "); 396 Macro.macroToString(msg, argArray); 397 msg.append(" : source = ").append(sourceTemplate); 398 log.debug(msg.toString()); 399 } 400 401 return true; 402 } 403 404 /** 405 * Adds a macro to the factory. 406 * 407 * @param name Name of the Macro to add. 408 * @param macroBody root node of the parsed macro AST 409 * @param argArray Name of the macro arguments. First element is the macro name. 410 * @param sourceTemplate Source template from which the macro gets registered. 411 * @return true if Macro was registered successfully. 412 * @since 1.6 413 */ 414 public boolean addVelocimacro(String name, Node macroBody, 415 String argArray[], String sourceTemplate) 416 { 417 // Called by RuntimeInstance.addVelocimacro 418 419 /* 420 * maybe we should throw an exception, maybe just tell 421 * the caller like this... 422 * 423 * I hate this : maybe exceptions are in order here... 424 * They definitely would be if this was only called by directly 425 * by users, but Velocity calls this internally. 426 */ 427 if (name == null || macroBody == null || argArray == null || 428 sourceTemplate == null) 429 { 430 String msg = "VM '"+name+"' addition rejected : "; 431 if (name == null) 432 { 433 msg += "name"; 434 } 435 else if (macroBody == null) 436 { 437 msg += "macroBody"; 438 } 439 else if (argArray == null) 440 { 441 msg += "argArray"; 442 } 443 else 444 { 445 msg += "sourceTemplate"; 446 } 447 msg += " argument was null"; 448 log.error(msg); 449 throw new NullPointerException(msg); 450 } 451 452 /* 453 * see if the current ruleset allows this addition 454 */ 455 456 if (!canAddVelocimacro(name, sourceTemplate)) 457 { 458 return false; 459 } 460 461 synchronized(this) 462 { 463 vmManager.addVM(name, macroBody, argArray, sourceTemplate, replaceAllowed); 464 } 465 return(true); 466 } 467 468 469 /** 470 * determines if a given macro/namespace (name, source) combo is allowed 471 * to be added 472 * 473 * @param name Name of VM to add 474 * @param sourceTemplate Source template that contains the defintion of the VM 475 * @return true if it is allowed to be added, false otherwise 476 */ 477 private synchronized boolean canAddVelocimacro(String name, String sourceTemplate) 478 { 479 /* 480 * short circuit and do it if autoloader is on, and the 481 * template is one of the library templates 482 */ 483 484 if (autoReloadLibrary && (macroLibVec != null)) 485 { 486 if( macroLibVec.contains(sourceTemplate) ) 487 return true; 488 } 489 490 491 /* 492 * maybe the rules should be in manager? I dunno. It's to manage 493 * the namespace issues first, are we allowed to add VMs at all? 494 * This trumps all. 495 */ 496 if (!addNewAllowed) 497 { 498 log.warn("VM addition rejected : "+name+" : inline VMs not allowed."); 499 return false; 500 } 501 502 /* 503 * are they local in scope? Then it is ok to add. 504 */ 505 if (!templateLocal) 506 { 507 /* 508 * otherwise, if we have it already in global namespace, and they can't replace 509 * since local templates are not allowed, the global namespace is implied. 510 * remember, we don't know anything about namespace managment here, so lets 511 * note do anything fancy like trying to give it the global namespace here 512 * 513 * so if we have it, and we aren't allowed to replace, bail 514 */ 515 if (!replaceAllowed && isVelocimacro(name, sourceTemplate)) 516 { 517 /* 518 * Concurrency fix: the log entry was changed to debug scope because it 519 * causes false alarms when several concurrent threads simultaneously (re)parse 520 * some macro 521 */ 522 if (log.isDebugEnabled()) 523 log.debug("VM addition rejected : "+name+" : inline not allowed to replace existing VM"); 524 return false; 525 } 526 } 527 528 return true; 529 } 530 531 /** 532 * Tells the world if a given directive string is a Velocimacro 533 * @param vm Name of the Macro. 534 * @param sourceTemplate Source template from which the macro should be loaded. 535 * @return True if the given name is a macro. 536 */ 537 public boolean isVelocimacro(String vm, String sourceTemplate) 538 { 539 // synchronization removed 540 return(vmManager.get(vm, sourceTemplate) != null); 541 } 542 543 /** 544 * actual factory : creates a Directive that will 545 * behave correctly wrt getting the framework to 546 * dig out the correct # of args 547 * @param vmName Name of the Macro. 548 * @param sourceTemplate Source template from which the macro should be loaded. 549 * @return A directive representing the Macro. 550 */ 551 public Directive getVelocimacro(String vmName, String sourceTemplate) 552 { 553 return(getVelocimacro(vmName, sourceTemplate, null)); 554 } 555 556 /** 557 * @since 1.6 558 */ 559 public Directive getVelocimacro(String vmName, String sourceTemplate, String renderingTemplate) 560 { 561 VelocimacroProxy vp = null; 562 563 vp = vmManager.get(vmName, sourceTemplate, renderingTemplate); 564 565 /* 566 * if this exists, and autoload is on, we need to check where this VM came from 567 */ 568 569 if (vp != null && autoReloadLibrary ) 570 { 571 synchronized (this) 572 { 573 /* 574 * see if this VM came from a library. Need to pass sourceTemplate in the event 575 * namespaces are set, as it could be masked by local 576 */ 577 578 String lib = vmManager.getLibraryName(vmName, sourceTemplate); 579 580 if (lib != null) 581 { 582 try 583 { 584 /* 585 * get the template from our map 586 */ 587 588 Twonk tw = (Twonk) libModMap.get(lib); 589 590 if (tw != null) 591 { 592 Template template = tw.template; 593 594 /* 595 * now, compare the last modified time of the resource with the last 596 * modified time of the template if the file has changed, then reload. 597 * Otherwise, we should be ok. 598 */ 599 600 long tt = tw.modificationTime; 601 long ft = template.getResourceLoader().getLastModified(template); 602 603 if (ft > tt) 604 { 605 log.debug("auto-reloading VMs from VM library : " + lib); 606 607 /* 608 * when there are VMs in a library that invoke each other, there are 609 * calls into getVelocimacro() from the init() process of the VM 610 * directive. To stop the infinite loop we save the current time 611 * reported by the resource loader and then be honest when the 612 * reload is complete 613 */ 614 615 tw.modificationTime = ft; 616 617 template = rsvc.getTemplate(lib); 618 619 /* 620 * and now we be honest 621 */ 622 623 tw.template = template; 624 tw.modificationTime = template.getLastModified(); 625 626 /* 627 * note that we don't need to put this twonk 628 * back into the map, as we can just use the 629 * same reference and this block is synchronized 630 */ 631 } 632 } 633 } 634 catch (Exception e) 635 { 636 String msg = "Velocimacro : Error using VM library : " + lib; 637 log.error(true, msg, e); 638 throw new VelocityException(msg, e); 639 } 640 641 vp = vmManager.get(vmName, sourceTemplate, renderingTemplate); 642 } 643 } 644 } 645 646 return vp; 647 } 648 649 /** 650 * tells the vmManager to dump the specified namespace 651 * 652 * @param namespace Namespace to dump. 653 * @return True if namespace has been dumped successfully. 654 */ 655 public boolean dumpVMNamespace(String namespace) 656 { 657 return vmManager.dumpNamespace(namespace); 658 } 659 660 /** 661 * sets permission to have VMs local in scope to their declaring template note that this is 662 * really taken care of in the VMManager class, but we need it here for gating purposes in addVM 663 * eventually, I will slide this all into the manager, maybe. 664 */ 665 private void setTemplateLocalInline(boolean b) 666 { 667 templateLocal = b; 668 } 669 670 private boolean getTemplateLocalInline() 671 { 672 return templateLocal; 673 } 674 675 /** 676 * sets the permission to add new macros 677 */ 678 private boolean setAddMacroPermission(final boolean addNewAllowed) 679 { 680 boolean b = this.addNewAllowed; 681 this.addNewAllowed = addNewAllowed; 682 return b; 683 } 684 685 /** 686 * sets the permission for allowing addMacro() calls to replace existing VM's 687 */ 688 private boolean setReplacementPermission(boolean arg) 689 { 690 boolean b = replaceAllowed; 691 replaceAllowed = arg; 692 vmManager.setInlineReplacesGlobal(arg); 693 return b; 694 } 695 696 /** 697 * set the switch for automatic reloading of 698 * global library-based VMs 699 */ 700 private void setAutoload(boolean b) 701 { 702 autoReloadLibrary = b; 703 } 704 705 /** 706 * get the switch for automatic reloading of 707 * global library-based VMs 708 */ 709 private boolean getAutoload() 710 { 711 return autoReloadLibrary; 712 } 713 714 /** 715 * small container class to hold the tuple 716 * of a template and modification time. 717 * We keep the modification time so we can 718 * 'override' it on a reload to prevent 719 * recursive reload due to inter-calling 720 * VMs in a library 721 */ 722 private static class Twonk 723 { 724 /** Template kept in this container. */ 725 public Template template; 726 727 /** modification time of the template. */ 728 public long modificationTime; 729 } 730 } 731 732 733 734 735 736 737