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

net.shibboleth.ext.spring.velocity.VelocityEngineFactory Maven / Gradle / Ivy

/*
 * Licensed to the University Corporation for Advanced Internet Development,
 * Inc. (UCAID) under one or more contributor license agreements.  See the
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID licenses this file to You 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
 *
 *    http://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 net.shibboleth.ext.spring.velocity;

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

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

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
 * {@link 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 * @see #setConfigLocation * @see #setVelocityProperties * @see #setResourceLoaderPath * @see #createVelocityEngine * @see VelocityEngineFactoryBean * @see VelocityConfigurer * @see org.apache.velocity.app.VelocityEngine * * @since 6.0.0 */ public class VelocityEngineFactory { /** Class logger. */ @Nonnull private final Logger log = LoggerFactory.getLogger(VelocityEngineFactory.class); /** Source of configuration. */ @Nullable private Resource configLocation; /** Configuration properties. */ @Nonnull private final Map velocityProperties; /** Path to load resources from. */ @Nullable private String resourceLoaderPath; /** Resource loader. */ @Nullable private ResourceLoader resourceLoader; /** Whether to favor file system lookup first. */ private boolean preferFileSystemAccess; /** Constructor. */ public VelocityEngineFactory() { velocityProperties = new HashMap<>(); resourceLoader = new DefaultResourceLoader(); preferFileSystemAccess = true; } /** * Set the location of the Velocity config file. * *

Alternatively, you can specify all properties locally.

* * @param location configuration resource * * @see #setVelocityProperties * @see #setResourceLoaderPath */ public void setConfigLocation(@Nullable final Resource location) { configLocation = location; } /** * 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 props additional properties * * @see #setVelocityPropertiesMap * @see #setConfigLocation * @see #setResourceLoaderPath */ public void setVelocityProperties(@Nullable final Properties props) { CollectionUtils.mergePropertiesIntoMap(props, velocityProperties); } /** * Set Velocity properties as Map, to allow for non-String values * like "ds.resource.loader.instance". * * @param map properties as map * * @see #setVelocityProperties */ public void setVelocityPropertiesMap(@Nullable final Map map) { if (map != null) { velocityProperties.putAll(map); } } /** * 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 paths comma-separated paths * * @see #setResourceLoader * @see #setVelocityProperties * @see #setPreferFileSystemAccess * @see SpringResourceLoader * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader */ public void setResourceLoaderPath(@Nullable final String paths) { resourceLoaderPath = paths; } /** * 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 loader resource loader * * @see org.springframework.core.io.DefaultResourceLoader * @see org.springframework.context.ApplicationContext */ public void setResourceLoader(@Nullable final ResourceLoader loader) { resourceLoader = loader; } /** * 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 resource (which will work * for expanded class path resources and ServletContext resources too).

* *

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 flag flag to set * * @see #setResourceLoaderPath */ public void setPreferFileSystemAccess(final boolean flag) { preferFileSystemAccess = flag; } /** * 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 */ @Nonnull public VelocityEngine createVelocityEngine() throws IOException, VelocityException { final Map props = new HashMap<>(); // Load config file if set. if (configLocation != null) { log.info("Loading Velocity config from '{}'", configLocation); CollectionUtils.mergePropertiesIntoMap(PropertiesLoaderUtils.loadProperties(configLocation), props); } // Merge local properties if set. if (!velocityProperties.isEmpty()) { props.putAll(velocityProperties); } final VelocityEngine velocityEngine = newVelocityEngine(); // Set a resource loader path, if required. if (resourceLoaderPath != null) { initVelocityResourceLoader(velocityEngine, resourceLoaderPath); } // Apply properties to VelocityEngine. for (final 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() */ @Nonnull 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. * * @param velocityEngine the VelocityEngine to configure * @param loaderPath the path to load Velocity resources from * * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader * @see SpringResourceLoader * @see #initSpringResourceLoader * @see #createVelocityEngine() */ protected void initVelocityResourceLoader(@Nonnull final VelocityEngine velocityEngine, @Nullable final String loaderPath) { if (preferFileSystemAccess && resourceLoader != null) { // Try to load via the file system, fall back to SpringResourceLoader // (for hot detection of template changes, if possible). try { final StringBuilder resolvedPath = new StringBuilder(); final String[] paths = StringUtils.commaDelimitedListToStringArray(loaderPath); for (int i = 0; i < paths.length; i++) { final String path = paths[i]; final Resource resource = resourceLoader.getResource(path); // Will fail if not resolvable in the file system. final File file = resource.getFile(); if (log.isDebugEnabled()) { log.debug("Resource loader path '{}' resolved to file '{}'", path, file.getAbsolutePath()); } resolvedPath.append(file.getAbsolutePath()); if (i < paths.length - 1) { resolvedPath.append(','); } } velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file"); velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true"); velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, resolvedPath.toString()); } catch (final IOException ex) { log.debug("Cannot resolve resource loader path '{}' to [java.io.File], will use SpringResourceLoader", loaderPath, ex); initSpringResourceLoader(velocityEngine, loaderPath); } } else { // Always load via SpringResourceLoader (without hot detection of template changes). log.debug("Filesystem access not preferred, will use SpringResourceLoader"); initSpringResourceLoader(velocityEngine, loaderPath); } } /** * Initialize a SpringResourceLoader for the given VelocityEngine. * *

Called by {@code initVelocityResourceLoader}.

* * @param velocityEngine the VelocityEngine to configure * @param path the path to load Velocity resources from * * @see SpringResourceLoader * @see #initVelocityResourceLoader */ protected void initSpringResourceLoader(@Nonnull final VelocityEngine velocityEngine, @Nullable final String path) { velocityEngine.setProperty(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, resourceLoader); velocityEngine.setApplicationAttribute(SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, path); } /** * To be implemented by subclasses that want to perform custom * post-processing of the VelocityEngine after the FactoryBean * performed its default configuration (but before VelocityEngine.init). * * @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(@Nonnull final VelocityEngine velocityEngine) throws IOException, VelocityException { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy