KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > webwork > views > velocity > VelocityManager


1 /*
2  * Copyright (c) 2002-2003 by OpenSymphony
3  * All rights reserved.
4  */

5 package com.opensymphony.webwork.views.velocity;
6
7 import com.opensymphony.webwork.ServletActionContext;
8 import com.opensymphony.webwork.config.Configuration;
9 import com.opensymphony.webwork.portlet.velocity.ApplyDecoratorDirective;
10 import com.opensymphony.webwork.util.VelocityWebWorkUtil;
11 import com.opensymphony.webwork.views.jsp.ui.OgnlTool;
12 import com.opensymphony.webwork.views.util.ContextUtil;
13 import com.opensymphony.webwork.views.velocity.components.*;
14 import com.opensymphony.xwork.ObjectFactory;
15 import com.opensymphony.xwork.util.OgnlValueStack;
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.apache.velocity.VelocityContext;
19 import org.apache.velocity.app.Velocity;
20 import org.apache.velocity.app.VelocityEngine;
21 import org.apache.velocity.context.Context;
22 import org.apache.velocity.tools.view.ToolboxManager;
23 import org.apache.velocity.tools.view.context.ChainedContext;
24 import org.apache.velocity.tools.view.servlet.ServletToolboxManager;
25
26 import javax.servlet.ServletContext JavaDoc;
27 import javax.servlet.http.HttpServletRequest JavaDoc;
28 import javax.servlet.http.HttpServletResponse JavaDoc;
29 import java.io.File JavaDoc;
30 import java.io.FileInputStream JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.io.InputStream JavaDoc;
33 import java.util.*;
34
35
36 /**
37  * Manages the environment for Velocity result types
38  *
39  * @author Matt Ho <matt@indigoegg.com>
40  */

