Commit c7db8bd2 authored by Eduard Moraru's avatar Eduard Moraru
Browse files

XWIKI-5347: Upgrade to VelocityTools 2.0

- Switching to the tool handling of velocity-tools 2.0 that supports better tool configuration and scope management (application/request/session)
parent da6f3ca3
......@@ -28,7 +28,7 @@
* <p>
* You can override the default values for each of the configuration properties below by defining them in XWiki's
* global configuration file using a prefix of "velocity" followed by the property name. For example:
* <code> = listtool =</code>
* <code> = application.listtool =</code>
* @version $Id$
* @since 2.0M1
......@@ -37,14 +37,14 @@
public interface VelocityConfiguration
* @return the Velocity properties as defined at
* @return the Velocity Engine configuration properties as defined at
Properties getProperties();
* @return the list of configured Velocity Tools (the key is the name under which the tool will be available
* in the Velocity Context and the value is the tool's class name)
* @return the Velocity Tools configuration properties as defined at
Properties getTools();
......@@ -80,24 +80,21 @@ public class DefaultVelocityConfiguration implements Initializable, VelocityConf
public void initialize() throws InitializationException
// Default Velocity tools.
this.defaultTools.setProperty("listtool", ListTool.class.getName());
this.defaultTools.setProperty("numbertool", NumberTool.class.getName());
this.defaultTools.setProperty("datetool", ComparisonDateTool.class.getName());
this.defaultTools.setProperty("mathtool", MathTool.class.getName());
this.defaultTools.setProperty("sorttool", SortTool.class.getName());
this.defaultTools.setProperty("escapetool", EscapeTool.class.getName());
this.defaultTools.setProperty("regextool", RegexTool.class.getName());
this.defaultTools.setProperty("collectionstool", CollectionsTool.class.getName());
this.defaultTools.setProperty("stringtool", StringUtils.class.getName());
this.defaultTools.setProperty("jsontool", JSONTool.class.getName());
// Since the Cookie Tool requires the Servlet API and since we don't want to force users of this Velocity
// module to add a dependency on the Servlet API we only add it if the Servlet API is available in the
// context classloader.
if (Thread.currentThread().getContextClassLoader().getResource("javax/servlet/http/Cookie") != null) {
this.defaultTools.setProperty("cookietool", CookieTool.class.getName());
// Default Velocity tools and their scopes.
// List of defined scopes.
this.defaultTools.setProperty("toolbox", "request,application");
// List of tools for each scope.
this.defaultTools.setProperty("application.listtool", ListTool.class.getName());
this.defaultTools.setProperty("application.numbertool", NumberTool.class.getName());
this.defaultTools.setProperty("application.datetool", ComparisonDateTool.class.getName());
this.defaultTools.setProperty("application.mathtool", MathTool.class.getName());
this.defaultTools.setProperty("application.sorttool", SortTool.class.getName());
this.defaultTools.setProperty("application.escapetool", EscapeTool.class.getName());
this.defaultTools.setProperty("application.regextool", RegexTool.class.getName());
this.defaultTools.setProperty("application.collectionstool", CollectionsTool.class.getName());
this.defaultTools.setProperty("application.stringtool", StringUtils.class.getName());
this.defaultTools.setProperty("application.jsontool", JSONTool.class.getName());
this.defaultTools.setProperty("request.cookietool", CookieTool.class.getName());
// Default Velocity properties
this.defaultProperties.setProperty("directive.set.null.allowed", Boolean.TRUE.toString());
......@@ -19,15 +19,19 @@
package org.xwiki.velocity.internal;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.context.Context;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager;
......@@ -61,45 +65,84 @@ public class DefaultVelocityContextFactory implements VelocityContextFactory, In
private VelocityConfiguration velocityConfiguration;
* The logger to use for logging.
* Velocity-Tools Manager that handles the registered velocity tools and performs per-scope instance caching (when
* needed) across all the velocity context created from it.
private Logger logger;
* An internal read-only Velocity Context containing the Tools defined in the component's configuration. We reuse
* them across Contexts for better performance.
private Context toolsContext;
private ToolManager velocityToolManager;
public void initialize() throws InitializationException
this.toolsContext = new VelocityContext();
// Instantiate Velocity tools
Properties properties = this.velocityConfiguration.getTools();
if (properties != null) {
for (Enumeration< ? > props = properties.propertyNames(); props.hasMoreElements();) {
String key = props.nextElement().toString();
String value = properties.getProperty(key);
Object toolInstance;
try {
toolInstance = Class.forName(value).newInstance();
} catch (Exception e) {
throw new InitializationException("Failed to initialize tool [" + value + "]", e);
velocityToolManager = new ToolManager();
PropertiesFactoryConfiguration toolsConfiguration = new PropertiesFactoryConfiguration()
public void validate()
// Do nothing.
protected void readTools(ExtendedProperties tools, ToolboxConfiguration toolbox)
for (Iterator i = tools.getKeys(); i.hasNext();) {
String key = (String);
// if it contains a period, it can't be a context key;
// it must be a tool property. ignore it for now.
if (key.indexOf('.') >= 0) {
String classname = tools.getString(key);
ToolConfiguration tool = new ToolConfiguration() {
public ToolInfo createInfo()
ToolInfo info = new ToolInfo(getKey(), getToolClass());
if (getSkipSetters() != null) {
// it's ok to use this here, because we know it's the
// first time properties have been added to this ToolInfo
return info;
// get tool properties prefixed by 'property'
ExtendedProperties toolProps = tools.subset(key);
readProperties(toolProps, tool);
// ok, get tool properties that aren't prefixed by 'property'
for (Iterator j = toolProps.getKeys(); j.hasNext();) {
String name = (String);
if (!name.equals(tool.getKey())) {
tool.setProperty(name, toolProps.getString(name));
// get special props explicitly
String restrictTo = toolProps.getString("restrictTo");
this.toolsContext.put(key, toolInstance);
this.logger.debug("Setting tool [{}] = [{}]", key, value);
// Load the default tool properties and the ones defined in into a configuration.
Properties toolsProperties = this.velocityConfiguration.getTools();;
public VelocityContext createContext() throws XWikiVelocityException
// Note: This constructor uses the passed context as an internal read-only context.
VelocityContext context = new VelocityContext(this.toolsContext);
VelocityContext context = new VelocityContext(velocityToolManager.createContext());
// Call all components implementing the VelocityContextInitializer's role.
try {
......@@ -60,7 +60,7 @@ public void configure() throws Exception
public void testDefaultToolsPresent() throws Exception
// Verify for example that the List tool is present.
assertEquals(ListTool.class.getName(), this.mocker.getComponentUnderTest().getTools().get("listtool"));
assertEquals(ListTool.class.getName(), this.mocker.getComponentUnderTest().getTools().get("application.listtool"));
......@@ -59,7 +59,8 @@ public void configure() throws Exception
VelocityConfiguration configuration = this.mocker.getInstance(VelocityConfiguration.class);
Properties properties = new Properties();
properties.put("listtool", ListTool.class.getName());
properties.put("toolbox", "application");
properties.put("application.listtool", ListTool.class.getName());
this.factory = this.mocker.getInstance(VelocityContextFactory.class);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment