1. /*
  2. * Copyright 2002-2004 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. package org.springframework.ui.velocity;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.util.HashMap;
  20. import java.util.Iterator;
  21. import java.util.Map;
  22. import java.util.Properties;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.apache.velocity.app.VelocityEngine;
  26. import org.apache.velocity.exception.VelocityException;
  27. import org.springframework.core.io.DefaultResourceLoader;
  28. import org.springframework.core.io.Resource;
  29. import org.springframework.core.io.ResourceLoader;
  30. /**
  31. * Factory that configures a VelocityEngine. Can be used standalone, but
  32. * typically you will either use VelocityEngineFactoryBean for preparing a
  33. * VelocityEngine as bean reference, or VelocityConfigurer for web views.
  34. *
  35. * <p>The optional "configLocation" property sets the location of the Velocity
  36. * properties file, within the current application. Velocity properties can be
  37. * overridden via "velocityProperties", or even completely specified locally,
  38. * avoiding the need for an external properties file.
  39. *
  40. * <p>The "resourceLoaderPath" property can be used to specify the Velocity
  41. * resource loader path via Spring's Resource abstraction, possibly relative
  42. * to the Spring application context.
  43. *
  44. * <p>If "overrideLogging" is true (the default), the VelocityEngine will be configured
  45. * to log via Commons Logging, i.e. using CommonsLoggingLogSystem as log system.
  46. *
  47. * <p>The simplest way to use this class is to specify just a "resourceLoaderPath":
  48. * The VelocityEngine does not need any further configuration then.
  49. *
  50. * @author Juergen Hoeller
  51. * @see #setConfigLocation
  52. * @see #setVelocityProperties
  53. * @see #setResourceLoaderPath
  54. * @see #setOverrideLogging
  55. * @see #createVelocityEngine
  56. * @see CommonsLoggingLogSystem
  57. * @see VelocityEngineFactoryBean
  58. * @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
  59. */
  60. public class VelocityEngineFactory {
  61. protected final Log logger = LogFactory.getLog(getClass());
  62. private Resource configLocation;
  63. private final Map velocityProperties = new HashMap();
  64. private String resourceLoaderPath;
  65. private ResourceLoader resourceLoader = new DefaultResourceLoader();
  66. private boolean overrideLogging = true;
  67. /**
  68. * Set the location of the Velocity config file.
  69. * Alternatively, you can specify all properties locally.
  70. * @see #setVelocityProperties
  71. * @see #setResourceLoaderPath
  72. */
  73. public void setConfigLocation(Resource configLocation) {
  74. this.configLocation = configLocation;
  75. }
  76. /**
  77. * Set Velocity properties, like "file.resource.loader.path".
  78. * Can be used to override values in a Velocity config file,
  79. * or to specify all necessary properties locally.
  80. * <p>Note that the Velocity resource loader path also be set to any
  81. * Spring resource location via the "resourceLoaderPath" property.
  82. * Setting it here is just necessary when using a non-file-based
  83. * resource loader.
  84. * @see #setVelocityPropertiesMap
  85. * @see #setConfigLocation
  86. * @see #setResourceLoaderPath
  87. */
  88. public void setVelocityProperties(Properties velocityProperties) {
  89. this.velocityProperties.putAll(velocityProperties);
  90. }
  91. /**
  92. * Set Velocity properties as Map, to allow for non-String values
  93. * like "ds.resource.loader.instance".
  94. * @see #setVelocityProperties
  95. */
  96. public void setVelocityPropertiesMap(Map velocityPropertiesMap) {
  97. this.velocityProperties.putAll(velocityPropertiesMap);
  98. }
  99. /**
  100. * Set the Velocity resource loader path via a Spring resource location.
  101. * <p>When populated via a String, standard URLs like "file:" and "classpath:"
  102. * pseudo URLs are supported, as understood by ResourceEditor. Allows for
  103. * relative paths when running in an ApplicationContext.
  104. * <p>Will define a path for the default Velocity resource loader with the name
  105. * "file". If the specified resource cannot be resolved to a java.io.File, the
  106. * generic SpringResourceLoader will be used, without modification detection.
  107. * @see org.springframework.core.io.ResourceEditor
  108. * @see org.springframework.context.ApplicationContext#getResource
  109. * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
  110. * @see SpringResourceLoader
  111. */
  112. public void setResourceLoaderPath(String resourceLoaderPath) {
  113. this.resourceLoaderPath = resourceLoaderPath;
  114. }
  115. /**
  116. * Set the Spring ResourceLoader to use for loading Velocity template files.
  117. * The default is DefaultResourceLoader. Will get overridden by the
  118. * ApplicationContext if running in a context.
  119. * @see org.springframework.core.io.DefaultResourceLoader
  120. */
  121. public void setResourceLoader(ResourceLoader resourceLoader) {
  122. this.resourceLoader = resourceLoader;
  123. }
  124. /**
  125. * If Velocity should log via Commons Logging, i.e. if Velocity's log system
  126. * should be set to CommonsLoggingLogSystem. Default value is true.
  127. * @see CommonsLoggingLogSystem
  128. */
  129. public void setOverrideLogging(boolean overrideLogging) {
  130. this.overrideLogging = overrideLogging;
  131. }
  132. /**
  133. * Prepare the VelocityEngine instance and return it.
  134. * @return the VelocityEngine instance
  135. * @throws IOException if the config file wasn't found
  136. * @throws VelocityException on Velocity initialization failure
  137. */
  138. public VelocityEngine createVelocityEngine() throws IOException, VelocityException {
  139. VelocityEngine velocityEngine = newVelocityEngine();
  140. Properties props = new Properties();
  141. // load config file if set
  142. if (this.configLocation != null) {
  143. logger.info("Loading Velocity config from [" + this.configLocation + "]");
  144. InputStream is = this.configLocation.getInputStream();
  145. try {
  146. props.load(is);
  147. }
  148. finally {
  149. is.close();
  150. }
  151. }
  152. // merge local properties if set
  153. if (!this.velocityProperties.isEmpty()) {
  154. props.putAll(this.velocityProperties);
  155. }
  156. // set properties
  157. for (Iterator it = props.keySet().iterator(); it.hasNext();) {
  158. String key = (String) it.next();
  159. velocityEngine.setProperty(key, props.get(key));
  160. }
  161. // set a resource loader path, if required
  162. if (this.resourceLoaderPath != null) {
  163. try {
  164. Resource path = this.resourceLoader.getResource(this.resourceLoaderPath);
  165. velocityEngine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH,
  166. path.getFile().getAbsolutePath());
  167. }
  168. catch (IOException ex) {
  169. if (logger.isDebugEnabled()) {
  170. logger.debug("Cannot resolve resource loader path [" + this.resourceLoaderPath +
  171. "] to File: using SpringResourceLoader", ex);
  172. }
  173. else if (logger.isInfoEnabled()) {
  174. logger.info("Cannot resolve resource loader path [" + this.resourceLoaderPath +
  175. "] to File: using SpringResourceLoader");
  176. }
  177. velocityEngine.setProperty(VelocityEngine.RESOURCE_LOADER,
  178. SpringResourceLoader.NAME);
  179. velocityEngine.setProperty(SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS,
  180. SpringResourceLoader.class.getName());
  181. velocityEngine.setApplicationAttribute(SpringResourceLoader.SPRING_RESOURCE_LOADER,
  182. this.resourceLoader);
  183. velocityEngine.setApplicationAttribute(SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH,
  184. this.resourceLoaderPath);
  185. }
  186. }
  187. // log via Commons Logging?
  188. if (this.overrideLogging) {
  189. velocityEngine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, new CommonsLoggingLogSystem());
  190. }
  191. postProcessVelocityEngine(velocityEngine);
  192. try {
  193. // perform initialization
  194. velocityEngine.init();
  195. }
  196. catch (IOException ex) {
  197. throw ex;
  198. }
  199. catch (VelocityException ex) {
  200. throw ex;
  201. }
  202. catch (RuntimeException ex) {
  203. throw ex;
  204. }
  205. catch (Exception ex) {
  206. logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
  207. throw new VelocityException(ex.getMessage());
  208. }
  209. return velocityEngine;
  210. }
  211. /**
  212. * Return a new VelocityEngine. Subclasses can override this for
  213. * custom initialization, or for using a mock object for testing.
  214. * Called by createVelocityEngine.
  215. * @return the VelocityEngine instance
  216. * @throws IOException if a config file wasn't found
  217. * @throws VelocityException on Velocity initialization failure
  218. * @see #createVelocityEngine
  219. */
  220. protected VelocityEngine newVelocityEngine() throws IOException, VelocityException {
  221. return new VelocityEngine();
  222. }
  223. /**
  224. * To be implemented by subclasses that want to to perform custom
  225. * post-processing of the VelocityEngine after this FactoryBean
  226. * performed its default configuration (but before VelocityEngine.init).
  227. * Called by createVelocityEngine.
  228. * @param velocityEngine the current VelocityEngine
  229. * @throws IOException if a config file wasn't found
  230. * @throws VelocityException on Velocity initialization failure
  231. * @see #createVelocityEngine
  232. * @see org.apache.velocity.app.VelocityEngine#init
  233. */
  234. protected void postProcessVelocityEngine(VelocityEngine velocityEngine)
  235. throws IOException, VelocityException {
  236. }
  237. }