All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.velocity.spring.VelocityEngineFactory Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.velocity.spring;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.RuntimeConstants;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * Factory that configures a VelocityEngine. Can be used standalone,
 * but typically you will either use {@link VelocityEngineFactoryBean}
 * for preparing a VelocityEngine as bean reference, or
 * org.springframework.web.servlet.view.velocity.VelocityConfigurer
 * for web views.
 *
 * 

The optional "configLocation" property sets the location of the Velocity * properties file, within the current application. Velocity properties can be * overridden via "velocityProperties", or even completely specified locally, * avoiding the need for an external properties file. * *

The "resourceLoaderPath" property can be used to specify the Velocity * resource loader path via Spring's Resource abstraction, possibly relative * to the Spring application context. * *

The simplest way to use this class is to specify a * {@link #setResourceLoaderPath(String) "resourceLoaderPath"}; the * VelocityEngine typically then does not need any further configuration. * * @author Juergen Hoeller * @author Claude Brisson * @since 2020-05-29 * @see #setConfigLocation * @see #setVelocityProperties * @see #setResourceLoaderPath * @see #createVelocityEngine * @see VelocityEngineFactoryBean * @see org.springframework.web.servlet.view.velocity.VelocityConfigurer * @see org.apache.velocity.app.VelocityEngine */ public class VelocityEngineFactory { protected static final Logger logger = LoggerFactory.getLogger(VelocityEngineFactory.class); private Resource configLocation; private final Map velocityProperties = new HashMap(); private String resourceLoaderPath; private ResourceLoader resourceLoader = new DefaultResourceLoader(); private boolean preferFileSystemAccess = true; /** * Set the location of the Velocity config file. * Alternatively, you can specify all properties locally. * * @param configLocation config resource * * @see #setVelocityProperties * @see #setResourceLoaderPath */ public void setConfigLocation(Resource configLocation) { this.configLocation = configLocation; } /** * Set Velocity properties, like "file.resource.loader.path". * Can be used to override values in a Velocity config file, * or to specify all necessary properties locally. * *

Note that the Velocity resource loader path also be set to any * Spring resource location via the "resourceLoaderPath" property. * Setting it here is just necessary when using a non-file-based * resource loader.

* * @param velocityProperties engine properties to include * * @see #setVelocityPropertiesMap * @see #setConfigLocation * @see #setResourceLoaderPath */ public void setVelocityProperties(Properties velocityProperties) { CollectionUtils.mergePropertiesIntoMap(velocityProperties, this.velocityProperties); } /** * Set Velocity properties as Map, to allow for non-String values * like "ds.resource.loader.instance". * * @param velocityPropertiesMap engine properties to include * * @see #setVelocityProperties */ public void setVelocityPropertiesMap(Map velocityPropertiesMap) { if (velocityPropertiesMap != null) { this.velocityProperties.putAll(velocityPropertiesMap); } } /** * Set the Velocity resource loader path via a Spring resource location. * Accepts multiple locations in Velocity's comma-separated path style. *

When populated via a String, standard URLs like "file:" and "classpath:" * pseudo URLs are supported, as understood by ResourceLoader. Allows for * relative paths when running in an ApplicationContext. * *

Will define a path for the default Velocity resource loader with the name * "file". If the specified resource cannot be resolved to a {@code java.io.File}, * a generic SpringResourceLoader will be used under the name "spring", without * modification detection.

* *

Note that resource caching will be enabled in any case. With the file * resource loader, the last-modified timestamp will be checked on access to * detect changes. With SpringResourceLoader, the resource will be cached * forever (for example for class path resources).

* *

To specify a modification check interval for files, use Velocity's * standard "file.resource.loader.modificationCheckInterval" property. By default, * the file timestamp is checked on every access (which is surprisingly fast). * Of course, this just applies when loading resources from the file system. *

To enforce the use of SpringResourceLoader, i.e. to not resolve a path * as file system resource in any case, turn off the "preferFileSystemAccess" * flag. See the latter's javadoc for details.

* * @param resourceLoaderPath comma-separated resource paths * * @see #setResourceLoader * @see #setVelocityProperties * @see #setPreferFileSystemAccess * @see SpringResourceLoader * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader */ public void setResourceLoaderPath(String resourceLoaderPath) { this.resourceLoaderPath = resourceLoaderPath; } /** * Set the Spring ResourceLoader to use for loading Velocity template files. * *

The default is DefaultResourceLoader. Will get overridden by the * ApplicationContext if running in a context.

* * @param resourceLoader Spring resource loader to ue * * @see org.springframework.core.io.DefaultResourceLoader * @see org.springframework.context.ApplicationContext */ public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } /** * Return the Spring ResourceLoader to use for loading Velocity template files. * * @return Spring resource loader to use */ protected ResourceLoader getResourceLoader() { return this.resourceLoader; } /** * Set whether to prefer file system access for template loading. * File system access enables hot detection of template changes. * *

If this is enabled, VelocityEngineFactory will try to resolve the * specified "resourceLoaderPath" as file system resources, but only when * non-classpath resource paths are included.

* *

Default is "true". Turn this off to always load via SpringResourceLoader * (i.e. as stream, without hot detection of template changes), which might * be necessary if some of your templates reside in an expanded classes * directory while others reside in jar files.

* * @param preferFileSystemAccess whether to rely on file-based loading when possible * * @see #setResourceLoaderPath */ public void setPreferFileSystemAccess(boolean preferFileSystemAccess) { this.preferFileSystemAccess = preferFileSystemAccess; } /** * Return whether to prefer file system access for template loading. * * @return whether to prefer file system access for template loading */ protected boolean isPreferFileSystemAccess() { return this.preferFileSystemAccess; } /** * Prepare the VelocityEngine instance and return it. * @return the VelocityEngine instance * @throws IOException if the config file wasn't found * @throws VelocityException on Velocity initialization failure */ public VelocityEngine createVelocityEngine() throws IOException, VelocityException { VelocityEngine velocityEngine = newVelocityEngine(); Map props = new HashMap(); // Load config file if set. if (this.configLocation != null) { logger.info("Loading Velocity config from [{}]", this.configLocation); CollectionUtils.mergePropertiesIntoMap(PropertiesLoaderUtils.loadProperties(this.configLocation), props); } // Merge local properties if set. if (!this.velocityProperties.isEmpty()) { props.putAll(this.velocityProperties); } // Set a resource loader path, if required. if (this.resourceLoaderPath != null) { initVelocityResourceLoader(velocityEngine, this.resourceLoaderPath); } // Apply properties to VelocityEngine. for (Map.Entry entry : props.entrySet()) { velocityEngine.setProperty(entry.getKey(), entry.getValue()); } postProcessVelocityEngine(velocityEngine); // Perform actual initialization. velocityEngine.init(); return velocityEngine; } /** * Return a new VelocityEngine. Subclasses can override this for * custom initialization, or for using a mock object for testing. *

Called by {@code createVelocityEngine()}. * @return the VelocityEngine instance * @throws IOException if a config file wasn't found * @throws VelocityException on Velocity initialization failure * @see #createVelocityEngine() */ protected VelocityEngine newVelocityEngine() throws IOException, VelocityException { return new VelocityEngine(); } /** * Initialize a Velocity resource loader for the given VelocityEngine: * either a standard Velocity FileResourceLoader or a SpringResourceLoader. *

Called by {@code createVelocityEngine()}. * @param velocityEngine the VelocityEngine to configure * @param resourceLoaderPath the path to load Velocity resources from * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader * @see SpringResourceLoader * @see #initSpringResourceLoader * @see #createVelocityEngine() */ protected void initVelocityResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) { final ResourceLoader loader = getResourceLoader(); if (loader != null && isPreferFileSystemAccess()) { // Try to load via the file system, fall back to SpringResourceLoader // (for hot detection of template changes, if possible). final List filePaths = new ArrayList<>(); final List nonFilePaths = new ArrayList<>(); String[] paths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath); // Try to load via the file system, fall back to SpringResourceLoader // (for hot detection of template changes, if possible). for (int i = 0; i < paths.length; i++) { String path = paths[i]; // Don't check classpath: locations for existence, they're not file-based. // Some containers will expand jars and trigger false positives. if (path.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX)) { logger.debug("Using SpringResourceLoader for '{}'", path); nonFilePaths.add(path); continue; } try { Resource resource = loader.getResource(path); File file = resource.getFile(); // will fail if not resolvable in the file system logger.debug("Resource loader path [{}] resolved to file [{}]", path, file.getAbsolutePath()); filePaths.add(file.getAbsolutePath()); } catch (IOException ex) { logger.debug("Cannot resolve resource loader path '{}' to filesystem, will use SpringResourceLoader", path, ex); nonFilePaths.add(path); } } if (!filePaths.isEmpty()) { velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file"); velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true"); velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, StringUtils.collectionToCommaDelimitedString(filePaths)); } if (!nonFilePaths.isEmpty()) { initSpringResourceLoader(velocityEngine, StringUtils.collectionToCommaDelimitedString(nonFilePaths)); } } else { // Always load via SpringResourceLoader // (without hot detection of template changes). logger.debug("File system access not preferred: using SpringResourceLoader"); initSpringResourceLoader(velocityEngine, resourceLoaderPath); } } /** * Initialize a SpringResourceLoader for the given VelocityEngine. *

Called by {@code initVelocityResourceLoader}. * @param velocityEngine the VelocityEngine to configure * @param resourceLoaderPath the path to load Velocity resources from * @see SpringResourceLoader * @see #initVelocityResourceLoader */ protected void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) { velocityEngine.addProperty( RuntimeConstants.RESOURCE_LOADERS, SpringResourceLoader.NAME); velocityEngine.setProperty( SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName()); velocityEngine.setProperty( SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true"); velocityEngine.setApplicationAttribute( SpringResourceLoader.SPRING_RESOURCE_LOADER, getResourceLoader()); velocityEngine.setApplicationAttribute( SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath); } /** * To be implemented by subclasses that want to perform custom * post-processing of the VelocityEngine after this FactoryBean * performed its default configuration (but before VelocityEngine.init). *

Called by {@code createVelocityEngine()}. * @param velocityEngine the current VelocityEngine * @throws IOException if a config file wasn't found * @throws VelocityException on Velocity initialization failure * @see #createVelocityEngine() * @see org.apache.velocity.app.VelocityEngine#init */ protected void postProcessVelocityEngine(VelocityEngine velocityEngine) throws IOException, VelocityException { } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy