KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > ui > velocity > VelocityEngineFactory


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

16
17 package org.springframework.ui.velocity;
18
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Properties JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.velocity.app.VelocityEngine;
29 import org.apache.velocity.exception.VelocityException;
30 import org.apache.velocity.runtime.RuntimeConstants;
31
32 import org.springframework.core.io.DefaultResourceLoader;
33 import org.springframework.core.io.Resource;
34 import org.springframework.core.io.ResourceLoader;
35 import org.springframework.core.io.support.PropertiesLoaderUtils;
36 import org.springframework.util.StringUtils;
37
38 /**
39  * Factory that configures a VelocityEngine. Can be used standalone,
40  * but typically you will either use {@link VelocityEngineFactoryBean}
41  * for preparing a VelocityEngine as bean reference, or
42  * {@link org.springframework.web.servlet.view.velocity.VelocityConfigurer}
43  * for web views.
44  *
45  * <p>The optional "configLocation" property sets the location of the Velocity
46  * properties file, within the current application. Velocity properties can be
47  * overridden via "velocityProperties", or even completely specified locally,
48  * avoiding the need for an external properties file.
49  *
50  * <p>The "resourceLoaderPath" property can be used to specify the Velocity
51  * resource loader path via Spring's Resource abstraction, possibly relative
52  * to the Spring application context.
53  *
54  * <p>If "overrideLogging" is true (the default), the VelocityEngine will be
55  * configured to log via Commons Logging, that is, using the Spring-provided
56  * {@link CommonsLoggingLogSystem} as log system.
57  *
58  * <p>The simplest way to use this class is to specify a
59  * {@link #setResourceLoaderPath(String) "resourceLoaderPath"}; the
60  * VelocityEngine typically then does not need any further configuration.
61  *
62  * @author Juergen Hoeller
63  * @see #setConfigLocation
64  * @see #setVelocityProperties
65  * @see #setResourceLoaderPath
66  * @see #setOverrideLogging
67  * @see #createVelocityEngine
68  * @see CommonsLoggingLogSystem
69  * @see VelocityEngineFactoryBean
70  * @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
71  * @see org.apache.velocity.app.VelocityEngine
72  */

73 public class VelocityEngineFactory {
74
75     protected final Log logger = LogFactory.getLog(getClass());
76
77     private Resource configLocation;
78
79     private final Map JavaDoc velocityProperties = new HashMap JavaDoc();
80
81     private String JavaDoc resourceLoaderPath;
82
83     private ResourceLoader resourceLoader = new DefaultResourceLoader();
84
85     private boolean preferFileSystemAccess = true;
86
87     private boolean overrideLogging = true;
88
89
90     /**
91      * Set the location of the Velocity config file.
92      * Alternatively, you can specify all properties locally.
93      * @see #setVelocityProperties
94      * @see #setResourceLoaderPath
95      */

96     public void setConfigLocation(Resource configLocation) {
97         this.configLocation = configLocation;
98     }
99
100     /**
101      * Set Velocity properties, like "file.resource.loader.path".
102      * Can be used to override values in a Velocity config file,
103      * or to specify all necessary properties locally.
104      * <p>Note that the Velocity resource loader path also be set to any
105      * Spring resource location via the "resourceLoaderPath" property.
106      * Setting it here is just necessary when using a non-file-based
107      * resource loader.
108      * @see #setVelocityPropertiesMap
109      * @see #setConfigLocation
110      * @see #setResourceLoaderPath
111      */

112     public void setVelocityProperties(Properties JavaDoc velocityProperties) {
113         setVelocityPropertiesMap(velocityProperties);
114     }
115
116     /**
117      * Set Velocity properties as Map, to allow for non-String values
118      * like "ds.resource.loader.instance".
119      * @see #setVelocityProperties
120      */

121     public void setVelocityPropertiesMap(Map JavaDoc velocityPropertiesMap) {
122         if (velocityPropertiesMap != null) {
123             this.velocityProperties.putAll(velocityPropertiesMap);
124         }
125     }
126
127     /**
128      * Set the Velocity resource loader path via a Spring resource location.
129      * Accepts multiple locations in Velocity's comma-separated path style.
130      * <p>When populated via a String, standard URLs like "file:" and "classpath:"
131      * pseudo URLs are supported, as understood by ResourceLoader. Allows for
132      * relative paths when running in an ApplicationContext.
133      * <p>Will define a path for the default Velocity resource loader with the name
134      * "file". If the specified resource cannot be resolved to a <code>java.io.File</code>,
135      * a generic SpringResourceLoader will be used under the name "spring", without
136      * modification detection.
137      * <p>Note that resource caching will be enabled in any case. With the file
138      * resource loader, the last-modified timestamp will be checked on access to
139      * detect changes. With SpringResourceLoader, the resource will be cached
140      * forever (for example for class path resources).
141      * <p>To specify a modification check interval for files, use Velocity's
142      * standard "file.resource.loader.modificationCheckInterval" property. By default,
143      * the file timestamp is checked on every access (which is surprisingly fast).
144      * Of course, this just applies when loading resources from the file system.
145      * <p>To enforce the use of SpringResourceLoader, i.e. to not resolve a path
146      * as file system resource in any case, turn off the "preferFileSystemAccess"
147      * flag. See the latter's javadoc for details.
148      * @see #setResourceLoader
149      * @see #setVelocityProperties
150      * @see #setPreferFileSystemAccess
151      * @see SpringResourceLoader
152      * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
153      */

154     public void setResourceLoaderPath(String JavaDoc resourceLoaderPath) {
155         this.resourceLoaderPath = resourceLoaderPath;
156     }
157
158     /**
159      * Set the Spring ResourceLoader to use for loading Velocity template files.
160      * The default is DefaultResourceLoader. Will get overridden by the
161      * ApplicationContext if running in a context.
162      * @see org.springframework.core.io.DefaultResourceLoader
163      * @see org.springframework.context.ApplicationContext
164      */

165     public void setResourceLoader(ResourceLoader resourceLoader) {
166         this.resourceLoader = resourceLoader;
167     }
168
169     /**
170      * Return the Spring ResourceLoader to use for loading Velocity template files.
171      */

172     protected ResourceLoader getResourceLoader() {
173         return this.resourceLoader;
174     }
175
176     /**
177      * Set whether to prefer file system access for template loading.
178      * File system access enables hot detection of template changes.
179      * <p>If this is enabled, VelocityEngineFactory will try to resolve the
180      * specified "resourceLoaderPath" as file system resource (which will work
181      * for expanded class path resources and ServletContext resources too).
182      * <p>Default is "true". Turn this off to always load via SpringResourceLoader
183      * (i.e. as stream, without hot detection of template changes), which might
184      * be necessary if some of your templates reside in an expanded classes
185      * directory while others reside in jar files.
186      * @see #setResourceLoaderPath
187      */

188     public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
189         this.preferFileSystemAccess = preferFileSystemAccess;
190     }
191
192     /**
193      * Return whether to prefer file system access for template loading.
194      */

