KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > ui > freemarker > FreeMarkerConfigurationFactory


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.freemarker;
18
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Arrays JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Properties JavaDoc;
26
27 import freemarker.cache.FileTemplateLoader;
28 import freemarker.cache.MultiTemplateLoader;
29 import freemarker.cache.TemplateLoader;
30 import freemarker.template.Configuration;
31 import freemarker.template.SimpleHash;
32 import freemarker.template.TemplateException;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35
36 import org.springframework.core.io.DefaultResourceLoader;
37 import org.springframework.core.io.Resource;
38 import org.springframework.core.io.ResourceLoader;
39 import org.springframework.core.io.support.PropertiesLoaderUtils;
40 import org.springframework.util.CollectionUtils;
41
42 /**
43  * Factory that configures a FreeMarker Configuration. Can be used standalone, but
44  * typically you will either use FreeMarkerConfigurationFactoryBean for preparing a
45  * Configuration as bean reference, or FreeMarkerConfigurer for web views.
46  *
47  * <p>The optional "configLocation" property sets the location of a FreeMarker
48  * properties file, within the current application. FreeMarker properties can be
49  * overridden via "freemarkerSettings". All of these properties will be set by
50  * calling FreeMarker's <code>Configuration.setSettings()</code> method and are
51  * subject to constraints set by FreeMarker.
52  *
53  * <p>The "freemarkerVariables" property can be used to specify a Map of
54  * shared variables that will be applied to the Configuration via the
55  * <code>setAllSharedVariables()</code> method. Like <code>setSettings()</code>,
56  * these entries are subject to FreeMarker constraints.
57  *
58  * <p>The simplest way to use this class is to specify a "templateLoaderPath";
59  * FreeMarker does not need any further configuration then.
60  *
61  * <p>Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher.
62  *
63  * @author Darren Davison
64  * @author Juergen Hoeller
65  * @since 03.03.2004
66  * @see #setConfigLocation
67  * @see #setFreemarkerSettings
68  * @see #setFreemarkerVariables
69  * @see #setTemplateLoaderPath
70  * @see #createConfiguration
71  * @see FreeMarkerConfigurationFactoryBean
72  * @see org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer
73  * @see freemarker.template.Configuration
74  */

