From ceba1207ae23d21f7208e3e9daeb773a93bfb65a Mon Sep 17 00:00:00 2001 From: Shane Argo Date: Thu, 29 Sep 2016 13:02:08 +1000 Subject: [PATCH 1/2] Remove actionBean request attribute when stack is empty. Tomcat's crossContext configuration item means that a single request may be forwarded to multiple servlets. This clears the actionBean request attribute when the stack is empty so that the next servlet is not polluted with an action bean from the previous servlet. Some of the tests assumed that the last action bean to be restored from the stack would remain in the actionBean request attribute. To resolve this, a new interceptor was introduced for testing which saves out the last action bean prior to it being cleared. --- .../stripes/controller/DispatcherServlet.java | 2 ++ .../stripes/controller/StripesConstants.java | 6 ++++++ .../stripes/mock/MockRoundtrip.java | 2 +- .../stripes/StripesTestFixture.java | 1 + .../test/RecordLastActionBeanInterceptor.java | 18 ++++++++++++++++++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 stripes/src/test/java/net/sourceforge/stripes/test/RecordLastActionBeanInterceptor.java diff --git a/stripes/src/main/java/net/sourceforge/stripes/controller/DispatcherServlet.java b/stripes/src/main/java/net/sourceforge/stripes/controller/DispatcherServlet.java index 4b562387e..bd38b1acd 100644 --- a/stripes/src/main/java/net/sourceforge/stripes/controller/DispatcherServlet.java +++ b/stripes/src/main/java/net/sourceforge/stripes/controller/DispatcherServlet.java @@ -375,6 +375,8 @@ protected void restoreActionBean(HttpServletRequest request) { Stack stack = getActionBeanStack(request, false); if (stack != null && !stack.empty()) { request.setAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN, stack.pop()); + } else { + request.removeAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN); } } } \ No newline at end of file diff --git a/stripes/src/main/java/net/sourceforge/stripes/controller/StripesConstants.java b/stripes/src/main/java/net/sourceforge/stripes/controller/StripesConstants.java index 4b9c79135..5dc1cf63c 100644 --- a/stripes/src/main/java/net/sourceforge/stripes/controller/StripesConstants.java +++ b/stripes/src/main/java/net/sourceforge/stripes/controller/StripesConstants.java @@ -65,6 +65,12 @@ public interface StripesConstants { */ String REQ_ATTR_ACTION_BEAN = "actionBean"; + /** + * The name under which the last ActionBean for a request is stored before restoring from + * the stack. + */ + String REQ_ATTR_LAST_ACTION_BEAN = "lastActionBean"; + /** * The name of a request attribute in which a Stack of action beans is some times stored * when a single request involves includes of action beans. diff --git a/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java b/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java index d1c43e830..cc2c7de53 100644 --- a/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java +++ b/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java @@ -285,7 +285,7 @@ public A getActionBean(Class type) { * Gets the (potentially empty) set of Validation Errors that were produced by the request. */ public ValidationErrors getValidationErrors() { - ActionBean bean = (ActionBean) this.request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN); + ActionBean bean = (ActionBean) this.request.getAttribute(StripesConstants.REQ_ATTR_LAST_ACTION_BEAN); return bean.getContext().getValidationErrors(); } diff --git a/stripes/src/test/java/net/sourceforge/stripes/StripesTestFixture.java b/stripes/src/test/java/net/sourceforge/stripes/StripesTestFixture.java index 7e38cc162..b73fb38ef 100644 --- a/stripes/src/test/java/net/sourceforge/stripes/StripesTestFixture.java +++ b/stripes/src/test/java/net/sourceforge/stripes/StripesTestFixture.java @@ -55,6 +55,7 @@ public static synchronized Configuration getDefaultConfiguration() { public static Map getDefaultFilterParams() { Map map = new HashMap(); map.put("ActionResolver.Packages", "net.sourceforge.stripes"); + map.put("Interceptor.Classes", "net.sourceforge.stripes.test.RecordLastActionBeanInterceptor"); map.put("LocalePicker.Class", "net.sourceforge.stripes.localization.MockLocalePicker"); return map; } diff --git a/stripes/src/test/java/net/sourceforge/stripes/test/RecordLastActionBeanInterceptor.java b/stripes/src/test/java/net/sourceforge/stripes/test/RecordLastActionBeanInterceptor.java new file mode 100644 index 000000000..15750f33d --- /dev/null +++ b/stripes/src/test/java/net/sourceforge/stripes/test/RecordLastActionBeanInterceptor.java @@ -0,0 +1,18 @@ +package net.sourceforge.stripes.test; + +import net.sourceforge.stripes.action.Resolution; +import net.sourceforge.stripes.controller.*; + +/** + * Created by shane on 23/9/16. + */ +@Intercepts(LifecycleStage.RequestComplete) +public class RecordLastActionBeanInterceptor implements Interceptor { + + @Override + public Resolution intercept(ExecutionContext context) throws Exception { + context.getActionBeanContext().getRequest().setAttribute(StripesConstants.REQ_ATTR_LAST_ACTION_BEAN, context.getActionBean()); + return null; + } + +} From 2c7f07130e653f0c2b0e206982caa6c8c3930fa7 Mon Sep 17 00:00:00 2001 From: Shane Argo Date: Thu, 29 Sep 2016 13:19:27 +1000 Subject: [PATCH 2/2] Prepend contextPath to the action bean's session attribute. Tomcat's crossContext configuration item means that a single request may be forwarded to multiple servlets. If multiple instances of stripes are running in different webapps in the same container, with this configuration it is possible that they'll share a context, and thus the URL binding may collide. The MockRoundtrip class expected the attribute key to be the URL Binding, so this was updated to align with this change. --- .../controller/AnnotatedClassActionResolver.java | 11 +++++++---- .../net/sourceforge/stripes/mock/MockRoundtrip.java | 7 +++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/stripes/src/main/java/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java b/stripes/src/main/java/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java index a4cdc5e36..35bcf6989 100644 --- a/stripes/src/main/java/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java +++ b/stripes/src/main/java/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java @@ -370,21 +370,24 @@ public ActionBean getActionBean(ActionBeanContext context, String path) throws S } String bindingPath = getUrlBinding(beanClass); + final String contextPath = context.getServletContext().getContextPath() != null ? + context.getServletContext().getContextPath() : ""; + final String attribName = contextPath + bindingPath; try { HttpServletRequest request = context.getRequest(); if (beanClass.isAnnotationPresent(SessionScope.class)) { - bean = (ActionBean) request.getSession().getAttribute(bindingPath); + bean = (ActionBean) request.getSession().getAttribute(attribName); if (bean == null) { bean = makeNewActionBean(beanClass, context); - request.getSession().setAttribute(bindingPath, bean); + request.getSession().setAttribute(attribName, bean); } } else { - bean = (ActionBean) request.getAttribute(bindingPath); + bean = (ActionBean) request.getAttribute(attribName); if (bean == null) { bean = makeNewActionBean(beanClass, context); - request.setAttribute(bindingPath, bean); + request.setAttribute(attribName, bean); } } diff --git a/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java b/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java index cc2c7de53..c560503ea 100644 --- a/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java +++ b/stripes/src/main/java/net/sourceforge/stripes/mock/MockRoundtrip.java @@ -274,9 +274,12 @@ public void execute(String event) throws Exception { */ @SuppressWarnings("unchecked") public A getActionBean(Class type) { - A bean = (A) this.request.getAttribute(getUrlBinding(type, this.context)); + final String bindingPath = getUrlBinding(type, this.context); + final String contextPath = context.getContextPath() != null ? context.getContextPath() : ""; + final String attribName = contextPath + bindingPath; + A bean = (A) this.request.getAttribute(attribName); if (bean == null) { - bean = (A) this.request.getSession().getAttribute(getUrlBinding(type, this.context)); + bean = (A) this.request.getSession().getAttribute(attribName); } return bean; }