195     protected boolean isPreferFileSystemAccess() {
196         return this.preferFileSystemAccess;
197     }
198
199     /**
200      * Set whether Velocity should log via Commons Logging, i.e. whether Velocity's
201      * log system should be set to CommonsLoggingLogSystem. Default value is true.
202      * @see CommonsLoggingLogSystem
203      */

204     public void setOverrideLogging(boolean overrideLogging) {
205         this.overrideLogging = overrideLogging;
206     }
207
208
209     /**
210      * Prepare the VelocityEngine instance and return it.
211      * @return the VelocityEngine instance
212      * @throws IOException if the config file wasn't found
213      * @throws VelocityException on Velocity initialization failure
214      */

215     public VelocityEngine createVelocityEngine() throws IOException JavaDoc, VelocityException {
216         VelocityEngine velocityEngine = newVelocityEngine();
217         Properties JavaDoc props = new Properties JavaDoc();
218
219         // Load config file if set.
220
if (this.configLocation != null) {
221             if (logger.isInfoEnabled()) {
222                 logger.info("Loading Velocity config from [" + this.configLocation + "]");
223             }
224             PropertiesLoaderUtils.fillProperties(props, this.configLocation);
225         }
226
227         // Merge local properties if set.
228
if (!this.velocityProperties.isEmpty()) {
229             props.putAll(this.velocityProperties);
230         }
231
232         // Set a resource loader path, if required.
233
if (this.resourceLoaderPath != null) {
234             initVelocityResourceLoader(velocityEngine, this.resourceLoaderPath);
235         }
236
237         // Log via Commons Logging?
238
if (this.overrideLogging) {
239             velocityEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new CommonsLoggingLogSystem());
240         }
241
242         // Apply properties to VelocityEngine.
243
for (Iterator JavaDoc it = props.entrySet().iterator(); it.hasNext();) {
244             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
245             if (!(entry.getKey() instanceof String JavaDoc)) {
246                 throw new IllegalArgumentException JavaDoc(
247                         "Illegal property key [" + entry.getKey() + "]: only Strings allowed");
248             }
249             velocityEngine.setProperty((String JavaDoc) entry.getKey(), entry.getValue());
250         }
251
252         postProcessVelocityEngine(velocityEngine);
253
254         try {
255             // Perform actual initialization.
256
velocityEngine.init();
257         }
258         catch (IOException JavaDoc ex) {
259             throw ex;
260         }
261         catch (VelocityException ex) {
262             throw ex;
263         }
264         catch (RuntimeException JavaDoc ex) {
265             throw ex;
266         }
267         catch (Exception JavaDoc ex) {
268             logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
269             throw new VelocityException(ex.toString());
270         }
271
272         return velocityEngine;
273     }
274
275     /**
276      * Return a new VelocityEngine. Subclasses can override this for
277      * custom initialization, or for using a mock object for testing.
278      * <p>Called by <code>createVelocityEngine()</code>.
279      * @return the VelocityEngine instance
280      * @throws IOException if a config file wasn't found
281      * @throws VelocityException on Velocity initialization failure
282      * @see #createVelocityEngine()
283      */

