From 2926c8352b0320d0ae72d42d832c62fe435c6a8e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 19:47:24 +0000 Subject: [PATCH 1/7] Initial plan From b28d1b8eded83dd35c7976279ad2377f940ca3b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 19:55:10 +0000 Subject: [PATCH 2/7] Upgrade Spring Framework to 5.3.18 with compatibility layer Co-authored-by: rvosa <106490+rvosa@users.noreply.github.com> --- oai-pmh_data_provider/pom.xml | 10 +- pom.xml | 18 +-- treebase-web/pom.xml | 7 ++ .../ShortPathUrlFilenameViewController.java | 24 +++- .../multiaction/MultiActionController.java | 57 ++++++++++ .../view/velocity/VelocityConfigurer.java | 88 +++++++++++++++ .../view/velocity/VelocityViewResolver.java | 103 ++++++++++++++++++ 7 files changed, 291 insertions(+), 16 deletions(-) create mode 100644 treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MultiActionController.java create mode 100644 treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java create mode 100644 treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java 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-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..fc660d87 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,28 @@ 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) { + int begin = urlPath.lastIndexOf('/') + 1; + int end = urlPath.indexOf(';'); + if (end == -1) { + end = urlPath.indexOf('?'); + if (end == -1) { + end = urlPath.length(); + } + } + 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/MultiActionController.java b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MultiActionController.java new file mode 100644 index 00000000..e5a12471 --- /dev/null +++ b/treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MultiActionController.java @@ -0,0 +1,57 @@ +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 { + + @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. + * Default implementation looks for a "method" parameter. + */ + protected String getMethodName(HttpServletRequest request) { + String methodName = request.getParameter("method"); + if (methodName == null || methodName.isEmpty()) { + // Fallback to a default method name + methodName = "defaultMethod"; + } + return methodName; + } + + /** + * 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/view/velocity/VelocityConfigurer.java b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java new file mode 100644 index 00000000..97943dfb --- /dev/null +++ b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java @@ -0,0 +1,88 @@ +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(); + + // Set default properties - use string literals instead of constants + props.setProperty("resource.loaders", "webapp"); + props.setProperty("resource.loader.webapp.class", + "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + + if (resourceLoaderPath != null) { + // Configure file resource loader for webapp paths + props.setProperty("resource.loaders", "file"); + props.setProperty("resource.loader.file.class", + "org.apache.velocity.runtime.resource.loader.FileResourceLoader"); + + String path = resourceLoaderPath; + if (servletContext != null && !path.startsWith("/")) { + path = "/" + path; + } + if (servletContext != null) { + path = servletContext.getRealPath(path); + } + props.setProperty("resource.loader.file.path", path); + } + + // 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..f60a244d --- /dev/null +++ b/treebase-web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java @@ -0,0 +1,103 @@ +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.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 { + + private VelocityEngine velocityEngine; + private String prefix = ""; + private String suffix = ".vm"; + private String contentType = "text/html;charset=UTF-8"; + private String encoding = "UTF-8"; + + 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; + } + + @Override + public void afterPropertiesSet() throws Exception { + 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, 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()); + } + } +} From f20a0f2729e049ae5694ccdb4648c522c4b169d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:02:12 +0000 Subject: [PATCH 3/7] Remove deprecated @TransactionConfiguration for Spring 5.x compatibility Co-authored-by: rvosa <106490+rvosa@users.noreply.github.com> --- .../src/test/java/org/cipres/treebase/dao/AbstractDAOTest.java | 2 -- .../java/org/cipres/treebase/service/AbstractServiceTest.java | 2 -- 2 files changed, 4 deletions(-) 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 { From 964fcd3c65ba35050eb23b26b4aa5a2ecf87a238 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:05:31 +0000 Subject: [PATCH 4/7] Address code review feedback: Add missing properties and improve error handling Co-authored-by: rvosa <106490+rvosa@users.noreply.github.com> --- .../ShortPathUrlFilenameViewController.java | 11 +++++ .../mvc/multiaction/MethodNameResolver.java | 19 ++++++++ .../multiaction/MultiActionController.java | 31 +++++++++---- .../NoSuchRequestHandlingMethodException.java | 24 ++++++++++ .../ParameterMethodNameResolver.java | 45 +++++++++++++++++++ .../view/velocity/VelocityConfigurer.java | 32 ++++++++----- .../view/velocity/VelocityViewResolver.java | 28 ++++++++++++ 7 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/MethodNameResolver.java create mode 100644 treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/NoSuchRequestHandlingMethodException.java create mode 100644 treebase-web/src/main/java/org/springframework/web/servlet/mvc/multiaction/ParameterMethodNameResolver.java 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 fc660d87..55429a8a 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 @@ -41,6 +41,10 @@ protected String extractViewNameFromUrlPath(String pUri) { * 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) { @@ -49,6 +53,13 @@ private String extractFilenameFromUrlPath(String urlPath) { end = urlPath.length(); } } + + // Ensure valid bounds + if (begin >= end || begin >= urlPath.length()) { + // 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) { 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 index e5a12471..1fc963a6 100644 --- 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 @@ -16,6 +16,25 @@ */ 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 { @@ -31,16 +50,10 @@ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpSer } /** - * Determine the method name from the request. - * Default implementation looks for a "method" parameter. + * Determine the method name from the request using the configured MethodNameResolver. */ - protected String getMethodName(HttpServletRequest request) { - String methodName = request.getParameter("method"); - if (methodName == null || methodName.isEmpty()) { - // Fallback to a default method name - methodName = "defaultMethod"; - } - return methodName; + protected String getMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { + return getMethodNameResolver().getHandlerMethodName(request); } /** 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 index 97943dfb..8e3de1dd 100644 --- 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 @@ -56,11 +56,6 @@ protected VelocityEngine createVelocityEngine() throws Exception { Properties props = new Properties(); - // Set default properties - use string literals instead of constants - props.setProperty("resource.loaders", "webapp"); - props.setProperty("resource.loader.webapp.class", - "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); - if (resourceLoaderPath != null) { // Configure file resource loader for webapp paths props.setProperty("resource.loaders", "file"); @@ -68,13 +63,30 @@ protected VelocityEngine createVelocityEngine() throws Exception { "org.apache.velocity.runtime.resource.loader.FileResourceLoader"); String path = resourceLoaderPath; - if (servletContext != null && !path.startsWith("/")) { - path = "/" + path; - } if (servletContext != null) { - path = servletContext.getRealPath(path); + if (!path.startsWith("/")) { + path = "/" + path; + } + String realPath = servletContext.getRealPath(path); + // Handle case where getRealPath returns null (e.g., running from WAR) + if (realPath != null) { + path = realPath; + } else { + // Fallback to classpath loader if real path cannot be determined + props.setProperty("resource.loaders", "classpath"); + props.setProperty("resource.loader.classpath.class", + "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + } + } + + if (props.getProperty("resource.loaders").equals("file")) { + props.setProperty("resource.loader.file.path", path); } - props.setProperty("resource.loader.file.path", path); + } else { + // Set default classpath loader when no path is specified + props.setProperty("resource.loaders", "classpath"); + props.setProperty("resource.loader.classpath.class", + "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); } // Add any custom properties 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 index f60a244d..24a93c6e 100644 --- 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 @@ -26,6 +26,8 @@ public class VelocityViewResolver implements ViewResolver, InitializingBean { 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; public void setVelocityEngine(VelocityEngine velocityEngine) { this.velocityEngine = velocityEngine; @@ -47,6 +49,14 @@ 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 afterPropertiesSet() throws Exception { if (this.velocityEngine == null) { @@ -86,6 +96,24 @@ public void render(Map model, HttpServletRequest request, HttpServlet } } + // 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); From a204e2876155371f1dd7186ad74c77b2b46bc75f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:08:53 +0000 Subject: [PATCH 5/7] Improve bounds checking and refactor loader configuration Co-authored-by: rvosa <106490+rvosa@users.noreply.github.com> --- .../ShortPathUrlFilenameViewController.java | 2 +- .../view/velocity/VelocityConfigurer.java | 37 ++++++++----------- 2 files changed, 17 insertions(+), 22 deletions(-) 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 55429a8a..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 @@ -55,7 +55,7 @@ private String extractFilenameFromUrlPath(String urlPath) { } // Ensure valid bounds - if (begin >= end || begin >= urlPath.length()) { + 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; } 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 index 8e3de1dd..9e12dae0 100644 --- 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 @@ -56,37 +56,32 @@ protected VelocityEngine createVelocityEngine() throws Exception { 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) { - // Configure file resource loader for webapp paths - props.setProperty("resource.loaders", "file"); - props.setProperty("resource.loader.file.class", - "org.apache.velocity.runtime.resource.loader.FileResourceLoader"); - String path = resourceLoaderPath; if (servletContext != null) { if (!path.startsWith("/")) { path = "/" + path; } String realPath = servletContext.getRealPath(path); - // Handle case where getRealPath returns null (e.g., running from WAR) + // Use file loader if real path is available if (realPath != null) { - path = realPath; - } else { - // Fallback to classpath loader if real path cannot be determined - props.setProperty("resource.loaders", "classpath"); - props.setProperty("resource.loader.classpath.class", - "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + loaderType = "file"; + loaderClass = "org.apache.velocity.runtime.resource.loader.FileResourceLoader"; + loaderPath = realPath; } } - - if (props.getProperty("resource.loaders").equals("file")) { - props.setProperty("resource.loader.file.path", path); - } - } else { - // Set default classpath loader when no path is specified - props.setProperty("resource.loaders", "classpath"); - props.setProperty("resource.loader.classpath.class", - "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + } + + // 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 From f1314eb3d9f103ef663ed3321741b5e1efbed850 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:28:52 +0000 Subject: [PATCH 6/7] Add Velocity compatibility classes to OAI-PMH module Co-authored-by: rvosa <106490+rvosa@users.noreply.github.com> --- .../data_provider_web/pom.xml | 6 + .../view/velocity/VelocityConfigurer.java | 95 +++++++++++++ .../view/velocity/VelocityViewResolver.java | 131 ++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityConfigurer.java create mode 100644 oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java 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..24a93c6e --- /dev/null +++ b/oai-pmh_data_provider/data_provider_web/src/main/java/org/springframework/web/servlet/view/velocity/VelocityViewResolver.java @@ -0,0 +1,131 @@ +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.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 { + + 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; + + 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 afterPropertiesSet() throws Exception { + 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()); + } + } +} From 84d5e7b691ef90e6952dbc91578b68652c669915 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:48:33 +0000 Subject: [PATCH 7/7] Auto-wire VelocityEngine from VelocityConfigurer bean Co-authored-by: rvosa <106490+rvosa@users.noreply.github.com> --- .../view/velocity/VelocityViewResolver.java | 20 ++++++++++++++++++- .../view/velocity/VelocityViewResolver.java | 20 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) 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 index 24a93c6e..eb5332c3 100644 --- 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 @@ -11,6 +11,8 @@ 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; @@ -19,7 +21,7 @@ * 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 { +public class VelocityViewResolver implements ViewResolver, InitializingBean, ApplicationContextAware { private VelocityEngine velocityEngine; private String prefix = ""; @@ -28,6 +30,7 @@ public class VelocityViewResolver implements ViewResolver, InitializingBean { private String encoding = "UTF-8"; private boolean exposeRequestAttributes = false; private boolean exposeSessionAttributes = false; + private ApplicationContext applicationContext; public void setVelocityEngine(VelocityEngine velocityEngine) { this.velocityEngine = velocityEngine; @@ -57,8 +60,23 @@ 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"); } 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 index 24a93c6e..eb5332c3 100644 --- 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 @@ -11,6 +11,8 @@ 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; @@ -19,7 +21,7 @@ * 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 { +public class VelocityViewResolver implements ViewResolver, InitializingBean, ApplicationContextAware { private VelocityEngine velocityEngine; private String prefix = ""; @@ -28,6 +30,7 @@ public class VelocityViewResolver implements ViewResolver, InitializingBean { private String encoding = "UTF-8"; private boolean exposeRequestAttributes = false; private boolean exposeSessionAttributes = false; + private ApplicationContext applicationContext; public void setVelocityEngine(VelocityEngine velocityEngine) { this.velocityEngine = velocityEngine; @@ -57,8 +60,23 @@ 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"); }