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()); + } + } +}