75 public class FreeMarkerConfigurationFactory {
76
77     protected final Log logger = LogFactory.getLog(getClass());
78
79     private Resource configLocation;
80
81     private Properties JavaDoc freemarkerSettings;
82
83     private Map JavaDoc freemarkerVariables;
84
85     private String JavaDoc defaultEncoding;
86
87     private final List JavaDoc templateLoaders = new ArrayList JavaDoc();
88
89     private List JavaDoc preTemplateLoaders;
90
91     private List JavaDoc postTemplateLoaders;
92
93     private String JavaDoc[] templateLoaderPaths;
94
95     private ResourceLoader resourceLoader = new DefaultResourceLoader();
96
97     private boolean preferFileSystemAccess = true;
98
99
100     /**
101      * Set the location of the FreeMarker config file.
102      * Alternatively, you can specify all setting locally.
103      * @see #setFreemarkerSettings
104      * @see #setTemplateLoaderPath
105      */

106     public void setConfigLocation(Resource resource) {
107         configLocation = resource;
108     }
109
110     /**
111      * Set properties that contain well-known FreeMarker keys which will be
112      * passed to FreeMarker's <code>Configuration.setSettings</code> method.
113      * @see freemarker.template.Configuration#setSettings
114      */

115     public void setFreemarkerSettings(Properties JavaDoc settings) {
116         this.freemarkerSettings = settings;
117     }
118
119     /**
120      * Set a Map that contains well-known FreeMarker objects which will be passed
121      * to FreeMarker's <code>Configuration.setAllSharedVariables()</code> method.
122      * @see freemarker.template.Configuration#setAllSharedVariables
123      */

124     public void setFreemarkerVariables(Map JavaDoc variables) {
125         this.freemarkerVariables = variables;
126     }
127
128     /**
129      * Set the default encoding for the FreeMarker configuration.
130      * If not specified, FreeMarker will use the platform file encoding.
131      * <p>Used for template rendering unless there is an explicit encoding specified
132      * for the rendering process (for example, on Spring's FreeMarkerView).
133      * @see freemarker.template.Configuration#setDefaultEncoding
134      * @see org.springframework.web.servlet.view.freemarker.FreeMarkerView#setEncoding
135      */

136     public void setDefaultEncoding(String JavaDoc defaultEncoding) {
137         this.defaultEncoding = defaultEncoding;
138     }
139
140     /**
141      * Set a List of <code>TemplateLoader<code>s that will be used to search
142      * for templates. For example, one or more custom loaders such as database
143      * loaders could be configured and injected here.
144      * @deprecated as of Spring 2.0.1, in favor of the "preTemplateLoaders"
145      * and "postTemplateLoaders" properties
146      * @see #setPreTemplateLoaders
147      * @see #setPostTemplateLoaders
148      */

149     public void setTemplateLoaders(TemplateLoader[] templateLoaders) {
150         if (templateLoaders != null) {
151             this.templateLoaders.addAll(Arrays.asList(templateLoaders));
152         }
153     }
154
155     /**
156      * Set a List of <code>TemplateLoader<code>s that will be used to search
157      * for templates. For example, one or more custom loaders such as database
158      * loaders could be configured and injected here.
159      * <p>The {@link TemplateLoader TemplateLoaders} specified here will be
160      * registered <i>before</i> the default template loaders that this factory
161      * registers (such as loaders for specified "templateLoaderPaths" or any
162      * loaders registered in {@link #postProcessTemplateLoaders}).
163      * @see #setTemplateLoaderPaths
164      * @see #postProcessTemplateLoaders
165      */

166     public void setPreTemplateLoaders(TemplateLoader[] preTemplateLoaders) {
167         this.preTemplateLoaders = Arrays.asList(preTemplateLoaders);
168     }
169
170     /**
171      * Set a List of <code>TemplateLoader<code>s that will be used to search
172      * for templates. For example, one or more custom loaders such as database
173      * loaders can be configured.
174      * <p>The {@link TemplateLoader TemplateLoaders} specified here will be
175      * registered <i>after</i> the default template loaders that this factory
176      * registers (such as loaders for specified "templateLoaderPaths" or any
177      * loaders registered in {@link #postProcessTemplateLoaders}).
178      * @see #setTemplateLoaderPaths
179      * @see #postProcessTemplateLoaders
180      */

181     public void setPostTemplateLoaders(TemplateLoader[] postTemplateLoaders) {
182         this.postTemplateLoaders = Arrays.asList(postTemplateLoaders);
183     }
184
185     /**
186      * Set the Freemarker template loader path via a Spring resource location.
187      * See the "templateLoaderPaths" property for details on path handling.
188      * @see #setTemplateLoaderPaths
189      */

190     public void setTemplateLoaderPath(String JavaDoc templateLoaderPath) {
191         this.templateLoaderPaths = new String JavaDoc[] {templateLoaderPath};
192     }
193
194     /**
195      * Set multiple Freemarker template loader paths via Spring resource locations.
196      * <p>When populated via a String, standard URLs like "file:" and "classpath:"
197      * pseudo URLs are supported, as understood by ResourceEditor. Allows for
198      * relative paths when running in an ApplicationContext.
199      * <p>Will define a path for the default FreeMarker template loader.
200      * If a specified resource cannot be resolved to a <code>java.io.File</code>,
201      * a generic SpringTemplateLoader will be used, without modification detection.
202      * <p>To enforce the use of SpringTemplateLoader, i.e. to not resolve a path
203      * as file system resource in any case, turn off the "preferFileSystemAccess"
204      * flag. See the latter's javadoc for details.
205      * <p>If you wish to specify your own list of TemplateLoaders, do not set this
206      * property and instead use <code>setTemplateLoaders(List templateLoaders)</code>
207      * @see org.springframework.core.io.ResourceEditor
208      * @see org.springframework.context.ApplicationContext#getResource
209      * @see freemarker.template.Configuration#setDirectoryForTemplateLoading
210      * @see SpringTemplateLoader
211      * @see #setTemplateLoaders
212      */

213     public void setTemplateLoaderPaths(String JavaDoc[] templateLoaderPaths) {
214         this.templateLoaderPaths = templateLoaderPaths;
215     }
216
217     /**
218      * Set the Spring ResourceLoader to use for loading FreeMarker template files.
219      * The default is DefaultResourceLoader. Will get overridden by the
220      * ApplicationContext if running in a context.
221      * @see org.springframework.core.io.DefaultResourceLoader
222      */

223     public void setResourceLoader(ResourceLoader resourceLoader) {
224         this.resourceLoader = resourceLoader;
225     }
226
227     /**
228      * Return the Spring ResourceLoader to use for loading FreeMarker template files.
229      */

230     protected ResourceLoader getResourceLoader() {
231         return resourceLoader;
232     }
233
234     /**
235      * Set whether to prefer file system access for template loading.
236      * File system access enables hot detection of template changes.
237      * <p>If this is enabled, FreeMarkerConfigurationFactory will try to resolve
238      * the specified "templateLoaderPath" as file system resource (which will work
239      * for expanded class path resources and ServletContext resources too).
240      * <p>Default is "true". Turn this off to always load via SpringTemplateLoader
241      * (i.e. as stream, without hot detection of template changes), which might
242      * be necessary if some of your templates reside in an expanded classes
243      * directory while others reside in jar files.
244      * @see #setTemplateLoaderPath
245      */

246     public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
247         this.preferFileSystemAccess = preferFileSystemAccess;
248     }
249
250     /**
251      * Return whether to prefer file system access for template loading.
252      */

