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