contextList = new ArrayList<>();
for (String className : chainedContextNames) {
try {
VelocityContext velocityContext = (VelocityContext) objectFactory.buildBean(className, extraContext);
contextList.add(velocityContext);
} catch (Exception e) {
- LOG.warn(format("Unable to instantiate chained VelocityContext %s, skipping", className), e);
+ LOG.warn("Unable to instantiate chained VelocityContext {}, skipping", className, e);
}
}
return contextList;
@@ -186,154 +164,118 @@ public class VelocityManager implements VelocityManagerInterface {
*/
@Override
public synchronized void init(ServletContext context) {
- if (velocityEngine == null) {
- velocityEngine = newVelocityEngine(context);
+ if (velocityEngine != null) {
+ return;
+ }
+ velocityEngine = newVelocityEngine(context);
+ if (velocityTools != null) {
+ velocityTools.init(context, velocityEngine);
}
- this.initToolbox(context);
}
- /**
- * load optional velocity properties using the following loading strategy
- *
- * - relative to the servlet context path
- * - relative to the WEB-INF directory
- * - on the classpath
- *
- *
- * @param context the current ServletContext. may not be null
- * @return the optional properties if struts.velocity.configfile was specified, an empty Properties file otherwise
- */
public Properties loadConfiguration(ServletContext context) {
if (context == null) {
- String gripe = "Error attempting to create a loadConfiguration from a null ServletContext!";
- LOG.error(gripe);
- throw new IllegalArgumentException(gripe);
+ throw new IllegalArgumentException("Error attempting to create a loadConfiguration from a null ServletContext!");
}
-
Properties properties = new Properties();
-
- // now apply our systemic defaults, then allow user to override
- applyDefaultConfiguration(context, properties);
-
+ applyDefaultConfiguration(context, properties); // Apply defaults before loading user overrides
String defaultUserDirective = properties.getProperty("userdirective");
- /*
- if the user has specified an external velocity configuration file, we'll want to search for it in the
- following order
+ applyUserConfiguration(context, properties);
- 1. relative to the context path
- 2. relative to /WEB-INF
- 3. in the class path
- */
- String configfile;
-
- if (customConfigFile != null) {
- configfile = customConfigFile;
- } else {
- configfile = "velocity.properties";
- }
- configfile = configfile.trim();
-
- InputStream in = null;
- String resourceLocation = null;
-
- try {
- if (context.getRealPath(configfile) != null) {
- // 1. relative to context path, i.e. /velocity.properties
- String filename = context.getRealPath(configfile);
-
- if (filename != null) {
- File file = new File(filename);
-
- if (file.isFile()) {
- resourceLocation = file.getCanonicalPath() + " from file system";
- in = new FileInputStream(file);
- }
-
- // 2. if nothing was found relative to the context path, search relative to the WEB-INF directory
- if (in == null) {
- file = new File(context.getRealPath("/WEB-INF/" + configfile));
-
- if (file.isFile()) {
- resourceLocation = file.getCanonicalPath() + " from file system";
- in = new FileInputStream(file);
- }
- }
- }
- }
-
- // 3. finally, if there's no physical file, how about something in our classpath
- if (in == null) {
- in = VelocityManager.class.getClassLoader().getResourceAsStream(configfile);
- if (in != null) {
- resourceLocation = configfile + " from classloader";
- }
- }
-
- // if we've got something, load 'er up
- if (in != null) {
- LOG.info("Initializing velocity using {}", resourceLocation);
- properties.load(in);
- }
- } catch (IOException e) {
- LOG.warn("Unable to load velocity configuration {}", resourceLocation, e);
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException ignore) {
- }
- }
+ if (velocityProperties != null) { // Apply additional overriding properties if any
+ velocityProperties.stringPropertyNames().forEach(k -> properties.setProperty(k, velocityProperties.getProperty(k)));
}
- // overide with programmatically set properties
- if (velocityProperties != null) {
- for (Object o : velocityProperties.keySet()) {
- String key = (String) o;
- properties.setProperty(key, this.velocityProperties.getProperty(key));
- }
- }
+ String userDirective = properties.getProperty(OLD_CUSTOM_DIRECTIVES);
+ String newDirective = isBlank(userDirective) ? defaultUserDirective : userDirective.strip() + "," + defaultUserDirective;
+ properties.setProperty(OLD_CUSTOM_DIRECTIVES, newDirective);
- String userdirective = properties.getProperty("userdirective");
- if (userdirective == null || userdirective.trim().isEmpty()) {
- userdirective = defaultUserDirective;
- } else {
- userdirective = userdirective.trim() + "," + defaultUserDirective;
- }
- properties.setProperty("userdirective", userdirective);
-
- // for debugging purposes, allows users to dump out the properties that have been configured
if (LOG.isDebugEnabled()) {
LOG.debug("Initializing Velocity with the following properties ...");
-
- for (Object o : properties.keySet()) {
- String key = (String) o;
- String value = properties.getProperty(key);
- LOG.debug(" '{}' = '{}'", key, value);
- }
+ properties.stringPropertyNames().forEach(k -> LOG.debug(" '{}' = '{}'", k, properties.getProperty(k)));
}
-
return properties;
}
+ /**
+ * Load optional velocity properties using the following loading strategy
+ *
+ * - relative to the servlet context path
+ * - relative to the WEB-INF directory
+ * - on the classpath
+ *
+ */
+ private void applyUserConfiguration(ServletContext context, Properties properties) {
+ String configFile = requireNonNullElse(customConfigFile, "velocity.properties").trim();
+ boolean isLoaded = false;
+ try {
+ isLoaded = loadFile(properties, context.getRealPath(configFile));
+ } catch (IOException e) {
+ LOG.warn("Unable to load Velocity configuration from servlet context path", e);
+ }
+ if (!isLoaded) {
+ try {
+ isLoaded = loadFile(properties, context.getRealPath("/WEB-INF/" + configFile));
+ } catch (IOException e) {
+ LOG.warn("Unable to load Velocity configuration from WEB-INF path", e);
+ }
+ }
+ if (!isLoaded) {
+ try {
+ isLoaded = loadClassPathFile(properties, configFile);
+ } catch (IOException e) {
+ LOG.warn("Unable to load Velocity configuration from classpath", e);
+ }
+ }
+ }
+
+ private boolean loadClassPathFile(Properties properties, String configFile) throws IOException {
+ try (InputStream is = VelocityManager.class.getClassLoader().getResourceAsStream(configFile)) {
+ if (is == null) {
+ return false;
+ }
+ properties.load(is);
+ LOG.info("Initializing Velocity using {} from classpath", configFile);
+ return true;
+ }
+ }
+
+ private boolean loadFile(Properties properties, String fileName) throws IOException {
+ if (fileName == null) {
+ return false;
+ }
+ File file = new File(fileName);
+ if (!file.isFile()) {
+ return false;
+ }
+ try (InputStream is = new FileInputStream(file)) {
+ properties.load(is);
+ LOG.info("Initializing Velocity using {}", file.getCanonicalPath() + " from file system");
+ return true;
+ }
+ }
+
@Inject(StrutsConstants.STRUTS_VELOCITY_CONFIGFILE)
- public void setCustomConfigFile(String val) {
- this.customConfigFile = val;
+ public void setCustomConfigFile(String customConfigFile) {
+ this.customConfigFile = customConfigFile;
}
@Inject(StrutsConstants.STRUTS_VELOCITY_TOOLBOXLOCATION)
public void setToolBoxLocation(String toolboxLocation) {
- this.toolBoxLocation = toolboxLocation;
+ if (!isBlank(toolboxLocation)) {
+ this.velocityTools = new VelocityTools(toolboxLocation);
+ }
}
- public ToolManager getToolboxManager() {
- return toolboxManager;
+ public VelocityTools getVelocityTools() {
+ return velocityTools;
}
/**
- * allow users to specify via the struts.properties file a set of additional VelocityContexts to chain to the
- * the StrutsVelocityContext. The intent is to allow these contexts to store helper objects that the ui
- * developer may want access to. Examples of reasonable VelocityContexts would be an IoCVelocityContext, a
+ * Allow users to specify via the struts.properties file a set of additional VelocityContexts to chain to the
+ * StrutsVelocityContext. The intent is to allow these contexts to store helper objects that the ui developer may
+ * want access to. Examples of reasonable VelocityContexts would be an IoCVelocityContext, a
* SpringReferenceVelocityContext, and a ToolboxVelocityContext
*
* @param contexts comma separated velocity context's
@@ -344,38 +286,17 @@ public class VelocityManager implements VelocityManagerInterface {
}
/**
- * Initializes the ServletToolboxManager for this servlet's
- * toolbox (if any).
- */
- protected void initToolbox(ServletContext servletContext) {
- if (StringUtils.isBlank(toolBoxLocation)) {
- LOG.debug("Skipping ToolManager initialisation, [{}] was not defined", StrutsConstants.STRUTS_VELOCITY_TOOLBOXLOCATION);
- return;
- }
- LOG.debug("Configuring Velocity ToolManager with {}", toolBoxLocation);
- toolboxManager = new ToolManager();
- toolboxManager.configure(toolBoxLocation);
- }
-
- /**
- *
* Instantiates a new VelocityEngine.
- *
*
* The following is the default Velocity configuration
- *
- *
*
* resource.loader = file, class
* file.resource.loader.path = real path of webapp
* class.resource.loader.description = Velocity Classpath Resource Loader
* class.resource.loader.class = org.apache.struts2.views.velocity.StrutsResourceLoader
*
- *
- * this default configuration can be overridden by specifying a struts.velocity.configfile property in the
+ * This default configuration can be overridden by specifying a struts.velocity.configfile property in the
* struts.properties file. the specified config file will be searched for in the following order:
- *
- *
*
* - relative to the servlet context path
* - relative to the WEB-INF directory
@@ -389,21 +310,18 @@ public class VelocityManager implements VelocityManagerInterface {
if (context == null) {
throw new IllegalArgumentException("Error attempting to create a new VelocityEngine from a null ServletContext!");
}
-
VelocityEngine velocityEngine = new VelocityEngine();
- // Set the velocity attribute for the servlet context, if this is not set the webapp loader WILL NOT WORK
- velocityEngine.setApplicationAttribute(ServletContext.class.getName(), context);
+ velocityEngine.setApplicationAttribute(ServletContext.class.getName(), context); // Required for webapp loader
try {
velocityEngine.init(loadConfiguration(context));
} catch (Exception e) {
throw new StrutsException("Unable to instantiate VelocityEngine!", e);
}
-
return velocityEngine;
}
/**
- * once we've loaded up the user defined configurations, we will want to apply Struts specification configurations.
+ * Once we've loaded up the user defined configurations, we will want to apply Struts specification configurations.
*
* - if Velocity.RESOURCE_LOADER has not been defined, then we will use the defaults which is a joined file,
* class loader for unpackaed wars and a straight class loader otherwise
@@ -414,60 +332,56 @@ public class VelocityManager implements VelocityManagerInterface {
* @param properties velocity properties
*/
private void applyDefaultConfiguration(ServletContext context, Properties properties) {
- // ensure that caching isn't overly aggressive
-
LOG.debug("Load a default resource loader definition if there isn't one present.");
if (properties.getProperty(Velocity.RESOURCE_LOADER) == null) {
properties.setProperty(Velocity.RESOURCE_LOADER, "strutsfile, strutsclass");
}
- /*
- * If there's a "real" path add it for the strutsfile resource loader.
- * If there's no real path and they haven't configured a loader then we change
- * resource loader property to just use the strutsclass loader
- * Ben Hall (22/08/2003)
- */
- if (context.getRealPath("") != null) {
- properties.setProperty("strutsfile.resource.loader.description", "Velocity File Resource Loader");
- properties.setProperty("strutsfile.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
- properties.setProperty("strutsfile.resource.loader.path", context.getRealPath(""));
- properties.setProperty("strutsfile.resource.loader.modificationCheckInterval", "2");
- properties.setProperty("strutsfile.resource.loader.cache", "true");
+ // If there's a "real" path add it for the strutsfile resource loader. If there's no real path, and they haven't
+ // configured a loader then we change resource loader property to just use the strutsclass loader
+ String realPath = context.getRealPath("");
+ if (realPath != null) {
+ setStrutsFileResourceLoader(properties, realPath);
} else {
- // remove strutsfile from resource loader property
- String prop = properties.getProperty(Velocity.RESOURCE_LOADER);
- if (prop.contains("strutsfile,")) {
- prop = prop.replace("strutsfile,", "");
- } else if (prop.contains(", strutsfile")) {
- prop = prop.replace(", strutsfile", "");
- } else if (prop.contains("strutsfile")) {
- prop = prop.replace("strutsfile", "");
- }
-
- properties.setProperty(Velocity.RESOURCE_LOADER, prop);
+ clearStrutsFileResourceLoader(properties);
}
- /*
- * Refactored the Velocity templates for the Struts taglib into the classpath from the web path. This will
- * enable Struts projects to have access to the templates by simply including the Struts jar file.
- * Unfortunately, there does not appear to be a macro for the class loader keywords
- */
+ setStrutsClasspathResourceLoader(properties);
+
+ String directives = tagLibraries.stream().map(TagLibraryDirectiveProvider::getDirectiveClasses)
+ .flatMap(Collection::stream).map(directive -> directive.getName() + ",").collect(joining());
+
+ String userDirective = properties.getProperty(OLD_CUSTOM_DIRECTIVES);
+ String newDirective = isBlank(userDirective) ? directives : userDirective.strip() + "," + directives;
+ properties.setProperty(OLD_CUSTOM_DIRECTIVES, newDirective);
+ }
+
+ private void setStrutsFileResourceLoader(Properties properties, String realPath) {
+ properties.setProperty("strutsfile.resource.loader.description", "Velocity File Resource Loader");
+ properties.setProperty("strutsfile.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
+ properties.setProperty("strutsfile.resource.loader.path", realPath);
+ properties.setProperty("strutsfile.resource.loader.modificationCheckInterval", "2");
+ properties.setProperty("strutsfile.resource.loader.cache", "true");
+ }
+
+ private void clearStrutsFileResourceLoader(Properties properties) {
+ String prop = properties.getProperty(Velocity.RESOURCE_LOADER)
+ .replace("strutsfile,", "")
+ .replace(", strutsfile", "")
+ .replace("strutsfile", "");
+ properties.setProperty(Velocity.RESOURCE_LOADER, prop);
+ }
+
+ /**
+ * Refactored the Velocity templates for the Struts taglib into the classpath from the web path. This will
+ * enable Struts projects to have access to the templates by simply including the Struts jar file.
+ * Unfortunately, there does not appear to be a macro for the class loader keywords
+ */
+ private void setStrutsClasspathResourceLoader(Properties properties) {
properties.setProperty("strutsclass.resource.loader.description", "Velocity Classpath Resource Loader");
properties.setProperty("strutsclass.resource.loader.class", "org.apache.struts2.views.velocity.StrutsResourceLoader");
properties.setProperty("strutsclass.resource.loader.modificationCheckInterval", "2");
properties.setProperty("strutsclass.resource.loader.cache", "true");
-
- // components
- String directives = tagLibraries.stream().map(TagLibraryDirectiveProvider::getDirectiveClasses).flatMap(
- Collection::stream).map(directive -> directive.getName() + ",").collect(joining());
-
- String userdirective = properties.getProperty("userdirective");
- if (userdirective == null || userdirective.trim().isEmpty()) {
- userdirective = directives;
- } else {
- userdirective = userdirective.trim() + "," + directives;
- }
- properties.setProperty("userdirective", userdirective);
}
/**
diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/VelocityTools.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/VelocityTools.java
new file mode 100644
index 000000000..df3f1ece3
--- /dev/null
+++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/VelocityTools.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.struts2.views.velocity;
+
+import com.opensymphony.xwork2.ActionContext;
+import jakarta.servlet.ServletContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.tools.ToolContext;
+import org.apache.velocity.tools.ToolManager;
+import org.apache.velocity.tools.ToolboxFactory;
+
+import static org.apache.commons.lang3.ObjectUtils.requireNonEmpty;
+
+/**
+ * @since 7.0
+ */
+public class VelocityTools {
+
+ private final String toolLocation;
+ private ToolManager toolManager;
+ private VelocityEngine velocityEngine;
+
+ public VelocityTools(String toolLocation) {
+ this.toolLocation = requireNonEmpty(toolLocation);
+ }
+
+ public void init(ServletContext servletContext, VelocityEngine velocityEngine) {
+ this.toolManager = new ToolManager();
+ this.toolManager.configure(toolLocation);
+ this.velocityEngine = velocityEngine;
+ }
+
+ public Context createContext() {
+ if (toolManager == null || velocityEngine == null ||
+ ActionContext.getContext() == null || ActionContext.getContext().getServletContext() == null) {
+ return null;
+ }
+ var toolContext = new ToolContext(velocityEngine);
+ toolContext.addToolbox(toolManager.getToolboxFactory().createToolbox(ToolboxFactory.DEFAULT_SCOPE));
+ return toolContext;
+ }
+}
diff --git a/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/VelocityManagerTest.java b/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/VelocityManagerTest.java
index b95fc8d8a..ec2ebe5ce 100644
--- a/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/VelocityManagerTest.java
+++ b/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/VelocityManagerTest.java
@@ -20,6 +20,8 @@ package org.apache.struts2.views.velocity;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.junit.StrutsJUnit4TestCase;
import org.apache.velocity.context.Context;
@@ -28,8 +30,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
import java.util.Properties;
import static org.apache.struts2.views.velocity.VelocityManager.KEY_VELOCITY_STRUTS_CONTEXT;
@@ -75,7 +75,7 @@ public class VelocityManagerTest extends StrutsJUnit4TestCase {
velocityManager.init(servletContext);
assertNotNull(velocityManager.getVelocityEngine());
- assertNotNull(velocityManager.getToolboxManager());
+ assertNotNull(velocityManager.getVelocityTools());
}
@Test