41 public class VelocityManager {
42     //~ Static fields/initializers /////////////////////////////////////////////
43

44     private static final Log log = LogFactory.getLog(VelocityManager.class);
45     private static VelocityManager instance;
46     public static final String JavaDoc WEBWORK = "webwork";
47
48     /**
49      * the parent JSP tag
50      */

51     public static final String JavaDoc PARENT = "parent";
52
53     /**
54      * the current JSP tag
55      */

56     public static final String JavaDoc TAG = "tag";
57
58     //~ Instance fields ////////////////////////////////////////////////////////
59

60     private OgnlTool ognlTool = OgnlTool.getInstance();
61     private VelocityEngine velocityEngine;
62
63     /**
64      * A reference to the toolbox manager.
65      */

66     protected ToolboxManager toolboxManager = null;
67     private String JavaDoc toolBoxLocation;
68
69
70     /**
71      * Names of contexts that will be chained on every request
72      */

73     private String JavaDoc[] chainedContextNames;
74
75     //~ Constructors ///////////////////////////////////////////////////////////
76

77     protected VelocityManager() {
78         init();
79     }
80
81     //~ Methods ////////////////////////////////////////////////////////////////
82

83     /**
84      * retrieve an instance to the current VelocityManager
85      */

86     public synchronized static VelocityManager getInstance() {
87         if (instance == null) {
88             String JavaDoc classname = VelocityManager.class.getName();
89
90             if (Configuration.isSet("webwork.velocity.manager.classname")) {
91                 classname = Configuration.getString("webwork.velocity.manager.classname").trim();
92             }
93
94             if (!classname.equals(VelocityManager.class.getName())) {
95                 try {
96                     log.info("Instantiating VelocityManager!, " + classname);
97                     instance = (VelocityManager) ObjectFactory.getObjectFactory().buildBean(Class.forName(classname));
98                 } catch (Exception JavaDoc e) {
99                     log.fatal("Fatal exception occurred while trying to instantiate a VelocityManager instance, " + classname, e);
100                     instance = new VelocityManager();
101                 }
102             } else {
103                 instance = new VelocityManager();
104             }
105         }
106
107         return instance;
108     }
109
110     /**
111      * @return a reference to the VelocityEngine used by <b>all</b> webwork velocity thingies with the exception of
112      * directly accessed *.vm pages
113      */

114     public VelocityEngine getVelocityEngine() {
115         return velocityEngine;
116     }
117
118     /**
119      * This method is responsible for creating the standard VelocityContext used by all WW2 velocity views. The
120      * following context parameters are defined:
121      * <p/>
122      * <ul>
123      * <li><strong>req</strong> - the current HttpServletRequest</li>
124      * <li><strong>res</strong> - the current HttpServletResponse</li>
125      * <li><strong>stack</strong> - the current {@link OgnlValueStack}</li>
126      * <li><strong>ognl</strong> - an {@link OgnlTool}</li>
127      * <li><strong>webwork</strong> - an instance of {@link com.opensymphony.webwork.util.WebWorkUtil}</li>
128      * <li><strong>action</strong> - the current WebWork action</li>
129      * </ul>
130      *
131      * @return a new WebWorkVelocityContext
132      */

133     public Context createContext(OgnlValueStack stack, HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res) {
134         VelocityContext[] chainedContexts = prepareChainedContexts(req, res);
135         WebWorkVelocityContext context = new WebWorkVelocityContext(chainedContexts, stack);
136         Map standardMap = ContextUtil.getStandardContext(stack, req, res);
137         for (Iterator iterator = standardMap.entrySet().iterator(); iterator.hasNext();) {
138             Map.Entry entry = (Map.Entry) iterator.next();
139             context.put((String JavaDoc) entry.getKey(), entry.getValue());
140         }
141         context.put(WEBWORK, new VelocityWebWorkUtil(context, stack, req, res));
142
143
144         ServletContext JavaDoc ctx = null;
145         try {
146             ctx = ServletActionContext.getServletContext();
147         } catch (NullPointerException JavaDoc npe) {
148             // in case this was used outside the lifecycle of webwork servlet
149
log.debug("internal toolbox context ignored");
150         }
151
152         if (toolboxManager != null && ctx != null) {
153             ChainedContext chained = new ChainedContext(context, req, res, ctx);
154             chained.setToolbox(toolboxManager.getToolboxContext(null));
155             return chained;
156         } else {
157             return context;
158         }
159
160     }
161
162     /**
163      * constructs contexts for chaining on this request. This method does not
164      * perform any initialization of the contexts. All that must be done in the
165      * context itself.
166      *
167      * @param servletRequest
168      * @param servletResponse
169      * @return an VelocityContext[] of contexts to chain
170      */

171     protected VelocityContext[] prepareChainedContexts(HttpServletRequest JavaDoc servletRequest, HttpServletResponse JavaDoc servletResponse) {
172         if (this.chainedContextNames == null) {
173             return null;
174         }
175         List contextList = new ArrayList();
176         for (int i = 0; i < chainedContextNames.length; i++) {
177             String JavaDoc className = chainedContextNames[i];
178             try {
179                 VelocityContext velocityContext = (VelocityContext) ObjectFactory.getObjectFactory().buildBean(className);
180                 contextList.add(velocityContext);
181             } catch (Exception JavaDoc e) {
182                 log.warn("Warning. " + e.getClass().getName() + " caught while attempting to instantiate a chained VelocityContext, " + className + " -- skipping");
183             }
184         }
185         if (contextList.size() > 0) {
186             VelocityContext[] extraContexts = new VelocityContext[contextList.size()];
187             contextList.toArray(extraContexts);
188             return extraContexts;
189         } else {
190             return null;
191         }
192     }
193
194     /**
195      * initializes the VelocityManager. this should be called during the initialization process, say by
196      * ServletDispatcher. this may be called multiple times safely although calls beyond the first won't do anything
197      *
198      * @param context the current servlet context
199      */

200     public synchronized void init(ServletContext JavaDoc context) {
201         if (velocityEngine == null) {
202             velocityEngine = newVelocityEngine(context);
203         }
204         this.initToolbox(context);
205     }
206
207     /**
208      * load optional velocity properties using the following loading strategy
209      * <ul>
210      * <li>relative to the servlet context path</li>
211      * <li>relative to the WEB-INF directory</li>
212      * <li>on the classpath</li>
213      * </ul>
214      *
215      * @param context the current ServletContext. may <b>not</b> be null
216      * @return the optional properties if webwork.velocity.configfile was specified, an empty Properties file otherwise
217      */

218     public Properties loadConfiguration(ServletContext JavaDoc context) {
219         if (context == null) {
220             String JavaDoc gripe = "Error attempting to create a loadConfiguration from a null ServletContext!";
221             log.error(gripe);
222             throw new IllegalArgumentException JavaDoc(gripe);
223         }
224
225         Properties properties = new Properties();
226
227         // now apply our systemic defaults, then allow user to override
228
applyDefaultConfiguration(context, properties);
229
230         String JavaDoc defaultUserDirective = properties.getProperty("userdirective");
231
232         /**
233          * if the user has specified an external velocity configuration file, we'll want to search for it in the
234          * following order
235          *
236          * 1. relative to the context path
237          * 2. relative to /WEB-INF
238          * 3. in the class path
239          */

240         String JavaDoc configfile;
241
242         if (Configuration.isSet("webwork.velocity.configfile")) {
243             configfile = Configuration.getString("webwork.velocity.configfile");
244         } else {
245             configfile = "velocity.properties";
246         }
247
248         configfile = configfile.trim();
249
250         InputStream JavaDoc in = null;
251         String JavaDoc resourceLocation = null;
252
253         try {
254             if (context.getRealPath(configfile) != null) {
255                 // 1. relative to context path, i.e. /velocity.properties
256
String JavaDoc filename = context.getRealPath(configfile);
257
258                 if (filename != null) {
259                     File JavaDoc file = new File JavaDoc(filename);
260
261                     if (file.isFile()) {
262                         resourceLocation = file.getCanonicalPath() + " from file system";
263                         in = new FileInputStream JavaDoc(file);
264                     }
265
266                     // 2. if nothing was found relative to the context path, search relative to the WEB-INF directory
267
if (in == null) {
268                         file = new File JavaDoc(context.getRealPath("/WEB-INF/" + configfile));
269
270                         if (file.isFile()) {
271                             resourceLocation = file.getCanonicalPath() + " from file system";
272                             in = new FileInputStream JavaDoc(file);
273                         }
274                     }
275                 }
276             }
277
278             // 3. finally, if there's no physical file, how about something in our classpath
279
if (in == null) {
280                 in = VelocityManager.class.getClassLoader().getResourceAsStream(configfile);
281                 if (in != null) {
282                     resourceLocation = configfile + " from classloader";
283                 }
284             }
285
286             // if we've got something, load 'er up
287
if (in != null) {
288                 log.info("Initializing velocity using " + resourceLocation);
289                 properties.load(in);
290             }
291         } catch (IOException JavaDoc e) {
292             log.warn("Unable to load velocity configuration " + resourceLocation, e);
293         } finally {
294             if (in != null) {
295                 try {
296                     in.close();
297                 } catch (IOException JavaDoc e) {
298                 }
299             }
300         }
301
302         String JavaDoc userdirective = properties.getProperty("userdirective");
303
304         if ((userdirective == null) || userdirective.trim().equals("")) {
305             userdirective = defaultUserDirective;
306         } else {
307             userdirective = userdirective.trim() + "," + defaultUserDirective;
308         }
309
310         properties.setProperty("userdirective", userdirective);
311
312         // for debugging purposes, allows users to dump out the properties that have been configured
313
if (log.isDebugEnabled()) {
314             log.debug("Initializing Velocity with the following properties ...");
315
316             for (Iterator iter = properties.keySet().iterator();
317                  iter.hasNext();) {
318                 String JavaDoc key = (String JavaDoc) iter.next();
319                 String JavaDoc value = properties.getProperty(key);
320
321                 if (log.isDebugEnabled()) {
322                     log.debug(" '" + key + "' = '" + value + "'");
323                 }
324             }
325         }
326
327         return properties;
328     }
329
330     /**
331      * performs one-time initializations
332      */

333     protected void init() {
334
335         // read in the names of contexts to add to each request
336
initChainedContexts();
337
338
339         if (Configuration.isSet("webwork.velocity.toolboxlocation")) {
340             toolBoxLocation = Configuration.get("webwork.velocity.toolboxlocation").toString();
341         }
342
343     }
344
345
346     /**
347      * Initializes the ServletToolboxManager for this servlet's
348      * toolbox (if any).
349      */

350     protected void initToolbox(ServletContext JavaDoc context) {
351         /* if we have a toolbox, get a manager for it */
352         if (toolBoxLocation != null) {
353             toolboxManager = ServletToolboxManager.getInstance(context, toolBoxLocation);
354         } else {
355             Velocity.info("VelocityViewServlet: No toolbox entry in configuration.");
356         }
357     }
358
359
360     /**
361      * allow users to specify via the webwork.properties file a set of additional VelocityContexts to chain to the
362      * the WebWorkVelocityContext. The intent is to allow these contexts to store helper objects that the ui
363      * developer may want access to. Examples of reasonable VelocityContexts would be an IoCVelocityContext, a
364      * SpringReferenceVelocityContext, and a ToolboxVelocityContext
365      */

366     protected void initChainedContexts() {
367
368         if (Configuration.isSet("webwork.velocity.contexts")) {
369             // we expect contexts to be a comma separated list of classnames
370
String JavaDoc contexts = Configuration.get("webwork.velocity.contexts").toString();
371             StringTokenizer st = new StringTokenizer(contexts, ",");
372             List contextList = new ArrayList();
373
374             while (st.hasMoreTokens()) {
375                 String JavaDoc classname = st.nextToken();
376                 contextList.add(classname);
377             }
378             if (contextList.size() > 0) {
379                 String JavaDoc[] chainedContexts = new String JavaDoc[contextList.size()];
380                 contextList.toArray(chainedContexts);
381                 this.chainedContextNames = chainedContexts;
382             }
383
384
385         }
386
387     }
388
389     /**
390      * <p/>
391      * Instantiates a new VelocityEngine.
392      * </p>
393      * <p/>
394      * The following is the default Velocity configuration
395      * </p>
396      * <pre>
397      * resource.loader = file, class
398      * file.resource.loader.path = real path of webapp
399      * class.resource.loader.description = Velocity Classpath Resource Loader
400      * class.resource.loader.class = com.opensymphony.webwork.views.velocity.WebWorkResourceLoader
401      * </pre>
402      * <p/>
403      * this default configuration can be overridden by specifying a webwork.velocity.configfile property in the
404      * webwork.properties file. the specified config file will be searched for in the following order:
405      * </p>
406      * <ul>
407      * <li>relative to the servlet context path</li>
408      * <li>relative to the WEB-INF directory</li>
409      * <li>on the classpath</li>
410      * </ul>
411      *
412      * @param context the current ServletContext. may <b>not</b> be null
413      */

414     protected VelocityEngine newVelocityEngine(ServletContext JavaDoc context) {
415         if (context == null) {
416             String JavaDoc gripe = "Error attempting to create a new VelocityEngine from a null ServletContext!";
417             log.error(gripe);
418             throw new IllegalArgumentException JavaDoc(gripe);
419         }
420
421         Properties p = loadConfiguration(context);
422
423         VelocityEngine velocityEngine = new VelocityEngine();
424
425         try {
426             velocityEngine.init(p);
427         } catch (Exception JavaDoc e) {
428             String JavaDoc gripe = "Unable to instantiate VelocityEngine!";
429             log.error(gripe, e);
430             throw new RuntimeException JavaDoc(gripe);
431         }
432
433         return velocityEngine;
434     }
435
436     /**
437      * once we've loaded up the user defined configurations, we will want to apply WebWork specification configurations.
438      * <ul>
439      * <li>if Velocity.RESOURCE_LOADER has not been defined, then we will use the defaults which is a joined file,
440      * class loader for unpackaed wars and a straight class loader otherwise</li>
441      * <li>we need to define the various WebWork custom user directives such as #param, #tag, and #bodytag</li>
442      * </ul>
443      *
444      * @param context
445      * @param p
446      */

447     private void applyDefaultConfiguration(ServletContext JavaDoc context, Properties p) {
448         // ensure that caching isn't overly aggressive
449

450         /**
451          * Load a default resource loader definition if there isn't one present.
452          * Ben Hall (22/08/2003)
453          */

454         if (p.getProperty(Velocity.RESOURCE_LOADER) == null) {
455             p.setProperty(Velocity.RESOURCE_LOADER, "wwfile, wwclass");
456         }
457
458         /**
459          * If there's a "real" path add it for the wwfile resource loader.
460          * If there's no real path and they haven't configured a loader then we change
461          * resource loader property to just use the wwclass loader
462          * Ben Hall (22/08/2003)
463          */

464         if (context.getRealPath("") != null) {
465             p.setProperty("wwfile.resource.loader.description", "Velocity File Resource Loader");
466             p.setProperty("wwfile.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
467             p.setProperty("wwfile.resource.loader.path", context.getRealPath(""));
468             p.setProperty("wwfile.resource.loader.modificationCheckInterval", "2");
469             p.setProperty("wwfile.resource.loader.cache", "true");
470         } else {
471             // remove wwfile from resource loader property
472
String JavaDoc prop = p.getProperty(Velocity.RESOURCE_LOADER);
473             if (prop.indexOf("wwfile,") != -1) {
474                 prop = replace(prop, "wwfile,", "");
475             } else if (prop.indexOf(", wwfile") != -1) {
476                 prop = replace(prop, ", wwfile", "");
477             } else if (prop.indexOf("wwfile") != -1) {
478                 prop = replace(prop, "wwfile", "");
479             }
480
481             p.setProperty(Velocity.RESOURCE_LOADER, prop);
482         }
483
484         /**
485          * Refactored the Velocity templates for the WebWork taglib into the classpath from the web path. This will
486          * enable WebWork projects to have access to the templates by simply including the WebWork jar file.
487          * Unfortunately, there does not appear to be a macro for the class loader keywords
488          * Matt Ho - Mon Mar 17 00:21:46 PST 2003
489          */

490         p.setProperty("wwclass.resource.loader.description", "Velocity Classpath Resource Loader");
491         p.setProperty("wwclass.resource.loader.class", "com.opensymphony.webwork.views.velocity.WebWorkResourceLoader");
492         p.setProperty("wwclass.resource.loader.modificationCheckInterval", "2");
493         p.setProperty("wwclass.resource.loader.cache", "true");
494
495         // components
496
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
497
498         //deprecated directive class
499
addDirective(sb, BodyTagDirective.class);
500         addDirective(sb, TagDirective.class);
501         addDirective(sb, ParamDirective.class);
502
503         addDirective(sb, ApplyDecoratorDirective.class);
504
505         addDirective(sb, ActionDirective.class);
506         addDirective(sb, CheckBoxDirective.class);
507         addDirective(sb, CheckBoxListDirective.class);
508         addDirective(sb, ComboBoxDirective.class);
509         addDirective(sb, ComponentDirective.class);
510         addDirective(sb, DatePickerDirective.class);
511         addDirective(sb, DivDirective.class);
512         addDirective(sb, DoubleSelectDirective.class);
513         addDirective(sb, FileDirective.class);
514         addDirective(sb, FormDirective.class);
515         addDirective(sb, HiddenDirective.class);
516         addDirective(sb, IncludeDirective.class);
517         addDirective(sb, HrefDirective.class);
518         addDirective(sb, LabelDirective.class);
519         addDirective(sb, PanelDirective.class);
520         addDirective(sb, PasswordDirective.class);
521         addDirective(sb, RadioDirective.class);
522         addDirective(sb, SelectDirective.class);
523         addDirective(sb, SubmitDirective.class);
524         addDirective(sb, TabbedPanelDirective.class);
525         addDirective(sb, TextAreaDirective.class);
526         addDirective(sb, TextFieldDirective.class);
527         addDirective(sb, TokenDirective.class);
528         addDirective(sb, URLDirective.class);
529
530         String JavaDoc directives = sb.toString();
531
532         String JavaDoc userdirective = p.getProperty("userdirective");
533         if ((userdirective == null) || userdirective.trim().equals("")) {
534             userdirective = directives;
535         } else {
536             userdirective = userdirective.trim() + "," + directives;
537         }
538
539         p.setProperty("userdirective", userdirective);
540     }
541
542     private void addDirective(StringBuffer JavaDoc sb, Class JavaDoc clazz) {
543         sb.append(clazz.getName()).append(",");
544     }
545
546     private static final String JavaDoc replace(String JavaDoc string, String JavaDoc oldString, String JavaDoc newString) {
547         if (string == null) {
548             return null;
549         }
550         // If the newString is null, just return the string since there's nothing to replace.
551
if (newString == null) {
552             return string;
553         }
554         int i = 0;
555         // Make sure that oldString appears at least once before doing any processing.
556
if ((i = string.indexOf(oldString, i)) >= 0) {
557             // Use char []'s, as they are more efficient to deal with.
558
char[] string2 = string.toCharArray();
559             char[] newString2 = newString.toCharArray();
560             int oLength = oldString.length();
561             StringBuffer JavaDoc buf = new StringBuffer JavaDoc(string2.length);
562             buf.append(string2, 0, i).append(newString2);
563             i += oLength;
564             int j = i;
565             // Replace all remaining instances of oldString with newString.
566
while ((i = string.indexOf(oldString, i)) > 0) {
567                 buf.append(string2, j, i - j).append(newString2);
568                 i += oLength;
569                 j = i;
570             }
571             buf.append(string2, j, string2.length - j);
572             return buf.toString();
573         }
574         return string;
575     }
576 }
577
Popular Tags