284     protected VelocityEngine newVelocityEngine() throws IOException JavaDoc, VelocityException {
285         return new VelocityEngine();
286     }
287
288     /**
289      * Initialize a Velocity resource loader for the given VelocityEngine:
290      * either a standard Velocity FileResourceLoader or a SpringResourceLoader.
291      * <p>Called by <code>createVelocityEngine()</code>.
292      * @param velocityEngine the VelocityEngine to configure
293      * @param resourceLoaderPath the path to load Velocity resources from
294      * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
295      * @see SpringResourceLoader
296      * @see #initSpringResourceLoader
297      * @see #createVelocityEngine()
298      */

299     protected void initVelocityResourceLoader(VelocityEngine velocityEngine, String JavaDoc resourceLoaderPath) {
300         if (isPreferFileSystemAccess()) {
301             // Try to load via the file system, fall back to SpringResourceLoader
302
// (for hot detection of template changes, if possible).
303
try {
304                 StringBuffer JavaDoc resolvedPath = new StringBuffer JavaDoc();
305                 String JavaDoc[] paths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath);
306                 for (int i = 0; i < paths.length; i++) {
307                     String JavaDoc path = paths[i];
308                     Resource resource = getResourceLoader().getResource(path);
309                     File JavaDoc file = resource.getFile(); // will fail if not resolvable in the file system
310
if (logger.isDebugEnabled()) {
311                         logger.debug("Resource loader path [" + path + "] resolved to file [" + file.getAbsolutePath() + "]");
312                     }
313                     resolvedPath.append(file.getAbsolutePath());
314                     if (i < paths.length - 1) {
315                         resolvedPath.append(',');
316                     }
317                 }
318                 velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
319                 velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true");
320                 velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, resolvedPath.toString());
321             }
322             catch (IOException JavaDoc ex) {
323                 if (logger.isDebugEnabled()) {
324                     logger.debug("Cannot resolve resource loader path [" + resourceLoaderPath +
325                             "] to [java.io.File]: using SpringResourceLoader", ex);
326                 }
327                 initSpringResourceLoader(velocityEngine, resourceLoaderPath);
328             }
329         }
330         else {
331             // Always load via SpringResourceLoader
332
// (without hot detection of template changes).
333
if (logger.isDebugEnabled()) {
334                 logger.debug("File system access not preferred: using SpringResourceLoader");
335             }
336             initSpringResourceLoader(velocityEngine, resourceLoaderPath);
337         }
338     }
339
340     /**
341      * Initialize a SpringResourceLoader for the given VelocityEngine.
342      * <p>Called by <code>initVelocityResourceLoader</code>.
343      * @param velocityEngine the VelocityEngine to configure
344      * @param resourceLoaderPath the path to load Velocity resources from
345      * @see SpringResourceLoader
346      * @see #initVelocityResourceLoader
347      */

348     protected void initSpringResourceLoader(VelocityEngine velocityEngine, String JavaDoc resourceLoaderPath) {
349         velocityEngine.setProperty(
350                 RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME);
351         velocityEngine.setProperty(
352                 SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName());
353         velocityEngine.setProperty(
354                 SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true");
355         velocityEngine.setApplicationAttribute(
356                 SpringResourceLoader.SPRING_RESOURCE_LOADER, getResourceLoader());
357         velocityEngine.setApplicationAttribute(
358                 SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath);
359     }
360
361     /**
362      * To be implemented by subclasses that want to to perform custom
363      * post-processing of the VelocityEngine after this FactoryBean
364      * performed its default configuration (but before VelocityEngine.init).
365      * <p>Called by <code>createVelocityEngine()</code>.
366      * @param velocityEngine the current VelocityEngine
367      * @throws IOException if a config file wasn't found
368      * @throws VelocityException on Velocity initialization failure
369      * @see #createVelocityEngine()
370      * @see org.apache.velocity.app.VelocityEngine#init
371      */

372     protected void postProcessVelocityEngine(VelocityEngine velocityEngine)
373             throws IOException JavaDoc, VelocityException {
374     }
375
376 }
377
Popular Tags