253     protected boolean isPreferFileSystemAccess() {
254         return preferFileSystemAccess;
255     }
256
257
258     /**
259      * Prepare the FreeMarker Configuration and return it.
260      * @return the FreeMarker Configuration object
261      * @throws IOException if the config file wasn't found
262      * @throws TemplateException on FreeMarker initialization failure
263      */

264     public Configuration createConfiguration() throws IOException JavaDoc, TemplateException {
265         Configuration config = newConfiguration();
266         Properties JavaDoc props = new Properties JavaDoc();
267
268         // Load config file if specified.
269
if (this.configLocation != null) {
270             if (logger.isInfoEnabled()) {
271                 logger.info("Loading FreeMarker configuration from " + this.configLocation);
272             }
273             PropertiesLoaderUtils.fillProperties(props, this.configLocation);
274         }
275
276         // Merge local properties if specified.
277
if (this.freemarkerSettings != null) {
278             props.putAll(this.freemarkerSettings);
279         }
280
281         // FreeMarker will only accept known keys in its setSettings and
282
// setAllSharedVariables methods.
283
if (!props.isEmpty()) {
284             config.setSettings(props);
285         }
286
287         if (!CollectionUtils.isEmpty(this.freemarkerVariables)) {
288             config.setAllSharedVariables(new SimpleHash(this.freemarkerVariables));
289         }
290
291         if (this.defaultEncoding != null) {
292             config.setDefaultEncoding(this.defaultEncoding);
293         }
294
295         // Register template loaders that are supposed to kick in early.
296
if (this.preTemplateLoaders != null) {
297             this.templateLoaders.addAll(this.preTemplateLoaders);
298         }
299
300         // Register default template loaders.
301
if (this.templateLoaderPaths != null) {
302             for (int i = 0; i < this.templateLoaderPaths.length; i++) {
303                 this.templateLoaders.add(getTemplateLoaderForPath(this.templateLoaderPaths[i]));
304             }
305         }
306         postProcessTemplateLoaders(this.templateLoaders);
307
308         // Register template loaders that are supposed to kick in late.
309
if (this.postTemplateLoaders != null) {
310             this.templateLoaders.addAll(this.postTemplateLoaders);
311         }
312
313         TemplateLoader loader = getAggregateTemplateLoader(this.templateLoaders);
314         if (loader != null) {
315             config.setTemplateLoader(loader);
316         }
317
318         postProcessConfiguration(config);
319         return config;
320     }
321
322     /**
323      * Return a new Configuration object. Subclasses can override this for
324      * custom initialization, or for using a mock object for testing.
325      * <p>Called by <code>createConfiguration()</code>.
326      * @return the Configuration object
327      * @throws IOException if a config file wasn't found
328      * @throws TemplateException on FreeMarker initialization failure
329      * @see #createConfiguration()
330      */

