KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > runtime > VelocimacroFactory


1 package org.apache.velocity.runtime;
2
3 /*
4  * Copyright 2000-2001,2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import org.apache.velocity.Template;
20 import org.apache.velocity.runtime.directive.Directive;
21 import org.apache.velocity.runtime.directive.VelocimacroProxy;
22
23 import java.util.Vector JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.HashMap JavaDoc;
26
27 /**
28  * VelocimacroFactory.java
29  *
30  * manages the set of VMs in a running Velocity engine.
31  *
32  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
33  * @version $Id: VelocimacroFactory.java,v 1.17.4.1 2004/03/03 23:22:55 geirm Exp $
34  */

35 public class VelocimacroFactory
36 {
37     /**
38      * runtime services for this instance
39      */

40     private RuntimeServices rsvc = null;
41
42     /**
43      * VMManager : deal with namespace management
44      * and actually keeps all the VM definitions
45      */

46     private VelocimacroManager vmManager = null;
47
48     /**
49      * determines if replacement of global VMs are allowed
50      * controlled by VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL
51      */

52     private boolean replaceAllowed = false;
53
54     /**
55      * controls if new VMs can be added. Set by
56      * VM_PERM_ALLOW_INLINE Note the assumption that only
57      * through inline defs can this happen.
58      * additions through autoloaded VMs is allowed
59      */

60     private boolean addNewAllowed = true;
61
62     /**
63      * sets if template-local namespace in used
64      */

65     private boolean templateLocal = false;
66
67     /**
68      * controls log output
69      */

70     private boolean blather = false;
71
72     /**
73      * determines if the libraries are auto-loaded
74      * when they change
75      */

76     private boolean autoReloadLibrary = false;
77
78     /**
79      * vector of the library names
80      */

81     private Vector JavaDoc macroLibVec = null;
82
83     /**
84      * map of the library Template objects
85      * used for reload determination
86      */

87     private Map JavaDoc libModMap;
88
89     /**
90      * CTOR : requires a runtime services from now
91      * on
92      */

93     public VelocimacroFactory( RuntimeServices rs )
94     {
95         this.rsvc = rs;
96
97         /*
98          * we always access in a synchronized(), so we
99          * can use an unsynchronized hashmap
100          */

101         libModMap = new HashMap JavaDoc();
102         vmManager = new VelocimacroManager( rsvc );
103     }
104
105     /**
106      * initialize the factory - setup all permissions
107      * load all global libraries.
108      */

109     public void initVelocimacro()
110     {
111         /*
112          * maybe I'm just paranoid...
113          */

114         synchronized( this )
115         {
116             /*
117              * allow replacements while we add the libraries, if exist
118              */

119             setReplacementPermission( true );
120             setBlather( true );
121
122             logVMMessageInfo("Velocimacro : initialization starting.");
123  
124             /*
125              * add all library macros to the global namespace
126              */

127             
128             vmManager.setNamespaceUsage( false );
129         
130             /*
131              * now, if there is a global or local libraries specified, use them.
132              * All we have to do is get the template. The template will be parsed;
133              * VM's are added during the parse phase
134              */

135
136              Object JavaDoc libfiles = rsvc.getProperty( RuntimeConstants.VM_LIBRARY );
137            
138              if( libfiles != null)
139              {
140                  if (libfiles instanceof Vector JavaDoc)
141                  {
142                      macroLibVec = (Vector JavaDoc) libfiles;
143                  }
144                  else if (libfiles instanceof String JavaDoc)
145                  {
146                      macroLibVec = new Vector JavaDoc();
147                      macroLibVec.addElement( libfiles );
148                  }
149                  
150                  for( int i = 0; i < macroLibVec.size(); i++)
151                  {
152                      String JavaDoc lib = (String JavaDoc) macroLibVec.elementAt(i);
153                  
154                      /*
155                       * only if it's a non-empty string do we bother
156                       */

157
158                      if (lib != null && !lib.equals(""))
159                      {
160                          /*
161                           * let the VMManager know that the following is coming
162                           * from libraries - need to know for auto-load
163                           */

164
165                          vmManager.setRegisterFromLib( true );
166
167                          logVMMessageInfo("Velocimacro : adding VMs from " +
168                              "VM library template : " + lib );
169
170                          try
171                          {
172                              Template template = rsvc.getTemplate( lib );
173
174                              /*
175                               * save the template. This depends on the assumption
176                               * that the Template object won't change - currently
177                               * this is how the Resource manager works
178                               */

179
180                              Twonk twonk = new Twonk();
181                              twonk.template = template;
182                              twonk.modificationTime = template.getLastModified();
183                              libModMap.put( lib, twonk );
184                          }
185                          catch (Exception JavaDoc e)
186                          {
187                              logVMMessageInfo("Velocimacro : error using VM " +
188                                               "library template " + lib + " : " + e );
189                          }
190
191                          logVMMessageInfo("Velocimacro : VM library template " +
192                                  "macro registration complete." );
193             
194                          vmManager.setRegisterFromLib( false );
195                      }
196                  }
197              }
198
199             /*
200              * now, the permissions
201              */

202             
203             /*
204              * allowinline : anything after this will be an inline macro, I think
205              * there is the question if a #include is an inline, and I think so
206              *
207              * default = true
208              */

209             setAddMacroPermission( true );
210                         
211             if ( !rsvc.getBoolean( RuntimeConstants.VM_PERM_ALLOW_INLINE, true) )
212             {
213                 setAddMacroPermission( false );
214                 
215                 logVMMessageInfo("Velocimacro : allowInline = false : VMs can not " +
216                     "be defined inline in templates");
217             }
218             else
219             {
220                 logVMMessageInfo("Velocimacro : allowInline = true : VMs can be " +
221                     "defined inline in templates");
222             }
223
224             /*
225              * allowInlineToReplaceGlobal : allows an inline VM , if allowed at all,
226              * to replace an existing global VM
227              *
228              * default = false
229              */

230             setReplacementPermission( false );
231             
232             if ( rsvc.getBoolean(
233                  RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, false) )
234             {
235                 setReplacementPermission( true );
236                 
237                 logVMMessageInfo("Velocimacro : allowInlineToOverride = true : VMs " +
238                     "defined inline may replace previous VM definitions");
239             }
240             else
241             {
242                 logVMMessageInfo("Velocimacro : allowInlineToOverride = false : VMs " +
243                     "defined inline may NOT replace previous VM definitions");
244             }
245
246             /*
247              * now turn on namespace handling as far as permissions allow in the
248              * manager, and also set it here for gating purposes
249              */

250             vmManager.setNamespaceUsage( true );
251
252             /*
253              * template-local inline VM mode : default is off
254              */

255             setTemplateLocalInline( rsvc.getBoolean(
256                 RuntimeConstants.VM_PERM_INLINE_LOCAL, false) );
257         
258             if ( getTemplateLocalInline() )
259             {
260                 logVMMessageInfo("Velocimacro : allowInlineLocal = true : VMs " +
261                     "defined inline will be local to their defining template only.");
262             }
263             else
264             {
265                 logVMMessageInfo("Velocimacro : allowInlineLocal = false : VMs " +
266                     "defined inline will be global in scope if allowed.");
267             }
268  
269             vmManager.setTemplateLocalInlineVM( getTemplateLocalInline() );
270
271             /*
272              * general message switch. default is on
273              */

274             setBlather( rsvc.getBoolean( RuntimeConstants.VM_MESSAGES_ON, true ));
275         
276             if (getBlather())
277             {
278                 logVMMessageInfo("Velocimacro : messages on : VM system " +
279                     "will output logging messages");
280             }
281             else
282             {
283                 logVMMessageInfo("Velocimacro : messages off : VM system will be quiet");
284             }
285
286             /*
287              * autoload VM libraries
288              */

289             setAutoload( rsvc.getBoolean( RuntimeConstants.VM_LIBRARY_AUTORELOAD, false ));
290         
291             if (getAutoload())
292             {
293                 logVMMessageInfo("Velocimacro : autoload on : VM system " +
294                                  "will automatically reload global library macros");
295             }
296             else
297             {
298                 logVMMessageInfo("Velocimacro : autoload off : VM system " +
299                                  "will not automatically reload global library macros");
300             }
301
302             rsvc.info("Velocimacro : initialization complete.");
303         }
304     
305         return;
306     }
307
308     /**
309      * adds a macro to the factory.
310      */

311     public boolean addVelocimacro( String JavaDoc name, String JavaDoc macroBody,
312         String JavaDoc argArray[], String JavaDoc sourceTemplate )
313     {
314         /*
315          * maybe we should throw an exception, maybe just tell
316          * the caller like this...
317          *
318          * I hate this : maybe exceptions are in order here...
319          */

320         if ( name == null || macroBody == null || argArray == null ||
321             sourceTemplate == null )
322         {
323             logVMMessageWarn("Velocimacro : VM addition rejected : " +
324                 "programmer error : arg null" );
325             
326             return false;
327         }
328         
329         /*
330          * see if the current ruleset allows this addition
331          */

332
333         if (!canAddVelocimacro( name, sourceTemplate ))
334         {
335             return false;
336         }
337
338         /*
339          * seems like all is good. Lets do it.
340          */

341         synchronized( this )
342         {
343             vmManager.addVM( name, macroBody, argArray, sourceTemplate );
344         }
345
346         /*
347          * if we are to blather, blather...
348          */

349         if ( blather)
350         {
351             String JavaDoc s = "#" + argArray[0];
352             s += "(";
353         
354             for( int i=1; i < argArray.length; i++)
355             {
356                 s += " ";
357                 s += argArray[i];
358             }
359
360             s += " ) : source = ";
361             s += sourceTemplate;
362             
363            logVMMessageInfo( "Velocimacro : added new VM : " + s );
364         }
365
366         return true;
367     }
368
369     /**
370      * determines if a given macro/namespace (name, source) combo is allowed
371      * to be added
372      *
373      * @param name Name of VM to add
374      * @param sourceTemplate Source template that contains the defintion of the VM
375      * @return true if it is allowed to be added, false otherwise
376      */

377     private boolean canAddVelocimacro( String JavaDoc name, String JavaDoc sourceTemplate)
378     {
379         /*
380          * short circuit and do it if autoloader is on, and the
381          * template is one of the library templates
382          */

383         
384         if ( getAutoload() )
385         {
386             /*
387              * see if this is a library template
388              */

389
390             for( int i = 0; i < macroLibVec.size(); i++)
391             {
392                 String JavaDoc lib = (String JavaDoc) macroLibVec.elementAt(i);
393
394                 if (lib.equals( sourceTemplate ) )
395                 {
396                     return true;
397                 }
398             }
399         }
400
401            
402         /*
403          * maybe the rules should be in manager? I dunno. It's to manage
404          * the namespace issues first, are we allowed to add VMs at all?
405          * This trumps all.
406          */

407         if (!addNewAllowed)
408         {
409             logVMMessageWarn("Velocimacro : VM addition rejected : " + name +
410                 " : inline VMs not allowed." );
411             
412             return false;
413         }
414
415         /*
416          * are they local in scope? Then it is ok to add.
417          */

418         if (!templateLocal)
419         {
420             /*
421              * otherwise, if we have it already in global namespace, and they can't replace
422              * since local templates are not allowed, the global namespace is implied.
423              * remember, we don't know anything about namespace managment here, so lets
424              * note do anything fancy like trying to give it the global namespace here
425              *
426              * so if we have it, and we aren't allowed to replace, bail
427              */

428             if ( isVelocimacro( name, sourceTemplate ) && !replaceAllowed )
429             {
430                 logVMMessageWarn("Velocimacro : VM addition rejected : "
431                     + name + " : inline not allowed to replace existing VM" );
432                 return false;
433             }
434         }
435         
436         return true;
437     }
438
439     /**
440      * localization of the logging logic
441      */

442     private void logVMMessageInfo( String JavaDoc s )
443     {
444         if (blather)
445             rsvc.info( s );
446     }
447
448     /**
449      * localization of the logging logic
450      */

451     private void logVMMessageWarn( String JavaDoc s )
452     {
453         if (blather)
454             rsvc.warn( s );
455     }
456       
457     /**
458      * Tells the world if a given directive string is a Velocimacro
459      */

460     public boolean isVelocimacro( String JavaDoc vm , String JavaDoc sourceTemplate )
461     {
462         synchronized(this)
463         {
464             /*
465              * first we check the locals to see if we have
466              * a local definition for this template
467              */

468             if (vmManager.get( vm, sourceTemplate ) != null)
469                 return true;
470         }
471         return false;
472     }
473
474     /**
475      * actual factory : creates a Directive that will
476      * behave correctly wrt getting the framework to
477      * dig out the correct # of args
478      */

479     public Directive getVelocimacro( String JavaDoc vmName, String JavaDoc sourceTemplate )
480     {
481         VelocimacroProxy vp = null;
482
483         synchronized( this )
484         {
485             /*
486              * don't ask - do
487              */

488
489             vp = vmManager.get( vmName, sourceTemplate);
490
491             /*
492              * if this exists, and autoload is on, we need to check
493              * where this VM came from
494              */

495
496             if ( vp != null && getAutoload() )
497             {
498                 /*
499                  * see if this VM came from a library. Need to pass sourceTemplate
500                  * in the event namespaces are set, as it could be masked by local
501                  */

502                 
503                 String JavaDoc lib = vmManager.getLibraryName( vmName, sourceTemplate );
504
505                 if (lib != null)
506                 {
507                     try
508                     {
509                         /*
510                          * get the template from our map
511                          */

512
513                         Twonk tw = (Twonk) libModMap.get( lib );
514                         
515                         if ( tw != null)
516                         {
517                             Template template = tw.template;
518                             
519                             /*
520                              * now, compare the last modified time of the resource
521                              * with the last modified time of the template
522                              * if the file has changed, then reload. Otherwise, we should
523                              * be ok.
524                              */

525
526                             long tt = tw.modificationTime;
527                             long ft = template.getResourceLoader().getLastModified( template );
528
529                             if ( ft > tt )
530                             {
531                                 logVMMessageInfo("Velocimacro : autoload reload for VMs from " +
532                                                  "VM library template : " + lib );
533              
534                                 /*
535                                  * when there are VMs in a library that invoke each other,
536                                  * there are calls into getVelocimacro() from the init()
537                                  * process of the VM directive. To stop the infinite loop
538                                  * we save the current time reported by the resource loader
539                                  * and then be honest when the reload is complete
540                                  */

541                                  
542                                 tw.modificationTime = ft;
543                                                                        
544                                 template = rsvc.getTemplate( lib );
545  
546                                 /*
547                                  * and now we be honest
548                                  */

549
550                                 tw.template = template;
551                                 tw.modificationTime = template.getLastModified();
552
553                                 /*
554                                  * note that we don't need to put this twonk back
555                                  * into the map, as we can just use the same reference
556                                  * and this block is synchronized
557                                  */

558                              }
559                          }
560                     }
561                     catch (Exception JavaDoc e)
562                     {
563                         logVMMessageInfo("Velocimacro : error using VM " +
564                                          "library template " + lib + " : " + e );
565                     }
566
567                     /*
568                      * and get again
569                      */

570
571                     vp = vmManager.get( vmName, sourceTemplate);
572                 }
573             }
574         }
575         
576         return vp;
577     }
578
579     /**
580      * tells the vmManager to dump the specified namespace
581      */

582     public boolean dumpVMNamespace( String JavaDoc namespace )
583     {
584         return vmManager.dumpNamespace( namespace );
585     }
586
587     /**
588      * sets permission to have VMs local in scope to their declaring template
589      * note that this is really taken care of in the VMManager class, but
590      * we need it here for gating purposes in addVM
591      * eventually, I will slide this all into the manager, maybe.
592      */

593     private void setTemplateLocalInline( boolean b )
594     {
595         templateLocal = b;
596     }
597
598     private boolean getTemplateLocalInline()
599     {
600         return templateLocal;
601     }
602
603     /**
604      * sets the permission to add new macros
605      */

606     private boolean setAddMacroPermission( boolean arg )
607     {
608         boolean b = addNewAllowed;
609         
610         addNewAllowed = arg;
611         return b;
612     }
613
614     /**
615      * sets the permission for allowing addMacro() calls to
616      * replace existing VM's
617      */

618     private boolean setReplacementPermission( boolean arg )
619     {
620         boolean b = replaceAllowed;
621         replaceAllowed = arg;
622         return b;
623     }
624
625     /**
626      * set output message mode
627      */

628     private void setBlather( boolean b )
629     {
630         blather = b;
631     }
632
633     /**
634      * get output message mode
635      */

636     private boolean getBlather()
637     {
638         return blather;
639     }
640
641     /**
642      * set the switch for automatic reloading of
643      * global library-based VMs
644      */

645     private void setAutoload( boolean b)
646     {
647         autoReloadLibrary = b;
648     }
649     
650     /**
651      * get the switch for automatic reloading of
652      * global library-based VMs
653      */

654     private boolean getAutoload()
655     {
656         return autoReloadLibrary;
657     }
658
659     /**
660      * small continer class to hold the duple
661      * of a template and modification time.
662      * We keep the modification time so we can
663      * 'override' it on a reload to prevent
664      * recursive reload due to inter-calling
665      * VMs in a library
666      */

667     private class Twonk
668     {
669         public Template template;
670         public long modificationTime;
671     }
672 }
673
674
675
676
677
678
679
680
Popular Tags