diff --git a/oai-pmh_data_provider/data_provider_web/pom.xml b/oai-pmh_data_provider/data_provider_web/pom.xml
index 8254d24b..e39def6d 100644
--- a/oai-pmh_data_provider/data_provider_web/pom.xml
+++ b/oai-pmh_data_provider/data_provider_web/pom.xml
@@ -301,6 +301,12 @@
velocity-tools-view
1.1
+
+
+ org.apache.velocity
+ velocity-engine-core
+ 2.3
+
org.cipres.treebase
treebase-core
diff --git a/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java b/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java
new file mode 100644
index 00000000..9e12dae0
--- /dev/null
+++ b/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java
@@ -0,0 +1,95 @@
+package org.springframework.web.servlet.view.velocity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.VelocityException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.web.context.ServletContextAware;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Custom VelocityConfigurer for Spring 5.x compatibility.
+ * This class provides Velocity template engine configuration that was removed from Spring 5.x.
+ */
+public class VelocityConfigurer implements InitializingBean, ResourceLoaderAware, ServletContextAware {
+
+ private VelocityEngine velocityEngine;
+ private String resourceLoaderPath;
+ private Properties velocityProperties;
+ private ResourceLoader resourceLoader;
+ private ServletContext servletContext;
+
+ public void setResourceLoaderPath(String resourceLoaderPath) {
+ this.resourceLoaderPath = resourceLoaderPath;
+ }
+
+ public void setVelocityProperties(Properties velocityProperties) {
+ this.velocityProperties = velocityProperties;
+ }
+
+ @Override
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
+ }
+
+ @Override
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ public VelocityEngine getVelocityEngine() {
+ return this.velocityEngine;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ this.velocityEngine = createVelocityEngine();
+ }
+
+ protected VelocityEngine createVelocityEngine() throws Exception {
+ VelocityEngine velocityEngine = new VelocityEngine();
+
+ Properties props = new Properties();
+
+ // Determine loader type and path
+ String loaderType = "classpath";
+ String loaderClass = "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader";
+ String loaderPath = null;
+
+ if (resourceLoaderPath != null) {
+ String path = resourceLoaderPath;
+ if (servletContext != null) {
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ String realPath = servletContext.getRealPath(path);
+ // Use file loader if real path is available
+ if (realPath != null) {
+ loaderType = "file";
+ loaderClass = "org.apache.velocity.runtime.resource.loader.FileResourceLoader";
+ loaderPath = realPath;
+ }
+ }
+ }
+
+ // Set loader configuration
+ props.setProperty("resource.loaders", loaderType);
+ props.setProperty("resource.loader." + loaderType + ".class", loaderClass);
+ if (loaderPath != null) {
+ props.setProperty("resource.loader." + loaderType + ".path", loaderPath);
+ }
+
+ // Add any custom properties
+ if (velocityProperties != null) {
+ props.putAll(velocityProperties);
+ }
+
+ velocityEngine.init(props);
+ return velocityEngine;
+ }
+}
diff --git a/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java b/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java
new file mode 100644
index 00000000..eb5332c3
--- /dev/null
+++ b/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java
@@ -0,0 +1,149 @@
+package org.springframework.web.servlet.view.velocity;
+
+import java.io.StringWriter;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.ViewResolver;
+import org.springframework.web.servlet.view.AbstractUrlBasedView;
+
+/**
+ * Custom VelocityViewResolver for Spring 5.x compatibility.
+ * This class provides Velocity view resolution that was removed from Spring 5.x.
+ */
+public class VelocityViewResolver implements ViewResolver, InitializingBean, ApplicationContextAware {
+
+ private VelocityEngine velocityEngine;
+ private String prefix = "";
+ private String suffix = ".vm";
+ private String contentType = "text/html;charset=UTF-8";
+ private String encoding = "UTF-8";
+ private boolean exposeRequestAttributes = false;
+ private boolean exposeSessionAttributes = false;
+ private ApplicationContext applicationContext;
+
+ public void setVelocityEngine(VelocityEngine velocityEngine) {
+ this.velocityEngine = velocityEngine;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = (prefix != null ? prefix : "");
+ }
+
+ public void setSuffix(String suffix) {
+ this.suffix = (suffix != null ? suffix : "");
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void setExposeRequestAttributes(boolean exposeRequestAttributes) {
+ this.exposeRequestAttributes = exposeRequestAttributes;
+ }
+
+ public void setExposeSessionAttributes(boolean exposeSessionAttributes) {
+ this.exposeSessionAttributes = exposeSessionAttributes;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ // If velocityEngine is not set, try to get it from VelocityConfigurer
+ if (this.velocityEngine == null && this.applicationContext != null) {
+ try {
+ VelocityConfigurer configurer = this.applicationContext.getBean(VelocityConfigurer.class);
+ this.velocityEngine = configurer.getVelocityEngine();
+ } catch (Exception e) {
+ // VelocityConfigurer bean not found or error getting engine
+ }
+ }
+
+ if (this.velocityEngine == null) {
+ throw new IllegalArgumentException("Property 'velocityEngine' is required");
+ }
+ }
+
+ @Override
+ public View resolveViewName(String viewName, Locale locale) throws Exception {
+ return new VelocityView(prefix + viewName + suffix);
+ }
+
+ private class VelocityView implements View {
+ private final String templatePath;
+
+ public VelocityView(String templatePath) {
+ this.templatePath = templatePath;
+ }
+
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public void render(Map model, HttpServletRequest request, HttpServletResponse response)
+ throws Exception {
+
+ response.setContentType(getContentType());
+
+ Context velocityContext = new VelocityContext();
+
+ // Add model attributes to Velocity context
+ if (model != null) {
+ for (Map.Entry entry : model.entrySet()) {
+ velocityContext.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // Add request attributes if configured
+ if (exposeRequestAttributes && request != null) {
+ java.util.Enumeration attrNames = request.getAttributeNames();
+ while (attrNames.hasMoreElements()) {
+ String attrName = attrNames.nextElement();
+ velocityContext.put(attrName, request.getAttribute(attrName));
+ }
+ }
+
+ // Add session attributes if configured
+ if (exposeSessionAttributes && request != null && request.getSession(false) != null) {
+ java.util.Enumeration attrNames = request.getSession().getAttributeNames();
+ while (attrNames.hasMoreElements()) {
+ String attrName = attrNames.nextElement();
+ velocityContext.put(attrName, request.getSession().getAttribute(attrName));
+ }
+ }
+
+ // Add request, response, and session to context for compatibility
+ velocityContext.put("request", request);
+ velocityContext.put("response", response);
+ if (request.getSession(false) != null) {
+ velocityContext.put("session", request.getSession());
+ }
+
+ // Merge template
+ StringWriter writer = new StringWriter();
+ velocityEngine.mergeTemplate(templatePath, encoding, velocityContext, writer);
+
+ response.getWriter().write(writer.toString());
+ }
+ }
+}
diff --git a/oai-pmh_data_provider/pom.xml b/oai-pmh_data_provider/pom.xml
index 766504b0..0d4243d7 100644
--- a/oai-pmh_data_provider/pom.xml
+++ b/oai-pmh_data_provider/pom.xml
@@ -39,31 +39,31 @@
org.springframework
spring-core
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-beans
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-context
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-webmvc
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-test
- 4.3.30.RELEASE
+ 5.3.18
test
diff --git a/pom.xml b/pom.xml
index 745b12f3..b4ff633b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,55 +75,55 @@
org.springframework
spring-core
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-beans
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-context
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-jdbc
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-orm
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-tx
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-webmvc
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-context-support
- 4.3.30.RELEASE
+ 5.3.18
org.springframework
spring-test
- 4.3.30.RELEASE
+ 5.3.18
test
diff --git a/treebase-core/src/test/java/org/cipres/treebase/dao/AbstractDAOTest.java b/treebase-core/src/test/java/org/cipres/treebase/dao/AbstractDAOTest.java
index 1b57184f..a3078215 100644
--- a/treebase-core/src/test/java/org/cipres/treebase/dao/AbstractDAOTest.java
+++ b/treebase-core/src/test/java/org/cipres/treebase/dao/AbstractDAOTest.java
@@ -18,7 +18,6 @@
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@@ -39,7 +38,6 @@
"classpath:applicationContext-dao.xml",
"classpath:applicationContext-service.xml"
})
-@TransactionConfiguration(defaultRollback = true)
@Transactional
public abstract class AbstractDAOTest {
diff --git a/treebase-core/src/test/java/org/cipres/treebase/service/AbstractServiceTest.java b/treebase-core/src/test/java/org/cipres/treebase/service/AbstractServiceTest.java
index 0e68a8c3..bc3c469f 100644
--- a/treebase-core/src/test/java/org/cipres/treebase/service/AbstractServiceTest.java
+++ b/treebase-core/src/test/java/org/cipres/treebase/service/AbstractServiceTest.java
@@ -5,7 +5,6 @@
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -23,7 +22,6 @@
"classpath:applicationContext-dao.xml",
"classpath:applicationContext-service.xml"
})
-@TransactionConfiguration(defaultRollback = true)
@Transactional
public abstract class AbstractServiceTest {
diff --git a/treebase-web/pom.xml b/treebase-web/pom.xml
index 930fe65d..5413ba15 100644
--- a/treebase-web/pom.xml
+++ b/treebase-web/pom.xml
@@ -340,6 +340,13 @@
velocity-tools-view
1.1
+
+
+
+ org.apache.velocity
+ velocity-engine-core
+ 2.3
+
com.sun.jersey
diff --git a/treebase-web/src/main/java/org/cipres/treebase/web/controllers/ShortPathUrlFilenameViewController.java b/treebase-web/src/main/java/org/cipres/treebase/web/controllers/ShortPathUrlFilenameViewController.java
index b6d88bb6..789f9b9b 100644
--- a/treebase-web/src/main/java/org/cipres/treebase/web/controllers/ShortPathUrlFilenameViewController.java
+++ b/treebase-web/src/main/java/org/cipres/treebase/web/controllers/ShortPathUrlFilenameViewController.java
@@ -2,7 +2,6 @@
package org.cipres.treebase.web.controllers;
import org.springframework.web.servlet.mvc.UrlFilenameViewController;
-import org.springframework.web.util.WebUtils;
/**
* A subclass of Spring UrlFilenameViewController. The UrlFilenameViewController class in Spring 2
@@ -34,7 +33,39 @@ public ShortPathUrlFilenameViewController() {
*/
@Override
protected String extractViewNameFromUrlPath(String pUri) {
- return WebUtils.extractFilenameFromUrlPath(pUri);
+ return extractFilenameFromUrlPath(pUri);
+ }
+
+ /**
+ * Extract the filename from the given URL path.
+ * This replaces the removed WebUtils.extractFilenameFromUrlPath method.
+ */
+ private String extractFilenameFromUrlPath(String urlPath) {
+ if (urlPath == null || urlPath.isEmpty()) {
+ return "";
+ }
+
+ int begin = urlPath.lastIndexOf('/') + 1;
+ int end = urlPath.indexOf(';');
+ if (end == -1) {
+ end = urlPath.indexOf('?');
+ if (end == -1) {
+ end = urlPath.length();
+ }
+ }
+
+ // Ensure valid bounds
+ if (begin < 0 || begin >= urlPath.length() || end > urlPath.length() || begin >= end) {
+ // Return the path as-is if we can't extract a valid filename
+ return urlPath.startsWith("/") ? urlPath.substring(1) : urlPath;
+ }
+
+ String filename = urlPath.substring(begin, end);
+ int dotIndex = filename.lastIndexOf('.');
+ if (dotIndex != -1) {
+ filename = filename.substring(0, dotIndex);
+ }
+ return filename;
}
}
diff --git a/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MethodNameResolver.java b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MethodNameResolver.java
new file mode 100644
index 00000000..adc4f2e1
--- /dev/null
+++ b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MethodNameResolver.java
@@ -0,0 +1,19 @@
+package org.springframework.web.servlet.mvc.multiaction;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Interface for method name resolvers used by MultiActionController.
+ * This interface was removed in Spring 5.x but is recreated here for backward compatibility.
+ */
+public interface MethodNameResolver {
+
+ /**
+ * Return the method name for the given request.
+ *
+ * @param request the current HTTP request
+ * @return the name of the method to invoke
+ * @throws NoSuchRequestHandlingMethodException if no method name could be determined
+ */
+ String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException;
+}
diff --git a/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MultiActionController.java b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MultiActionController.java
new file mode 100644
index 00000000..1fc963a6
--- /dev/null
+++ b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MultiActionController.java
@@ -0,0 +1,70 @@
+package org.springframework.web.servlet.mvc.multiaction;
+
+import java.lang.reflect.Method;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.AbstractController;
+
+/**
+ * Custom MultiActionController for Spring 5.x compatibility.
+ * This class provides multi-action controller functionality that was removed from Spring 5.x.
+ *
+ * Note: This is a simplified implementation that maintains backward compatibility.
+ * In Spring 5.x, the recommended approach is to use @Controller with @RequestMapping.
+ */
+public class MultiActionController extends AbstractController {
+
+ private MethodNameResolver methodNameResolver;
+
+ /**
+ * Set the MethodNameResolver to use for determining the handler method name.
+ */
+ public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
+ this.methodNameResolver = methodNameResolver;
+ }
+
+ /**
+ * Get the current MethodNameResolver.
+ */
+ public MethodNameResolver getMethodNameResolver() {
+ if (this.methodNameResolver == null) {
+ this.methodNameResolver = new ParameterMethodNameResolver();
+ }
+ return this.methodNameResolver;
+ }
+
+ @Override
+ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
+ throws Exception {
+
+ String methodName = getMethodName(request);
+ Method method = findMethod(methodName);
+
+ if (method == null) {
+ throw new NoSuchMethodException("No method found with name: " + methodName);
+ }
+
+ return (ModelAndView) method.invoke(this, request, response);
+ }
+
+ /**
+ * Determine the method name from the request using the configured MethodNameResolver.
+ */
+ protected String getMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException {
+ return getMethodNameResolver().getHandlerMethodName(request);
+ }
+
+ /**
+ * Find a method by name that matches the MultiActionController signature.
+ */
+ protected Method findMethod(String methodName) {
+ try {
+ return getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
+ } catch (NoSuchMethodException e) {
+ // Method not found
+ return null;
+ }
+ }
+}
diff --git a/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/NoSuchRequestHandlingMethodException.java b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/NoSuchRequestHandlingMethodException.java
new file mode 100644
index 00000000..d33951d8
--- /dev/null
+++ b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/NoSuchRequestHandlingMethodException.java
@@ -0,0 +1,24 @@
+package org.springframework.web.servlet.mvc.multiaction;
+
+/**
+ * Exception thrown when no handler method can be found for a request.
+ * This exception was removed in Spring 5.x but is recreated here for backward compatibility.
+ */
+public class NoSuchRequestHandlingMethodException extends Exception {
+
+ private final String methodName;
+
+ public NoSuchRequestHandlingMethodException(String methodName) {
+ super("No handler method '" + methodName + "' found");
+ this.methodName = methodName;
+ }
+
+ public NoSuchRequestHandlingMethodException(String message, String methodName) {
+ super(message);
+ this.methodName = methodName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+}
diff --git a/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/ParameterMethodNameResolver.java b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/ParameterMethodNameResolver.java
new file mode 100644
index 00000000..413d2bf1
--- /dev/null
+++ b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/ParameterMethodNameResolver.java
@@ -0,0 +1,45 @@
+package org.springframework.web.servlet.mvc.multiaction;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Implementation of MethodNameResolver that determines the method name from a request parameter.
+ * This class was removed in Spring 5.x but is recreated here for backward compatibility.
+ */
+public class ParameterMethodNameResolver implements MethodNameResolver {
+
+ public static final String DEFAULT_PARAM_NAME = "action";
+
+ private String paramName = DEFAULT_PARAM_NAME;
+ private String defaultMethodName;
+
+ /**
+ * Set the name of the parameter that contains the method name.
+ * Default is "action".
+ */
+ public void setParamName(String paramName) {
+ this.paramName = paramName;
+ }
+
+ /**
+ * Set the default method name to use when no parameter is specified.
+ */
+ public void setDefaultMethodName(String defaultMethodName) {
+ this.defaultMethodName = defaultMethodName;
+ }
+
+ @Override
+ public String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException {
+ String methodName = request.getParameter(this.paramName);
+
+ if (methodName == null || methodName.trim().isEmpty()) {
+ if (this.defaultMethodName != null) {
+ return this.defaultMethodName;
+ }
+ throw new NoSuchRequestHandlingMethodException(
+ "No method name found in request parameter '" + this.paramName + "'", "");
+ }
+
+ return methodName;
+ }
+}
diff --git a/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java
new file mode 100644
index 00000000..9e12dae0
--- /dev/null
+++ b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java
@@ -0,0 +1,95 @@
+package org.springframework.web.servlet.view.velocity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.VelocityException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.web.context.ServletContextAware;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Custom VelocityConfigurer for Spring 5.x compatibility.
+ * This class provides Velocity template engine configuration that was removed from Spring 5.x.
+ */
+public class VelocityConfigurer implements InitializingBean, ResourceLoaderAware, ServletContextAware {
+
+ private VelocityEngine velocityEngine;
+ private String resourceLoaderPath;
+ private Properties velocityProperties;
+ private ResourceLoader resourceLoader;
+ private ServletContext servletContext;
+
+ public void setResourceLoaderPath(String resourceLoaderPath) {
+ this.resourceLoaderPath = resourceLoaderPath;
+ }
+
+ public void setVelocityProperties(Properties velocityProperties) {
+ this.velocityProperties = velocityProperties;
+ }
+
+ @Override
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
+ }
+
+ @Override
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ public VelocityEngine getVelocityEngine() {
+ return this.velocityEngine;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ this.velocityEngine = createVelocityEngine();
+ }
+
+ protected VelocityEngine createVelocityEngine() throws Exception {
+ VelocityEngine velocityEngine = new VelocityEngine();
+
+ Properties props = new Properties();
+
+ // Determine loader type and path
+ String loaderType = "classpath";
+ String loaderClass = "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader";
+ String loaderPath = null;
+
+ if (resourceLoaderPath != null) {
+ String path = resourceLoaderPath;
+ if (servletContext != null) {
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ String realPath = servletContext.getRealPath(path);
+ // Use file loader if real path is available
+ if (realPath != null) {
+ loaderType = "file";
+ loaderClass = "org.apache.velocity.runtime.resource.loader.FileResourceLoader";
+ loaderPath = realPath;
+ }
+ }
+ }
+
+ // Set loader configuration
+ props.setProperty("resource.loaders", loaderType);
+ props.setProperty("resource.loader." + loaderType + ".class", loaderClass);
+ if (loaderPath != null) {
+ props.setProperty("resource.loader." + loaderType + ".path", loaderPath);
+ }
+
+ // Add any custom properties
+ if (velocityProperties != null) {
+ props.putAll(velocityProperties);
+ }
+
+ velocityEngine.init(props);
+ return velocityEngine;
+ }
+}
diff --git a/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java
new file mode 100644
index 00000000..eb5332c3
--- /dev/null
+++ b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java
@@ -0,0 +1,149 @@
+package org.springframework.web.servlet.view.velocity;
+
+import java.io.StringWriter;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.ViewResolver;
+import org.springframework.web.servlet.view.AbstractUrlBasedView;
+
+/**
+ * Custom VelocityViewResolver for Spring 5.x compatibility.
+ * This class provides Velocity view resolution that was removed from Spring 5.x.
+ */
+public class VelocityViewResolver implements ViewResolver, InitializingBean, ApplicationContextAware {
+
+ private VelocityEngine velocityEngine;
+ private String prefix = "";
+ private String suffix = ".vm";
+ private String contentType = "text/html;charset=UTF-8";
+ private String encoding = "UTF-8";
+ private boolean exposeRequestAttributes = false;
+ private boolean exposeSessionAttributes = false;
+ private ApplicationContext applicationContext;
+
+ public void setVelocityEngine(VelocityEngine velocityEngine) {
+ this.velocityEngine = velocityEngine;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = (prefix != null ? prefix : "");
+ }
+
+ public void setSuffix(String suffix) {
+ this.suffix = (suffix != null ? suffix : "");
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void setExposeRequestAttributes(boolean exposeRequestAttributes) {
+ this.exposeRequestAttributes = exposeRequestAttributes;
+ }
+
+ public void setExposeSessionAttributes(boolean exposeSessionAttributes) {
+ this.exposeSessionAttributes = exposeSessionAttributes;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ // If velocityEngine is not set, try to get it from VelocityConfigurer
+ if (this.velocityEngine == null && this.applicationContext != null) {
+ try {
+ VelocityConfigurer configurer = this.applicationContext.getBean(VelocityConfigurer.class);
+ this.velocityEngine = configurer.getVelocityEngine();
+ } catch (Exception e) {
+ // VelocityConfigurer bean not found or error getting engine
+ }
+ }
+
+ if (this.velocityEngine == null) {
+ throw new IllegalArgumentException("Property 'velocityEngine' is required");
+ }
+ }
+
+ @Override
+ public View resolveViewName(String viewName, Locale locale) throws Exception {
+ return new VelocityView(prefix + viewName + suffix);
+ }
+
+ private class VelocityView implements View {
+ private final String templatePath;
+
+ public VelocityView(String templatePath) {
+ this.templatePath = templatePath;
+ }
+
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public void render(Map model, HttpServletRequest request, HttpServletResponse response)
+ throws Exception {
+
+ response.setContentType(getContentType());
+
+ Context velocityContext = new VelocityContext();
+
+ // Add model attributes to Velocity context
+ if (model != null) {
+ for (Map.Entry entry : model.entrySet()) {
+ velocityContext.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // Add request attributes if configured
+ if (exposeRequestAttributes && request != null) {
+ java.util.Enumeration attrNames = request.getAttributeNames();
+ while (attrNames.hasMoreElements()) {
+ String attrName = attrNames.nextElement();
+ velocityContext.put(attrName, request.getAttribute(attrName));
+ }
+ }
+
+ // Add session attributes if configured
+ if (exposeSessionAttributes && request != null && request.getSession(false) != null) {
+ java.util.Enumeration attrNames = request.getSession().getAttributeNames();
+ while (attrNames.hasMoreElements()) {
+ String attrName = attrNames.nextElement();
+ velocityContext.put(attrName, request.getSession().getAttribute(attrName));
+ }
+ }
+
+ // Add request, response, and session to context for compatibility
+ velocityContext.put("request", request);
+ velocityContext.put("response", response);
+ if (request.getSession(false) != null) {
+ velocityContext.put("session", request.getSession());
+ }
+
+ // Merge template
+ StringWriter writer = new StringWriter();
+ velocityEngine.mergeTemplate(templatePath, encoding, velocityContext, writer);
+
+ response.getWriter().write(writer.toString());
+ }
+ }
+}