331     protected Configuration newConfiguration() throws IOException JavaDoc, TemplateException {
332         return new Configuration();
333     }
334
335     /**
336      * Determine a FreeMarker TemplateLoader for the given path.
337      * <p>Default implementation creates either a FileTemplateLoader or
338      * a SpringTemplateLoader.
339      * @param templateLoaderPath the path to load templates from
340      * @return an appropriate TemplateLoader
341      * @see freemarker.cache.FileTemplateLoader
342      * @see SpringTemplateLoader
343      */

344     protected TemplateLoader getTemplateLoaderForPath(String JavaDoc templateLoaderPath) {
345         if (isPreferFileSystemAccess()) {
346             // Try to load via the file system, fall back to SpringTemplateLoader
347
// (for hot detection of template changes, if possible).
348
try {
349                 Resource path = getResourceLoader().getResource(templateLoaderPath);
350                 File JavaDoc file = path.getFile(); // will fail if not resolvable in the file system
351
if (logger.isDebugEnabled()) {
352                     logger.debug(
353                             "Template loader path [" + path + "] resolved to file path [" + file.getAbsolutePath() + "]");
354                 }
355                 return new FileTemplateLoader(file);
356             }
357             catch (IOException JavaDoc ex) {
358                 if (logger.isDebugEnabled()) {
359                     logger.debug("Cannot resolve template loader path [" + templateLoaderPath +
360                             "] to [java.io.File]: using SpringTemplateLoader as fallback", ex);
361                 }
362                 return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
363             }
364         }
365         else {
366             // Always load via SpringTemplateLoader (without hot detection of template changes).
367
logger.debug("File system access not preferred: using SpringTemplateLoader");
368             return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
369         }
370     }
371
372     /**
373      * To be overridden by subclasses that want to to register custom
374      * TemplateLoader instances after this factory created its default
375      * template loaders.
376      * <p>Called by <code>createConfiguration()</code>. Note that specified
377      * "postTemplateLoaders" will be registered <i>after</i> any loaders
378      * registered by this callback; as a consequence, they are are <i>not</i>
379      * included in the given List.
380      * @param templateLoaders the current List of TemplateLoader instances,
381      * to be modified by a subclass
382      * @see #createConfiguration()
383      * @see #setPostTemplateLoaders
384      */

385     protected void postProcessTemplateLoaders(List JavaDoc templateLoaders) {
386     }
387
388     /**
389      * Return a TemplateLoader based on the given TemplateLoader list.
390      * If more than one TemplateLoader has been registered, a FreeMarker
391      * MultiTemplateLoader needs to be created.
392      * @param templateLoaders the final List of TemplateLoader instances
393      * @return the aggregate TemplateLoader
394      */

395     protected TemplateLoader getAggregateTemplateLoader(List JavaDoc templateLoaders) {
396         int loaderCount = templateLoaders.size();
397         switch (loaderCount) {
398             case 0:
399                 logger.info("No FreeMarker TemplateLoaders specified");
400                 return null;
401             case 1:
402                 return (TemplateLoader) templateLoaders.get(0);
403             default:
404                 TemplateLoader[] loaders = (TemplateLoader[]) templateLoaders.toArray(new TemplateLoader[loaderCount]);
405                 return new MultiTemplateLoader(loaders);
406         }
407     }
408
409     /**
410      * To be overridden by subclasses that want to to perform custom
411      * post-processing of the Configuration object after this factory
412      * performed its default initialization.
413      * <p>Called by <code>createConfiguration()</code>.
414      * @param config the current Configuration object
415      * @throws IOException if a config file wasn't found
416      * @throws TemplateException on FreeMarker initialization failure
417      * @see #createConfiguration()
418      */

419     protected void postProcessConfiguration(Configuration config) throws IOException JavaDoc, TemplateException {
420     }
421
422 }
423
Popular Tags