You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2020/04/05 05:47:11 UTC

[struts] 01/05: WW-4789 WW-3788 ActionContext refactoring

This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch action-context-boost
in repository https://gitbox.apache.org/repos/asf/struts.git

commit f4aa4ee09a12b16dd2aef3542c8b787e106bbe77
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Sat Mar 21 10:05:48 2020 +0100

    WW-4789 WW-3788 ActionContext refactoring
---
 .../com/opensymphony/xwork2/ActionChainResult.java |   8 +-
 .../com/opensymphony/xwork2/ActionContext.java     | 139 +++++++--
 .../xwork2/DefaultActionInvocation.java            |   2 +-
 .../opensymphony/xwork2/DefaultActionProxy.java    |   4 +-
 .../xwork2/config/impl/DefaultConfiguration.java   |   5 +-
 .../opensymphony/xwork2/ognl/OgnlValueStack.java   |   5 +
 .../interceptor/ActionAutowiringInterceptor.java   |   5 +-
 .../com/opensymphony/xwork2/util/ValueStack.java   |  44 +--
 .../xwork2/util/XWorkTestCaseHelper.java           |   7 +-
 .../org/apache/struts2/ServletActionContext.java   |  40 +--
 .../java/org/apache/struts2/StrutsStatics.java     |  38 ++-
 .../apache/struts2/components/ActionComponent.java |  10 +-
 .../struts2/components/ServletUrlRenderer.java     |   2 +-
 .../template/FreemarkerTemplateEngine.java         |   8 +-
 .../org/apache/struts2/dispatcher/Dispatcher.java  | 156 +++++-----
 .../apache/struts2/dispatcher/InitOperations.java  |   7 +-
 .../struts2/dispatcher/PrepareOperations.java      |  11 +-
 .../dispatcher/listener/StrutsListener.java        |   1 -
 .../struts2/dispatcher/mapper/ActionMapping.java   |   2 +-
 .../dispatcher/mapper/DefaultActionMapper.java     |   2 +-
 .../apache/struts2/factory/StrutsActionProxy.java  |   4 +-
 .../ActionMappingParametersInterceptor.java        |   2 +-
 .../struts2/interceptor/BackgroundProcess.java     |   4 +-
 .../interceptor/CookieProviderInterceptor.java     |   2 +-
 .../interceptor/CreateSessionInterceptor.java      |   9 +-
 .../struts2/interceptor/FileUploadInterceptor.java |   3 +-
 .../interceptor/MessageStoreInterceptor.java       |   2 +-
 .../interceptor/ServletConfigInterceptor.java      |  16 +-
 .../interceptor/TokenSessionStoreInterceptor.java  |   6 +-
 .../debugging/DebuggingInterceptor.java            |   6 +-
 .../struts2/util/InvocationSessionStore.java       |   7 +-
 .../apache/struts2/util/StrutsTestCaseHelper.java  |   4 +-
 .../main/java/org/apache/struts2/util/URLBean.java |   3 +-
 .../struts2/views/freemarker/FreemarkerResult.java |   2 +-
 .../org/apache/struts2/views/jsp/TagUtils.java     |   9 +-
 core/src/main/resources/struts-default.xml         |   1 -
 .../com/opensymphony/xwork2/ActionContextTest.java |  10 +-
 .../xwork2/ActionContextThreadLocalTest.java       |  25 +-
 .../com/opensymphony/xwork2/ActionSupportTest.java |   6 +-
 .../com/opensymphony/xwork2/ChainResultTest.java   |  26 +-
 .../xwork2/DefaultTextProviderTest.java            |   6 +-
 .../com/opensymphony/xwork2/LocaleAwareTest.java   |   4 +-
 .../com/opensymphony/xwork2/StubValueStack.java    |   5 +
 .../opensymphony/xwork2/WildCardResultTest.java    |   6 +-
 .../impl/AnnotationXWorkConverterTest.java         |   3 +-
 .../conversion/impl/XWorkBasicConverterTest.java   |   2 +-
 .../interceptor/ChainingInterceptorTest.java       |   2 +-
 .../ConversionErrorInterceptorTest.java            |   4 +-
 .../DefaultWorkflowInterceptorTest.java            |  23 +-
 .../ExceptionMappingInterceptorTest.java           |   2 +-
 .../ParameterFilterInterceptorTest.java            |  20 +-
 .../ParameterRemoverInterceptorTest.java           | 191 ++++++-------
 .../interceptor/ValidationErrorAwareTest.java      |  20 +-
 ...ationInterceptorPrefixMethodInvocationTest.java |  97 ++++---
 .../AnnotationParameterFilterInterceptorTest.java  | 313 ++++++++++-----------
 .../ActionAutowiringInterceptorTest.java           |  17 +-
 .../ConversionErrorFieldValidatorTest.java         |  10 +-
 .../DefaultActionValidatorManagerTest.java         |   4 +-
 .../validator/SimpleActionValidationTest.java      |   7 +-
 .../validator/VisitorFieldValidatorModelTest.java  |   2 +-
 .../validator/VisitorFieldValidatorTest.java       |   2 +-
 .../apache/struts2/ServletActionContextTest.java   |  10 +-
 .../struts2/components/ActionComponentTest.java    |  16 +-
 .../struts2/dispatcher/PrepareOperationsTest.java  |   2 +-
 .../dispatcher/TwoFilterIntegrationTest.java       |  21 +-
 .../interceptor/ClearSessionInterceptorTest.java   |   9 +-
 .../interceptor/CreateSessionInterceptorTest.java  |  13 +-
 .../interceptor/ExecuteAndWaitInterceptorTest.java |   4 +-
 .../struts2/interceptor/I18nInterceptorTest.java   |  23 +-
 .../interceptor/MessageStoreInterceptorTest.java   |  83 +++---
 .../MessageStorePreResultListenerTest.java         |  26 +-
 .../StrutsConversionErrorInterceptorTest.java      |   2 +-
 .../struts2/interceptor/TokenInterceptorTest.java  |   5 +-
 .../struts2/result/HttpHeaderResultTest.java       |   2 +-
 .../apache/struts2/result/PlainTextResultTest.java |   2 +-
 .../struts2/result/ServletRedirectResultTest.java  | 106 ++++---
 .../struts2/util/InvocationSessionStoreTest.java   |  26 +-
 .../org/apache/struts2/util/TokenHelperTest.java   |  67 +++--
 .../views/freemarker/FreeMarkerResultTest.java     |  31 +-
 .../freemarker/FreemarkerResultMockedTest.java     |  42 ++-
 .../struts2/views/freemarker/FreemarkerTest.java   |   2 +-
 .../apache/struts2/views/jsp/AbstractTagTest.java  |  22 +-
 .../struts2/views/jsp/AbstractUITagTest.java       |   2 +-
 .../apache/struts2/views/jsp/ActionTagTest.java    |   3 +-
 .../org/apache/struts2/views/jsp/TextTagTest.java  |  15 +-
 .../org/apache/struts2/views/jsp/URLTagTest.java   |  29 +-
 .../PackageBasedActionConfigBuilderTest.java       |   5 +-
 .../main/java/org/apache/struts2/JSPLoader.java    |   6 +-
 .../org/apache/struts2/EmbeddedJSPResultTest.java  |  52 ++--
 .../struts2/views/java/simple/AbstractTest.java    |  50 ++--
 .../struts2/views/java/simple/TokenTest.java       |   6 +-
 .../apache/struts2/json/JSONInterceptorTest.java   |   7 +-
 .../org/apache/struts2/StrutsJUnit4TestCase.java   |   4 +-
 .../org/apache/struts2/StrutsRestTestCase.java     |  48 ++--
 .../java/org/apache/struts2/StrutsTestCase.java    |   4 +-
 .../struts2/osgi/OsgiConfigurationProvider.java    |   9 +-
 .../struts2/components/PortletUrlRenderer.java     |   2 +-
 .../portlet/dispatcher/Jsr168Dispatcher.java       |   4 +-
 .../portlet/dispatcher/Jsr286Dispatcher.java       |   4 +-
 .../portlet/context/PortletActionContextTest.java  |   6 +-
 .../interceptor/PortletAwareInterceptorTest.java   |  12 +-
 .../interceptor/PortletStateInterceptorTest.java   | 230 +++++++--------
 .../struts2/portlet/result/PortletResultTest.java  |  81 +++---
 .../struts2/portlet/util/PortletUrlHelperTest.java | 148 +++++-----
 .../struts2/views/jsp/PortletUrlTagTest.java       | 228 ++++++++-------
 .../rest/ContentTypeHandlerManagerTest.java        |  20 +-
 .../struts2/rest/RestWorkflowInterceptorTest.java  |   7 +-
 .../struts2/rest/handler/JuneauXmlHandlerTest.java |   2 +-
 .../sitemesh/FreemarkerDecoratorServlet.java       |   2 +-
 .../sitemesh/OldDecorator2NewStrutsDecorator.java  |   2 +-
 .../struts2/sitemesh/VelocityDecoratorServlet.java |   2 +-
 111 files changed, 1440 insertions(+), 1447 deletions(-)

diff --git a/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java b/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java
index 9261e61..6094f4b 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java
@@ -202,7 +202,7 @@ public class ActionChainResult implements Result {
      * @param invocation the DefaultActionInvocation calling the action call stack
      */
     public void execute(ActionInvocation invocation) throws Exception {
-        ValueStack stack = ActionContext.getContext().getValueStack();
+        ValueStack stack = invocation.getInvocationContext().getValueStack();
         String finalNamespace = this.namespace != null
                 ? TextParseUtil.translateVariables(namespace, stack)
                 : invocation.getProxy().getNamespace();
@@ -216,14 +216,14 @@ public class ActionChainResult implements Result {
             throw new StrutsException("Infinite recursion detected: " + ActionChainResult.getChainHistory().toString());
         }
 
-        if (ActionChainResult.getChainHistory().isEmpty() && invocation != null && invocation.getProxy() != null) {
+        if (ActionChainResult.getChainHistory().isEmpty() && invocation.getProxy() != null) {
             addToHistory(finalNamespace, invocation.getProxy().getActionName(), invocation.getProxy().getMethod());
         }
         addToHistory(finalNamespace, finalActionName, finalMethodName);
 
         HashMap<String, Object> extraContext = new HashMap<>();
-        extraContext.put(ActionContext.VALUE_STACK, ActionContext.getContext().getValueStack());
-        extraContext.put(ActionContext.PARAMETERS, ActionContext.getContext().getParameters());
+        extraContext.put(ActionContext.VALUE_STACK, invocation.getInvocationContext().getValueStack());
+        extraContext.put(ActionContext.PARAMETERS, invocation.getInvocationContext().getParameters());
         extraContext.put(CHAIN_HISTORY, ActionChainResult.getChainHistory());
 
         LOG.debug("Chaining to action {}", finalActionName);
diff --git a/core/src/main/java/com/opensymphony/xwork2/ActionContext.java b/core/src/main/java/com/opensymphony/xwork2/ActionContext.java
index 757dffd..ac6b2bf 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ActionContext.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ActionContext.java
@@ -22,8 +22,14 @@ import com.opensymphony.xwork2.conversion.impl.ConversionData;
 import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.util.ValueStack;
 import org.apache.struts2.StrutsException;
+import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.dispatcher.HttpParameters;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
 
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.PageContext;
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Locale;
@@ -99,7 +105,7 @@ public class ActionContext implements Serializable {
      * Constant for the container
      */
     public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
-    
+
     private Map<String, Object> context;
 
     /**
@@ -107,10 +113,65 @@ public class ActionContext implements Serializable {
      *
      * @param context a context map.
      */
-    public ActionContext(Map<String, Object> context) {
+    private ActionContext(Map<String, Object> context) {
         this.context = context;
     }
 
+    /**
+     * Creates a new ActionContext based on passed in Map
+     * and assign this instance to the current thread
+     *
+     * @param context a map with context values
+     * @return new ActionContext
+     */
+    public static ActionContext of(Map<String, Object> context) {
+        return new ActionContext(context);
+    }
+
+    public static ActionContext bound(ActionContext actionContext) {
+        ActionContext.setContext(actionContext);
+        return ActionContext.getContext();
+    }
+
+    public static ActionContext ofAndBound(Map<String, Object> context) {
+        return bound(of(context));
+    }
+
+    /**
+     * Creates a new ActionContext based on passed in ActionContext
+     * and assign this instance to the current thread
+     *
+     * @param actionContext ActionContext to be used for current thread
+     * @return new ActionContext
+     */
+    public static ActionContext ofAndBound(ActionContext actionContext) {
+        return bound(actionContext);
+    }
+
+    /**
+     * Wipes out current ActionContext, use wisely!
+     */
+    public static void clear() {
+        actionContext.remove();
+    }
+
+    /**
+     * Sets the action context for the current thread.
+     *
+     * @param context the action context.
+     */
+    private static void setContext(ActionContext context) {
+        actionContext.set(context);
+    }
+
+    /**
+     * Returns the ActionContext specific to the current thread.
+     *
+     * @return the ActionContext for the current thread, is never <tt>null</tt>.
+     */
+    public static ActionContext getContext() {
+        return actionContext.get();
+    }
 
     /**
      * Sets the action invocation (the execution state).
@@ -149,24 +210,6 @@ public class ActionContext implements Serializable {
     }
 
     /**
-     * Sets the action context for the current thread.
-     *
-     * @param context the action context.
-     */
-    public static void setContext(ActionContext context) {
-        actionContext.set(context);
-    }
-
-    /**
-     * Returns the ActionContext specific to the current thread.
-     *
-     * @return the ActionContext for the current thread, is never <tt>null</tt>.
-     */
-    public static ActionContext getContext() {
-        return actionContext.get();
-    }
-
-    /**
      * Gets the context map.
      *
      * @return the context map.
@@ -188,7 +231,7 @@ public class ActionContext implements Serializable {
      * Gets the map of conversion errors which occurred when executing the action.
      *
      * @return the map of conversion errors which occurred when executing the action or an empty map if
-     *         there were no errors.
+     * there were no errors.
      */
     public Map<String, ConversionData> getConversionErrors() {
         Map<String, ConversionData> errors = (Map) get(CONVERSION_ERRORS);
@@ -259,7 +302,7 @@ public class ActionContext implements Serializable {
      * parameters otherwise.
      *
      * @return a Map of HttpServletRequest parameters or a multipart map when in a servlet environment, or a
-     *         generic Map of parameters otherwise.
+     * generic Map of parameters otherwise.
      */
     public HttpParameters getParameters() {
         return (HttpParameters) get(PARAMETERS);
@@ -268,7 +311,7 @@ public class ActionContext implements Serializable {
     /**
      * Sets a map of action session values.
      *
-     * @param session  the session values.
+     * @param session the session values.
      */
     public void setSession(Map<String, Object> session) {
         put(SESSION, session);
@@ -300,25 +343,25 @@ public class ActionContext implements Serializable {
     public ValueStack getValueStack() {
         return (ValueStack) get(VALUE_STACK);
     }
-    
+
     /**
      * Gets the container for this request
-     * 
+     *
      * @param cont The container
      */
     public void setContainer(Container cont) {
         put(CONTAINER, cont);
     }
-    
+
     /**
      * Sets the container for this request
-     * 
+     *
      * @return The container
      */
     public Container getContainer() {
         return (Container) get(CONTAINER);
     }
-    
+
     public <T> T getInstance(Class<T> type) {
         Container cont = getContainer();
         if (cont != null) {
@@ -347,4 +390,44 @@ public class ActionContext implements Serializable {
     public void put(String key, Object value) {
         context.put(key, value);
     }
+
+    public ServletContext getServletContext() {
+        return (ServletContext) get(StrutsStatics.SERVLET_CONTEXT);
+    }
+
+    public HttpServletRequest getServletRequest() {
+        return (HttpServletRequest) get(StrutsStatics.HTTP_REQUEST);
+    }
+
+    public HttpServletResponse getServletResponse() {
+        return (HttpServletResponse) get(StrutsStatics.HTTP_RESPONSE);
+    }
+
+    public void setServletContext(ServletContext servletContext) {
+        put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+    }
+
+    public void setServletRequest(HttpServletRequest request) {
+        put(StrutsStatics.HTTP_REQUEST, request);
+    }
+
+    public void setServletResponse(HttpServletResponse response) {
+        put(StrutsStatics.HTTP_RESPONSE, response);
+    }
+
+    public PageContext getPageContext() {
+        return (PageContext) get(StrutsStatics.PAGE_CONTEXT);
+    }
+
+    public void setPageContext(PageContext pageContext) {
+        put(StrutsStatics.PAGE_CONTEXT, pageContext);
+    }
+
+    public ActionMapping getActionMapping() {
+        return (ActionMapping) get(StrutsStatics.ACTION_MAPPING);
+    }
+
+    public void setActionMapping(ActionMapping actionMapping) {
+        put(StrutsStatics.ACTION_MAPPING, actionMapping);
+    }
 }
diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
index caa8d0f..b38d8d0 100644
--- a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
+++ b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
@@ -395,7 +395,7 @@ public class DefaultActionInvocation implements ActionInvocation {
             contextMap.put("action", action);
         }
 
-        invocationContext = new ActionContext(contextMap);
+        invocationContext = ActionContext.of(contextMap);
         invocationContext.setName(proxy.getActionName());
 
         createInterceptors(proxy);
diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultActionProxy.java b/core/src/main/java/com/opensymphony/xwork2/DefaultActionProxy.java
index 6e0458e..85a7c19 100644
--- a/core/src/main/java/com/opensymphony/xwork2/DefaultActionProxy.java
+++ b/core/src/main/java/com/opensymphony/xwork2/DefaultActionProxy.java
@@ -145,7 +145,7 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
 
     public String execute() throws Exception {
         ActionContext nestedContext = ActionContext.getContext();
-        ActionContext.setContext(invocation.getInvocationContext());
+        ActionContext.bound(invocation.getInvocationContext());
 
         String retCode = null;
 
@@ -153,7 +153,7 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
             retCode = invocation.invoke();
         } finally {
             if (cleanupContext) {
-                ActionContext.setContext(nestedContext);
+                ActionContext.bound(nestedContext);
             }
         }
 
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
index 42cfabb..4d65b2e 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
@@ -213,7 +213,7 @@ public class DefaultConfiguration implements Configuration {
             rebuildRuntimeConfiguration();
         } finally {
             if (oldContext == null) {
-                ActionContext.setContext(null);
+                ActionContext.clear();
             }
         }
         return packageProviders;
@@ -223,8 +223,7 @@ public class DefaultConfiguration implements Configuration {
         ActionContext context = ActionContext.getContext();
         if (context == null) {
             ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
-            context = new ActionContext(vs.getContext());
-            ActionContext.setContext(context);
+            context = ActionContext.ofAndBound(vs.getContext());
         }
         return context;
     }
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java
index 5aec937..036407b 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java
@@ -117,6 +117,11 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
         return context;
     }
 
+    @Override
+    public ActionContext getActionContext() {
+        return ActionContext.of(context);
+    }
+
     /**
      * @see com.opensymphony.xwork2.util.ValueStack#setDefaultType(java.lang.Class)
      */
diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
index 72f8438..bf87805 100644
--- a/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
+++ b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
@@ -64,9 +64,8 @@ import org.springframework.web.context.WebApplicationContext;
  * @author Eric Hauser
  */
 public class ActionAutowiringInterceptor extends AbstractInterceptor implements ApplicationContextAware {
-    private static final Logger LOG = LogManager.getLogger(ActionAutowiringInterceptor.class);
 
-    public static final String APPLICATION_CONTEXT = "com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor.applicationContext";
+    private static final Logger LOG = LogManager.getLogger(ActionAutowiringInterceptor.class);
 
     private boolean initialized = false;
     private ApplicationContext context;
@@ -121,8 +120,6 @@ public class ActionAutowiringInterceptor extends AbstractInterceptor implements
         if (factory != null) {
             Object bean = invocation.getAction();
             factory.autoWireBean(bean);
-    
-            ActionContext.getContext().put(APPLICATION_CONTEXT, context);
         }
         return invocation.invoke();
     }
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/ValueStack.java b/core/src/main/java/com/opensymphony/xwork2/util/ValueStack.java
index c14b01b..4d02b23 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/ValueStack.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/ValueStack.java
@@ -18,6 +18,8 @@
  */
 package com.opensymphony.xwork2.util;
 
+import com.opensymphony.xwork2.ActionContext;
+
 import java.util.Map;
 
 /**
@@ -28,23 +30,25 @@ import java.util.Map;
  */
 public interface ValueStack {
 
-    public static final String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
+    String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
 
-    public static final String REPORT_ERRORS_ON_NO_PROP = "com.opensymphony.xwork2.util.ValueStack.ReportErrorsOnNoProp";
+    String REPORT_ERRORS_ON_NO_PROP = "com.opensymphony.xwork2.util.ValueStack.ReportErrorsOnNoProp";
 
     /**
      * Gets the context for this value stack. The context holds all the information in the value stack and it's surroundings.
      *
      * @return  the context.
      */
-    public abstract Map<String, Object> getContext();
+    Map<String, Object> getContext();
+
+    ActionContext getActionContext();
 
     /**
      * Sets the default type to convert to if no type is provided when getting a value.
      *
      * @param defaultType the new default type
      */
-    public abstract void setDefaultType(Class defaultType);
+    void setDefaultType(Class defaultType);
 
     /**
      * Set a override map containing <code> key -&gt; values </code> that takes precedent when doing find operations on the ValueStack.
@@ -54,21 +58,21 @@ public interface ValueStack {
      *
      * @param overrides  overrides map.
      */
-    public abstract void setExprOverrides(Map<Object, Object> overrides);
+    void setExprOverrides(Map<Object, Object> overrides);
 
     /**
      * Gets the override map if anyone exists.
      *
      * @return the override map, <tt>null</tt> if not set.
      */
-    public abstract Map<Object, Object> getExprOverrides();
+    Map<Object, Object> getExprOverrides();
 
     /**
      * Get the CompoundRoot which holds the objects pushed onto the stack
      *
      * @return the root
      */
-    public abstract CompoundRoot getRoot();
+    CompoundRoot getRoot();
 
     /**
      * Attempts to set a property on a bean in the stack with the given expression using the default search order.
@@ -76,7 +80,7 @@ public interface ValueStack {
      * @param expr  the expression defining the path to the property to be set.
      * @param value the value to be set into the named property
      */
-    public abstract void setValue(String expr, Object value);
+    void setValue(String expr, Object value);
 
     /**
      * Attempts to set a property on a bean in the stack with the given expression using the default search order.
@@ -94,10 +98,10 @@ public interface ValueStack {
      * @param throwExceptionOnFailure a flag to tell whether an exception should be thrown if there is no property with
      *                                the given name.
      */
-    public abstract void setValue(String expr, Object value, boolean throwExceptionOnFailure);
+    void setValue(String expr, Object value, boolean throwExceptionOnFailure);
 
-    public abstract String findString(String expr);
-    public abstract String findString(String expr, boolean throwExceptionOnFailure);
+    String findString(String expr);
+    String findString(String expr, boolean throwExceptionOnFailure);
 
     /**
      * Find a value by evaluating the given expression against the stack in the default search order.
@@ -105,9 +109,9 @@ public interface ValueStack {
      * @param expr the expression giving the path of properties to navigate to find the property value to return
      * @return the result of evaluating the expression
      */
-    public abstract Object findValue(String expr);
+    Object findValue(String expr);
 
-    public abstract Object findValue(String expr, boolean throwExceptionOnFailure);
+    Object findValue(String expr, boolean throwExceptionOnFailure);
 
     /**
      * Find a value by evaluating the given expression against the stack in the default search order.
@@ -116,8 +120,8 @@ public interface ValueStack {
      * @param asType the type to convert the return value to
      * @return the result of evaluating the expression
      */
-    public abstract Object findValue(String expr, Class asType);
-    public abstract Object findValue(String expr, Class asType,  boolean throwExceptionOnFailure);
+    Object findValue(String expr, Class asType);
+    Object findValue(String expr, Class asType, boolean throwExceptionOnFailure);
 
     /**
      * Get the object on the top of the stack <b>without</b> changing the stack.
@@ -125,7 +129,7 @@ public interface ValueStack {
      * @return the object on the top.
      * @see CompoundRoot#peek()
      */
-    public abstract Object peek();
+    Object peek();
 
     /**
      * Get the object on the top of the stack and <b>remove</b> it from the stack.
@@ -133,7 +137,7 @@ public interface ValueStack {
      * @return the object on the top of the stack
      * @see CompoundRoot#pop()
      */
-    public abstract Object pop();
+    Object pop();
 
     /**
      * Put this object onto the top of the stack
@@ -141,7 +145,7 @@ public interface ValueStack {
      * @param o the object to be pushed onto the stack
      * @see CompoundRoot#push(Object)
      */
-    public abstract void push(Object o);
+    void push(Object o);
 
     /**
      * Sets an object on the stack with the given key
@@ -150,13 +154,13 @@ public interface ValueStack {
      * @param key  the key
      * @param o    the object
      */
-    public abstract void set(String key, Object o);
+    void set(String key, Object o);
 
     /**
      * Get the number of objects in the stack
      *
      * @return the number of objects in the stack
      */
-    public abstract int size();
+    int size();
 
 }
\ No newline at end of file
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/XWorkTestCaseHelper.java b/core/src/main/java/com/opensymphony/xwork2/util/XWorkTestCaseHelper.java
index 24f70e4..08913c8 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/XWorkTestCaseHelper.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/XWorkTestCaseHelper.java
@@ -40,7 +40,7 @@ public class XWorkTestCaseHelper {
         // Reset the value stack
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
         stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
+        ActionContext.ofAndBound(stack.getContext());
     
         // clear out localization
         //container.getInstance(LocalizedTextUtil.class).reset();
@@ -80,17 +80,16 @@ public class XWorkTestCaseHelper {
         // Reset the value stack
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
         stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
+        ActionContext.ofAndBound(stack.getContext());
         
         return configurationManager;
     }
 
     public static void tearDown(ConfigurationManager configurationManager) throws Exception {
-    
         //  clear out configuration
         if (configurationManager != null) {
             configurationManager.destroyConfiguration();
         }
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/struts2/ServletActionContext.java b/core/src/main/java/org/apache/struts2/ServletActionContext.java
index 4af8d53..d3518cc 100644
--- a/core/src/main/java/org/apache/struts2/ServletActionContext.java
+++ b/core/src/main/java/org/apache/struts2/ServletActionContext.java
@@ -26,23 +26,18 @@ import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.jsp.PageContext;
-import java.util.Map;
 
 /**
  * Web-specific context information for actions. This class subclasses <tt>ActionContext</tt> which
  * provides access to things like the action name, value stack, etc. This class adds access to
  * web objects like servlet parameters, request attributes and things like the HTTP session.
  */
-public class ServletActionContext extends ActionContext implements StrutsStatics {
-
-    private static final long serialVersionUID = -666854718275106687L;
+public class ServletActionContext implements StrutsStatics {
 
     public static final String STRUTS_VALUESTACK_KEY = "struts.valueStack";
-    public static final String ACTION_MAPPING = "struts.actionMapping";
 
     @SuppressWarnings("unused")
-    private ServletActionContext(Map context) {
-        super(context);
+    private ServletActionContext() {
     }
 
     /**
@@ -54,13 +49,26 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
     public static ActionContext getActionContext(HttpServletRequest req) {
         ValueStack vs = getValueStack(req);
         if (vs != null) {
-            return new ActionContext(vs.getContext());
+            return ActionContext.ofAndBound(vs.getContext());
         } else {
             return null;
         }
     }
 
     /**
+     * Do not use this method, use {@link #getActionContext()}
+     * @return action context
+     * @deprecated Use {@link #getActionContext()} instead
+     */
+    @Deprecated
+    public static ActionContext getContext() {
+        return ActionContext.getContext();
+    }
+
+    public static ActionContext getActionContext() {
+        return ActionContext.getContext();
+    }
+    /**
      * Gets the current value stack for this request
      *
      * @param req The request
@@ -76,7 +84,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @return The action mapping
      */
     public static ActionMapping getActionMapping() {
-        return (ActionMapping) ActionContext.getContext().get(ACTION_MAPPING);
+        return ActionContext.getContext().getActionMapping();
     }
 
     /**
@@ -85,7 +93,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @return the HTTP page context.
      */
     public static PageContext getPageContext() {
-        return (PageContext) ActionContext.getContext().get(PAGE_CONTEXT);
+        return ActionContext.getContext().getPageContext();
     }
 
     /**
@@ -94,7 +102,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @param request the HTTP servlet request object.
      */
     public static void setRequest(HttpServletRequest request) {
-        ActionContext.getContext().put(HTTP_REQUEST, request);
+        ActionContext.getContext().setServletRequest(request);
     }
 
     /**
@@ -103,7 +111,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @return the HTTP servlet request object.
      */
     public static HttpServletRequest getRequest() {
-        return (HttpServletRequest) ActionContext.getContext().get(HTTP_REQUEST);
+        return ActionContext.getContext().getServletRequest();
     }
 
     /**
@@ -112,7 +120,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @param response the HTTP servlet response object.
      */
     public static void setResponse(HttpServletResponse response) {
-        ActionContext.getContext().put(HTTP_RESPONSE, response);
+        ActionContext.getContext().setServletResponse(response);
     }
 
     /**
@@ -121,7 +129,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @return the HTTP servlet response object.
      */
     public static HttpServletResponse getResponse() {
-        return (HttpServletResponse) ActionContext.getContext().get(HTTP_RESPONSE);
+        return ActionContext.getContext().getServletResponse();
     }
 
     /**
@@ -130,7 +138,7 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @return the servlet context.
      */
     public static ServletContext getServletContext() {
-        return (ServletContext) ActionContext.getContext().get(SERVLET_CONTEXT);
+        return ActionContext.getContext().getServletContext();
     }
 
     /**
@@ -139,6 +147,6 @@ public class ServletActionContext extends ActionContext implements StrutsStatics
      * @param servletContext The servlet context to use
      */
     public static void setServletContext(ServletContext servletContext) {
-        ActionContext.getContext().put(SERVLET_CONTEXT, servletContext);
+        ActionContext.getContext().setServletContext(servletContext);
     }
 }
diff --git a/core/src/main/java/org/apache/struts2/StrutsStatics.java b/core/src/main/java/org/apache/struts2/StrutsStatics.java
index c4e2c1c..e0adccc 100644
--- a/core/src/main/java/org/apache/struts2/StrutsStatics.java
+++ b/core/src/main/java/org/apache/struts2/StrutsStatics.java
@@ -18,22 +18,12 @@
  */
 package org.apache.struts2;
 
+import javax.servlet.ServletContext;
+
 /**
  * <p>
- * Constants used by Struts. The constants can be used to get or set objects
- * out of the action context or other collections.
- * </p>
- *
- * <p>
- * Example:
- *
- * <code>ActionContext.getContext().put(HTTP_REQUEST, request);</code>
- *
- * or
- *
- * <code>
- * ActionContext context = ActionContext.getContext();<br>
- * HttpServletRequest request = (HttpServletRequest)context.get(HTTP_REQUEST);</code>
+ * Constants used internally by Struts. Do not use these constants directly,
+ * instead use exposed helpers eg.: {@link org.apache.struts2.dispatcher.Dispatcher#getInstance(ServletContext)}
  * </p>
  */
 public interface StrutsStatics {
@@ -41,34 +31,38 @@ public interface StrutsStatics {
     /**
      * Constant for the HTTP request object.
      */
-    public static final String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";
+    String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";
 
     /**
      * Constant for the HTTP response object.
      */
-    public static final String HTTP_RESPONSE = "com.opensymphony.xwork2.dispatcher.HttpServletResponse";
+    String HTTP_RESPONSE = "com.opensymphony.xwork2.dispatcher.HttpServletResponse";
 
     /**
      * Constant for an HTTP {@link javax.servlet.RequestDispatcher request dispatcher}.
      */
-    public static final String SERVLET_DISPATCHER = "com.opensymphony.xwork2.dispatcher.ServletDispatcher";
+    String SERVLET_DISPATCHER = "com.opensymphony.xwork2.dispatcher.ServletDispatcher";
 
     /**
      * Constant for the {@link javax.servlet.ServletContext servlet context} object.
      */
-    public static final String SERVLET_CONTEXT = "com.opensymphony.xwork2.dispatcher.ServletContext";
+    String SERVLET_CONTEXT = "com.opensymphony.xwork2.dispatcher.ServletContext";
 
     /**
      * Constant for the JSP {@link javax.servlet.jsp.PageContext page context}.
      */
-    public static final String PAGE_CONTEXT = "com.opensymphony.xwork2.dispatcher.PageContext";
+    String PAGE_CONTEXT = "com.opensymphony.xwork2.dispatcher.PageContext";
 
-    /** Constant for the PortletContext object */
-    public static final String STRUTS_PORTLET_CONTEXT = "struts.portlet.context";
+    /**
+     * Constant for the PortletContext object
+     */
+    String STRUTS_PORTLET_CONTEXT = "struts.portlet.context";
 
     /**
      * Set as an attribute in the request to let other parts of the framework know that the invocation is happening inside an
      * action tag
      */
-    public static final String STRUTS_ACTION_TAG_INVOCATION= "struts.actiontag.invocation";
+    String STRUTS_ACTION_TAG_INVOCATION = "struts.actiontag.invocation";
+
+    String ACTION_MAPPING = "struts.actionMapping";
 }
diff --git a/core/src/main/java/org/apache/struts2/components/ActionComponent.java b/core/src/main/java/org/apache/struts2/components/ActionComponent.java
index 901556f..3862366 100644
--- a/core/src/main/java/org/apache/struts2/components/ActionComponent.java
+++ b/core/src/main/java/org/apache/struts2/components/ActionComponent.java
@@ -178,10 +178,10 @@ public class ActionComponent extends ContextBean {
     protected Map createExtraContext() {
         HttpParameters newParams = createParametersForContext();
 
-        ActionContext ctx = new ActionContext(stack.getContext());
-        PageContext pageContext = (PageContext) ctx.get(ServletActionContext.PAGE_CONTEXT);
-        Map session = ctx.getSession();
-        Map application = ctx.getApplication();
+        ActionContext ctx = stack.getActionContext();
+        PageContext pageContext = ctx.getPageContext();
+        Map<String, Object> session = ctx.getSession();
+        Map<String, Object> application = ctx.getApplication();
 
         Dispatcher du = Dispatcher.getInstance();
         Map<String, Object> extraContext = du.createContextMap(new RequestMap(req),
@@ -210,7 +210,7 @@ public class ActionComponent extends ContextBean {
         HttpParameters parentParams = null;
 
         if (!ignoreContextParams) {
-            parentParams = new ActionContext(getStack().getContext()).getParameters();
+            parentParams = getStack().getActionContext().getParameters();
         }
 
         HttpParameters.Builder builder = HttpParameters.create();
diff --git a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java
index b41b4d6..550fa35 100644
--- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java
+++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java
@@ -74,7 +74,7 @@ public class ServletUrlRenderer implements UrlRenderer {
         }
 
         String result;
-        ActionInvocation ai = (ActionInvocation) ActionContext.getContext().get(ActionContext.ACTION_INVOCATION);
+        ActionInvocation ai = ActionContext.getContext().getActionInvocation();
         if (urlComponent.getValue() == null && urlComponent.getAction() != null) {
             result = urlComponent.determineActionURL(urlComponent.getAction(), urlComponent.getNamespace(), urlComponent.getMethod(), urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp());
         } else if (urlComponent.getValue() == null && urlComponent.getAction() == null && ai != null) {
diff --git a/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java b/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
index a272697..7cec84a 100644
--- a/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
+++ b/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
@@ -68,10 +68,10 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
     public void renderTemplate(TemplateRenderingContext templateContext) throws Exception {
     	// get the various items required from the stack
         ValueStack stack = templateContext.getStack();
-        Map context = stack.getContext();
-        ServletContext servletContext = (ServletContext) context.get(ServletActionContext.SERVLET_CONTEXT);
-        HttpServletRequest req = (HttpServletRequest) context.get(ServletActionContext.HTTP_REQUEST);
-        HttpServletResponse res = (HttpServletResponse) context.get(ServletActionContext.HTTP_RESPONSE);
+        ActionContext context = stack.getActionContext();
+        ServletContext servletContext = context.getServletContext();
+        HttpServletRequest req = context.getServletRequest();
+        HttpServletResponse res = context.getServletResponse();
 
         // prepare freemarker
         Configuration config = freemarkerManager.getConfiguration(servletContext);
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
index 27d025b..44c7b91 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
@@ -18,8 +18,22 @@
  */
 package org.apache.struts2.dispatcher;
 
-import com.opensymphony.xwork2.*;
-import com.opensymphony.xwork2.config.*;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.FileManager;
+import com.opensymphony.xwork2.FileManagerFactory;
+import com.opensymphony.xwork2.LocaleProviderFactory;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.config.ConfigurationManager;
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.config.FileManagerFactoryProvider;
+import com.opensymphony.xwork2.config.FileManagerProvider;
+import com.opensymphony.xwork2.config.ServletContextAwareConfigurationProvider;
 import com.opensymphony.xwork2.config.entities.InterceptorMapping;
 import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
 import com.opensymphony.xwork2.config.entities.PackageConfig;
@@ -34,8 +48,8 @@ import com.opensymphony.xwork2.util.ValueStackFactory;
 import com.opensymphony.xwork2.util.location.LocatableProperties;
 import com.opensymphony.xwork2.util.location.Location;
 import com.opensymphony.xwork2.util.location.LocationUtils;
-import org.apache.commons.lang3.LocaleUtils;
 import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.LocaleUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -44,9 +58,9 @@ import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsConstants;
 import org.apache.struts2.StrutsException;
 import org.apache.struts2.StrutsStatics;
-import org.apache.struts2.config.StrutsBeanSelectionProvider;
 import org.apache.struts2.config.DefaultPropertiesProvider;
 import org.apache.struts2.config.PropertiesConfigurationProvider;
+import org.apache.struts2.config.StrutsBeanSelectionProvider;
 import org.apache.struts2.config.StrutsJavaConfiguration;
 import org.apache.struts2.config.StrutsJavaConfigurationProvider;
 import org.apache.struts2.config.StrutsXmlConfigurationProvider;
@@ -63,7 +77,13 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
-import java.util.*;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.regex.Pattern;
 
@@ -220,15 +240,20 @@ public class Dispatcher {
      * Create the Dispatcher instance for a given ServletContext and set of initialization parameters.
      *
      * @param servletContext Our servlet context
-     * @param initParams The set of initialization parameters
+     * @param initParams     The set of initialization parameters
      */
     public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
         this.servletContext = servletContext;
         this.initParams = initParams;
     }
 
+    public static Dispatcher getInstance(ServletContext servletContext) {
+        return (Dispatcher) servletContext.getAttribute(StrutsStatics.SERVLET_DISPATCHER);
+    }
+
     /**
      * Modify state of StrutsConstants.STRUTS_DEVMODE setting.
+     *
      * @param mode New setting
      */
     @Inject(StrutsConstants.STRUTS_DEVMODE)
@@ -242,24 +267,27 @@ public class Dispatcher {
 
     /**
      * Modify state of StrutsConstants.DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP setting.
+     *
      * @param disableRequestAttributeValueStackLookup New setting
      */
-    @Inject(value=StrutsConstants.STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP, required=false)
+    @Inject(value = StrutsConstants.STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP, required = false)
     public void setDisableRequestAttributeValueStackLookup(String disableRequestAttributeValueStackLookup) {
         this.disableRequestAttributeValueStackLookup = BooleanUtils.toBoolean(disableRequestAttributeValueStackLookup);
     }
 
     /**
      * Modify state of StrutsConstants.STRUTS_LOCALE setting.
+     *
      * @param val New setting
      */
-    @Inject(value=StrutsConstants.STRUTS_LOCALE, required=false)
+    @Inject(value = StrutsConstants.STRUTS_LOCALE, required = false)
     public void setDefaultLocale(String val) {
         defaultLocale = val;
     }
 
     /**
      * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
+     *
      * @param val New setting
      */
     @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
@@ -269,6 +297,7 @@ public class Dispatcher {
 
     /**
      * Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
+     *
      * @param val New setting
      */
     @Inject(StrutsConstants.STRUTS_MULTIPART_SAVEDIR)
@@ -314,17 +343,15 @@ public class Dispatcher {
      * Releases all instances bound to this dispatcher instance.
      */
     public void cleanup() {
-
-    	// clean up ObjectFactory
+        // clean up ObjectFactory
         ObjectFactory objectFactory = getContainer().getInstance(ObjectFactory.class);
         if (objectFactory == null) {
-        	LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
+            LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
         }
         if (objectFactory instanceof ObjectFactoryDestroyable) {
             try {
-                ((ObjectFactoryDestroyable)objectFactory).destroy();
-            }
-            catch(Exception e) {
+                ((ObjectFactoryDestroyable) objectFactory).destroy();
+            } catch (Exception e) {
                 // catch any exception that may occurred during destroy() and log it
                 LOG.error("Exception occurred while destroying ObjectFactory [{}]", objectFactory.toString(), e);
             }
@@ -332,6 +359,7 @@ public class Dispatcher {
 
         // clean up Dispatcher itself for this thread
         instance.set(null);
+        servletContext.setAttribute(StrutsStatics.SERVLET_DISPATCHER, null);
 
         // clean up DispatcherListeners
         if (!dispatcherListeners.isEmpty()) {
@@ -347,24 +375,24 @@ public class Dispatcher {
             for (Object config : packageConfig.getAllInterceptorConfigs().values()) {
                 if (config instanceof InterceptorStackConfig) {
                     for (InterceptorMapping interceptorMapping : ((InterceptorStackConfig) config).getInterceptors()) {
-                	    interceptors.add(interceptorMapping.getInterceptor());
+                        interceptors.add(interceptorMapping.getInterceptor());
                     }
                 }
             }
         }
         for (Interceptor interceptor : interceptors) {
-        	interceptor.destroy();
+            interceptor.destroy();
         }
 
         // Clear container holder when application is unloaded / server shutdown
         ContainerHolder.clear();
 
         //cleanup action context
-        ActionContext.setContext(null);
+        ActionContext.clear();
 
         // clean up configuration
-    	configurationManager.destroyConfiguration();
-    	configurationManager = null;
+        configurationManager.destroyConfiguration();
+        configurationManager = null;
     }
 
     private void init_FileManager() throws ClassNotFoundException {
@@ -388,7 +416,7 @@ public class Dispatcher {
     private void init_DefaultProperties() {
         configurationManager.addContainerProvider(new DefaultPropertiesProvider());
     }
-    
+
     private void init_LegacyStrutsProperties() {
         configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
     }
@@ -443,17 +471,17 @@ public class Dispatcher {
             for (String cname : classes) {
                 try {
                     Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
-                    ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
+                    ConfigurationProvider prov = (ConfigurationProvider) cls.newInstance();
                     if (prov instanceof ServletContextAwareConfigurationProvider) {
-                        ((ServletContextAwareConfigurationProvider)prov).initWithContext(servletContext);
+                        ((ServletContextAwareConfigurationProvider) prov).initWithContext(servletContext);
                     }
                     configurationManager.addContainerProvider(prov);
                 } catch (InstantiationException e) {
-                    throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
+                    throw new ConfigurationException("Unable to instantiate provider: " + cname, e);
                 } catch (IllegalAccessException e) {
-                    throw new ConfigurationException("Unable to access provider: "+cname, e);
+                    throw new ConfigurationException("Unable to access provider: " + cname, e);
                 } catch (ClassNotFoundException e) {
-                    throw new ConfigurationException("Unable to locate provider class: "+cname, e);
+                    throw new ConfigurationException("Unable to locate provider class: " + cname, e);
                 }
             }
         }
@@ -495,7 +523,7 @@ public class Dispatcher {
             paramsWorkaroundEnabled = true;
         } else {
             paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,
-                    StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
+                StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
         }
     }
 
@@ -504,10 +532,9 @@ public class Dispatcher {
      * and update optional settings, including whether to reload configurations and resource files.
      */
     public void init() {
-
-    	if (configurationManager == null) {
-    		configurationManager = createConfigurationManager(Container.DEFAULT_NAME);
-    	}
+        if (configurationManager == null) {
+            configurationManager = createConfigurationManager(Container.DEFAULT_NAME);
+        }
 
         try {
             init_FileManager();
@@ -516,8 +543,8 @@ public class Dispatcher {
             init_JavaConfigurations();
             init_LegacyStrutsProperties(); // [3]
             init_CustomConfigurationProviders(); // [5]
-            init_FilterInitParameters() ; // [6]
-            init_AliasStandardObjects() ; // [7]
+            init_FilterInitParameters(); // [6]
+            init_AliasStandardObjects(); // [7]
 
             Container container = init_PreloadConfiguration();
             container.inject(this);
@@ -530,6 +557,9 @@ public class Dispatcher {
             }
             errorHandler.init(servletContext);
 
+            if (servletContext.getAttribute(StrutsStatics.SERVLET_DISPATCHER) == null) {
+                servletContext.setAttribute(StrutsStatics.SERVLET_DISPATCHER, this);
+            }
         } catch (Exception ex) {
             LOG.error("Dispatcher initialization failed", ex);
             throw new StrutsException(ex);
@@ -559,11 +589,10 @@ public class Dispatcher {
      * @param mapping  the action mapping object
      * @throws ServletException when an unknown error occurs (not a 404, but typically something that
      *                          would end up as a 5xx by the servlet container)
-     *
      * @since 2.3.17
      */
     public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
-            throws ServletException {
+        throws ServletException {
 
         Map<String, Object> extraContext = createContextMap(request, response, mapping);
 
@@ -591,7 +620,7 @@ public class Dispatcher {
             ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
             if (invocation == null || invocation.isExecuted()) {
                 proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method,
-                        extraContext, true, false);
+                    extraContext, true, false);
             } else {
                 proxy = invocation.getProxy();
             }
@@ -629,7 +658,7 @@ public class Dispatcher {
      * Performs logging of missing action/result configuration exception
      *
      * @param request current {@link HttpServletRequest}
-     * @param e {@link ConfigurationException} that occurred
+     * @param e       {@link ConfigurationException} that occurred
      */
     protected void logConfigurationException(HttpServletRequest request, ConfigurationException e) {
         // WW-2874 Only log error if in devMode
@@ -647,15 +676,14 @@ public class Dispatcher {
     /**
      * Create a context map containing all the wrapped request objects
      *
-     * @param request The servlet request
+     * @param request  The servlet request
      * @param response The servlet response
-     * @param mapping The action mapping
+     * @param mapping  The action mapping
      * @return A map of context objects
-     *
      * @since 2.3.17
      */
-    public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
-            ActionMapping mapping) {
+    public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
+                                                ActionMapping mapping) {
 
         // request map wrapping the http request objects
         Map requestMap = new RequestMap(request);
@@ -669,7 +697,7 @@ public class Dispatcher {
         // application map wrapping the ServletContext
         Map application = new ApplicationMap(servletContext);
 
-        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);
+        Map<String, Object> extraContext = createContextMap(requestMap, params, session, application, request, response);
 
         if (mapping != null) {
             extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
@@ -688,15 +716,14 @@ public class Dispatcher {
      * @param request        the HttpServletRequest object.
      * @param response       the HttpServletResponse object.
      * @return a HashMap representing the <tt>Action</tt> context.
-     *
      * @since 2.3.17
      */
-    public HashMap<String,Object> createContextMap(Map requestMap,
-                                    HttpParameters parameters,
-                                    Map sessionMap,
-                                    Map applicationMap,
-                                    HttpServletRequest request,
-                                    HttpServletResponse response) {
+    public HashMap<String, Object> createContextMap(Map requestMap,
+                                                    HttpParameters parameters,
+                                                    Map sessionMap,
+                                                    Map applicationMap,
+                                                    HttpServletRequest request,
+                                                    HttpServletResponse response) {
         HashMap<String, Object> extraContext = new HashMap<>();
         extraContext.put(ActionContext.PARAMETERS, parameters);
         extraContext.put(ActionContext.SESSION, sessionMap);
@@ -727,7 +754,7 @@ public class Dispatcher {
                 locale = LocaleUtils.toLocale(defaultLocale);
             } catch (IllegalArgumentException e) {
                 LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
-                                defaultLocale, request.getLocale()), e);
+                    defaultLocale, request.getLocale()), e);
                 locale = request.getLocale();
             }
         } else {
@@ -746,7 +773,7 @@ public class Dispatcher {
 
         if (saveDir.equals("")) {
             File tempdir = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
-        	LOG.info("Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");
+            LOG.info("Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");
 
             if (tempdir != null) {
                 saveDir = tempdir.toString();
@@ -780,7 +807,7 @@ public class Dispatcher {
     /**
      * Prepare a request, including setting the encoding and locale.
      *
-     * @param request The request
+     * @param request  The request
      * @param response The response
      */
     public void prepare(HttpServletRequest request, HttpServletResponse response) {
@@ -834,9 +861,8 @@ public class Dispatcher {
      *
      * @param request the HttpServletRequest object.
      * @return a wrapped request or original request.
-     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
      * @throws java.io.IOException on any error.
-     *
+     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
      * @since 2.3.17
      */
     public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
@@ -850,11 +876,11 @@ public class Dispatcher {
             LocaleProviderFactory localeProviderFactory = getContainer().getInstance(LocaleProviderFactory.class);
 
             request = new MultiPartRequestWrapper(
-                    multiPartRequest,
-                    request,
-                    getSaveDir(),
-                    localeProviderFactory.createLocaleProvider(),
-                    disableRequestAttributeValueStackLookup
+                multiPartRequest,
+                request,
+                getSaveDir(),
+                localeProviderFactory.createLocaleProvider(),
+                disableRequestAttributeValueStackLookup
             );
         } else {
             request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
@@ -865,10 +891,9 @@ public class Dispatcher {
 
     /**
      * Checks if support to parse multipart requests is enabled
-     * 
+     *
      * @param request current servlet request
      * @return false if disabled
-     *
      * @since 2.5.11
      */
     protected boolean isMultipartSupportEnabled(HttpServletRequest request) {
@@ -880,7 +905,6 @@ public class Dispatcher {
      *
      * @param request current servlet request
      * @return true if it is a multipart request
-     *
      * @since 2.5.11
      */
     protected boolean isMultipartRequest(HttpServletRequest request) {
@@ -888,8 +912,8 @@ public class Dispatcher {
         String contentType = request.getContentType();
 
         return REQUEST_POST_METHOD.equalsIgnoreCase(httpMethod) &&
-                contentType != null &&
-                multipartValidationPattern.matcher(contentType.toLowerCase(Locale.ENGLISH)).matches();
+            contentType != null &&
+            multipartValidationPattern.matcher(contentType.toLowerCase(Locale.ENGLISH)).matches();
     }
 
     /**
@@ -907,7 +931,7 @@ public class Dispatcher {
                 mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
             }
         }
-        if (mpr == null ) {
+        if (mpr == null) {
             mpr = getContainer().getInstance(MultiPartRequest.class);
         }
         return mpr;
@@ -935,7 +959,6 @@ public class Dispatcher {
      * @param response the HttpServletResponse object.
      * @param code     the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes).
      * @param e        the Exception that is reported.
-     *
      * @since 2.3.17
      */
     public void sendError(HttpServletRequest request, HttpServletResponse response, int code, Exception e) {
@@ -976,6 +999,7 @@ public class Dispatcher {
 
     /**
      * Expose the dependency injection container.
+     *
      * @return Our dependency injection container
      */
     public Container getContainer() {
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
index 55f6481..7c6b762 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
@@ -19,7 +19,6 @@
 package org.apache.struts2.dispatcher;
 
 import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.util.ClassLoaderUtil;
 import org.apache.struts2.StrutsConstants;
 
 import java.util.*;
@@ -81,8 +80,8 @@ public class InitOperations {
      */
     protected Dispatcher createDispatcher(HostConfig filterConfig) {
         Map<String, String> params = new HashMap<>();
-        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
-            String name = (String) e.next();
+        for ( Iterator<String> parameterNames = filterConfig.getInitParameterNames(); parameterNames.hasNext(); ) {
+            String name = parameterNames.next();
             String value = filterConfig.getInitParameter(name);
             params.put(name, value);
         }
@@ -90,7 +89,7 @@ public class InitOperations {
     }
 
     public void cleanup() {
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     /**
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java
index c216cdf..3e4f268 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java
@@ -68,7 +68,7 @@ public class PrepareOperations {
      */
     public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
         ActionContext ctx;
-        Integer counter = 1;
+        int counter = 1;
         Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
         if (oldCounter != null) {
             counter = oldCounter + 1;
@@ -77,17 +77,16 @@ public class PrepareOperations {
         ActionContext oldContext = ActionContext.getContext();
         if (oldContext != null) {
             // detected existing context, so we are probably in a forward
-            ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));
+            ctx = ActionContext.ofAndBound(new HashMap<>(oldContext.getContextMap()));
         } else {
             ctx = ServletActionContext.getActionContext(request);   //checks if we are probably in an async
             if (ctx == null) {
                 ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
                 stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
-                ctx = new ActionContext(stack.getContext());
+                ctx = ActionContext.ofAndBound(stack.getContext());
             }
         }
         request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
-        ActionContext.setContext(ctx);
         return ctx;
     }
 
@@ -110,7 +109,7 @@ public class PrepareOperations {
         try {
             dispatcher.cleanUpRequest(request);
         } finally {
-            ActionContext.setContext(null);
+            ActionContext.clear();
             Dispatcher.setInstance(null);
             devModeOverride.remove();
         }
@@ -208,7 +207,7 @@ public class PrepareOperations {
             try {
                 dispatcher.cleanup();
             } finally {
-                ActionContext.setContext(null);
+                ActionContext.clear();
             }
         }
     }
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/listener/StrutsListener.java b/core/src/main/java/org/apache/struts2/dispatcher/listener/StrutsListener.java
index bf83984..5bdeee1 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/listener/StrutsListener.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/listener/StrutsListener.java
@@ -44,7 +44,6 @@ public class StrutsListener implements ServletContextListener {
             init.initStaticContentLoader(config, dispatcher);
 
             prepare = new PrepareOperations(dispatcher);
-            sce.getServletContext().setAttribute(StrutsStatics.SERVLET_DISPATCHER, dispatcher);
         } finally {
             if (dispatcher != null) {
                 dispatcher.cleanUpAfterInit();
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionMapping.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionMapping.java
index 4e2a0f4..1d9bde5 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionMapping.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionMapping.java
@@ -95,7 +95,7 @@ public class ActionMapping {
      * @return The method
      */
     public String getMethod() {
-        if (null != method && "".equals(method)) {
+        if ("".equals(method)) {
             return null;
         } else {
             return method;
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
index 06c31d4..a9679a3 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
@@ -564,7 +564,7 @@ public class DefaultActionMapper implements ActionMapper {
             // Look for the current extension, if available
             ActionContext context = ActionContext.getContext();
             if (context != null) {
-                ActionMapping orig = (ActionMapping) context.get(ServletActionContext.ACTION_MAPPING);
+                ActionMapping orig = context.getActionMapping();
                 if (orig != null) {
                     extension = orig.getExtension();
                 }
diff --git a/core/src/main/java/org/apache/struts2/factory/StrutsActionProxy.java b/core/src/main/java/org/apache/struts2/factory/StrutsActionProxy.java
index e0732bd..56310d9 100644
--- a/core/src/main/java/org/apache/struts2/factory/StrutsActionProxy.java
+++ b/core/src/main/java/org/apache/struts2/factory/StrutsActionProxy.java
@@ -36,7 +36,7 @@ public class StrutsActionProxy extends DefaultActionProxy {
 
     public String execute() throws Exception {
         ActionContext previous = ActionContext.getContext();
-        ActionContext.setContext(invocation.getInvocationContext());
+        ActionContext.bound(invocation.getInvocationContext());
         try {
 // This is for the new API:
 //            return RequestContextImpl.callInContext(invocation, new Callable<String>() {
@@ -48,7 +48,7 @@ public class StrutsActionProxy extends DefaultActionProxy {
             return invocation.invoke();
         } finally {
             if (cleanupContext)
-                ActionContext.setContext(previous);
+                ActionContext.bound(previous);
         }
     }
 
diff --git a/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java
index 2b04795..79cd0cc 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java
@@ -81,7 +81,7 @@ public class ActionMappingParametersInterceptor extends ParametersInterceptor {
      */
     @Override
     protected HttpParameters retrieveParameters(ActionContext ac) {
-        ActionMapping mapping = (ActionMapping) ac.get(ServletActionContext.ACTION_MAPPING);
+        ActionMapping mapping = ac.getActionMapping();
         if (mapping != null) {
             return HttpParameters.create(mapping.getParams()).buildNoNestedWrapping();
         } else {
diff --git a/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java b/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java
index cc30f8a..f9be7c1 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java
@@ -76,7 +76,7 @@ public class BackgroundProcess implements Serializable {
      * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
      */
     protected void beforeInvocation() throws Exception {
-        ActionContext.setContext(invocation.getInvocationContext());
+        ActionContext.bound(invocation.getInvocationContext());
     }
 
     /**
@@ -87,7 +87,7 @@ public class BackgroundProcess implements Serializable {
      * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
      */
     protected void afterInvocation() throws Exception {
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     /**
diff --git a/core/src/main/java/org/apache/struts2/interceptor/CookieProviderInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CookieProviderInterceptor.java
index 4e415e2..e04c3fa 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/CookieProviderInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/CookieProviderInterceptor.java
@@ -96,7 +96,7 @@ public class CookieProviderInterceptor extends AbstractInterceptor implements Pr
             LOG.trace("beforeResult start");
             ActionContext ac = invocation.getInvocationContext();
             if (invocation.getAction() instanceof CookieProvider) {
-                HttpServletResponse response = (HttpServletResponse) ac.get(StrutsStatics.HTTP_RESPONSE);
+                HttpServletResponse response = ac.getServletResponse();
                 addCookiesToResponse((CookieProvider) invocation.getAction(), response);
             }
             LOG.trace("beforeResult end");
diff --git a/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java
index 32aea1e..9091220 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java
@@ -18,6 +18,7 @@
  */
 package org.apache.struts2.interceptor;
 
+import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
 import org.apache.logging.log4j.LogManager;
@@ -25,6 +26,7 @@ import org.apache.logging.log4j.Logger;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.SessionMap;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 /**
@@ -86,11 +88,12 @@ public class CreateSessionInterceptor extends AbstractInterceptor {
      * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
      */
     public String intercept(ActionInvocation invocation) throws Exception {
-        HttpSession httpSession = ServletActionContext.getRequest().getSession(false);
+        HttpServletRequest servletRequest = invocation.getInvocationContext().getServletRequest();
+        HttpSession httpSession = servletRequest.getSession(false);
         if (httpSession == null) {
             LOG.debug("Creating new HttpSession and new SessionMap in ServletActionContext");
-            ServletActionContext.getRequest().getSession(true);
-            ServletActionContext.getContext().setSession(new SessionMap<String, Object>(ServletActionContext.getRequest()));
+            servletRequest.getSession(true);
+            invocation.getInvocationContext().setSession(new SessionMap<>(servletRequest));
         }
         return invocation.invoke();
     }
diff --git a/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
index b6a0fa9..3105b7d 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
@@ -26,7 +26,6 @@ import com.opensymphony.xwork2.interceptor.ValidationAware;
 import com.opensymphony.xwork2.util.TextParseUtil;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.LocalizedMessage;
 import org.apache.struts2.dispatcher.Parameter;
 import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
@@ -232,7 +231,7 @@ public class FileUploadInterceptor extends AbstractInterceptor {
     public String intercept(ActionInvocation invocation) throws Exception {
         ActionContext ac = invocation.getInvocationContext();
 
-        HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);
+        HttpServletRequest request = ac.getServletRequest();
 
         if (!(request instanceof MultiPartRequestWrapper)) {
             if (LOG.isDebugEnabled()) {
diff --git a/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java
index 5d72945..1596be5 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java
@@ -233,7 +233,7 @@ public class MessageStoreInterceptor extends AbstractInterceptor {
             Object action = invocation.getAction();
             if (action instanceof ValidationAware) {
                 // retrieve error / message from session
-                Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
+                Map<String, Object> session = invocation.getInvocationContext().getSession();
 
                 if (session == null) {
                     LOG.debug("Session is not open, no errors / messages could be retrieve for action [{}]", action);
diff --git a/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
index 9707896..0ef0f37 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
@@ -125,22 +125,22 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
         final ActionContext context = invocation.getInvocationContext();
 
         if (action instanceof ServletRequestAware) {
-            HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
+            HttpServletRequest request = context.getServletRequest();
             ((ServletRequestAware) action).setServletRequest(request);
         }
 
         if (action instanceof org.apache.struts2.action.ServletRequestAware) {
-            HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
+            HttpServletRequest request = context.getServletRequest();
             ((org.apache.struts2.action.ServletRequestAware) action).withServletRequest(request);
         }
 
         if (action instanceof ServletResponseAware) {
-            HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
+            HttpServletResponse response = context.getServletResponse();
             ((ServletResponseAware) action).setServletResponse(response);
         }
 
         if (action instanceof org.apache.struts2.action.ServletResponseAware) {
-            HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
+            HttpServletResponse response = context.getServletResponse();
             ((org.apache.struts2.action.ServletResponseAware) action).withServletResponse(response);
         }
 
@@ -177,7 +177,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
         }
 
         if (action instanceof PrincipalAware) {
-            HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
+            HttpServletRequest request = context.getServletRequest();
             if(request != null) {
                 // We are in servtlet environment, so principal information resides in HttpServletRequest
                 ((PrincipalAware) action).setPrincipalProxy(new ServletPrincipalProxy(request));
@@ -185,7 +185,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
         }
 
         if (action instanceof org.apache.struts2.action.PrincipalAware) {
-            HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
+            HttpServletRequest request = context.getServletRequest();
             if(request != null) {
                 // We are in servlet environment, so principal information resides in HttpServletRequest
                 ((org.apache.struts2.action.PrincipalAware) action).withPrincipalProxy(new ServletPrincipalProxy(request));
@@ -193,12 +193,12 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
         }
 
         if (action instanceof ServletContextAware) {
-            ServletContext servletContext = (ServletContext) context.get(SERVLET_CONTEXT);
+            ServletContext servletContext = context.getServletContext();
             ((ServletContextAware) action).setServletContext(servletContext);
         }
 
         if (action instanceof org.apache.struts2.action.ServletContextAware) {
-            ServletContext servletContext = (ServletContext) context.get(SERVLET_CONTEXT);
+            ServletContext servletContext = context.getServletContext();
             ((org.apache.struts2.action.ServletContextAware) action).withServletContext(servletContext);
         }
 
diff --git a/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java
index 863e804..05e491e 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java
@@ -130,8 +130,8 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
     protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
         ActionContext ac = invocation.getInvocationContext();
 
-        HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);
-        HttpServletResponse response = (HttpServletResponse) ac.get(ServletActionContext.HTTP_RESPONSE);
+        HttpServletRequest request = ac.getServletRequest();
+        HttpServletResponse response = ac.getServletResponse();
         String tokenName = TokenHelper.getTokenName();
         String token = TokenHelper.getToken(tokenName);
 
@@ -169,7 +169,7 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
 
     /**
      * Handles processing of valid tokens.  Stores the current invocation for
-     * later use by {@link handleInvalidToken}.
+     * later use by {@see #handleValidToken(ActionInvocation)}.
      * See {@link org.apache.struts2.util.InvocationSessionStore#storeInvocation(String key, String token, ActionInvocation invocation)} for details.
      * 
      * @param invocation
diff --git a/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java
index 10fdcf4..1cff710 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java
@@ -192,7 +192,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
                 ValueStack stack = (ValueStack) ctx.getSession().get(SESSION_KEY);
                 if (stack == null) {
                     //allows it to be embedded on another page
-                    stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
+                    stack = ctx.getValueStack();
                     ctx.getSession().put(SESSION_KEY, stack);
                 }
                 String cmd = getParameter(EXPRESSION_PARAM);
@@ -218,7 +218,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
                                 rootObjectExpression = "action";
                             }
                             String decorate = getParameter(DECORATE_PARAM);
-                            ValueStack stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
+                            ValueStack stack = ctx.getValueStack();
                             Object rootObject = stack.findValue(rootObjectExpression);
 
                             try (StringWriter writer = new StringWriter()) {
@@ -258,7 +258,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
             } finally {
                 if (devMode && consoleEnabled) {
                     final ActionContext ctx = ActionContext.getContext();
-                    ctx.getSession().put(SESSION_KEY, ctx.get(ActionContext.VALUE_STACK));
+                    ctx.getSession().put(SESSION_KEY, ctx.getValueStack());
                 }
             }
         } else {
diff --git a/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java b/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java
index 238df3c..5c8054c 100644
--- a/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java
+++ b/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java
@@ -20,7 +20,6 @@ package org.apache.struts2.util;
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
-import org.apache.struts2.ServletActionContext;
 
 import java.io.Serializable;
 import java.util.HashMap;
@@ -63,12 +62,12 @@ public class InvocationSessionStore {
             // would already be closed at this point (causing failures if used for output).
             final ActionContext savedActionContext = savedInvocation.getInvocationContext();
             final ActionContext previousActionContext = ActionContext.getContext();
-            ActionContext.setContext(savedActionContext);
+            ActionContext.bound(savedActionContext);
             savedActionContext.setValueStack(savedInvocation.getStack());
             if (previousActionContext != null) {
-                savedActionContext.put(ServletActionContext.PAGE_CONTEXT, previousActionContext.get(ServletActionContext.PAGE_CONTEXT));
+                savedActionContext.setPageContext(previousActionContext.getPageContext());
             } else {
-                savedActionContext.put(ServletActionContext.PAGE_CONTEXT, null);
+                savedActionContext.setPageContext(null);
             }
         }
 
diff --git a/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java b/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
index 39cf045..b46ffbe 100644
--- a/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
+++ b/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
@@ -48,14 +48,14 @@ public class StrutsTestCaseHelper {
         Container container = du.getContainer();
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
         stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
+        ActionContext.ofAndBound(stack.getContext());
         
         return du;
     }
 
     public static void tearDown() throws Exception {
         Dispatcher.setInstance(null);
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     private static class DispatcherWrapper extends Dispatcher {
diff --git a/core/src/main/java/org/apache/struts2/util/URLBean.java b/core/src/main/java/org/apache/struts2/util/URLBean.java
index c5cf6fb..5e09718 100644
--- a/core/src/main/java/org/apache/struts2/util/URLBean.java
+++ b/core/src/main/java/org/apache/struts2/util/URLBean.java
@@ -18,6 +18,7 @@
  */
 package org.apache.struts2.util;
 
+import com.opensymphony.xwork2.ActionContext;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.views.util.DefaultUrlHelper;
 import org.apache.struts2.views.util.UrlHelper;
@@ -48,7 +49,7 @@ public class URLBean {
 
     public void setRequest(HttpServletRequest request) {
         this.request = request;
-        urlHelper = ServletActionContext.getContext().getInstance(DefaultUrlHelper.class);
+        urlHelper = ActionContext.getContext().getInstance(DefaultUrlHelper.class);
     }
 
     public void setResponse(HttpServletResponse response) {
diff --git a/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java b/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java
index 5bdb2e9..5f57297 100644
--- a/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java
+++ b/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java
@@ -275,7 +275,7 @@ public class FreemarkerResult extends StrutsResultSupport {
         ServletContext servletContext = ServletActionContext.getServletContext();
         HttpServletRequest request = ServletActionContext.getRequest();
         HttpServletResponse response = ServletActionContext.getResponse();
-        ValueStack stack = ServletActionContext.getContext().getValueStack();
+        ValueStack stack = ActionContext.getContext().getValueStack();
 
         Object action = null;
         if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException
diff --git a/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java b/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java
index 1484ccf..a6fa1ea 100644
--- a/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java
+++ b/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java
@@ -43,7 +43,7 @@ public class TagUtils {
 
     public static ValueStack getStack(PageContext pageContext) {
         HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
-        ValueStack stack = (ValueStack) req.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
+        ValueStack stack = ServletActionContext.getValueStack(req);
 
         if (stack == null) {
 
@@ -60,7 +60,7 @@ public class TagUtils {
 
             Map<String, Object> extraContext = du.createContextMap(new RequestMap(req),
                     params,
-                    new SessionMap(req),
+                    new SessionMap<>(req),
                     new ApplicationMap(pageContext.getServletContext()),
                     req,
                     res);
@@ -69,9 +69,10 @@ public class TagUtils {
             req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
 
             // also tie this stack/context to the ThreadLocal
-            ActionContext.setContext(new ActionContext(stack.getContext()));
+            ActionContext.ofAndBound(stack.getContext());
         } else {
             // let's make sure that the current page context is in the action context
+            // TODO: refactor this to stop using put()
             Map<String, Object> context = stack.getContext();
             context.put(ServletActionContext.PAGE_CONTEXT, pageContext);
 
@@ -83,7 +84,7 @@ public class TagUtils {
     }
 
     public static String buildNamespace(ActionMapper mapper, ValueStack stack, HttpServletRequest request) {
-        ActionContext context = new ActionContext(stack.getContext());
+        ActionContext context = ActionContext.of(stack.getContext());
         ActionInvocation invocation = context.getActionInvocation();
 
         if (invocation == null) {
diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml
index 0988d87..ee55f9a 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -68,7 +68,6 @@
     <constant name="struts.excludedPackageNames"
               value="
                 ognl.,
-                java.io.,
                 java.net.,
                 java.nio.,
                 javax.,
diff --git a/core/src/test/java/com/opensymphony/xwork2/ActionContextTest.java b/core/src/test/java/com/opensymphony/xwork2/ActionContextTest.java
index 05fcc35..503aee7 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ActionContextTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ActionContextTest.java
@@ -57,8 +57,7 @@ public class ActionContextTest extends XWorkTestCase {
         extraContext.put(ActionContext.SESSION, session);
         extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
         extraContext.put(ActionContext.ACTION_NAME, ACTION_NAME);
-        context = new ActionContext(extraContext);
-        ActionContext.setContext(context);
+        context = ActionContext.ofAndBound(extraContext);
     }
 
     public void testContextParams() {
@@ -88,7 +87,7 @@ public class ActionContextTest extends XWorkTestCase {
 
     public void testContextMap() {
         Map<String, Object> map = new HashMap<>();
-        ActionContext.setContext(new ActionContext(map));
+        ActionContext.ofAndBound(map);
 
         assertEquals(map, ActionContext.getContext().getContextMap());
     }
@@ -111,10 +110,9 @@ public class ActionContextTest extends XWorkTestCase {
     public void testStaticMethods() {
         assertEquals(context, ActionContext.getContext());
 
-        ActionContext context2 = new ActionContext(null);
-        ActionContext.setContext(context2);
+        ActionContext.clear();
 
-        assertEquals(context2, ActionContext.getContext());
+        assertNull(ActionContext.getContext());
     }
 
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/ActionContextThreadLocalTest.java b/core/src/test/java/com/opensymphony/xwork2/ActionContextThreadLocalTest.java
index ac6966e..75bc0d5 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ActionContextThreadLocalTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ActionContextThreadLocalTest.java
@@ -25,21 +25,22 @@ import java.util.HashMap;
 
 /**
  * Simple Test ActionContext's ThreadLocal
- * 
+ *
  * @author tm_jee
  * @version $Date$ $Id$
  */
 public class ActionContextThreadLocalTest extends TestCase {
 
-	
-	public void testGetContext() throws Exception {
-	    ActionContext.setContext(null);
-		assertNull(ActionContext.getContext());
-	}
-	
-	public void testSetContext() throws Exception {
-		ActionContext context = new ActionContext(new HashMap<String, Object>());
-		ActionContext.setContext(context);
-		assertEquals(context, ActionContext.getContext());
-	}
+    public void testGetContext() {
+        ActionContext.clear();
+
+        assertNull(ActionContext.getContext());
+    }
+
+    public void testSetContext() {
+        ActionContext context = ActionContext.ofAndBound(new HashMap<>());
+
+        assertEquals(context, ActionContext.getContext());
+    }
+
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java b/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java
index f712ee7..739c0dd 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java
@@ -159,7 +159,7 @@ public class ActionSupportTest extends XWorkTestCase {
         ActionContext.getContext().setLocale(Locale.ITALY);
         assertEquals(Locale.ITALY, as.getLocale());
 
-        ActionContext.setContext(new ActionContext(new HashMap<String, Object>()));
+        ActionContext.ofAndBound(new HashMap<>());
         assertEquals(defLocale, as.getLocale()); // ActionContext will create a new context, when it was set to null before
     }
 
@@ -167,9 +167,9 @@ public class ActionSupportTest extends XWorkTestCase {
         assertEquals("santa", mas.execute());
         assertNotNull(mas.getTexts());
 
-        assertEquals(false, mas.hasActionMessages());
+        assertFalse(mas.hasActionMessages());
         mas.validate();
-        assertEquals(true, mas.hasActionMessages());
+        assertTrue(mas.hasActionMessages());
     }
 
     public void testSimpleGetTexts() throws Exception {
diff --git a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
index 2d6ac54..c1e22ed 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
@@ -58,19 +58,23 @@ public class ChainResultTest extends XWorkTestCase {
         stack.push(values);
 
         Mock actionProxyMock = new Mock(ActionProxy.class);
+        actionProxyMock.matchAndReturn("getActionName", expectedActionName);
+        actionProxyMock.matchAndReturn("getMethod", "execute");
         actionProxyMock.expect("execute");
 
         ActionProxyFactory testActionProxyFactory = new NamespaceActionNameTestActionProxyFactory(expectedNamespace, expectedActionName, (ActionProxy) actionProxyMock.proxy());
         result.setActionProxyFactory(testActionProxyFactory);
-        try {
 
-            ActionContext testContext = new ActionContext(stack.getContext());
-            ActionContext.setContext(testContext);
-            result.execute(null);
-            actionProxyMock.verify();
-        } finally {
-            ActionContext.setContext(null);
-        }
+        ActionProxy actionProxy = (ActionProxy) actionProxyMock.proxy();
+        result.setActionProxyFactory(testActionProxyFactory);
+
+        Mock invocationMock = new Mock(ActionInvocation.class);
+        invocationMock.matchAndReturn("getProxy", actionProxy);
+        invocationMock.matchAndReturn("getInvocationContext", ActionContext.getContext());
+
+        result.execute((ActionInvocation) invocationMock.proxy());
+
+        actionProxyMock.verify();
     }
 
     public void testWithNoNamespace() throws Exception {
@@ -97,14 +101,14 @@ public class ChainResultTest extends XWorkTestCase {
 
         Mock invocationMock = new Mock(ActionInvocation.class);
         invocationMock.matchAndReturn("getProxy", actionProxy);
+        invocationMock.matchAndReturn("getInvocationContext", ActionContext.getContext());
         try {
 
-            ActionContext testContext = new ActionContext(stack.getContext());
-            ActionContext.setContext(testContext);
+            ActionContext.bound(stack.getActionContext());
             result.execute((ActionInvocation) invocationMock.proxy());
             actionProxyMock.verify();
         } finally {
-            ActionContext.setContext(null);
+            ActionContext.clear();
         }
     }
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java
index c407dc4..51692b8 100644
--- a/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java
@@ -129,8 +129,7 @@ public class DefaultTextProviderTest extends XWorkTestCase {
     protected void setUp() throws Exception {
         super.setUp();
 
-        ActionContext ctx = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(ctx);
+        ActionContext ctx = ActionContext.ofAndBound(new HashMap<>());
         ctx.setLocale(Locale.CANADA);
 
         container.getInstance(LocalizedTextProvider.class).addDefaultResourceBundle(DefaultTextProviderTest.class.getName());
@@ -140,7 +139,8 @@ public class DefaultTextProviderTest extends XWorkTestCase {
 
     @Override
     protected void tearDown() throws Exception {
-        ActionContext.setContext(null);
+        super.tearDown();
+        ActionContext.clear();
         tp = null;
     }
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/LocaleAwareTest.java b/core/src/test/java/com/opensymphony/xwork2/LocaleAwareTest.java
index 614d223..2db12ea 100644
--- a/core/src/test/java/com/opensymphony/xwork2/LocaleAwareTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/LocaleAwareTest.java
@@ -68,7 +68,7 @@ public class LocaleAwareTest extends XWorkTestCase {
         loadConfigurationProviders(configurationProvider, new MockConfigurationProvider());
 
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
-        stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
+        stack.getActionContext().setContainer(container);
+        ActionContext.ofAndBound(stack.getContext());
     }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/StubValueStack.java b/core/src/test/java/com/opensymphony/xwork2/StubValueStack.java
index ea475f0..197d3ad 100644
--- a/core/src/test/java/com/opensymphony/xwork2/StubValueStack.java
+++ b/core/src/test/java/com/opensymphony/xwork2/StubValueStack.java
@@ -35,6 +35,11 @@ public class StubValueStack implements ValueStack {
         return ctx;
     }
 
+    @Override
+    public ActionContext getActionContext() {
+        return ActionContext.ofAndBound(ctx);
+    }
+
     public void setDefaultType(Class defaultType) {
     }
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/WildCardResultTest.java b/core/src/test/java/com/opensymphony/xwork2/WildCardResultTest.java
index 179e77a..08b4c39 100644
--- a/core/src/test/java/com/opensymphony/xwork2/WildCardResultTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/WildCardResultTest.java
@@ -40,17 +40,17 @@ public class WildCardResultTest extends XWorkTestCase {
     }
 
     public void testWildCardEvaluation() throws Exception {
-        ActionContext.setContext(null);
+        ActionContext.clear();
         ActionProxy proxy = actionProxyFactory.createActionProxy(null, "WildCard", null, null);
         assertEquals("success", proxy.execute());
         assertEquals(VoidResult.class, proxy.getInvocation().getResult().getClass());
 
-        ActionContext.setContext(null);
+        ActionContext.clear();
         proxy = actionProxyFactory.createActionProxy(null, "WildCardInput", null, null);
         assertEquals("input", proxy.execute());
         assertEquals(MockResult.class, proxy.getInvocation().getResult().getClass());
 
-        ActionContext.setContext(null);
+        ActionContext.clear();
         proxy = actionProxyFactory.createActionProxy(null, "WildCardError", null, null);
         assertEquals("error", proxy.execute());
         assertEquals(MockResult.class, proxy.getInvocation().getResult().getClass());
diff --git a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/AnnotationXWorkConverterTest.java b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/AnnotationXWorkConverterTest.java
index 0d7bbcd..25a57fd 100644
--- a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/AnnotationXWorkConverterTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/AnnotationXWorkConverterTest.java
@@ -478,6 +478,7 @@ public class AnnotationXWorkConverterTest extends XWorkTestCase {
 
     @Override
     protected void tearDown() throws Exception {
-        ActionContext.setContext(null);
+        super.tearDown();
+        ActionContext.clear();
     }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java
index e171261..72b26e0 100644
--- a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java
@@ -304,7 +304,7 @@ public class XWorkBasicConverterTest extends XWorkTestCase {
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java
index 30c2ea0..a344ffc 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java
@@ -154,7 +154,7 @@ public class ChainingInterceptorTest extends XWorkTestCase {
         mockInvocation = new Mock(ActionInvocation.class);
         mockInvocation.expectAndReturn("getStack", stack);
         mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
-        mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(new HashMap<String, Object>()));
+        mockInvocation.expectAndReturn("getInvocationContext", ActionContext.ofAndBound(new HashMap<>()));
         mockInvocation.expectAndReturn("getResult", new ActionChainResult());
         invocation = (ActionInvocation) mockInvocation.proxy();
         interceptor = new ChainingInterceptor();
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptorTest.java
index 9ed6f5d..5bbdc87 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptorTest.java
@@ -128,7 +128,7 @@ public class ConversionErrorInterceptorTest extends XWorkTestCase {
     }
 
     private ActionContext createActionContext() {
-        ActionContext ac = new ActionContext(stack.getContext());
+        ActionContext ac = ActionContext.ofAndBound(stack.getContext());
         ac.setConversionErrors(conversionErrors);
         ac.setValueStack(stack);
         return ac;
@@ -141,7 +141,7 @@ public class ConversionErrorInterceptorTest extends XWorkTestCase {
         mockInvocation = new Mock(ActionInvocation.class);
         invocation = (ActionInvocation) mockInvocation.proxy();
         stack = ActionContext.getContext().getValueStack();
-        context = new ActionContext(stack.getContext());
+        context = ActionContext.ofAndBound(stack.getContext());
         conversionErrors = new HashMap<>();
         context.setConversionErrors(conversionErrors);
         mockInvocation.matchAndReturn("getInvocationContext", context);
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/DefaultWorkflowInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/DefaultWorkflowInterceptorTest.java
index 00c1fa0..ce2a316 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/DefaultWorkflowInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/DefaultWorkflowInterceptorTest.java
@@ -23,7 +23,6 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.config.entities.InterceptorConfig;
 import com.opensymphony.xwork2.validator.ValidationInterceptor;
 import org.easymock.EasyMock;
-import org.easymock.IAnswer;
 
 import java.util.HashMap;
 
@@ -37,12 +36,8 @@ public class DefaultWorkflowInterceptorTest extends XWorkTestCase {
 
     DefaultWorkflowInterceptor interceptor;
     private ActionInvocation invocation;
-    private Action action;
-    private ActionProxy proxy;
-    private ActionConfig config;
     private String result = "testing123";
 
-
     public void testInvokesActionInvocationIfNoErrors() throws Exception {
         ValidationInterceptor validationInterceptor = create();
         validationInterceptor.intercept(invocation);
@@ -171,30 +166,24 @@ public class DefaultWorkflowInterceptorTest extends XWorkTestCase {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        config = new ActionConfig.Builder("", "name", "").build();
-        action = EasyMock.createNiceMock(ValidateAction.class);
+        ActionConfig config = new ActionConfig.Builder("", "name", "").build();
+        Action action = EasyMock.createNiceMock(ValidateAction.class);
         invocation = EasyMock.createNiceMock(ActionInvocation.class);
         interceptor = new DefaultWorkflowInterceptor();
-        proxy = EasyMock.createNiceMock(ActionProxy.class);
+        ActionProxy proxy = EasyMock.createNiceMock(ActionProxy.class);
 
         EasyMock.expect(invocation.getProxy()).andReturn(proxy).anyTimes();
         EasyMock.expect(invocation.getAction()).andReturn(action).anyTimes();
-        EasyMock.expect(invocation.invoke()).andAnswer(new IAnswer<String>() {
-            public String answer() throws Throwable {
-                return result;
-            }
-        }).anyTimes();
+        EasyMock.expect(invocation.invoke()).andAnswer(() -> result).anyTimes();
 
         EasyMock.expect(proxy.getConfig()).andReturn(config).anyTimes();
         EasyMock.expect(proxy.getMethod()).andReturn("execute").anyTimes();
 
-
         EasyMock.replay(invocation);
         EasyMock.replay(action);
         EasyMock.replay(proxy);
 
-        ActionContext actionContext = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(actionContext);
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setActionInvocation(invocation);
     }
 
@@ -206,7 +195,7 @@ public class DefaultWorkflowInterceptorTest extends XWorkTestCase {
     protected ValidationInterceptor create() {
         ObjectFactory objectFactory = container.getInstance(ObjectFactory.class);
         return (ValidationInterceptor) objectFactory.buildInterceptor(
-                new InterceptorConfig.Builder("model", ValidationInterceptor.class.getName()).build(), new HashMap<String, String>());
+                new InterceptorConfig.Builder("model", ValidationInterceptor.class.getName()).build(), new HashMap<>());
     }
 
     
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ExceptionMappingInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ExceptionMappingInterceptorTest.java
index 8c759d5..c276bba 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ExceptionMappingInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ExceptionMappingInterceptorTest.java
@@ -293,7 +293,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
         stack = ActionContext.getContext().getValueStack();
         mockInvocation = new Mock(ActionInvocation.class);
         mockInvocation.expectAndReturn("getStack", stack);
-        mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(new HashMap<>()));
+        mockInvocation.expectAndReturn("getInvocationContext", ActionContext.ofAndBound(new HashMap<>()));
         interceptor = new ExceptionMappingInterceptor();
         interceptor.init();
     }
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptorTest.java
index 226216c..79b23ce 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptorTest.java
@@ -34,22 +34,20 @@ import java.util.Map;
  */
 public class ParameterFilterInterceptorTest extends XWorkTestCase {
 
-    ActionInvocation invocation;
-    ParameterFilterInterceptor interceptor;
-    Mock mockInvocation;
-    ValueStack stack;
-    Map<String, Object> contextMap;
+    private ActionInvocation invocation;
+    private ParameterFilterInterceptor interceptor;
+    private Mock mockInvocation;
+    private ValueStack stack;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        contextMap = new HashMap<>();
         stack = ActionContext.getContext().getValueStack();
         mockInvocation = new Mock(ActionInvocation.class);
-        mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
+        mockInvocation.expectAndReturn("getInvocationContext", ActionContext.getContext());
         mockInvocation.expectAndReturn("getStack", stack);
         mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
-        mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
+        mockInvocation.expectAndReturn("getInvocationContext", ActionContext.getContext());
         mockInvocation.matchAndReturn("getAction", new SimpleAction());
         invocation = (ActionInvocation) mockInvocation.proxy();
         interceptor = new ParameterFilterInterceptor();
@@ -115,11 +113,11 @@ public class ParameterFilterInterceptorTest extends XWorkTestCase {
             params.put(paramName, "irrelevant what this is");
 
         }
-        contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
+        ActionContext.getContext().setParameters(HttpParameters.create(params).build());
     }
     
-    private Collection getParameterNames() {
-        return ((HttpParameters)contextMap.get(ActionContext.PARAMETERS)).keySet();
+    private Collection<String> getParameterNames() {
+        return ActionContext.getContext().getParameters().keySet();
     }
     
     public void runAction() throws Exception  {
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptorTest.java
index a89c3ce..511b73a 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptorTest.java
@@ -24,108 +24,111 @@ import com.opensymphony.xwork2.ActionSupport;
 import junit.framework.TestCase;
 import org.apache.struts2.dispatcher.HttpParameters;
 
-import static org.easymock.EasyMock.*;
-
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
 /**
  * @author tmjee
  * @version $Date$ $Id$
  */
 public class ParameterRemoverInterceptorTest extends TestCase {
 
-	protected Map<String, Object> contextMap;
-	protected ActionContext context;
-	protected ActionInvocation actionInvocation;
-	
-	@Override
+    protected Map<String, Object> contextMap;
+    protected ActionContext context;
+    protected ActionInvocation actionInvocation;
+
+    @Override
     protected void setUp() throws Exception {
-		contextMap = new LinkedHashMap<>();
-		context = new ActionContext(contextMap);
-		
-		actionInvocation = (ActionInvocation) createMock(ActionInvocation.class);
-		expect(actionInvocation.getAction()).andStubReturn(new SampleAction());
-		expect(actionInvocation.getInvocationContext()).andStubReturn(context);
-		expect(actionInvocation.invoke()).andStubReturn("success");
-	}
-	
-	public void testInterception1() throws Exception {
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(new LinkedHashMap<String, Object>() {
-			{
-				put("param1", new String[]{"paramValue1"});
-				put("param2", new String[]{"paramValue2"});
-				put("param3", new String[]{"paramValue3"});
-				put("param", new String[]{"paramValue"});
-			}
-		}).build());
-		
-		replay(actionInvocation);
-		
-		ParameterRemoverInterceptor interceptor = new ParameterRemoverInterceptor();
-		interceptor.setParamNames("param1,param2");
-		interceptor.setParamValues("paramValue1,paramValue2");
-		interceptor.intercept(actionInvocation);
-		
-		HttpParameters params = (HttpParameters) contextMap.get(ActionContext.PARAMETERS);
-		assertEquals(params.keySet().size(), 2);
-		assertTrue(params.contains("param3"));
-		assertTrue(params.contains("param"));
-		assertEquals(params.get("param3").getValue(), "paramValue3");
-		assertEquals(params.get("param").getValue(), "paramValue");
-		
-		verify(actionInvocation);
-	}
-	
-	
-	public void testInterception2() throws Exception {
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(new LinkedHashMap<String, Object>() {
-			{
-				put("param1", new String[] { "paramValue2" });
-				put("param2", new String[] { "paramValue1" });
-			}
-		}).build());
-		
-		replay(actionInvocation);
-		
-		ParameterRemoverInterceptor interceptor = new ParameterRemoverInterceptor();
-		interceptor.setParamNames("param1,param2");
-		interceptor.setParamValues("paramValue1,paramValue2");
-		interceptor.intercept(actionInvocation);
-		
-		HttpParameters params = (HttpParameters) contextMap.get(ActionContext.PARAMETERS);
-		assertEquals(params.keySet().size(), 0);
-		
-		verify(actionInvocation);
-	}
-	
-	
-	public void testInterception3() throws Exception {
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(new LinkedHashMap<String, Object>() {
-			{
-				put("param1", new String[] { "paramValueOne" });
-				put("param2", new String[] { "paramValueTwo" });
-			}
-		}).build());
-		
-		replay(actionInvocation);
-		
-		ParameterRemoverInterceptor interceptor = new ParameterRemoverInterceptor();
-		interceptor.setParamNames("param1,param2");
-		interceptor.setParamValues("paramValue1,paramValue2");
-		interceptor.intercept(actionInvocation);
-		
-		HttpParameters params = (HttpParameters) contextMap.get(ActionContext.PARAMETERS);
-		assertEquals(params.keySet().size(), 2);
-		assertTrue(params.contains("param1"));
-		assertTrue(params.contains("param2"));
-		assertEquals(params.get("param1").getValue(), "paramValueOne");
-		assertEquals(params.get("param2").getValue(), "paramValueTwo");
-		
-		verify(actionInvocation);
-	}
-	
-	class SampleAction extends ActionSupport {
-		private static final long serialVersionUID = 7489487258845368260L;
-	}
+        contextMap = new LinkedHashMap<>();
+        context = ActionContext.ofAndBound(contextMap);
+
+        actionInvocation = createMock(ActionInvocation.class);
+        expect(actionInvocation.getAction()).andStubReturn(new SampleAction());
+        expect(actionInvocation.getInvocationContext()).andStubReturn(context);
+        expect(actionInvocation.invoke()).andStubReturn("success");
+    }
+
+    public void testInterception1() throws Exception {
+        contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(new LinkedHashMap<String, Object>() {
+            {
+                put("param1", new String[]{"paramValue1"});
+                put("param2", new String[]{"paramValue2"});
+                put("param3", new String[]{"paramValue3"});
+                put("param", new String[]{"paramValue"});
+            }
+        }).build());
+
+        replay(actionInvocation);
+
+        ParameterRemoverInterceptor interceptor = new ParameterRemoverInterceptor();
+        interceptor.setParamNames("param1,param2");
+        interceptor.setParamValues("paramValue1,paramValue2");
+        interceptor.intercept(actionInvocation);
+
+        HttpParameters params = (HttpParameters) contextMap.get(ActionContext.PARAMETERS);
+        assertEquals(params.keySet().size(), 2);
+        assertTrue(params.contains("param3"));
+        assertTrue(params.contains("param"));
+        assertEquals(params.get("param3").getValue(), "paramValue3");
+        assertEquals(params.get("param").getValue(), "paramValue");
+
+        verify(actionInvocation);
+    }
+
+
+    public void testInterception2() throws Exception {
+        contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(new LinkedHashMap<String, Object>() {
+            {
+                put("param1", new String[]{"paramValue2"});
+                put("param2", new String[]{"paramValue1"});
+            }
+        }).build());
+
+        replay(actionInvocation);
+
+        ParameterRemoverInterceptor interceptor = new ParameterRemoverInterceptor();
+        interceptor.setParamNames("param1,param2");
+        interceptor.setParamValues("paramValue1,paramValue2");
+        interceptor.intercept(actionInvocation);
+
+        HttpParameters params = (HttpParameters) contextMap.get(ActionContext.PARAMETERS);
+        assertEquals(params.keySet().size(), 0);
+
+        verify(actionInvocation);
+    }
+
+
+    public void testInterception3() throws Exception {
+        contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(new LinkedHashMap<String, Object>() {
+            {
+                put("param1", new String[]{"paramValueOne"});
+                put("param2", new String[]{"paramValueTwo"});
+            }
+        }).build());
+
+        replay(actionInvocation);
+
+        ParameterRemoverInterceptor interceptor = new ParameterRemoverInterceptor();
+        interceptor.setParamNames("param1,param2");
+        interceptor.setParamValues("paramValue1,paramValue2");
+        interceptor.intercept(actionInvocation);
+
+        HttpParameters params = (HttpParameters) contextMap.get(ActionContext.PARAMETERS);
+        assertEquals(params.keySet().size(), 2);
+        assertTrue(params.contains("param1"));
+        assertTrue(params.contains("param2"));
+        assertEquals(params.get("param1").getValue(), "paramValueOne");
+        assertEquals(params.get("param2").getValue(), "paramValueTwo");
+
+        verify(actionInvocation);
+    }
+
+    static class SampleAction extends ActionSupport {
+        private static final long serialVersionUID = 7489487258845368260L;
+    }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationErrorAwareTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationErrorAwareTest.java
index a79ea69..eadf1e9 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationErrorAwareTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationErrorAwareTest.java
@@ -30,7 +30,6 @@ import com.opensymphony.xwork2.config.entities.InterceptorConfig;
 import com.opensymphony.xwork2.validator.ValidationInterceptor;
 import org.junit.Assert;
 import org.easymock.EasyMock;
-import org.easymock.IAnswer;
 
 import java.util.HashMap;
 
@@ -73,20 +72,12 @@ public class ValidationErrorAwareTest extends XWorkTestCase {
         interceptor = new DefaultWorkflowInterceptor();
         ActionProxy proxy = EasyMock.createNiceMock(ActionProxy.class);
 
-        EasyMock.expect(action.actionErrorOccurred(EasyMock.<String>anyObject())).andAnswer(new IAnswer<String>() {
-            public String answer() throws Throwable {
-                return actionResult;
-            }
-        }).anyTimes();
+        EasyMock.expect(action.actionErrorOccurred(EasyMock.anyObject())).andAnswer(() -> actionResult).anyTimes();
         EasyMock.expect(action.hasErrors()).andReturn(true).anyTimes();
 
         EasyMock.expect(invocation.getProxy()).andReturn(proxy).anyTimes();
         EasyMock.expect(invocation.getAction()).andReturn(action).anyTimes();
-        EasyMock.expect(invocation.invoke()).andAnswer(new IAnswer<String>() {
-            public String answer() throws Throwable {
-                return result;
-            }
-        }).anyTimes();
+        EasyMock.expect(invocation.invoke()).andAnswer(() -> result).anyTimes();
 
         EasyMock.expect(proxy.getConfig()).andReturn(config).anyTimes();
         EasyMock.expect(proxy.getMethod()).andReturn("execute").anyTimes();
@@ -96,9 +87,8 @@ public class ValidationErrorAwareTest extends XWorkTestCase {
         EasyMock.replay(action);
         EasyMock.replay(proxy);
 
-        ActionContext contex = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(contex);
-        contex.setActionInvocation(invocation);
+        ActionContext context = ActionContext.ofAndBound(new HashMap<>());
+        context.setActionInvocation(invocation);
     }
 
     @Override
@@ -109,7 +99,7 @@ public class ValidationErrorAwareTest extends XWorkTestCase {
     protected ValidationInterceptor create() {
         ObjectFactory objectFactory = container.getInstance(ObjectFactory.class);
         return (ValidationInterceptor) objectFactory.buildInterceptor(
-                new InterceptorConfig.Builder("model", ValidationInterceptor.class.getName()).build(), new HashMap<String, String>());
+                new InterceptorConfig.Builder("model", ValidationInterceptor.class.getName()).build(), new HashMap<>());
     }
 
     private interface ValidateErrorAction extends Action, Validateable, ValidationAware, ValidationErrorAware {
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationInterceptorPrefixMethodInvocationTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationInterceptorPrefixMethodInvocationTest.java
index 418455c..644b5fa 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationInterceptorPrefixMethodInvocationTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ValidationInterceptorPrefixMethodInvocationTest.java
@@ -18,93 +18,90 @@
  */
 package com.opensymphony.xwork2.interceptor;
 
-import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.Validateable;
+import com.opensymphony.xwork2.XWorkTestCase;
 import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.config.entities.InterceptorConfig;
 import com.opensymphony.xwork2.validator.ValidationInterceptor;
 import org.easymock.EasyMock;
-import org.easymock.IAnswer;
 
 import java.util.HashMap;
 
 /**
  * Test ValidationInterceptor's prefix method invocation capabilities.
- * 
+ *
  * @author tm_jee
  * @version $Date$ $Id$
  */
 public class ValidationInterceptorPrefixMethodInvocationTest extends XWorkTestCase {
     private ActionInvocation invocation;
-    private ActionConfig config;
-    private ActionProxy proxy;
-    private ValidateAction action;
     private String result;
     private String method;
 
     public void testPrefixMethodInvocation1() throws Exception {
-		method = "save";
-		result = Action.INPUT;
-		
-		ValidationInterceptor interceptor = create();
-		String result = interceptor.intercept(invocation);
-		
-		assertEquals(Action.INPUT, result);
-	}
-	
-	public void testPrefixMethodInvocation2() throws Exception {
-		method = "save";
-		result = "okok";
-
-		ValidationInterceptor interceptor = create();
-		String result = interceptor.intercept(invocation);
-		
-		assertEquals("okok", result);
-	}
-	
-	protected ValidationInterceptor create() {
-	    ObjectFactory objectFactory = container.getInstance(ObjectFactory.class);
-	    return (ValidationInterceptor) objectFactory.buildInterceptor(
-                new InterceptorConfig.Builder("model", ValidationInterceptor.class.getName()).build(), new HashMap<String, String>());
+        method = "save";
+        result = Action.INPUT;
+
+        ValidationInterceptor interceptor = create();
+        String result = interceptor.intercept(invocation);
+
+        assertEquals(Action.INPUT, result);
+    }
+
+    public void testPrefixMethodInvocation2() throws Exception {
+        method = "save";
+        result = "okok";
+
+        ValidationInterceptor interceptor = create();
+        String result = interceptor.intercept(invocation);
+
+        assertEquals("okok", result);
+    }
+
+    protected ValidationInterceptor create() {
+        ObjectFactory objectFactory = container.getInstance(ObjectFactory.class);
+        return (ValidationInterceptor) objectFactory.buildInterceptor(
+            new InterceptorConfig.Builder("model", ValidationInterceptor.class.getName()).build(), new HashMap<>());
     }
-	
-	private interface ValidateAction extends Action, Validateable, ValidationAware {
-		void validateDoSave();
-		void validateSubmit();
-		String submit();
+
+    private interface ValidateAction extends Action, Validateable, ValidationAware {
+        @SuppressWarnings("unused")
+        void validateDoSave();
+
+        @SuppressWarnings("unused")
+        void validateSubmit();
+
+        String submit();
     }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        config = new ActionConfig.Builder("", "action", "").build();
+        ActionConfig config = new ActionConfig.Builder("", "action", "").build();
         invocation = EasyMock.createNiceMock(ActionInvocation.class);
-        proxy = EasyMock.createNiceMock(ActionProxy.class);
-        action = EasyMock.createNiceMock(ValidateAction.class);
+        ActionProxy proxy = EasyMock.createNiceMock(ActionProxy.class);
+        ValidateAction action = EasyMock.createNiceMock(ValidateAction.class);
 
 
         EasyMock.expect(invocation.getProxy()).andReturn(proxy).anyTimes();
         EasyMock.expect(invocation.getAction()).andReturn(action).anyTimes();
-        EasyMock.expect(invocation.invoke()).andAnswer(new IAnswer<String>() {
-            public String answer() throws Throwable {
-                return result;
-            }
-        }).anyTimes();
+        EasyMock.expect(invocation.invoke()).andAnswer(() -> result).anyTimes();
 
         EasyMock.expect(proxy.getConfig()).andReturn(config).anyTimes();
-        EasyMock.expect(proxy.getMethod()).andAnswer(new IAnswer<String>() {
-            public String answer() throws Throwable {
-                return method;
-            }
-        }).anyTimes();
+        EasyMock.expect(proxy.getMethod()).andAnswer(() -> method).anyTimes();
 
 
         EasyMock.replay(invocation);
         EasyMock.replay(action);
         EasyMock.replay(proxy);
 
-        ActionContext contex = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(contex);
-        contex.setActionInvocation(invocation);
+        ActionContext context = ActionContext.ofAndBound(new HashMap<>());
+        context.setActionInvocation(invocation);
     }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterInterceptorTest.java
index 84c8027..5809716 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterInterceptorTest.java
@@ -33,172 +33,167 @@ import java.util.Map;
 /**
  * @author martin.gilday
  * @author jafl
- *
  */
 public class AnnotationParameterFilterInterceptorTest extends TestCase {
 
-	ValueStack stack;
+    ValueStack stack;
 
-	@Override
-	protected void setUp() throws Exception {
-		super.setUp();
-		stack = new StubValueStack();
-	}
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        stack = new StubValueStack();
+    }
 
-	/**
-	 * Only "name" should remain in the parameter map.  All others
-	 * should be removed
-	 * @throws Exception
-	 */
-	public void testBlockingByDefault() throws Exception {
+    /**
+     * Only "name" should remain in the parameter map.  All others
+     * should be removed
+     */
+    public void testBlockingByDefault() throws Exception {
 
-		Map<String, Object> contextMap = new HashMap<>();
-		Map<String, Object> parameterMap = new HashMap<>();
-		
-		parameterMap.put("job", "Baker");
-		parameterMap.put("name", "Martin");
-
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(parameterMap).build());
-		
-		Action action = new BlockingByDefaultAction();
-		stack.push(action);
-		
-		Mock mockInvocation = new Mock(ActionInvocation.class);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.matchAndReturn("getAction", action);
-		mockInvocation.matchAndReturn("getStack", stack);
-		mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
-
-		HttpParameters parameters = invocation.getInvocationContext().getParameters();
-		assertEquals("Parameter map should contain one entry", 1, parameters.keySet().size());
-		assertFalse(parameters.get("job").isDefined());
-		assertTrue(parameters.get("name").isDefined());
-		
-	}
-
-	/**
-	 * "name" should be removed from the map, as it is blocked.
-	 * All other parameters should remain
-	 * @throws Exception
-	 */
-	public void testAllowingByDefault() throws Exception {
-
-		Map<String, Object> contextMap = new HashMap<>();
 		Map<String, Object> parameterMap = new HashMap<>();
-		
-		parameterMap.put("job", "Baker");
-		parameterMap.put("name", "Martin");
-
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(parameterMap).build());
-		
-		Action action = new AllowingByDefaultAction();
-		stack.push(action);
-		
-		Mock mockInvocation = new Mock(ActionInvocation.class);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.matchAndReturn("getAction", action);
-		mockInvocation.matchAndReturn("getStack", stack);
-		mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
-
-		HttpParameters parameters = invocation.getInvocationContext().getParameters();
-		assertEquals("Paramwter map should contain one entry", 1, parameters.keySet().size());
-		assertTrue(parameters.get("job").isDefined());
-		assertFalse(parameters.get("name").isDefined());
-		
-	}
-
-	/**
-	 * Only "name" should remain in the parameter map.  All others
-	 * should be removed
-	 * @throws Exception
-	 */
-	public void testBlockingByDefaultWithModel() throws Exception {
-
-		Map<String, Object> contextMap = new HashMap<>();
+
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
+
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
+
+        Action action = new BlockingByDefaultAction();
+        stack.push(action);
+
+        Mock mockInvocation = new Mock(ActionInvocation.class);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.matchAndReturn("getAction", action);
+        mockInvocation.matchAndReturn("getStack", stack);
+        mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+
+        ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
+
+        AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
+        interceptor.intercept(invocation);
+
+        HttpParameters parameters = invocation.getInvocationContext().getParameters();
+        assertEquals("Parameter map should contain one entry", 1, parameters.keySet().size());
+        assertFalse(parameters.get("job").isDefined());
+        assertTrue(parameters.get("name").isDefined());
+
+    }
+
+    /**
+     * "name" should be removed from the map, as it is blocked.
+     * All other parameters should remain
+     */
+    public void testAllowingByDefault() throws Exception {
+
+        Map<String, Object> parameterMap = new HashMap<>();
+
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
+
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
+
+        Action action = new AllowingByDefaultAction();
+        stack.push(action);
+
+        Mock mockInvocation = new Mock(ActionInvocation.class);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.matchAndReturn("getAction", action);
+        mockInvocation.matchAndReturn("getStack", stack);
+        mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+
+        ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
+
+        AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
+        interceptor.intercept(invocation);
+
+        HttpParameters parameters = invocation.getInvocationContext().getParameters();
+        assertEquals("Paramwter map should contain one entry", 1, parameters.keySet().size());
+        assertTrue(parameters.get("job").isDefined());
+        assertFalse(parameters.get("name").isDefined());
+
+    }
+
+    /**
+     * Only "name" should remain in the parameter map.  All others
+     * should be removed
+     */
+    public void testBlockingByDefaultWithModel() throws Exception {
+
 		Map<String, Object> parameterMap = new HashMap<>();
-		
-		parameterMap.put("job", "Baker");
-		parameterMap.put("name", "Martin");
-		parameterMap.put("m1", "s1");
-		parameterMap.put("m2", "s2");
-
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(parameterMap).build());
-		stack.push(new BlockingByDefaultModel());
-		
-		Mock mockInvocation = new Mock(ActionInvocation.class);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.matchAndReturn("getAction", new BlockingByDefaultAction());
-		mockInvocation.matchAndReturn("getStack", stack);
-		mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
-
-		HttpParameters parameters = invocation.getInvocationContext().getParameters();
-		assertEquals("Parameter map should contain two entries", 2, parameters.keySet().size());
-		assertFalse(parameters.get("job").isDefined());
-		assertTrue(parameters.get("name").isDefined());
-		assertTrue(parameters.get("m1").isDefined());
-		assertFalse(parameters.get("m2").isDefined());
-		
-	}
-
-	/**
-	 * "name" should be removed from the map, as it is blocked.
-	 * All other parameters should remain
-	 * @throws Exception
-	 */
-	public void testAllowingByDefaultWithModel() throws Exception {
-
-		Map<String, Object> contextMap = new HashMap<>();
+
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
+        parameterMap.put("m1", "s1");
+        parameterMap.put("m2", "s2");
+
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
+        stack.push(new BlockingByDefaultModel());
+
+        Mock mockInvocation = new Mock(ActionInvocation.class);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.matchAndReturn("getAction", new BlockingByDefaultAction());
+        mockInvocation.matchAndReturn("getStack", stack);
+        mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+
+        ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
+
+        AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
+        interceptor.intercept(invocation);
+
+        HttpParameters parameters = invocation.getInvocationContext().getParameters();
+        assertEquals("Parameter map should contain two entries", 2, parameters.keySet().size());
+        assertFalse(parameters.get("job").isDefined());
+        assertTrue(parameters.get("name").isDefined());
+        assertTrue(parameters.get("m1").isDefined());
+        assertFalse(parameters.get("m2").isDefined());
+
+    }
+
+    /**
+     * "name" should be removed from the map, as it is blocked.
+     * All other parameters should remain
+     */
+    public void testAllowingByDefaultWithModel() throws Exception {
+
 		Map<String, Object> parameterMap = new HashMap<>();
-		
-		parameterMap.put("job", "Baker");
-		parameterMap.put("name", "Martin");
-		parameterMap.put("m1", "s1");
-		parameterMap.put("m2", "s2");
-
-		contextMap.put(ActionContext.PARAMETERS, HttpParameters.create(parameterMap).build());
-		stack.push(new AllowingByDefaultModel());
-		
-		Mock mockInvocation = new Mock(ActionInvocation.class);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.matchAndReturn("getAction", new AllowingByDefaultAction());
-		mockInvocation.matchAndReturn("getStack", stack);
-		mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-		mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(contextMap));
-
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
-
-		HttpParameters parameters = invocation.getInvocationContext().getParameters();
-		assertEquals("Parameter map should contain two entries", 2, parameters.keySet().size());
-		assertTrue(parameters.get("job").isDefined());
-		assertFalse(parameters.get("name").isDefined());
-		assertFalse(parameters.get("m1").isDefined());
-		assertTrue(parameters.get("m2").isDefined());
-		
-	}
-	
+
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
+        parameterMap.put("m1", "s1");
+        parameterMap.put("m2", "s2");
+
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
+        stack.push(new AllowingByDefaultModel());
+
+        Mock mockInvocation = new Mock(ActionInvocation.class);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.matchAndReturn("getAction", new AllowingByDefaultAction());
+        mockInvocation.matchAndReturn("getStack", stack);
+        mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+        mockInvocation.expectAndReturn("getInvocationContext", actionContext);
+
+        ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
+
+        AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
+        interceptor.intercept(invocation);
+
+        HttpParameters parameters = invocation.getInvocationContext().getParameters();
+        assertEquals("Parameter map should contain two entries", 2, parameters.keySet().size());
+        assertTrue(parameters.get("job").isDefined());
+        assertFalse(parameters.get("name").isDefined());
+        assertFalse(parameters.get("m1").isDefined());
+        assertTrue(parameters.get("m2").isDefined());
+
+    }
+
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptorTest.java
index 70766d5..3bb2b84 100644
--- a/core/src/test/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptorTest.java
@@ -77,13 +77,12 @@ public class ActionAutowiringInterceptorTest extends XWorkTestCase {
     }
 
     protected void loadSpringApplicationContextIntoApplication(ApplicationContext appContext) {
-        Map<Object, Object> application = new HashMap<>();
-        application.put(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, appContext);
-
         Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.APPLICATION, application);
-        ActionContext actionContext = new ActionContext(context);
-        ActionContext.setContext(actionContext);
+        ActionContext actionContext = ActionContext.ofAndBound(context);
+
+        Map<String, Object> application = new HashMap<>();
+        application.put(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, appContext);
+        actionContext.setApplication(application);
     }
 
     public void testLoadsApplicationContextUsingWebApplicationContextUtils() throws Exception {
@@ -106,9 +105,9 @@ public class ActionAutowiringInterceptorTest extends XWorkTestCase {
 
     public void testIfApplicationContextIsNullThenBeanWillNotBeWiredUp() throws Exception {
         Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.APPLICATION, new HashMap());
-        ActionContext actionContext = new ActionContext(context);
-        ActionContext.setContext(actionContext);
+        ActionContext actionContext = ActionContext.ofAndBound(context);
+
+        actionContext.setApplication(new HashMap<>());
 
         ActionAutowiringInterceptor interceptor = new ActionAutowiringInterceptor();
         interceptor.init();
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/ConversionErrorFieldValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/ConversionErrorFieldValidatorTest.java
index f36a1aa..56c0732 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/ConversionErrorFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/ConversionErrorFieldValidatorTest.java
@@ -49,7 +49,7 @@ public class ConversionErrorFieldValidatorTest extends XWorkTestCase {
     public void setUp() throws Exception {
         super.setUp();
         ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext context = new ActionContext(stack.getContext());
+        ActionContext context = ActionContext.ofAndBound(stack.getContext());
 
         Map<String, ConversionData> conversionErrors = new HashMap<>();
         conversionErrors.put("foo", new ConversionData("bar", Integer.class));
@@ -71,17 +71,17 @@ public class ConversionErrorFieldValidatorTest extends XWorkTestCase {
         validator.validate(validationAware);
 
 
-        Map fieldErrors = validationAware.getFieldErrors();
+        Map<String, List<String>> fieldErrors = validationAware.getFieldErrors();
         assertTrue(fieldErrors.containsKey("foo"));
-        assertEquals(message, ((List) fieldErrors.get("foo")).get(0));
+        assertEquals(message, fieldErrors.get("foo").get(0));
     }
 
     public void testConversionErrorsAreAddedToFieldErrors() throws ValidationException {
         validator.validate(validationAware);
 
-        Map fieldErrors = validationAware.getFieldErrors();
+        Map<String, List<String>> fieldErrors = validationAware.getFieldErrors();
         assertTrue(fieldErrors.containsKey("foo"));
-        assertEquals(defaultFooMessage, ((List) fieldErrors.get("foo")).get(0));
+        assertEquals(defaultFooMessage, fieldErrors.get("foo").get(0));
     }
 
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java
index 814b8d8..9876924 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java
@@ -67,8 +67,8 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
         actionValidatorManager.setValidatorFactory((ValidatorFactory)mockValidatorFactory.proxy());
 
         stubValueStack = new StubValueStack();
-        ActionContext.setContext(new ActionContext(new HashMap<String, Object>()));
-        ActionContext.getContext().setValueStack(stubValueStack);
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setValueStack(stubValueStack);
 
         DefaultFileManagerFactory factory = new DefaultFileManagerFactory();
         factory.setContainer(container);
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/SimpleActionValidationTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/SimpleActionValidationTest.java
index 42075db..bd830ce 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/SimpleActionValidationTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/SimpleActionValidationTest.java
@@ -54,7 +54,7 @@ public class SimpleActionValidationTest extends XWorkTestCase {
             assertFalse(validationAware.hasFieldErrors());
 
             // put in an out-of-range value to see if the old validators still work
-            ActionContext.setContext(new ActionContext(new HashMap<String, Object>()));
+            ActionContext.ofAndBound(new HashMap<>());
 
             params.put("bar", "42");
             extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
@@ -124,8 +124,9 @@ public class SimpleActionValidationTest extends XWorkTestCase {
         try {
             ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.VALIDATION_ACTION_NAME, null, extraContext);
             ValueStack stack = ActionContext.getContext().getValueStack();
-            ActionContext.setContext(new ActionContext(stack.getContext()));
-            ActionContext.getContext().setLocale(Locale.US);
+            ActionContext actionContext = ActionContext.ofAndBound(stack.getContext());
+            actionContext.setLocale(Locale.US);
+
             proxy.execute();
             assertTrue(((ValidationAware) proxy.getAction()).hasFieldErrors());
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorModelTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorModelTest.java
index ef3ccd1..f33569e 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorModelTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorModelTest.java
@@ -122,6 +122,6 @@ public class VisitorFieldValidatorModelTest extends XWorkTestCase {
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java
index 62ed2ba..7116753 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java
@@ -205,7 +205,7 @@ public class VisitorFieldValidatorTest extends XWorkTestCase {
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     private void validate(String context) throws ValidationException {
diff --git a/core/src/test/java/org/apache/struts2/ServletActionContextTest.java b/core/src/test/java/org/apache/struts2/ServletActionContextTest.java
index bd748c6..4f12bb2 100644
--- a/core/src/test/java/org/apache/struts2/ServletActionContextTest.java
+++ b/core/src/test/java/org/apache/struts2/ServletActionContextTest.java
@@ -37,15 +37,14 @@ import java.util.Map;
  */
 public class ServletActionContextTest extends TestCase implements StrutsStatics {
 
-    ActionContext actionContext;
-    ServletActionContext servletActionContext;
+    private ActionContext actionContext;
     private HttpServletRequest request;
     private HttpServletResponse response;
     private MockServletContext servletContext;
 
 
     public void setUp() {
-        Map extraContext = new HashMap();
+        Map<String, Object> extraContext = new HashMap<>();
 
         request = new MockHttpServletRequest();
         response = new MockHttpServletResponse();
@@ -55,8 +54,7 @@ public class ServletActionContextTest extends TestCase implements StrutsStatics
         extraContext.put(HTTP_RESPONSE, response);
         extraContext.put(SERVLET_CONTEXT, servletContext);
 
-        actionContext = new ActionContext(extraContext);
-        ServletActionContext.setContext(actionContext);
+        actionContext = ActionContext.ofAndBound(extraContext);
     }
 
     public void testContextParams() {
@@ -66,7 +64,7 @@ public class ServletActionContextTest extends TestCase implements StrutsStatics
     }
 
     public void testGetContext() {
-        ActionContext threadContext = ServletActionContext.getContext();
+        ActionContext threadContext = ServletActionContext.getActionContext();
         assertEquals(actionContext, threadContext);
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/components/ActionComponentTest.java b/core/src/test/java/org/apache/struts2/components/ActionComponentTest.java
index c34a2a8..0956700 100644
--- a/core/src/test/java/org/apache/struts2/components/ActionComponentTest.java
+++ b/core/src/test/java/org/apache/struts2/components/ActionComponentTest.java
@@ -18,27 +18,27 @@
  */
 package org.apache.struts2.components;
 
-import java.util.HashMap;
-
+import com.mockobjects.dynamic.Mock;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.util.ValueStack;
 import org.apache.struts2.StrutsInternalTestCase;
 import org.apache.struts2.dispatcher.HttpParameters;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 
-import com.mockobjects.dynamic.Mock;
-import com.opensymphony.xwork2.util.ValueStack;
+import java.util.HashMap;
 
 public class ActionComponentTest extends StrutsInternalTestCase {
 
-    public void testCreateParametersForContext() throws Exception {
+    public void testCreateParametersForContext() {
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
         Mock mockValueStack = new Mock(ValueStack.class);
-        HashMap ctx = new HashMap();
+        HashMap<String, Object> ctx = new HashMap<>();
         mockValueStack.expectAndReturn("getContext", ctx);
         mockValueStack.expectAndReturn("getContext", ctx);
-        mockValueStack.expectAndReturn("getContext", ctx);
-        
+        mockValueStack.expectAndReturn("getActionContext", ActionContext.getContext());
+
         ActionComponent comp = new ActionComponent((ValueStack) mockValueStack.proxy(), req, res);
         comp.addParameter("foo", "bar");
         comp.addParameter("baz", new String[]{"jim", "sarah"});
diff --git a/core/src/test/java/org/apache/struts2/dispatcher/PrepareOperationsTest.java b/core/src/test/java/org/apache/struts2/dispatcher/PrepareOperationsTest.java
index 02b705b..5a5e0bf 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/PrepareOperationsTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/PrepareOperationsTest.java
@@ -34,7 +34,7 @@ public class PrepareOperationsTest extends StrutsInternalTestCase {
 
         PrepareOperations prepare = new PrepareOperations(null);
 
-        ActionContext.setContext(null);
+        ActionContext.clear();
         ActionContext actionContext = prepare.createActionContext(req, null);
 
         assertEquals(stack.getContext(), actionContext.getContextMap());
diff --git a/core/src/test/java/org/apache/struts2/dispatcher/TwoFilterIntegrationTest.java b/core/src/test/java/org/apache/struts2/dispatcher/TwoFilterIntegrationTest.java
index 76351e2..b432a8d 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/TwoFilterIntegrationTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/TwoFilterIntegrationTest.java
@@ -101,10 +101,7 @@ public class TwoFilterIntegrationTest extends TestCase {
     }
 
     private MockHttpServletResponse run(String uri, final Filter... filters) throws ServletException, IOException {
-        return run(uri, null, filters);
-    }
-    private MockHttpServletResponse run(String uri, ActionContext existingContext, final Filter... filters) throws ServletException, IOException {
-        final LinkedList<Filter> filterList = new LinkedList<Filter>(Arrays.asList(filters));
+        final LinkedList<Filter> filterList = new LinkedList<>(Arrays.asList(filters));
         MockHttpServletRequest request = new MockHttpServletRequest();
         MockHttpServletResponse response = new MockHttpServletResponse();
         MockFilterConfig filterConfig = new MockFilterConfig();
@@ -115,31 +112,21 @@ public class TwoFilterIntegrationTest extends TestCase {
                 if (next != null) {
                     try {
                         next.doFilter(req, res, this);
-                    } catch (IOException e) {
-                        throw new RuntimeException(e);
-                    } catch (ServletException e) {
+                    } catch (IOException | ServletException e) {
                         throw new RuntimeException(e);
                     }
                 }
             }
         };
 
-        if (existingContext != null) {
-            request.setAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER, 1);
-        }
         request.setRequestURI(uri);
         for (Filter filter : filters) {
             filter.init(filterConfig);
         }
 
-        ActionContext.setContext(existingContext);
         filterList.removeFirst().doFilter(request, response, filterChain);
-        if (existingContext == null) {
-            assertNull(ActionContext.getContext());
-            assertNull(Dispatcher.getInstance());
-        } else {
-            assertEquals(Integer.valueOf(1), request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER));
-        }
+        assertNull(ActionContext.getContext());
+        assertNull(Dispatcher.getInstance());
         return response;
     }
 
diff --git a/core/src/test/java/org/apache/struts2/interceptor/ClearSessionInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/ClearSessionInterceptorTest.java
index 8602b10..e0b0350 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/ClearSessionInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/ClearSessionInterceptorTest.java
@@ -21,10 +21,7 @@ package org.apache.struts2.interceptor;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.servlet.http.HttpServletRequest;
-
 import org.apache.struts2.StrutsInternalTestCase;
-import org.jmock.Mock;
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
@@ -35,12 +32,10 @@ import com.opensymphony.xwork2.mock.MockActionInvocation;
 public class ClearSessionInterceptorTest extends StrutsInternalTestCase {
 
     public void testCreateSession() throws Exception {
-        Mock httpServletRequestMock = new Mock(HttpServletRequest.class);
-
         ClearSessionInterceptor interceptor = new ClearSessionInterceptor();
         MockActionInvocation invocation = new MockActionInvocation();
-        ActionContext context = new ActionContext(new HashMap());
-        Map session = new HashMap();
+        ActionContext context = ActionContext.ofAndBound(new HashMap<>());
+        Map<String, Object> session = new HashMap<>();
         session.put("Test1", "Test1");
         session.put("Test2", "Test2");
         session.put("Test3", "Test3");
diff --git a/core/src/test/java/org/apache/struts2/interceptor/CreateSessionInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CreateSessionInterceptorTest.java
index 0ea33f8..3a887c2 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/CreateSessionInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/CreateSessionInterceptorTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.struts2.interceptor;
 
+import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
@@ -33,6 +34,15 @@ import javax.servlet.http.HttpServletRequest;
  */
 public class CreateSessionInterceptorTest extends StrutsInternalTestCase {
 
+    private MockActionInvocation invocation;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        invocation = new MockActionInvocation();
+        invocation.setInvocationContext(ActionContext.getContext());
+    }
+
     public void testCreateSession() throws Exception {
         Mock httpServletRequestMock = new Mock(HttpServletRequest.class);
         httpServletRequestMock.expects(new InvokeOnceMatcher()).method("getSession").with(new IsEqual(Boolean.FALSE));
@@ -43,7 +53,8 @@ public class CreateSessionInterceptorTest extends StrutsInternalTestCase {
         ServletActionContext.setRequest(request);
 
         CreateSessionInterceptor interceptor = new CreateSessionInterceptor();
-        interceptor.intercept(new MockActionInvocation());
+
+        interceptor.intercept(invocation);
 
         httpServletRequestMock.verify();
     }
diff --git a/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java
index c99cf08..1372a5a 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java
@@ -220,9 +220,7 @@ public class ExecuteAndWaitInterceptorTest extends StrutsInternalTestCase {
     }
 
     protected void tearDown() throws Exception {
-        configurationManager.clearContainerProviders();
-        configurationManager.destroyConfiguration();
-        ActionContext.setContext(null);
+        super.tearDown();
     }
 
     private class WaitConfigurationProvider implements ConfigurationProvider {
diff --git a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java
index da5607f..8570a90 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java
@@ -45,14 +45,14 @@ public class I18nInterceptorTest extends TestCase {
     private I18nInterceptor interceptor;
     private ActionInvocation mai;
     private ActionContext ac;
-    private Map session;
+    private Map<String, Object> session;
     private MockHttpServletRequest request;
 
     public void testEmptyParamAndSession() throws Exception {
         interceptor.intercept(mai);
     }
 
-    public void testNoSessionNoLocale() throws Exception {
+    public void testNoSessionNoLocale() {
         request.setSession(null);
         try {
             interceptor.intercept(mai);
@@ -67,7 +67,7 @@ public class I18nInterceptorTest extends TestCase {
         assertNull("should not be stored here", session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE));
     }
 
-    public void testNoSessionButLocale() throws Exception {
+    public void testNoSessionButLocale() {
         prepare(I18nInterceptor.DEFAULT_PARAMETER, "da_DK"); //prevents shouldStore to being false
         request.setSession(null);
         try {
@@ -254,24 +254,17 @@ public class I18nInterceptorTest extends TestCase {
         interceptor = new I18nInterceptor();
         interceptor.setLocaleProviderFactory(new DefaultLocaleProviderFactory());
         interceptor.init();
-        session = new HashMap();
+        session = new HashMap<>();
 
-        Map<String, Object> ctx = new HashMap<String, Object>();
-        ctx.put(ActionContext.PARAMETERS, HttpParameters.create().build());
-        ctx.put(ActionContext.SESSION, session);
+        ac = ActionContext.ofAndBound(new HashMap<>());
+        ac.setSession(session);
+        ac.setParameters(HttpParameters.create().build());
 
-        ac = new ActionContext(ctx);
-
-        ServletActionContext.setContext(ac);
         request = new MockHttpServletRequest();
         request.setSession(new MockHttpSession());
         ServletActionContext.setRequest(request);
 
-        Action action = new Action() {
-            public String execute() throws Exception {
-                return SUCCESS;
-            }
-        };
+        Action action = () -> Action.SUCCESS;
 
         MockActionProxy proxy = new MockActionProxy();
         proxy.setAction(action);
diff --git a/core/src/test/java/org/apache/struts2/interceptor/MessageStoreInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/MessageStoreInterceptorTest.java
index 4a5cd8a..56d7f4d 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/MessageStoreInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/MessageStoreInterceptorTest.java
@@ -18,26 +18,23 @@
  */
 package org.apache.struts2.interceptor;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.opensymphony.xwork2.interceptor.PreResultListener;
-import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.StrutsInternalTestCase;
-import org.apache.struts2.dispatcher.HttpParameters;
-import org.easymock.EasyMock;
-
 import com.opensymphony.xwork2.Action;
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ActionSupport;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.dispatcher.HttpParameters;
+import org.easymock.EasyMock;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 
 /**
@@ -69,7 +66,7 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         action.addActionMessage("some action message 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create().build());
 
         HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
@@ -90,7 +87,7 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         mockActionInvocation.invoke();
         EasyMock.expectLastCall().andReturn(Action.SUCCESS);
 
-        mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
+        mockActionInvocation.addPreResultListener(EasyMock.anyObject());
         EasyMock.expectLastCall();
 
         EasyMock.replay(mockActionInvocation);
@@ -114,19 +111,19 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         mockActionInvocation.invoke();
         EasyMock.expectLastCall().andReturn(Action.SUCCESS);
 
-        Map sessionMap = new LinkedHashMap();
+        Map<String, Object> sessionMap = new LinkedHashMap<>();
 
-        List actionErrors = new ArrayList();
-        List actionMessages = new ArrayList();
-        Map fieldErrors = new LinkedHashMap();
+        List<String> actionErrors = new ArrayList<>();
+        List<String> actionMessages = new ArrayList<>();
+        Map<String, List<String>> fieldErrors = new LinkedHashMap<>();
 
         actionErrors.add("some action error 1");
         actionErrors.add("some action error 2");
         actionMessages.add("some action messages 1");
         actionMessages.add("some action messages 2");
-        List field1Errors = new ArrayList();
+        List<String> field1Errors = new ArrayList<>();
         field1Errors.add("some field error 1");
-        List field2Errors = new ArrayList();
+        List<String> field2Errors = new ArrayList<>();
         field2Errors.add("some field error 2");
         fieldErrors.put("field1", field1Errors);
         fieldErrors.put("field2", field2Errors);
@@ -144,9 +141,9 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
 
         EasyMock.replay(mockedRequest);
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create().build());
-        actionContext.put(ActionContext.SESSION, sessionMap);
+        actionContext.setSession(sessionMap);
 
         mockActionInvocation.getInvocationContext();
         EasyMock.expectLastCall().andReturn(actionContext);
@@ -155,8 +152,8 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         mockActionInvocation.getAction();
         EasyMock.expectLastCall().andReturn(action);
         EasyMock.expectLastCall().anyTimes();
-        
-        mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
+
+        mockActionInvocation.addPreResultListener(EasyMock.anyObject());
         EasyMock.expectLastCall();
 
         EasyMock.replay(mockActionInvocation);
@@ -172,21 +169,21 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         assertTrue(action.getActionErrors().contains("some action error 2"));
         assertTrue(action.getActionMessages().contains("some action messages 1"));
         assertTrue(action.getActionMessages().contains("some action messages 2"));
-        assertEquals(((List)action.getFieldErrors().get("field1")).size(), 1);
-        assertEquals(((List)action.getFieldErrors().get("field2")).size(), 1);
-        assertEquals(((List)action.getFieldErrors().get("field1")).get(0), "some field error 1");
-        assertEquals(((List)action.getFieldErrors().get("field2")).get(0), "some field error 2");
+        assertEquals(action.getFieldErrors().get("field1").size(), 1);
+        assertEquals(action.getFieldErrors().get("field2").size(), 1);
+        assertEquals(action.getFieldErrors().get("field1").get(0), "some field error 1");
+        assertEquals(action.getFieldErrors().get("field2").get(0), "some field error 2");
 
         EasyMock.verify(mockActionInvocation);
     }
-    
+
     public void testAutomatic() throws Exception {
         MessageStoreInterceptor interceptor = new MessageStoreInterceptor();
         interceptor.setAllowRequestParameterSwitch(true);
         interceptor.setOperationMode(MessageStoreInterceptor.AUTOMATIC_MODE);
 
 
-        Map sessionMap = new LinkedHashMap();
+        Map<String, Object> sessionMap = new LinkedHashMap<>();
 
         ActionSupport action = new ActionSupport();
         action.addActionError("some action error 1");
@@ -196,9 +193,9 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         action.addFieldError("field1", "some field error 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create().build());
-        actionContext.put(ActionContext.SESSION, sessionMap);
+        actionContext.setSession(sessionMap);
 
         HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
         HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
@@ -215,7 +212,7 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         EasyMock.expectLastCall().andReturn(actionContext);
         EasyMock.expectLastCall().anyTimes();
 
-        mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
+        mockActionInvocation.addPreResultListener(EasyMock.anyObject());
         EasyMock.expectLastCall();
 
         mockActionInvocation.invoke();
@@ -234,12 +231,12 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         EasyMock.verify(mockActionInvocation);
     }
 
-    public void testRequestOperationMode1() throws Exception {
+    public void testRequestOperationMode1() {
 
-        Map paramMap = new LinkedHashMap();
-        paramMap.put("operationMode", new String[] { MessageStoreInterceptor.RETRIEVE_MODE });
+        Map<String, Object> paramMap = new LinkedHashMap<>();
+        paramMap.put("operationMode", new String[]{MessageStoreInterceptor.RETRIEVE_MODE});
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create(paramMap).build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
@@ -257,12 +254,12 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         EasyMock.verify(mockActionInvocation);
     }
 
-    public void testRequestOperationMode2() throws Exception {
+    public void testRequestOperationMode2() {
 
-        Map paramMap = new LinkedHashMap();
-        paramMap.put("operationMode", new String[] { MessageStoreInterceptor.STORE_MODE });
+        Map<String, Object> paramMap = new LinkedHashMap<>();
+        paramMap.put("operationMode", new String[]{MessageStoreInterceptor.STORE_MODE});
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create(paramMap).build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
@@ -280,9 +277,9 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
         EasyMock.verify(mockActionInvocation);
     }
 
-    public void testRequestOperationMode3() throws Exception {
+    public void testRequestOperationMode3() {
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create().build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
diff --git a/core/src/test/java/org/apache/struts2/interceptor/MessageStorePreResultListenerTest.java b/core/src/test/java/org/apache/struts2/interceptor/MessageStorePreResultListenerTest.java
index bfbbd5c..9b65ace 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/MessageStorePreResultListenerTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/MessageStorePreResultListenerTest.java
@@ -41,10 +41,10 @@ import java.util.Map;
 
 public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
 
-    public void testSessionWasInvalidated() throws Exception {
+    public void testSessionWasInvalidated() {
         // given
-        ActionContext actionContext = new ActionContext(new HashMap());
-        actionContext.put(ActionContext.PARAMETERS, new LinkedHashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setParameters(HttpParameters.create().build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
 
@@ -82,10 +82,10 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
         EasyMock.verify(mockedResponse);
     }
 
-    public void testResponseWasComitted() throws Exception {
+    public void testResponseWasComitted() {
         // given
-        ActionContext actionContext = new ActionContext(new HashMap());
-        actionContext.put(ActionContext.PARAMETERS, new LinkedHashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setParameters(HttpParameters.create().build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
 
@@ -114,14 +114,14 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
         EasyMock.verify(mockedResponse);
     }
 
-    public void testAutomatic() throws Exception {
+    public void testAutomatic() {
         MessageStoreInterceptor interceptor = new MessageStoreInterceptor();
         interceptor.setOperationMode(MessageStoreInterceptor.AUTOMATIC_MODE);
 
         MessageStorePreResultListener listener = new MessageStorePreResultListener();
         listener.init(interceptor);
 
-        Map sessionMap = new LinkedHashMap();
+        Map<String, Object> sessionMap = new LinkedHashMap<>();
 
         ActionSupport action = new ActionSupport();
         action.addActionError("some action error 1");
@@ -131,9 +131,9 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
         action.addFieldError("field1", "some field error 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create().build());
-        actionContext.put(ActionContext.SESSION, sessionMap);
+        actionContext.setSession(sessionMap);
 
         HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
         HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
@@ -194,7 +194,7 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
         EasyMock.verify(mockActionInvocation);
     }
 
-    public void testStoreMessage() throws Exception {
+    public void testStoreMessage() {
         MessageStoreInterceptor interceptor = new MessageStoreInterceptor();
         interceptor.setAllowRequestParameterSwitch(true);
         interceptor.setOperationMode(MessageStoreInterceptor.STORE_MODE);
@@ -202,7 +202,7 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
         MessageStorePreResultListener listener = new MessageStorePreResultListener();
         listener.init(interceptor);
 
-        Map sessionMap = new LinkedHashMap();
+        Map<String, Object> sessionMap = new LinkedHashMap<>();
 
         ActionSupport action = new ActionSupport();
         action.addActionError("some action error 1");
@@ -212,7 +212,7 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
         action.addFieldError("field1", "some field error 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create().build());
         actionContext.setSession(sessionMap);
 
diff --git a/core/src/test/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptorTest.java
index d7189b8..801c607 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptorTest.java
@@ -83,7 +83,7 @@ public class StrutsConversionErrorInterceptorTest extends StrutsInternalTestCase
         mockInvocation = new Mock(ActionInvocation.class);
         invocation = (ActionInvocation) mockInvocation.proxy();
         stack = ActionContext.getContext().getValueStack();
-        context = new ActionContext(stack.getContext());
+        context = ActionContext.ofAndBound(stack.getContext());
         conversionErrors = new HashMap<>();
         context.setConversionErrors(conversionErrors);
         mockInvocation.matchAndReturn("getInvocationContext", context);
diff --git a/core/src/test/java/org/apache/struts2/interceptor/TokenInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/TokenInterceptorTest.java
index 69fba98..45e9f8e 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/TokenInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/TokenInterceptorTest.java
@@ -104,6 +104,8 @@ public class TokenInterceptorTest extends StrutsInternalTestCase {
     }
 
     protected void setUp() throws Exception {
+        super.setUp();
+
         loadConfigurationProviders(new TestConfigurationProvider());
 
         session = new TreeMap<>();
@@ -120,8 +122,7 @@ public class TokenInterceptorTest extends StrutsInternalTestCase {
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.getContext().putAll(extraContext);
-        oldContext = new ActionContext(stack.getContext());
-        ActionContext.setContext(oldContext);
+        oldContext = ActionContext.ofAndBound(stack.getContext());
     }
 
     protected ActionProxy buildProxy(String actionName) throws Exception {
diff --git a/core/src/test/java/org/apache/struts2/result/HttpHeaderResultTest.java b/core/src/test/java/org/apache/struts2/result/HttpHeaderResultTest.java
index 18dcba8..4a79dd3 100644
--- a/core/src/test/java/org/apache/struts2/result/HttpHeaderResultTest.java
+++ b/core/src/test/java/org/apache/struts2/result/HttpHeaderResultTest.java
@@ -125,7 +125,7 @@ public class HttpHeaderResultTest extends StrutsInternalTestCase {
 
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
 }
diff --git a/core/src/test/java/org/apache/struts2/result/PlainTextResultTest.java b/core/src/test/java/org/apache/struts2/result/PlainTextResultTest.java
index 291bdb4..1645129 100644
--- a/core/src/test/java/org/apache/struts2/result/PlainTextResultTest.java
+++ b/core/src/test/java/org/apache/struts2/result/PlainTextResultTest.java
@@ -130,7 +130,7 @@ public class PlainTextResultTest extends StrutsInternalTestCase {
         response.setWriter(writer);
         servletContext = new StrutsMockServletContext();
         stack = ActionContext.getContext().getValueStack();
-        context = new ActionContext(stack.getContext());
+        context = ActionContext.ofAndBound(stack.getContext());
         context.put(StrutsStatics.HTTP_RESPONSE, response);
         context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
         invocation = new MockActionInvocation();
diff --git a/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java
index 1bcee85..8267b4c 100644
--- a/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java
+++ b/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java
@@ -18,38 +18,6 @@
  */
 package org.apache.struts2.result;
 
-import static javax.servlet.http.HttpServletResponse.SC_SEE_OTHER;
-import static org.easymock.EasyMock.createControl;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
-import ognl.Ognl;
-
-import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.StrutsInternalTestCase;
-import org.apache.struts2.StrutsStatics;
-import org.apache.struts2.dispatcher.mapper.ActionMapper;
-import org.apache.struts2.result.ServletRedirectResult;
-import org.apache.struts2.views.util.DefaultUrlHelper;
-import org.easymock.IMocksControl;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-
 import com.mockobjects.dynamic.C;
 import com.mockobjects.dynamic.Mock;
 import com.opensymphony.xwork2.ActionContext;
@@ -60,10 +28,33 @@ import com.opensymphony.xwork2.config.entities.PackageConfig;
 import com.opensymphony.xwork2.config.entities.ResultConfig;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
 import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.apache.struts2.views.util.DefaultUrlHelper;
+import org.easymock.IMocksControl;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static javax.servlet.http.HttpServletResponse.SC_SEE_OTHER;
+import static org.easymock.EasyMock.createControl;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
 
-/**
- */
 public class ServletRedirectResultTest extends StrutsInternalTestCase implements StrutsStatics {
 
     protected ServletRedirectResult view;
@@ -167,6 +158,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
             fail();
         }
     }
+
     public void testPrependServletContextFalse() {
         view.setLocation("/bar/foo.jsp");
         view.setPrependServletContext(false);
@@ -197,7 +189,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
         requestMock.verify();
         responseMock.verify();
     }
-    
+
     public void testMultipleParametersRedirect() throws Exception {
         view.setLocation("foo.jsp?foo=bar&amp;baz=jim");
         requestMock.expectAndReturn("getParameterMap", new HashMap());
@@ -235,11 +227,11 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
         context.put(ServletActionContext.HTTP_RESPONSE, res);
 
 
-        Map<String, ResultConfig> results=  new HashMap<String, ResultConfig>();
+        Map<String, ResultConfig> results = new HashMap<>();
         results.put("myResult", resultConfig);
 
         ActionConfig actionConfig = new ActionConfig.Builder("", "", "")
-                .addResultConfigs(results).build();
+            .addResultConfigs(results).build();
 
         ServletRedirectResult result = new ServletRedirectResult();
         result.setLocation("/myNamespace/myAction.action");
@@ -265,7 +257,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
     }
 
     public void testIncludeCollectionParameterInResult() throws Exception {
-        List<String> paramValues = new ArrayList<String>();
+        List<String> paramValues = new ArrayList<>();
         paramValues.add("value 1");
         paramValues.add("");
         paramValues.add("value 2");
@@ -282,11 +274,11 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
         context.put(ServletActionContext.HTTP_REQUEST, req);
         context.put(ServletActionContext.HTTP_RESPONSE, res);
 
-        Map<String, ResultConfig> results=  new HashMap<String, ResultConfig>();
+        Map<String, ResultConfig> results = new HashMap<>();
         results.put("myResult", resultConfig);
 
         ActionConfig actionConfig = new ActionConfig.Builder("", "", "")
-                .addResultConfigs(results).build();
+            .addResultConfigs(results).build();
 
         ServletRedirectResult result = new ServletRedirectResult();
         result.setLocation("/myNamespace/myAction.action");
@@ -301,7 +293,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
         ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
 
         ValueStack mockValueStack = control.createMock(ValueStack.class);
-        Map<String, Object> mockContext = new HashMap<String, Object>();
+        Map<String, Object> mockContext = new HashMap<>();
         mockContext.put(ActionContext.CONTAINER, container);
 
         expect(mockInvocation.getStack()).andReturn(mockValueStack);
@@ -326,11 +318,11 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
     }
 
     /**
-     * Test to exercise the code path and prove sendRedirect() will output 
+     * Test to exercise the code path and prove sendRedirect() will output
      * the desired log warning when an IOException is thrown for statusCode SC_FOUND.
      */
     public void testSendRedirectSCFoundIOException() {
-        HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
+        HttpServletResponse httpServletResponseMock = createMock(HttpServletResponse.class);
         boolean ioeCaught = false;
         view.setLocation("/bar/foo.jsp");
         view.setStatusCode(HttpServletResponse.SC_FOUND);
@@ -352,11 +344,11 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
     }
 
     /**
-     * Test to exercise the code path and prove sendRedirect() will output 
+     * Test to exercise the code path and prove sendRedirect() will output
      * the desired log warning when an IOException is thrown for statusCode SC_MOVED_PERMANENTLY.
      */
     public void testSendRedirectSCMovedPermanentlyIOException() {
-        HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
+        HttpServletResponse httpServletResponseMock = createMock(HttpServletResponse.class);
         boolean ioeCaught = false;
         view.setLocation("/bar/foo.jsp");
         view.setStatusCode(HttpServletResponse.SC_MOVED_PERMANENTLY);  // Any non SC_FOUND will suffice
@@ -381,7 +373,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
     }
 
     /**
-     * Test to exercise the code path and prove sendRedirect() will output 
+     * Test to exercise the code path and prove sendRedirect() will output
      * the desired log warning when an IllegalStateException is thrown for statusCode SC_FOUND.
      */
     public void testSendRedirectSCFoundIllegalStateException() {
@@ -410,11 +402,11 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
     }
 
     /**
-     * Test to exercise the code path and prove sendRedirect() will output 
+     * Test to exercise the code path and prove sendRedirect() will output
      * the desired log warning when an IllegalStateException is thrown for statusCode SC_MOVED_PERMANENTLY.
      */
     public void testSendRedirectSCMovedPermanentlyIllegalStateException() {
-        HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
+        HttpServletResponse httpServletResponseMock = createMock(HttpServletResponse.class);
         boolean iseCaught = false;
         view.setLocation("/bar/foo.jsp");
         view.setStatusCode(HttpServletResponse.SC_MOVED_PERMANENTLY);  // Any non SC_FOUND will suffice
@@ -454,25 +446,29 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
         requestMock = new Mock(HttpServletRequest.class);
         requestMock.matchAndReturn("getContextPath", "/context");
 
-         ResultConfig resultConfig = new ResultConfig.Builder("", "").build();
+        ResultConfig resultConfig = new ResultConfig.Builder("", "").build();
 
-        Map<String, ResultConfig> results=  new HashMap<String, ResultConfig>();
+        Map<String, ResultConfig> results = new HashMap<>();
         results.put("myResult", resultConfig);
 
         ActionConfig actionConfig = new ActionConfig.Builder("", "", "")
-                .addResultConfigs(results).build();
+            .addResultConfigs(results).build();
+
+        ActionContext ac = ActionContext.getContext();
+        ac.setServletRequest((HttpServletRequest) requestMock.proxy());
+        ac.setServletResponse((HttpServletResponse) responseMock.proxy());
 
-        ActionContext ac = new ActionContext(Ognl.createDefaultContext(null, new SecurityMemberAccess(false, true)));
-        ac.put(ServletActionContext.HTTP_REQUEST, requestMock.proxy());
-        ac.put(ServletActionContext.HTTP_RESPONSE, responseMock.proxy());
         MockActionInvocation ai = new MockActionInvocation();
         ai.setInvocationContext(ac);
         ai.setResultCode("myResult");
+        ai.setInvocationContext(ac);
+
         ActionProxy mockActionProxy = createNiceMock(ActionProxy.class);
         ai.setProxy(mockActionProxy);
         expect(mockActionProxy.getConfig()).andReturn(actionConfig).anyTimes();
         replay(mockActionProxy);
-        this.ai = ai;
         ai.setStack(ActionContext.getContext().getValueStack());
+
+        this.ai = ai;
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java b/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java
index 5d0843b..0a7976c 100644
--- a/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java
+++ b/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java
@@ -32,7 +32,6 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.HashMap;
 import java.util.Map;
-import org.apache.struts2.ServletActionContext;
 
 
 /**
@@ -72,9 +71,9 @@ public class InvocationSessionStoreTest extends StrutsInternalTestCase {
         ActionContext actionContext = ActionContext.getContext();
         InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
 
-        ActionContext actionContext2 = new ActionContext(new HashMap<String, Object>());
+        ActionContext actionContext2 = ActionContext.ofAndBound(new HashMap<>());
         actionContext2.setSession(session);
-        ActionContext.setContext(actionContext2);
+
         assertEquals(actionContext2, ActionContext.getContext());
 
         InvocationSessionStore.loadInvocation(INVOCATION_KEY, TOKEN_VALUE);
@@ -88,12 +87,12 @@ public class InvocationSessionStoreTest extends StrutsInternalTestCase {
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(session);//WW-4873 invocation is not serializable but we should not fail at this line
         oos.close();
-        byte b[] = baos.toByteArray();
+        byte[] b = baos.toByteArray();
         baos.close();
 
         ByteArrayInputStream bais = new ByteArrayInputStream(b);
         ObjectInputStream ois = new ObjectInputStream(bais);
-        session = (Map) ois.readObject();
+        session = (Map<String, Object>) ois.readObject();
         ActionContext.getContext().setSession(session);
         ois.close();
         bais.close();
@@ -108,14 +107,14 @@ public class InvocationSessionStoreTest extends StrutsInternalTestCase {
         // Create mock PageContext to put with the saved context (simulating a PageContext previously
         // used and closed after generating JSP output).
         MockPageContext mockSavedPageContext = new MockPageContext();
-        actionContext.put(ServletActionContext.PAGE_CONTEXT, mockSavedPageContext);
-        assertEquals(mockSavedPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT));
+        actionContext.setPageContext(mockSavedPageContext);
+        assertEquals(mockSavedPageContext, ActionContext.getContext().getPageContext());
 
         InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
 
-        ActionContext actionContext2 = new ActionContext(new HashMap<String, Object>());
+        ActionContext actionContext2 = ActionContext.ofAndBound(new HashMap<>());
         actionContext2.setSession(session);
-        ActionContext.setContext(actionContext2);
+
         assertEquals(actionContext2, ActionContext.getContext());
 
         // Create mock PageContext to put with the current context (simulating a PageContext 
@@ -123,20 +122,19 @@ public class InvocationSessionStoreTest extends StrutsInternalTestCase {
         // will usually be null, but if non-null it should be preserved/restored upon load of the
         // saved context.
         MockPageContext mockPreviousPageContext = new MockPageContext();
-        actionContext2.put(ServletActionContext.PAGE_CONTEXT, mockPreviousPageContext);
-        assertEquals(mockPreviousPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT));
+        actionContext2.setPageContext(mockPreviousPageContext);
+        assertEquals(mockPreviousPageContext, ActionContext.getContext().getPageContext());
 
         InvocationSessionStore.loadInvocation(INVOCATION_KEY, TOKEN_VALUE);
         assertEquals(actionContext, ActionContext.getContext());
-        assertEquals(mockPreviousPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT));
+        assertEquals(mockPreviousPageContext, ActionContext.getContext().getPageContext());
     }
 
     protected void setUp() throws Exception {
         super.setUp();
         stack = ActionContext.getContext().getValueStack();
 
-        ActionContext actionContext = new ActionContext(stack.getContext());
-        ActionContext.setContext(actionContext);
+        ActionContext actionContext = ActionContext.ofAndBound(stack.getContext());
 
         session = new HashMap<>();
         actionContext.setSession(session);
diff --git a/core/src/test/java/org/apache/struts2/util/TokenHelperTest.java b/core/src/test/java/org/apache/struts2/util/TokenHelperTest.java
index 8e6bb2f..bb489de 100644
--- a/core/src/test/java/org/apache/struts2/util/TokenHelperTest.java
+++ b/core/src/test/java/org/apache/struts2/util/TokenHelperTest.java
@@ -18,56 +18,54 @@
  */
 package org.apache.struts2.util;
 
+import com.opensymphony.xwork2.ActionContext;
+import junit.framework.TestCase;
+import org.apache.struts2.dispatcher.HttpParameters;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
-import junit.framework.TestCase;
-
-import com.opensymphony.xwork2.ActionContext;
-import org.apache.struts2.dispatcher.HttpParameters;
-
 
 /**
  * TokenHelperTest
- *
  */
 public class TokenHelperTest extends TestCase {
 
-    private Map session;
+    private Map<String, Object> session;
 
-	public void testTokenSessionNameBuilding() throws Exception {
-		String name = "foo";
-		String sessionName = TokenHelper.buildTokenSessionAttributeName(name);
-		assertEquals(TokenHelper.TOKEN_NAMESPACE + "." + name, sessionName);
-	}
+    public void testTokenSessionNameBuilding() {
+        String name = "foo";
+        String sessionName = TokenHelper.buildTokenSessionAttributeName(name);
+        assertEquals(TokenHelper.TOKEN_NAMESPACE + "." + name, sessionName);
+    }
 
     public void testSetToken() {
         String token = TokenHelper.setToken();
-		final String defaultSessionTokenName = TokenHelper.buildTokenSessionAttributeName(TokenHelper.DEFAULT_TOKEN_NAME);
-		assertEquals(token, session.get(defaultSessionTokenName));
+        final String defaultSessionTokenName = TokenHelper.buildTokenSessionAttributeName(TokenHelper.DEFAULT_TOKEN_NAME);
+        assertEquals(token, session.get(defaultSessionTokenName));
     }
 
     public void testSetTokenWithName() {
         String tokenName = "myTestToken";
         String token = TokenHelper.setToken(tokenName);
-		final String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
-		assertEquals(token, session.get(sessionTokenName));
+        final String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
+        assertEquals(token, session.get(sessionTokenName));
     }
 
-	public void testSetSessionToken() {
-		String tokenName = "myOtherTestToken";
-		String token = "foobar";
-		TokenHelper.setSessionToken(tokenName, token);
-		final String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
-		assertEquals(token, session.get(sessionTokenName));
-	}
+    public void testSetSessionToken() {
+        String tokenName = "myOtherTestToken";
+        String token = "foobar";
+        TokenHelper.setSessionToken(tokenName, token);
+        final String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
+        assertEquals(token, session.get(sessionTokenName));
+    }
 
-	public void testValidToken() {
+    public void testValidToken() {
         String tokenName = "validTokenTest";
         String token = TokenHelper.setToken(tokenName);
-		final String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
-		assertEquals(token, session.get(sessionTokenName));
+        final String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
+        assertEquals(token, session.get(sessionTokenName));
 
         Map<String, String[]> params = new HashMap<>();
         params.put(TokenHelper.TOKEN_NAME_FIELD, new String[]{tokenName});
@@ -80,23 +78,22 @@ public class TokenHelperTest extends TestCase {
 
     public void testGetTokenDoesNotNpe() {
         String token = TokenHelper.getToken(null);
-        assertTrue(token == null);
+        assertNull(token);
 
         String token2 = TokenHelper.getToken("");
-        assertTrue(token2 == null);
+        assertNull(token2);
     }
 
     protected void setUp() throws Exception {
-        session = new HashMap();
-        Map ctxMap = new TreeMap();
-        ctxMap.put(ActionContext.SESSION, session);
-        ctxMap.put(ActionContext.PARAMETERS, HttpParameters.create().build());
-        ActionContext ctx = new ActionContext(ctxMap);
-        ActionContext.setContext(ctx);
+        session = new HashMap<>();
+        Map<String, Object> ctxMap = new TreeMap<>();
+        ActionContext ctx = ActionContext.ofAndBound(ctxMap);
+        ctx.setSession(session);
+        ctx.setParameters(HttpParameters.create().build());
     }
 
     protected void tearDown() {
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 }
 
diff --git a/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java b/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java
index 56aa078..07d1fb2 100644
--- a/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java
+++ b/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java
@@ -23,9 +23,7 @@ import com.opensymphony.xwork2.mock.MockActionInvocation;
 import com.opensymphony.xwork2.mock.MockActionProxy;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
-import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.views.jsp.StrutsMockHttpServletResponse;
 import org.apache.struts2.views.jsp.StrutsMockServletContext;
 import org.springframework.mock.web.MockHttpServletRequest;
@@ -34,8 +32,6 @@ import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
-import static org.apache.struts2.views.jsp.AbstractUITagTest.normalize;
-
 /**
  * Test case for FreeMarkerResult.
  */
@@ -51,7 +47,7 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
     private FreemarkerManager mgr;
     private MockHttpServletRequest request;
 
-    public void testWriteIfCompleted() throws Exception {
+    public void testWriteIfCompleted() {
         FreemarkerResult result = new FreemarkerResult();
         result.setLocation("someFreeMarkerFile.ftl");
         result.setFreemarkerManager(mgr);
@@ -59,20 +55,20 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
 
         try {
             result.execute(invocation);
-            assertTrue(false);
+            fail();
         } catch (Exception e) {
             assertEquals(0, stringWriter.getBuffer().length());
         }
     }
 
-    public void testWithoutWriteIfCompleted() throws Exception {
+    public void testWithoutWriteIfCompleted() {
         FreemarkerResult result = new FreemarkerResult();
         result.setLocation("someFreeMarkerFile.ftl");
         result.setFreemarkerManager(mgr);
 
         try {
             result.execute(invocation);
-            assertTrue(false);
+            fail();
         } catch (Exception e) {
             assertTrue(stringWriter.getBuffer().length() > 0);
         }
@@ -80,7 +76,7 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
 
     public void testContentTypeIsNotOverwritten() throws Exception {
         servletContext.setRealPath(new File(FreeMarkerResultTest.class.getResource(
-                "nested.ftl").toURI()).toURL().getFile());
+            "nested.ftl").toURI()).toURL().getFile());
 
         FreemarkerResult result = new FreemarkerResult();
         result.setLocation("nested.ftl");
@@ -93,7 +89,7 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
 
     public void testDefaultContentType() throws Exception {
         servletContext.setRealPath(new File(FreeMarkerResultTest.class.getResource(
-                "nested.ftl").toURI()).toURL().getFile());
+            "nested.ftl").toURI()).toURL().getFile());
 
         FreemarkerResult result = new FreemarkerResult();
         result.setLocation("nested.ftl");
@@ -106,7 +102,7 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
 
     public void testContentTypeFromTemplate() throws Exception {
         servletContext.setRealPath(new File(FreeMarkerResultTest.class.getResource(
-                "something.ftl").toURI()).toURL().getFile());
+            "something.ftl").toURI()).toURL().getFile());
 
         FreemarkerResult result = new FreemarkerResult();
         result.setLocation("something.ftl");
@@ -138,14 +134,11 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
         servletContext = new StrutsMockServletContext();
         stack = ActionContext.getContext().getValueStack();
 
-        context = new ActionContext(stack.getContext());
-        context.put(StrutsStatics.HTTP_RESPONSE, response);
-        context.put(StrutsStatics.HTTP_REQUEST, request);
-        context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+        context = ActionContext.ofAndBound(stack.getContext());
+        context.setServletResponse(response);
+        context.setServletRequest(request);
+        context.setServletContext(servletContext);
 
-        ServletActionContext.setServletContext(servletContext);
-        ServletActionContext.setRequest(request);
-        ServletActionContext.setResponse(response);
         servletContext.setAttribute(FreemarkerManager.CONFIG_SERVLET_CONTEXT_KEY, null);
 
         invocation = new MockActionInvocation();
@@ -153,7 +146,7 @@ public class FreeMarkerResultTest extends StrutsInternalTestCase {
         invocation.setInvocationContext(context);
         invocation.setProxy(new MockActionProxy());
         servletContext.setRealPath(new File(FreeMarkerResultTest.class.getResource(
-                "someFreeMarkerFile.ftl").toURI()).toURL().getFile());
+            "someFreeMarkerFile.ftl").toURI()).toURL().getFile());
     }
 
     protected void tearDown() throws Exception {
diff --git a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
index d82a660..d88b8ab 100644
--- a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
+++ b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
@@ -22,13 +22,10 @@ import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
 import com.opensymphony.xwork2.util.ClassLoaderUtil;
 import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
 import freemarker.template.Configuration;
-import freemarker.template.TemplateException;
 import freemarker.template.TemplateExceptionHandler;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
-import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.dispatcher.mapper.ActionMapper;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
 import org.apache.struts2.views.jsp.StrutsMockHttpServletResponse;
@@ -39,8 +36,6 @@ import javax.servlet.ServletContext;
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
 
 import static org.apache.struts2.views.jsp.AbstractUITagTest.normalize;
 
@@ -119,15 +114,15 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
         dispatcher.serviceAction(request, response, mapping);
 
         String expectedJDK17 =
-                "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" placeholder=\"input\" foo=\"bar\"/>"
-                        + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" placeholder=\"input\" foo=\"bar\"/>"
-                        + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" break=\"true\"/>"
-                        + "<input type=\"text\" name=\"required\" value=\"\" id=\"required\" required=\"true\"/>";
+            "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" placeholder=\"input\" foo=\"bar\"/>"
+                + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" placeholder=\"input\" foo=\"bar\"/>"
+                + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" break=\"true\"/>"
+                + "<input type=\"text\" name=\"required\" value=\"\" id=\"required\" required=\"true\"/>";
         String expectedJDK18 =
-                "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" foo=\"bar\" placeholder=\"input\"/>"
-                        + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" foo=\"bar\" placeholder=\"input\"/>"
-                        + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" break=\"true\"/>"
-                        + "<input type=\"text\" name=\"required\" value=\"\" id=\"required\" required=\"true\"/>";
+            "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" foo=\"bar\" placeholder=\"input\"/>"
+                + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" foo=\"bar\" placeholder=\"input\"/>"
+                + "<input type=\"text\" name=\"test\" value=\"\" id=\"test\" break=\"true\"/>"
+                + "<input type=\"text\" name=\"required\" value=\"\" id=\"required\" required=\"true\"/>";
 
         String result = stringWriter.toString();
 
@@ -169,10 +164,10 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
         ActionMapping mapping = container.getInstance(ActionMapper.class).getMapping(request, configurationManager);
         dispatcher.serviceAction(request, response, mapping);
         String expected = "<input type=\"radio\" name=\"client\" id=\"client_foo\" value=\"foo\"/><label for=\"client_foo\">foo</label>\n"
-                + "<input type=\"radio\" name=\"client\" id=\"client_bar\" value=\"bar\"/><label for=\"client_bar\">bar</label>\n"
-                + "\n"
-                + "<input type=\"radio\" name=\"car\" id=\"carford\" value=\"ford\"/><label for=\"carford\">Ford Motor Co</label>\n"
-                + "<input type=\"radio\" name=\"car\" id=\"cartoyota\" value=\"toyota\"/><label for=\"cartoyota\">Toyota</label>\n";
+            + "<input type=\"radio\" name=\"client\" id=\"client_bar\" value=\"bar\"/><label for=\"client_bar\">bar</label>\n"
+            + "\n"
+            + "<input type=\"radio\" name=\"car\" id=\"carford\" value=\"ford\"/><label for=\"carford\">Ford Motor Co</label>\n"
+            + "<input type=\"radio\" name=\"car\" id=\"cartoyota\" value=\"toyota\"/><label for=\"cartoyota\">Toyota</label>\n";
         assertEquals(normalize(expected), normalize(stringWriter.toString()));
     }
 
@@ -243,7 +238,7 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
         assertTrue(result.contains("<option value=\"2\">2</option>"));
     }
 
-    private void init() throws MalformedURLException, URISyntaxException {
+    private void init() {
         stringWriter = new StringWriter();
         writer = new PrintWriter(stringWriter);
         response = new StrutsMockHttpServletResponse();
@@ -251,14 +246,11 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
         request = new MockHttpServletRequest();
         stack = ActionContext.getContext().getValueStack();
 
-        context = new ActionContext(stack.getContext());
-        context.put(StrutsStatics.HTTP_RESPONSE, response);
-        context.put(StrutsStatics.HTTP_REQUEST, request);
-        context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+        context = ActionContext.ofAndBound(stack.getContext());
+        context.setServletResponse(response);
+        context.setServletRequest(request);
+        context.setServletContext(servletContext);
 
-        ServletActionContext.setServletContext(servletContext);
-        ServletActionContext.setRequest(request);
-        ServletActionContext.setResponse(response);
         servletContext.setAttribute(FreemarkerManager.CONFIG_SERVLET_CONTEXT_KEY, null);
 
         invocation = new MockActionInvocation();
diff --git a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerTest.java b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerTest.java
index 1dc75d8..afa6ba0 100644
--- a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerTest.java
+++ b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerTest.java
@@ -71,6 +71,6 @@ public class FreemarkerTest extends StrutsInternalTestCase {
 
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
index bc056dc..dc814fd 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
@@ -49,8 +49,8 @@ import com.opensymphony.xwork2.util.ValueStack;
  */
 public abstract class AbstractTagTest extends StrutsInternalTestCase {
     protected Action action;
-    protected Map context;
-    protected Map session;
+    protected Map<String, Object> context;
+    protected Map<String, Object> session;
     protected ValueStack stack;
 
     /**
@@ -75,13 +75,10 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
 
     protected void setUp() throws Exception {
         super.setUp();
-        /**
-         * create our standard mock objects
-         */
         createMocks();
     }
 
-    protected void createMocks() throws Exception {
+    protected void createMocks() {
         action = this.getAction();
         container.inject(action);
 
@@ -109,10 +106,10 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
         pageContext.setServletContext(servletContext);
 
         mockContainer = new Mock(Container.class);
-        MockDispatcher du = new MockDispatcher(pageContext.getServletContext(), new HashMap<String, String>(), configurationManager);
+        MockDispatcher du = new MockDispatcher(pageContext.getServletContext(), new HashMap<>(), configurationManager);
         du.init();
         Dispatcher.setInstance(du);
-        session = new SessionMap(request);
+        session = new SessionMap<>(request);
         Map<String, Object> extraContext = du.createContextMap(new RequestMap(request),
                 HttpParameters.create(request.getParameterMap()).build(),
                 session,
@@ -124,11 +121,10 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
         extraContext.remove(ActionContext.LOCALE);
         stack.getContext().putAll(extraContext);
 
-        context.put(ServletActionContext.HTTP_REQUEST, request);
-        context.put(ServletActionContext.HTTP_RESPONSE, response);
-        context.put(ServletActionContext.SERVLET_CONTEXT, servletContext);
-
-        ActionContext.setContext(new ActionContext(context));
+        ActionContext actionContext = ActionContext.ofAndBound(context);
+        actionContext.setServletRequest(request);
+        actionContext.setServletResponse(response);
+        actionContext.setServletContext(servletContext);
     }
 
     protected void tearDown() throws Exception {
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java
index 2cde3d7..79603ac 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java
@@ -251,7 +251,7 @@ public abstract class AbstractUITagTest extends AbstractTagTest {
 
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     /**
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ActionTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ActionTagTest.java
index ea6f572..627f0a5 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ActionTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ActionTagTest.java
@@ -107,7 +107,7 @@ public class ActionTagTest extends AbstractTagTest {
         this.testSimple();
     }
 
-    public void testSimpleWithctionMethodInOriginalURI() {
+    public void testSimpleWithActionMethodInOriginalURI() {
         request.setupGetServletPath("/foo!foo.action");
 
         ActionConfig config = configuration.getRuntimeConfiguration().getActionConfig("", "testAction");
@@ -310,7 +310,6 @@ public class ActionTagTest extends AbstractTagTest {
     }
 
     protected void tearDown() throws Exception {
-
         super.tearDown();
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java
index fce231a..1ec0175 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java
@@ -104,10 +104,10 @@ public class TextTagTest extends AbstractTagTest {
     public void testMessageFormatWorks() throws Exception {
         String key = "messageFormatKey";
         String pattern = "Params are {0} {1} {2}";
-        Object param1 = new Integer(12);
+        Object param1 = 12;
         Object param2 = new Date();
         Object param3 = "StringVal";
-        List params = new ArrayList();
+        List<Object> params = new ArrayList<>();
         params.add(param1);
         params.add(param2);
         params.add(param3);
@@ -255,7 +255,7 @@ public class TextTagTest extends AbstractTagTest {
     }
 
     public void testPutId() throws Exception {
-        assertEquals(null, stack.findString("myId")); // nothing in stack
+        assertNull(stack.findString("myId")); // nothing in stack
         tag.setVar("myId");
         tag.setName("bar.baz");
         tag.doStartTag();
@@ -286,7 +286,7 @@ public class TextTagTest extends AbstractTagTest {
 
     public void testEscapeJavaScript() throws Exception {
         final String key = "foo.escape.javascript";
-        final String value = "\\t\\b\\n\\f\\r\\\"\\\'\\/\\\\";
+        final String value = "\\t\\b\\n\\f\\r\\\"\\'\\/\\\\";
         tag.setName(key);
         tag.setEscapeJavaScript(true);
         tag.doStartTag();
@@ -304,16 +304,11 @@ public class TextTagTest extends AbstractTagTest {
         assertEquals(value, writer.toString());
     }
 
-    /**
-     * todo remove ActionContext set after LocalizedTextUtil is fixed to not use ThreadLocal
-     *
-     * @throws Exception
-     */
     protected void setUp() throws Exception {
         super.setUp();
         tag = new TextTag();
         tag.setPageContext(pageContext);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
+        ActionContext.ofAndBound(stack.getContext());
     }
 
     protected void tearDown() throws Exception {
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java
index 8e0bccb..5f89ab1 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java
@@ -147,14 +147,13 @@ public class URLTagTest extends AbstractUITagTest {
 
     /**
      * Use Iterable values as the value of the param tags
-     * @throws Exception
      */
     public void testIterableParameters() throws Exception {
         tag.setValue("/TestAction.action?p0=z");
         
         tag.doStartTag();
         //Iterable
-        List<ValueHolder> list = new ArrayList<ValueHolder>();
+        List<ValueHolder> list = new ArrayList<>();
         list.add(new ValueHolder("a"));
         list.add(new ValueHolder("b"));
         tag.component.addParameter("p1", list);
@@ -180,8 +179,6 @@ public class URLTagTest extends AbstractUITagTest {
      *  In this case only parameters from the tag itself is taken into account.
      *  Those from request will not count, only those in tag's value attribute
      *  and nested param tag.
-     *
-     * @throws Exception
      */
     public void testParametersPriorityWithIncludeParamsAsNONE() throws Exception {
         request.setQueryString("id1=urlId1&id2=urlId2&urlParam1=urlValue1&urlParam2=urlValue2");
@@ -229,7 +226,7 @@ public class URLTagTest extends AbstractUITagTest {
 
         // request parameter map should not have any effect, as includeParams
         // default to GET, which get its param from request.getQueryString()
-        Map tmp = new HashMap();
+        Map<String, String> tmp = new HashMap<>();
         tmp.put("one", "aaa");
         tmp.put("two", "bbb");
         tmp.put("three", "ccc");
@@ -353,7 +350,7 @@ public class URLTagTest extends AbstractUITagTest {
 
     public void testPutId() throws Exception {
         tag.setValue("/public/about");
-        assertEquals(null, stack.findString("myId")); // nothing in stack
+        assertNull(stack.findString("myId")); // nothing in stack
         tag.setVar("myId");
         tag.doStartTag();
         tag.doEndTag();
@@ -516,16 +513,13 @@ public class URLTagTest extends AbstractUITagTest {
     }
     
     public void testEmptyActionCustomMapper() throws Exception {
-        Map<String,String> props = new HashMap<String, String>();
+        Map<String,String> props = new HashMap<>();
         props.put("config", "struts-default.xml,struts-plugin.xml,struts.xml,org/apache/struts2/views/jsp/WW3090-struts.xml");
         
         this.tearDown();
         
         Dispatcher du = this.initDispatcher(props);
         
-        /**
-         * create our standard mock objects
-         */
         action = this.getAction();
         stack = ActionContext.getContext().getValueStack();
         context = stack.getContext();
@@ -550,7 +544,7 @@ public class URLTagTest extends AbstractUITagTest {
 
         mockContainer = new Mock(Container.class);
 
-        session = new SessionMap(request);
+        session = new SessionMap<>(request);
         Map<String, Object> extraContext = du.createContextMap(new RequestMap(request),
                 HttpParameters.create(request.getParameterMap()).build(),
                 session,
@@ -562,12 +556,12 @@ public class URLTagTest extends AbstractUITagTest {
         extraContext.remove(ActionContext.LOCALE);
         stack.getContext().putAll(extraContext);
 
-        context.put(ServletActionContext.HTTP_REQUEST, request);
-        context.put(ServletActionContext.HTTP_RESPONSE, response);
-        context.put(ServletActionContext.SERVLET_CONTEXT, servletContext);
+        ActionContext actionContext = ActionContext.ofAndBound(context);
+        actionContext.setServletRequest(request);
+        actionContext.setServletResponse(response);
+        actionContext.setServletContext(servletContext);
+
 
-        ActionContext.setContext(new ActionContext(context));
-        
         // Make sure we have an action invocation available
         ActionContext.getContext().setActionInvocation(new DefaultActionInvocation(null, true));
         DefaultActionProxyFactory apFactory = new DefaultActionProxyFactory();
@@ -780,7 +774,8 @@ public class URLTagTest extends AbstractUITagTest {
         
         
     }
-    
+
+    @SuppressWarnings("unused")
     public static class RedBlueActionMapper extends DefaultActionMapper {
         
         @Override
diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
index 02a4878..ad6f927 100644
--- a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
+++ b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
@@ -89,9 +89,8 @@ public class PackageBasedActionConfigBuilderTest extends TestCase {
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        ActionContext context = new ActionContext(new HashMap<String, Object>());
+        ActionContext context = ActionContext.ofAndBound(new HashMap<>());
         context.setContainer(new DummyContainer());
-        ActionContext.setContext(context);
     }
 
     public void testActionPackages() throws MalformedURLException {
@@ -692,7 +691,7 @@ public class PackageBasedActionConfigBuilderTest extends TestCase {
 
         ObjectFactory workingFactory = configuration.getContainer().getInstance(ObjectFactory.class);
         ConventionUnknownHandler uh = new ConventionUnknownHandler(configuration, workingFactory, context, mockContainer, "struts-default", null, "-");
-        ActionContext actionContext = new ActionContext(Collections.EMPTY_MAP);
+        ActionContext actionContext = ActionContext.ofAndBound(Collections.emptyMap());
 
         Result result = uh.handleUnknownResult(actionContext, "foo", pkgConfig.getActionConfigs().get("foo"), "bar");
         assertNotNull(result);
diff --git a/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPLoader.java b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPLoader.java
index 713fcb8..57c19fb 100644
--- a/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPLoader.java
+++ b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPLoader.java
@@ -182,11 +182,11 @@ public class JSPLoader {
         List<URL> urls = urlSet.getUrls();
 
         if (urls != null && urls.size() > 0) {
-            final FileManagerFactory fileManagerFactoryGetInstance = ServletActionContext.getContext().getInstance(FileManagerFactory.class);
-            final FileManagerFactory contextFileManagerFactory = (fileManagerFactoryGetInstance != null ? fileManagerFactoryGetInstance : (FileManagerFactory) ServletActionContext.getContext().get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)); 
+            final FileManagerFactory fileManagerFactoryGetInstance = ServletActionContext.getActionContext().getInstance(FileManagerFactory.class);
+            final FileManagerFactory contextFileManagerFactory = (fileManagerFactoryGetInstance != null ? fileManagerFactoryGetInstance : (FileManagerFactory) ServletActionContext.getActionContext().get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY));
             final FileManagerFactory fileManagerFactory = (contextFileManagerFactory != null ? contextFileManagerFactory : new DefaultFileManagerFactory());
             final FileManager fileManagerGetInstance = fileManagerFactory.getFileManager();
-            final FileManager contextFileManager = (fileManagerGetInstance != null ? fileManagerGetInstance : (FileManager) ServletActionContext.getContext().get(StrutsConstants.STRUTS_FILE_MANAGER));
+            final FileManager contextFileManager = (fileManagerGetInstance != null ? fileManagerGetInstance : (FileManager) ServletActionContext.getActionContext().get(StrutsConstants.STRUTS_FILE_MANAGER));
             final FileManager fileManager = (contextFileManager != null ? contextFileManager : new DefaultFileManager());
             for (URL url : urls) {
                 URL normalizedUrl = fileManager.normalizeToFileProtocol(url);
diff --git a/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java b/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java
index ca72f8f..27321dc 100644
--- a/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java
+++ b/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java
@@ -25,8 +25,8 @@ import com.opensymphony.xwork2.FileManager;
 import com.opensymphony.xwork2.FileManagerFactory;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.util.TextParser;
 import com.opensymphony.xwork2.util.OgnlTextParser;
+import com.opensymphony.xwork2.util.TextParser;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
 import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
@@ -39,7 +39,6 @@ import org.apache.struts2.views.util.DefaultUrlHelper;
 import org.apache.struts2.views.util.UrlHelper;
 import org.apache.tomcat.InstanceManager;
 import org.easymock.EasyMock;
-import org.easymock.IAnswer;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockServletConfig;
@@ -49,7 +48,11 @@ import javax.servlet.Servlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 import java.io.InputStream;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
 
@@ -205,7 +208,7 @@ public class EmbeddedJSPResultTest extends TestCase {
         CyclicBarrier startBarrier = new CyclicBarrier(numThreads + 1);
         CyclicBarrier endBarrier = new CyclicBarrier(numThreads + 1);
 
-        List<ServletGetRunnable> runnables = new ArrayList<ServletGetRunnable>(numThreads);
+        List<ServletGetRunnable> runnables = new ArrayList<>(numThreads);
 
         //create the threads
         for (int i = 0; i < numThreads; i++) {
@@ -261,13 +264,13 @@ public class EmbeddedJSPResultTest extends TestCase {
         int lastHtmlIndex = responseString.indexOf("</html>");
         assertTrue("Did not find title index (" + titleIndex + ") ?", titleIndex > 0);
         assertTrue("Test value 1 not present or index (" + testValue1Index + ") not > title index (" + titleIndex + ") ?",
-                testValue1Index > titleIndex);
+            testValue1Index > titleIndex);
         assertTrue("Test value 5 not present or index (" + testValue5Index + ") not > test value 1 index (" + testValue1Index + ") ?",
-                testValue5Index > testValue1Index);
+            testValue5Index > testValue1Index);
         assertTrue("Last group index not present or index (" + lastGroupIndex + ") not > test value 5 index (" + testValue5Index + ") ?",
-                lastGroupIndex > testValue5Index);
+            lastGroupIndex > testValue5Index);
         assertTrue("Last html index not present or index (" + lastHtmlIndex + ") not > last group index (" + lastGroupIndex + ") ?",
-                lastHtmlIndex > lastGroupIndex);
+            lastHtmlIndex > lastGroupIndex);
         // complex0.jsp length 3439 in Windows and estimated 3221 in Linux/Unix (with 218 lines, Windows has around 218 additional
         //   characters (crlf vs. lf).  Test length larger than the min(Windows,Linux)), rounded down to the nearest 100.
         assertTrue("Response length (" + responseLength + ") not at least length: 3200 ?", responseLength > 3200);
@@ -279,10 +282,10 @@ public class EmbeddedJSPResultTest extends TestCase {
         assertNotNull("instanceManager (servlet) is null ?", instanceManagerServlet);
         assertNotNull("instanceManager (classloader) is null ?", instanceManagerClassLoader);
         assertEquals("instanceManager (servlet) is not equal to instanceManager (classloader) ?", instanceManagerServlet, instanceManagerClassLoader);
-        final Double instanceDouble = new Double(0);
-        final Long instanceLong = new Long(0);
+        final Double instanceDouble = (double) 0;
+        final Long instanceLong = 0L;
         final Object instanceObject = new Object();
-        final String instanceString = new String("test string");
+        final String instanceString = "test string";
         final MockHttpServletRequest intanceMockHttpServletRequest = new MockHttpServletRequest();
         intanceMockHttpServletRequest.setContextPath("context path");
         InstanceHelper.postConstruct(instanceManagerServlet, instanceDouble);
@@ -292,7 +295,7 @@ public class EmbeddedJSPResultTest extends TestCase {
         InstanceHelper.postConstruct(instanceManagerServlet, intanceMockHttpServletRequest);
         assertEquals("test string value changed after postConstruct ?", instanceString, "test string");
         assertEquals("mock servlet request context path value changed after postConstruct ?",
-                intanceMockHttpServletRequest.getContextPath(), "context path");
+            intanceMockHttpServletRequest.getContextPath(), "context path");
         InstanceHelper.preDestroy(instanceManagerServlet, instanceDouble);
         InstanceHelper.preDestroy(instanceManagerServlet, instanceLong);
         InstanceHelper.preDestroy(instanceManagerServlet, instanceObject);
@@ -300,7 +303,7 @@ public class EmbeddedJSPResultTest extends TestCase {
         InstanceHelper.preDestroy(instanceManagerServlet, intanceMockHttpServletRequest);
         assertEquals("test string value changed after preDestroy ?", instanceString, "test string");
         assertEquals("mock servlet request context path value changed after preDestroy ?",
-                intanceMockHttpServletRequest.getContextPath(), "context path");
+            intanceMockHttpServletRequest.getContextPath(), "context path");
     }
 
     @Override
@@ -321,24 +324,19 @@ public class EmbeddedJSPResultTest extends TestCase {
 
         EasyMock.expect(request.getSession()).andReturn(session).anyTimes();
         EasyMock.expect(request.getParameterMap()).andReturn(params).anyTimes();
-        EasyMock.expect(request.getParameter("username")).andAnswer(new IAnswer<String>() {
-            public String answer() throws Throwable {
-                return ActionContext.getContext().getParameters().get("username").getValue();
-            }
-        });
+        EasyMock.expect(request.getParameter("username")).andAnswer(() -> ActionContext.getContext().getParameters().get("username").getValue());
         EasyMock.expect(request.getAttribute("something")).andReturn("somethingelse").anyTimes();
 
         EasyMock.replay(request);
 
-        ActionContext actionContext = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(actionContext);
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
         actionContext.setParameters(HttpParameters.create(params).build());
-        ServletActionContext.setRequest(request);
-        ServletActionContext.setResponse(response);
-        ServletActionContext.setServletContext(context);
+        actionContext.setServletRequest(request);
+        actionContext.setServletResponse(response);
+        actionContext.setServletContext(context);
 
         //mock value stack
-        Map stackContext = new HashMap();
+        Map<String, Object> stackContext = new HashMap<>();
         ValueStack valueStack = EasyMock.createNiceMock(ValueStack.class);
         EasyMock.expect(valueStack.getContext()).andReturn(stackContext).anyTimes();
         EasyMock.replay(valueStack);
@@ -354,7 +352,7 @@ public class EmbeddedJSPResultTest extends TestCase {
         EasyMock.expect(container.getInstance(XWorkConverter.class)).andReturn(converter).anyTimes();
         TextParser parser = new OgnlTextParser();
         EasyMock.expect(container.getInstance(TextParser.class)).andReturn(parser).anyTimes();
-        EasyMock.expect(container.getInstanceNames(FileManager.class)).andReturn(new HashSet<String>()).anyTimes();
+        EasyMock.expect(container.getInstanceNames(FileManager.class)).andReturn(new HashSet<>()).anyTimes();
         EasyMock.expect(container.getInstance(FileManager.class)).andReturn(fileManager).anyTimes();
 
         UrlHelper urlHelper = new DefaultUrlHelper();
@@ -408,7 +406,7 @@ class ServletGetRunnable implements Runnable {
     }
 
     public void run() {
-        ActionContext.setContext(actionContext);
+        actionContext = ActionContext.bound(actionContext);
         //wait to start all threads at once..or try at least
         try {
             startBarrier.await();
@@ -432,7 +430,7 @@ class ServletGetRunnable implements Runnable {
 }
 
 class CountingClassLoaderInterface extends ClassLoaderInterfaceDelegate {
-    public Map<String, Integer> counters = new HashMap<String, Integer>();
+    public Map<String, Integer> counters = new HashMap<>();
 
     public CountingClassLoaderInterface(ClassLoader classLoader) {
         super(classLoader);
diff --git a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AbstractTest.java b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AbstractTest.java
index 7dd36c9..0810447 100644
--- a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AbstractTest.java
+++ b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AbstractTest.java
@@ -24,17 +24,15 @@ package org.apache.struts2.views.java.simple;
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.util.TextParser;
 import com.opensymphony.xwork2.util.OgnlTextParser;
+import com.opensymphony.xwork2.util.TextParser;
 import com.opensymphony.xwork2.util.ValueStack;
 import junit.framework.TestCase;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsConstants;
 import org.apache.struts2.components.Component;
 import org.apache.struts2.components.UIBean;
 import org.apache.struts2.components.template.Template;
 import org.apache.struts2.components.template.TemplateRenderingContext;
-import static org.easymock.EasyMock.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -43,10 +41,15 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Stack;
 
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
 public abstract class AbstractTest extends TestCase {
-    private Map<String, String> scriptingAttrs = new HashMap<String, String>();
-    private Map<String, String> commonAttrs = new HashMap<String, String>();
-    private Map<String, Object> dynamicAttrs = new HashMap<String, Object>();
+    private Map<String, String> scriptingAttrs = new HashMap<>();
+    private Map<String, String> commonAttrs = new HashMap<>();
+    private Map<String, Object> dynamicAttrs = new HashMap<>();
 
     protected SimpleTheme theme;
 
@@ -54,7 +57,7 @@ public abstract class AbstractTest extends TestCase {
     protected Map map;
 
     protected Template template;
-    protected Map stackContext;
+    protected Map<String, Object> stackContext;
     protected ValueStack stack;
     protected TemplateRenderingContext context;
     protected HttpServletRequest request;
@@ -66,7 +69,7 @@ public abstract class AbstractTest extends TestCase {
 
     @Override
     protected void setUp() throws Exception {
-        super.setUp();    
+        super.setUp();
         scriptingAttrs.put("onclick", "onclick_");
         scriptingAttrs.put("ondblclick", "ondblclick_");
         scriptingAttrs.put("onmousedown", "onmousedown_");
@@ -89,12 +92,12 @@ public abstract class AbstractTest extends TestCase {
 
         theme = new SimpleTheme();
         writer = new StringWriter();
-        map = new HashMap();
+        map = new HashMap<>();
 
         template = createMock(Template.class);
         stack = createNiceMock(ValueStack.class);
         setUpStack();
-        stackContext = new HashMap();
+        stackContext = new HashMap<>();
 
         context = new TemplateRenderingContext(template, writer, stack, map, null);
         stackContext.put(Component.COMPONENT_STACK, new Stack());
@@ -118,15 +121,15 @@ public abstract class AbstractTest extends TestCase {
         replay(stack);
         replay(container);
 
-        ActionContext.setContext(new ActionContext(stackContext));
-        ServletActionContext.setRequest(request);
+        ActionContext actionContext = ActionContext.ofAndBound(stackContext);
+        actionContext.setServletRequest(request);
     }
 
     protected static String s(String input) {
         return input.replaceAll("'", "\"");
     }
 
-    protected void expectFind(String expr, Class toClass, Object returnVal) {
+    protected void expectFind(String expr, Class<?> toClass, Object returnVal) {
         expect(stack.findValue(expr, toClass)).andReturn(returnVal);
         expect(stack.findValue(expr, toClass, false)).andReturn(returnVal);
     }
@@ -162,41 +165,32 @@ public abstract class AbstractTest extends TestCase {
     }
 
     protected void applyDynamicAttrs(UIBean bean) {
-    	bean.setDynamicAttributes(dynamicAttrs);
+        bean.setDynamicAttributes(dynamicAttrs);
     }
 
     protected void assertScriptingAttrs(String str) {
         for (Map.Entry<String, String> entry : scriptingAttrs.entrySet()) {
             String substr = entry.getKey() + "=\"" + entry.getValue() + "\"";
-            assertTrue("String [" + substr + "] was not found in [" + str + "]", str.indexOf(substr) >= 0);
+            assertTrue("String [" + substr + "] was not found in [" + str + "]", str.contains(substr));
         }
     }
 
     protected void assertCommonAttrs(String str) {
         for (Map.Entry<String, String> entry : commonAttrs.entrySet()) {
             String substr = entry.getKey() + "=\"" + entry.getValue() + "\"";
-            assertTrue("String [" + substr + "] was not found in [" + str + "]", str.indexOf(substr) >= 0);
+            assertTrue("String [" + substr + "] was not found in [" + str + "]", str.contains(substr));
         }
     }
 
     protected void assertDynamicAttrs(String str) {
         for (Map.Entry<String, Object> entry : dynamicAttrs.entrySet()) {
             String substr = entry.getKey() + "=\"" + entry.getValue() + "\"";
-            assertTrue("String [" + substr + "] was not found in [" + str + "]", str.indexOf(substr) >= 0);
+            assertTrue("String [" + substr + "] was not found in [" + str + "]", str.contains(substr));
         }
     }
 
-    protected Object doFindValue(String expr, Class toType) {
-        Object val = stack.findValue(expr);
-
-        if (toType == String.class)
-            return val == null ? expr : val;
-        else
-            return val == null ? null : val;
-    }
-
-    //XWorkConverter doesnt have a public onstructor (the one with parameters will require mor config)
-    public class ConverterEx extends XWorkConverter {
+    //XWorkConverter doesnt have a public constructor (the one with parameters will require mor config)
+    public static class ConverterEx extends XWorkConverter {
         public ConverterEx() {
 
         }
diff --git a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/TokenTest.java b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/TokenTest.java
index 5330220..42b6ad3 100644
--- a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/TokenTest.java
+++ b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/TokenTest.java
@@ -25,7 +25,6 @@ import org.apache.struts2.components.Token;
 import org.apache.struts2.components.UIBean;
 
 import java.util.HashMap;
-import java.util.Map;
 import java.util.regex.Pattern;
 
 public class TokenTest extends AbstractTest {
@@ -51,9 +50,8 @@ public class TokenTest extends AbstractTest {
         super.setUp();
         this.tag = new Token(stack, request, response);
 
-        Map map = new HashMap();
-        map.put(ActionContext.SESSION, new HashMap());
-        ActionContext.setContext(new ActionContext(map));
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setSession(new HashMap<>());
     }
 
     @Override
diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
index 9f6b76f..134c6fb 100644
--- a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
+++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
@@ -520,13 +520,12 @@ public class JSONInterceptorTest extends StrutsTestCase {
         ActionContext context = ActionContext.getContext();
         ValueStack stack = context.getValueStack();
 
-        ActionContext.setContext(context);
-        context.put(StrutsStatics.HTTP_REQUEST, this.request);
-        context.put(StrutsStatics.HTTP_RESPONSE, this.response);
+        context.setServletRequest(request);
+        context.setServletResponse(response);
 
         MockServletContext servletContext = new MockServletContext();
 
-        context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+        context.setServletContext(servletContext);
         this.invocation = new MockActionInvocationEx();
         this.invocation.setInvocationContext(context);
         this.invocation.setStack(stack);
diff --git a/plugins/junit/src/main/java/org/apache/struts2/StrutsJUnit4TestCase.java b/plugins/junit/src/main/java/org/apache/struts2/StrutsJUnit4TestCase.java
index 153fcf6..f463716 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/StrutsJUnit4TestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/StrutsJUnit4TestCase.java
@@ -44,8 +44,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.UnsupportedEncodingException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
@@ -140,7 +138,7 @@ public abstract class StrutsJUnit4TestCase<T> extends XWorkJUnit4TestCase {
         actionContext.setParameters(HttpParameters.create(request.getParameterMap()).build());
         initSession(actionContext);
         // set the action context to the one used by the proxy
-        ActionContext.setContext(actionContext);
+        ActionContext.bound(actionContext);
     }
 
     protected void initSession(ActionContext actionContext) {
diff --git a/plugins/junit/src/main/java/org/apache/struts2/StrutsRestTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/StrutsRestTestCase.java
index eba18ab..f065ceb 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/StrutsRestTestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/StrutsRestTestCase.java
@@ -22,7 +22,6 @@ import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionProxy;
 import com.opensymphony.xwork2.ActionProxyFactory;
 import com.opensymphony.xwork2.config.Configuration;
-import com.sun.net.httpserver.HttpsParameters;
 import org.apache.struts2.dispatcher.Dispatcher;
 import org.apache.struts2.dispatcher.HttpParameters;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
@@ -48,8 +47,7 @@ public class StrutsRestTestCase<T> extends StrutsJUnit4TestCase<T> {
      *
      * @param uri action uri to test
      * @return execution result
-     *
-     * @throws ServletException in case of servlet errors
+     * @throws ServletException             in case of servlet errors
      * @throws UnsupportedEncodingException in case of unsupported encoding
      */
     @Override
@@ -64,10 +62,9 @@ public class StrutsRestTestCase<T> extends StrutsJUnit4TestCase<T> {
      * FreeMarker, or Velocity (JSPs can be used with the Embedded JSP plugin)
      *
      * @param httpMethod HTTP method of request like GET, POST, PUT or DELETE
-     * @param uri action uri to test
+     * @param uri        action uri to test
      * @return execution result
-     *
-     * @throws ServletException in case of servlet errors
+     * @throws ServletException             in case of servlet errors
      * @throws UnsupportedEncodingException in case of unsupported encoding
      */
     protected String executeAction(String httpMethod, String uri) throws ServletException, UnsupportedEncodingException {
@@ -81,7 +78,7 @@ public class StrutsRestTestCase<T> extends StrutsJUnit4TestCase<T> {
 
         if (response.getStatus() != HttpServletResponse.SC_OK)
             throw new ServletException("Error code [" + response.getStatus() + "], Error: ["
-                    + response.getErrorMessage() + "]");
+                + response.getErrorMessage() + "]");
 
         return response.getContentAsString();
     }
@@ -104,41 +101,38 @@ public class StrutsRestTestCase<T> extends StrutsJUnit4TestCase<T> {
      * parameters. Make sure to set the request parameters in the protected "request" object before calling this method.
      *
      * @param httpMethod HTTP method of request like GET, POST, PUT or DELETE
-     * @param uri request uri to test
+     * @param uri        request uri to test
      * @return action proxy found for this request uri
      */
     protected ActionProxy getActionProxy(String httpMethod, String uri) {
-		request.setRequestURI(uri);
-		request.setMethod(httpMethod);
+        request.setRequestURI(uri);
+        request.setMethod(httpMethod);
 
-		ActionMapping mapping = getActionMapping(request);
-		String namespace = mapping.getNamespace();
-		String name = mapping.getName();
-		String method = mapping.getMethod();
+        ActionMapping mapping = getActionMapping(request);
+        String namespace = mapping.getNamespace();
+        String name = mapping.getName();
+        String method = mapping.getMethod();
 
-		Configuration config = configurationManager.getConfiguration();
-		ActionProxy proxy = config.getContainer()
-				                    .getInstance(ActionProxyFactory.class)
-				                    .createActionProxy(namespace, name, method, new HashMap<String, Object>(), true, false);
+        Configuration config = configurationManager.getConfiguration();
+        ActionProxy proxy = config.getContainer()
+            .getInstance(ActionProxyFactory.class)
+            .createActionProxy(namespace, name, method, new HashMap<>(), true, false);
 
         ActionContext invocationContext = proxy.getInvocation().getInvocationContext();
         invocationContext.getContextMap().put(ServletActionContext.ACTION_MAPPING, mapping);
         invocationContext.setParameters(HttpParameters.create(request.getParameterMap()).build());
         // set the action context to the one used by the proxy
-        ActionContext.setContext(invocationContext);
-
-        // set the action context to the one used by the proxy
-        ActionContext.setContext(invocationContext);
+        ActionContext.bound(invocationContext);
 
         // this is normally done in onSetUp(), but we are using Struts internal
         // objects (proxy and action invocation)
         // so we have to hack around so it works
-		ServletActionContext.setServletContext(servletContext);
-		ServletActionContext.setRequest(request);
-		ServletActionContext.setResponse(response);
+        ServletActionContext.setServletContext(servletContext);
+        ServletActionContext.setRequest(request);
+        ServletActionContext.setResponse(response);
 
-		return proxy;
-	}
+        return proxy;
+    }
 
     @Override
     protected void initServletMockObjects() {
diff --git a/plugins/junit/src/main/java/org/apache/struts2/StrutsTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/StrutsTestCase.java
index c72623c..cf4bf79 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/StrutsTestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/StrutsTestCase.java
@@ -39,8 +39,6 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.UnsupportedEncodingException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -124,7 +122,7 @@ public abstract class StrutsTestCase extends XWorkTestCase {
         initSession(actionContext);
         applyAdditionalParams(actionContext);
         // set the action context to the one used by the proxy
-        ActionContext.setContext(actionContext);
+        ActionContext.bound(actionContext);
     }
 
     protected void initSession(ActionContext actionContext) {
diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java
index 96285ca..ba9c0d0 100644
--- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java
+++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java
@@ -80,10 +80,8 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
             LOG.trace("Loading packages from XML and Convention on startup");                
 
         //init action context
-        ActionContext ctx = ActionContext.getContext();
-        if (ctx == null) {
-            ctx = createActionContext();
-            ActionContext.setContext(ctx);
+        if (ActionContext.getContext() == null) {
+            createActionContext();
         }
 
         Set<String> bundleNames = new HashSet<>();
@@ -103,7 +101,7 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
     }
 
     protected ActionContext createActionContext() {
-        return new ActionContext(new HashMap<String, Object>());
+        return ActionContext.ofAndBound(new HashMap<>());
     }
 
     /**
@@ -120,7 +118,6 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
         ActionContext ctx = ActionContext.getContext();
         if (ctx == null) {
             ctx = createActionContext();
-            ActionContext.setContext(ctx);
         }
 
         try {
diff --git a/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java b/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
index c31a3ff..9277d67 100644
--- a/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
+++ b/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
@@ -137,7 +137,7 @@ public class PortletUrlRenderer implements UrlRenderer {
 	}
 
     private String createDefaultUrl(UrlProvider urlComponent) {
-        ActionInvocation ai = (ActionInvocation) urlComponent.getStack().getContext().get(ActionContext.ACTION_INVOCATION);
+        ActionInvocation ai = urlComponent.getStack().getActionContext().getActionInvocation();
         String action = ai.getProxy().getActionName();
         return portletUrlHelper.buildUrl(action, urlComponent.getNamespace(), urlComponent.getMethod(), urlComponent.getParameters(),
                 urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState());
diff --git a/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java b/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
index 2794721..6b985ff 100644
--- a/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
+++ b/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
@@ -307,7 +307,7 @@ public class Jsr168Dispatcher extends GenericPortlet implements StrutsStatics {
                     portletNamespace, PortletPhase.ACTION_PHASE);
             if (LOG.isDebugEnabled()) LOG.debug("Leaving processAction");
         } finally {
-            ActionContext.setContext(null);
+            ActionContext.clear();
         }
     }
 
@@ -348,7 +348,7 @@ public class Jsr168Dispatcher extends GenericPortlet implements StrutsStatics {
      *  Reset the action context.
      */
     void resetActionContext() {
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     /**
diff --git a/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr286Dispatcher.java b/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr286Dispatcher.java
index 78946e2..cd39b7f 100644
--- a/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr286Dispatcher.java
+++ b/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr286Dispatcher.java
@@ -53,7 +53,7 @@ public class Jsr286Dispatcher extends Jsr168Dispatcher {
                     portletNamespace, PortletPhase.EVENT_PHASE);
             if (LOG.isDebugEnabled()) LOG.debug("Leaving processEvent");
         } finally {
-            ActionContext.setContext(null);
+            ActionContext.clear();
         }
     }
 
@@ -70,7 +70,7 @@ public class Jsr286Dispatcher extends Jsr168Dispatcher {
                     getSessionMap(request), getApplicationMap(),
                     portletNamespace, PortletPhase.SERVE_RESOURCE_PHASE);
         } finally {
-            ActionContext.setContext(null);
+            ActionContext.clear();
         }
     }
 
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java b/plugins/portlet/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java
index c015c0d..f544c23 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java
@@ -67,7 +67,7 @@ public class PortletActionContextTest extends MockObjectTestCase {
 
     PortletConfig portletConfig;
 
-    Map<String, Object> context = new HashMap<String, Object>();
+    Map<String, Object> context = new HashMap<>();
 
     public void setUp() throws Exception {
         super.setUp();
@@ -84,7 +84,7 @@ public class PortletActionContextTest extends MockObjectTestCase {
         portletConfig = (PortletConfig)mockPortletConfig.proxy();
 
 
-        ActionContext.setContext(new ActionContext(context));
+        ActionContext.ofAndBound(context);
     }
 
     public void testGetPhase() {
@@ -200,7 +200,7 @@ public class PortletActionContextTest extends MockObjectTestCase {
     }
 
     public void tearDown() throws Exception {
-        ActionContext.setContext(null);
+        ActionContext.clear();
         super.tearDown();
     }
 
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java b/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java
index b9664c9..b75f44c 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java
@@ -44,13 +44,15 @@ public class PortletAwareInterceptorTest extends TestCase {
 
     public void testPortletRequestIsSet() throws Exception {
         PortletRequest request = EasyMock.createMock(PortletRequest.class);
-        Map<String, Object> ctx = new HashMap<String, Object>();
+        Map<String, Object> ctx = new HashMap<>();
         ctx.put(PortletConstants.REQUEST, request);
+        ActionContext actionContext = ActionContext.ofAndBound(ctx);
+
         PortletRequestAware action = EasyMock.createMock(PortletRequestAware.class);
         action.setPortletRequest(request);
 
         ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
-        EasyMock.expect(invocation.getInvocationContext()).andReturn(new ActionContext(ctx));
+        EasyMock.expect(invocation.getInvocationContext()).andReturn(actionContext);
         EasyMock.expect(invocation.getAction()).andReturn(action);
 
         EasyMock.replay(action);
@@ -64,12 +66,13 @@ public class PortletAwareInterceptorTest extends TestCase {
     public void testActionPortletRequestAware() throws Exception {
         PortletRequest request = EasyMock.createMock(PortletRequest.class);
         Map<String, Object> ctx = new HashMap<>();
+        ActionContext actionContext = ActionContext.ofAndBound(ctx);
         ctx.put(PortletConstants.REQUEST, request);
         org.apache.struts2.portlet.action.PortletRequestAware action = EasyMock.createMock(org.apache.struts2.portlet.action.PortletRequestAware.class);
         action.withPortletRequest(request);
 
         ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
-        EasyMock.expect(invocation.getInvocationContext()).andReturn(new ActionContext(ctx));
+        EasyMock.expect(invocation.getInvocationContext()).andReturn(actionContext);
         EasyMock.expect(invocation.getAction()).andReturn(action);
 
         EasyMock.replay(action);
@@ -84,11 +87,12 @@ public class PortletAwareInterceptorTest extends TestCase {
         PortletResponse response = EasyMock.createMock(PortletResponse.class);
         Map<String, Object> ctx = new HashMap<>();
         ctx.put(PortletConstants.RESPONSE, response);
+        ActionContext actionContext = ActionContext.ofAndBound(ctx);
         org.apache.struts2.portlet.action.PortletResponseAware action = EasyMock.createMock(org.apache.struts2.portlet.action.PortletResponseAware.class);
         action.withPortletResponse(response);
 
         ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
-        EasyMock.expect(invocation.getInvocationContext()).andReturn(new ActionContext(ctx));
+        EasyMock.expect(invocation.getInvocationContext()).andReturn(actionContext);
         EasyMock.expect(invocation.getAction()).andReturn(action);
 
         EasyMock.replay(action);
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java b/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java
index 3cf0675..796aa8f 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java
@@ -41,119 +41,119 @@ import static org.apache.struts2.portlet.PortletConstants.STACK_FROM_EVENT_PHASE
 
 public class PortletStateInterceptorTest extends StrutsTestCasePortletTests {
 
-	private PortletStateInterceptor interceptor;
-	
-	public void setUp() throws Exception {
-	    super.setUp();
-		interceptor = new PortletStateInterceptor();
-	}
-	
-	public void testCopyValueStackFromEventToRenderPhase() throws Exception {
-		ActionResponse actionResponse = EasyMock.createNiceMock(ActionResponse.class);
-		ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
-		
-		Map<String, Object> ctxMap = new HashMap<String, Object>();
-		ctxMap.put(PHASE, PortletPhase.ACTION_PHASE);
-		ctxMap.put(RESPONSE, actionResponse);
-		Map<String, Object> session = new HashMap<String, Object>();
-		
-		ActionContext ctx = new ActionContext(ctxMap);
-		ctx.setSession(session);
-		EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
-		actionResponse.setRenderParameter(EVENT_ACTION, "true");
-		
-		ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
-		EasyMock.expect(invocation.getStack()).andStubReturn(stack);
-		
-		EasyMock.replay(actionResponse);
-		EasyMock.replay(invocation);
-		
-		interceptor.intercept(invocation);
-		
-		EasyMock.verify(actionResponse);
-		EasyMock.verify(invocation);
-		
-		assertSame(stack, session.get(STACK_FROM_EVENT_PHASE));
-		
-	}
-	
-	public void testDoNotRestoreValueStackInRenderPhaseWhenProperPrg() throws Exception {
-		RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);
-		ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
-		
-		
-		ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();
-		eventPhaseStack.set("testKey", "testValue");
-		
-		ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();
-		currentStack.set("anotherTestKey", "anotherTestValue");
-		
-		Map<String, Object> ctxMap = new HashMap<String, Object>();
-		Map<String, Object> session = new HashMap<String, Object>();
-		
-		session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);
-		
-		ctxMap.put(PHASE, PortletPhase.RENDER_PHASE);
-		ctxMap.put(REQUEST, renderRequest);
-		
-		ActionContext ctx = new ActionContext(ctxMap);
-		ctx.setSession(session);
-		
-		EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
-		EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
-		EasyMock.expect(invocation.getAction()).andStubReturn(new DefaultActionSupport());
-		EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");
-		
-		EasyMock.replay(renderRequest);
-		EasyMock.replay(invocation);
-		
-		interceptor.intercept(invocation);
-		
-		ValueStack resultingStack = invocation.getStack();
-		
-		assertNull(resultingStack.findValue("testKey"));
-		assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));
-		
-		
-	}
-	
-	public void testRestoreValueStackInRenderPhaseWhenNotProperPrg() throws Exception {
-		RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);
-		ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
-		
-		ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();
-		eventPhaseStack.set("testKey", "testValue");
-		
-		ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();
-		currentStack.set("anotherTestKey", "anotherTestValue");
-		
-		EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
-		
-		Map<String, Object> ctxMap = new HashMap<String, Object>();
-		Map<String, Object> session = new HashMap<String, Object>();
-		
-		session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);
-		
-		ctxMap.put(PHASE, PortletPhase.RENDER_PHASE);
-		ctxMap.put(REQUEST, renderRequest);
-		
-		ActionContext ctx = new ActionContext(ctxMap);
-		ctx.setSession(session);
-		
-		EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
-		EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
-		EasyMock.expect(invocation.getAction()).andStubReturn(new DirectRenderFromEventAction());
-		EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");
-		
-		EasyMock.replay(renderRequest);
-		EasyMock.replay(invocation);
-		
-		interceptor.intercept(invocation);
-		
-		ValueStack resultingStack = invocation.getStack();
-		assertEquals("testValue", resultingStack.findValue("testKey"));
-		assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));
-		
-		
-	}
+    private PortletStateInterceptor interceptor;
+
+    public void setUp() throws Exception {
+        super.setUp();
+        interceptor = new PortletStateInterceptor();
+    }
+
+    public void testCopyValueStackFromEventToRenderPhase() throws Exception {
+        ActionResponse actionResponse = EasyMock.createNiceMock(ActionResponse.class);
+        ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
+
+        Map<String, Object> ctxMap = new HashMap<>();
+        ctxMap.put(PHASE, PortletPhase.ACTION_PHASE);
+        ctxMap.put(RESPONSE, actionResponse);
+        Map<String, Object> session = new HashMap<>();
+
+        ActionContext ctx = ActionContext.ofAndBound(ctxMap);
+        ctx.setSession(session);
+        EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
+        actionResponse.setRenderParameter(EVENT_ACTION, "true");
+
+        ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
+        EasyMock.expect(invocation.getStack()).andStubReturn(stack);
+
+        EasyMock.replay(actionResponse);
+        EasyMock.replay(invocation);
+
+        interceptor.intercept(invocation);
+
+        EasyMock.verify(actionResponse);
+        EasyMock.verify(invocation);
+
+        assertSame(stack, session.get(STACK_FROM_EVENT_PHASE));
+
+    }
+
+    public void testDoNotRestoreValueStackInRenderPhaseWhenProperPrg() throws Exception {
+        RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);
+        ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
+
+
+        ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();
+        eventPhaseStack.set("testKey", "testValue");
+
+        ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();
+        currentStack.set("anotherTestKey", "anotherTestValue");
+
+        Map<String, Object> ctxMap = new HashMap<>();
+        Map<String, Object> session = new HashMap<>();
+
+        session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);
+
+        ctxMap.put(PHASE, PortletPhase.RENDER_PHASE);
+        ctxMap.put(REQUEST, renderRequest);
+
+        ActionContext ctx = ActionContext.ofAndBound(ctxMap);
+        ctx.setSession(session);
+
+        EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
+        EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
+        EasyMock.expect(invocation.getAction()).andStubReturn(new DefaultActionSupport());
+        EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");
+
+        EasyMock.replay(renderRequest);
+        EasyMock.replay(invocation);
+
+        interceptor.intercept(invocation);
+
+        ValueStack resultingStack = invocation.getStack();
+
+        assertNull(resultingStack.findValue("testKey"));
+        assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));
+
+
+    }
+
+    public void testRestoreValueStackInRenderPhaseWhenNotProperPrg() throws Exception {
+        RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);
+        ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
+
+        ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();
+        eventPhaseStack.set("testKey", "testValue");
+
+        ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();
+        currentStack.set("anotherTestKey", "anotherTestValue");
+
+        EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
+
+        Map<String, Object> ctxMap = new HashMap<>();
+        Map<String, Object> session = new HashMap<>();
+
+        session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);
+
+        ctxMap.put(PHASE, PortletPhase.RENDER_PHASE);
+        ctxMap.put(REQUEST, renderRequest);
+
+        ActionContext ctx = ActionContext.ofAndBound(ctxMap);
+        ctx.setSession(session);
+
+        EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
+        EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
+        EasyMock.expect(invocation.getAction()).andStubReturn(new DirectRenderFromEventAction());
+        EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");
+
+        EasyMock.replay(renderRequest);
+        EasyMock.replay(invocation);
+
+        interceptor.intercept(invocation);
+
+        ValueStack resultingStack = invocation.getStack();
+        assertEquals("testValue", resultingStack.findValue("testKey"));
+        assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));
+
+
+    }
 }
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java b/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java
index 6615234..b7ee3d1 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java
@@ -21,7 +21,6 @@ package org.apache.struts2.portlet.result;
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ActionProxy;
-import junit.textui.TestRunner;
 import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.dispatcher.HttpParameters;
 import org.apache.struts2.portlet.PortletConstants;
@@ -40,8 +39,6 @@ import javax.portlet.RenderResponse;
 import java.util.HashMap;
 import java.util.Map;
 
-import static com.opensymphony.xwork2.ActionContext.PARAMETERS;
-import static com.opensymphony.xwork2.ActionContext.SESSION;
 import static org.apache.struts2.portlet.PortletConstants.ACTION_PARAM;
 import static org.apache.struts2.portlet.PortletConstants.MODE_PARAM;
 import static org.apache.struts2.portlet.PortletConstants.PHASE;
@@ -51,15 +48,14 @@ import static org.apache.struts2.portlet.PortletConstants.RESPONSE;
 
 /**
  * PortletResultTest. Insert description.
- *
  */
 public class PortletResultTest extends MockObjectTestCase implements StrutsStatics {
 
     Mock mockInvocation = null;
     Mock mockCtx = null;
     Mock mockProxy = null;
-	ActionProxy proxy = null;
-	ActionInvocation invocation = null;
+    ActionProxy proxy = null;
+    ActionInvocation invocation = null;
 
     public void setUp() throws Exception {
         super.setUp();
@@ -67,14 +63,13 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         mockCtx = mock(PortletContext.class);
         mockProxy = mock(ActionProxy.class);
 
-        Map<String, Object> sessionMap = new HashMap<String, Object>();
+        Map<String, Object> sessionMap = new HashMap<>();
 
-        Map<String, Object> context = new HashMap<String, Object>();
-        context.put(SESSION, sessionMap);
-        context.put(PARAMETERS, HttpParameters.create().build());
-        context.put(STRUTS_PORTLET_CONTEXT, mockCtx.proxy());
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setSession(sessionMap);
+        actionContext.setParameters(HttpParameters.create().build());
+        actionContext.put(STRUTS_PORTLET_CONTEXT, mockCtx.proxy());
 
-        ActionContext.setContext(new ActionContext(context));
         mockProxy.stubs().method("getNamespace").will(returnValue("/test"));
         proxy = (ActionProxy) mockProxy.proxy();
         mockInvocation.stubs().method("getInvocationContext").will(returnValue(ActionContext.getContext()));
@@ -88,11 +83,11 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         Mock mockResponse = mock(RenderResponse.class);
         Mock mockRd = mock(PortletRequestDispatcher.class);
 
-        RenderRequest req = (RenderRequest)mockRequest.proxy();
-        RenderResponse res = (RenderResponse)mockResponse.proxy();
-        PortletRequestDispatcher rd = (PortletRequestDispatcher)mockRd.proxy();
-        PortletContext ctx = (PortletContext)mockCtx.proxy();
-        ActionInvocation inv = (ActionInvocation)mockInvocation.proxy();
+        RenderRequest req = (RenderRequest) mockRequest.proxy();
+        RenderResponse res = (RenderResponse) mockResponse.proxy();
+        PortletRequestDispatcher rd = (PortletRequestDispatcher) mockRd.proxy();
+        PortletContext ctx = (PortletContext) mockCtx.proxy();
+        ActionInvocation inv = (ActionInvocation) mockInvocation.proxy();
 
         Constraint[] params = new Constraint[]{same(req), same(res)};
         mockRd.expects(once()).method("include").with(params);
@@ -111,10 +106,9 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         PortletResult result = new PortletResult();
         try {
             result.doExecute("/WEB-INF/pages/testPage.jsp", inv);
-        }
-        catch(Exception e) {
+        } catch (Exception e) {
             e.printStackTrace();
-            fail("Error occured!");
+            fail("Error occurred!");
         }
 
     }
@@ -130,7 +124,7 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         mockResponse.expects(once()).method("setRenderParameter").with(params);
         params = new Constraint[]{eq(PortletConstants.RENDER_DIRECT_NAMESPACE), eq("/test")};
         mockResponse.expects(once()).method("setRenderParameter").with(params);
-        
+
         mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
         ActionContext ctx = ActionContext.getContext();
@@ -142,10 +136,9 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         PortletResult result = new PortletResult();
         try {
             result.doExecute("testView.action", invocation);
-        }
-        catch(Exception e) {
+        } catch (Exception e) {
             e.printStackTrace();
-            fail("Error occured!");
+            fail("Error occurred!");
         }
 
     }
@@ -163,23 +156,22 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
 
         mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
- 
+
         ActionContext ctx = ActionContext.getContext();
 
-        Map session = new HashMap();
-        
+        Map<String, Object> session = new HashMap<>();
+        ctx.setSession(session);
+
         ctx.put(REQUEST, mockRequest.proxy());
         ctx.put(RESPONSE, mockResponse.proxy());
         ctx.put(PHASE, PortletPhase.ACTION_PHASE);
-        ctx.put(ActionContext.SESSION, session);
 
         PortletResult result = new PortletResult();
         try {
-            result.doExecute("/WEB-INF/pages/testJsp.jsp", (ActionInvocation)mockInvocation.proxy());
-        }
-        catch(Exception e) {
+            result.doExecute("/WEB-INF/pages/testJsp.jsp", (ActionInvocation) mockInvocation.proxy());
+        } catch (Exception e) {
             e.printStackTrace();
-            fail("Error occured!");
+            fail("Error occurred!");
         }
         assertEquals("/WEB-INF/pages/testJsp.jsp", session.get(RENDER_DIRECT_LOCATION));
     }
@@ -198,7 +190,7 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         mockResponse.expects(once()).method("setRenderParameter").with(params);
         params = new Constraint[]{eq(PortletConstants.RENDER_DIRECT_NAMESPACE), eq("/test")};
         mockResponse.expects(once()).method("setRenderParameter").with(params);
-        
+
         mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
@@ -210,11 +202,10 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
 
         PortletResult result = new PortletResult();
         try {
-            result.doExecute("testView.action?testParam1=testValue1&testParam2=testValue2", (ActionInvocation)mockInvocation.proxy());
-        }
-        catch(Exception e) {
+            result.doExecute("testView.action?testParam1=testValue1&testParam2=testValue2", (ActionInvocation) mockInvocation.proxy());
+        } catch (Exception e) {
             e.printStackTrace();
-            fail("Error occured!");
+            fail("Error occurred!");
         }
     }
 
@@ -223,10 +214,10 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         Mock mockResponse = mock(RenderResponse.class);
         Mock mockRd = mock(PortletRequestDispatcher.class);
 
-        RenderRequest req = (RenderRequest)mockRequest.proxy();
-        RenderResponse res = (RenderResponse)mockResponse.proxy();
-        PortletRequestDispatcher rd = (PortletRequestDispatcher)mockRd.proxy();
-        PortletContext ctx = (PortletContext)mockCtx.proxy();
+        RenderRequest req = (RenderRequest) mockRequest.proxy();
+        RenderResponse res = (RenderResponse) mockResponse.proxy();
+        PortletRequestDispatcher rd = (PortletRequestDispatcher) mockRd.proxy();
+        PortletContext ctx = (PortletContext) mockCtx.proxy();
 
         Constraint[] params = new Constraint[]{same(req), same(res)};
         mockRd.expects(once()).method("include").with(params);
@@ -247,16 +238,12 @@ public class PortletResultTest extends MockObjectTestCase implements StrutsStati
         PortletResult result = new PortletResult();
         result.setTitle("testTitle");
         result.setContentType("testContentType");
-        result.doExecute("/WEB-INF/pages/testPage.jsp", (ActionInvocation)mockInvocation.proxy());
+        result.doExecute("/WEB-INF/pages/testPage.jsp", (ActionInvocation) mockInvocation.proxy());
     }
 
     public void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
-    }
-
-    public static void main(String[] args) {
-        TestRunner.run(PortletResultTest.class);
+        ActionContext.clear();
     }
 
 }
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java b/plugins/portlet/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java
index 78bed30..15dbe3e 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java
@@ -44,6 +44,7 @@ import static org.apache.struts2.portlet.PortletConstants.REQUEST;
 import static org.apache.struts2.portlet.PortletConstants.RESPONSE;
 
 /**
+ *
  */
 public class PortletUrlHelperTest extends TestCase {
 
@@ -59,146 +60,143 @@ public class PortletUrlHelperTest extends TestCase {
         renderResponse = EasyMock.createMock(RenderResponse.class);
         renderRequest = EasyMock.createMock(RenderRequest.class);
         url = new MockUrl();
-        
+
         EasyMock.expect(renderRequest.getPortletMode()).andReturn(PortletMode.VIEW).anyTimes();
         EasyMock.expect(renderRequest.getWindowState()).andReturn(WindowState.NORMAL).anyTimes();
 
-        Map<String, String> modeNamespaceMap = new HashMap<String, String>();
+        Map<String, String> modeNamespaceMap = new HashMap<>();
         modeNamespaceMap.put("view", "/view");
         modeNamespaceMap.put("edit", "/edit");
         modeNamespaceMap.put("help", "/help");
 
-        Map<String, Object> context = new HashMap<String, Object>();
-        context.put(REQUEST, renderRequest);
-        context.put(RESPONSE, renderResponse);
-        context.put(PHASE, PortletPhase.RENDER_PHASE);
-        context.put(MODE_NAMESPACE_MAP, modeNamespaceMap);
-
-        ActionContext.setContext(new ActionContext(context));
-
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.put(REQUEST, renderRequest);
+        actionContext.put(RESPONSE, renderResponse);
+        actionContext.put(PHASE, PortletPhase.RENDER_PHASE);
+        actionContext.put(MODE_NAMESPACE_MAP, modeNamespaceMap);
     }
 
-    public void testCreateRenderUrlWithNoModeOrState() throws Exception {
-    	EasyMock.expect(renderResponse.createRenderURL()).andReturn(url);
+    public void testCreateRenderUrlWithNoModeOrState() {
+        EasyMock.expect(renderResponse.createRenderURL()).andReturn(url);
 
         EasyMock.replay(renderRequest);
         EasyMock.replay(renderResponse);
 
         (new PortletUrlHelper()).buildUrl("testAction", null, null,
-                new HashMap<String, Object>(), null, null, null);
+            new HashMap<>(), null, null, null);
         assertEquals(PortletMode.VIEW, url.getPortletMode());
         assertEquals(WindowState.NORMAL, url.getWindowState());
         assertEquals("testAction", url.getParameterMap().get(ACTION_PARAM)[0]);
         assertEquals("view", url.getParameterMap().get(MODE_PARAM)[0]);
     }
 
-    public void testCreateRenderUrlWithDifferentPortletMode() throws Exception {
-    	EasyMock.expect(renderResponse.createRenderURL()).andReturn(url);
+    public void testCreateRenderUrlWithDifferentPortletMode() {
+        EasyMock.expect(renderResponse.createRenderURL()).andReturn(url);
 
         EasyMock.replay(renderRequest);
         EasyMock.replay(renderResponse);
 
         (new PortletUrlHelper()).buildUrl("testAction", null, null,
-                new HashMap<String, Object>(), null, "edit", null);
-        
+            new HashMap<>(), null, "edit", null);
+
         assertEquals(PortletMode.EDIT, url.getPortletMode());
         assertEquals(WindowState.NORMAL, url.getWindowState());
         assertEquals("testAction", url.getParameterMap().get(ACTION_PARAM)[0]);
         assertEquals("edit", url.getParameterMap().get(MODE_PARAM)[0]);
     }
 
-    public void testCreateRenderUrlWithDifferentWindowState() throws Exception {
-    	EasyMock.expect(renderResponse.createRenderURL()).andReturn(url);
-        
+    public void testCreateRenderUrlWithDifferentWindowState() {
+        EasyMock.expect(renderResponse.createRenderURL()).andReturn(url);
+
         EasyMock.replay(renderRequest);
         EasyMock.replay(renderResponse);
-        
+
         (new PortletUrlHelper()).buildUrl("testAction", null, null,
-                new HashMap<String, Object>(), null, null, "maximized");
-        
+            new HashMap<>(), null, null, "maximized");
+
         assertEquals(PortletMode.VIEW, url.getPortletMode());
         assertEquals(WindowState.MAXIMIZED, url.getWindowState());
         assertEquals("testAction", url.getParameterMap().get(ACTION_PARAM)[0]);
         assertEquals("view", url.getParameterMap().get(MODE_PARAM)[0]);
     }
 
-    public void testCreateActionUrl() throws Exception {
-    	EasyMock.expect(renderResponse.createActionURL()).andReturn(url);
-        
+    public void testCreateActionUrl() {
+        EasyMock.expect(renderResponse.createActionURL()).andReturn(url);
+
         EasyMock.replay(renderResponse);
         EasyMock.replay(renderRequest);
-        
+
         (new PortletUrlHelper()).buildUrl("testAction", null, null,
-                new HashMap<String, Object>(), "action", null, null);
-        
+            new HashMap<>(), "action", null, null);
+
         assertEquals(PortletMode.VIEW, url.getPortletMode());
         assertEquals(WindowState.NORMAL, url.getWindowState());
         assertEquals("testAction", url.getParameterMap().get(ACTION_PARAM)[0]);
         assertEquals("view", url.getParameterMap().get(MODE_PARAM)[0]);
     }
-    
+
     @Override
     public void tearDown() {
-    	EasyMock.verify(renderResponse);
-    	EasyMock.verify(renderRequest);
+        EasyMock.verify(renderResponse);
+        EasyMock.verify(renderRequest);
     }
-    
-    private class MockUrl implements PortletURL {
 
-    	private PortletMode portletMode;
-		private WindowState windowState;
-		private Map<String, String[]> parameters;
-    	
-		public PortletMode getPortletMode() {
-			return portletMode;
-		}
+    private static class MockUrl implements PortletURL {
+
+        private PortletMode portletMode;
+        private WindowState windowState;
+        private Map<String, String[]> parameters;
+
+        public PortletMode getPortletMode() {
+            return portletMode;
+        }
+
+        public WindowState getWindowState() {
+            return windowState;
+        }
 
-		public WindowState getWindowState() {
-			return windowState;
-		}
+        public void removePublicRenderParameter(String name) {
+        }
 
-		public void removePublicRenderParameter(String name) {
-		}
+        public void setPortletMode(PortletMode portletMode) throws PortletModeException {
+            this.portletMode = portletMode;
+        }
 
-		public void setPortletMode(PortletMode portletMode) throws PortletModeException {
-			this.portletMode = portletMode;
-		}
+        public void setWindowState(WindowState windowState) throws WindowStateException {
+            this.windowState = windowState;
+        }
 
-		public void setWindowState(WindowState windowState) throws WindowStateException {
-			this.windowState = windowState;
-		}
+        public void addProperty(String arg0, String arg1) {
+        }
 
-		public void addProperty(String arg0, String arg1) {
-		}
+        public Map<String, String[]> getParameterMap() {
+            return parameters;
+        }
 
-		public Map<String, String[]> getParameterMap() {
-			return parameters;
-		}
+        public void setParameter(String name, String value) {
+            parameters.put(name, new String[]{value});
+        }
 
-		public void setParameter(String name, String value) {
-			parameters.put(name, new String[]{value});
-		}
+        public void setParameter(String name, String[] values) {
+            parameters.put(name, values);
+        }
 
-		public void setParameter(String name, String[] values) {
-			parameters.put(name, values);
-		}
+        public void setParameters(Map<String, String[]> parameters) {
+            this.parameters = parameters;
+        }
 
-		public void setParameters(Map<String, String[]> parameters) {
-			this.parameters = parameters;
-		}
+        public void setProperty(String arg0, String arg1) {
+        }
 
-		public void setProperty(String arg0, String arg1) {
-		}
+        public void setSecure(boolean arg0) throws PortletSecurityException {
+        }
 
-		public void setSecure(boolean arg0) throws PortletSecurityException {
-		}
+        public void write(Writer arg0) throws IOException {
+        }
 
-		public void write(Writer arg0) throws IOException {
-		}
+        public void write(Writer arg0, boolean arg1) throws IOException {
+        }
 
-		public void write(Writer arg0, boolean arg1) throws IOException {
-		}
-    	
     }
 
 }
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java b/plugins/portlet/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java
index 1e7c7d8..8628ef5 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ActionProxy;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.ValueStackFactory;
-import junit.textui.TestRunner;
 import org.apache.struts2.dispatcher.Dispatcher;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
 import org.apache.struts2.portlet.PortletConstants;
@@ -35,16 +34,26 @@ import org.jmock.cglib.MockObjectTestCase;
 import org.jmock.core.Constraint;
 import org.springframework.mock.web.MockServletContext;
 
-import javax.portlet.*;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletURL;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.portlet.WindowState;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.jsp.PageContext;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 
 import static org.apache.struts2.StrutsStatics.STRUTS_PORTLET_CONTEXT;
 
 /**
+ *
  */
 @SuppressWarnings("unchecked")
 public class PortletUrlTagTest extends MockObjectTestCase {
@@ -68,32 +77,24 @@ public class PortletUrlTagTest extends MockObjectTestCase {
     Mock mockCtx = null;
 
     ValueStack stack = null;
-    
+
     Mock mockActionProxy = null;
 
-	Mock mockActionInvocation = null;
+    Mock mockActionInvocation = null;
 
     private Dispatcher dispatcher;
 
-    public static void main(String[] args) {
-        TestRunner.run(PortletUrlTagTest.class);
-    }
-
-
     public void setUp() throws Exception {
         super.setUp();
 
         ServletContext servletContext = new MockServletContext();
-        dispatcher = new Dispatcher(servletContext, new HashMap());
+        dispatcher = new Dispatcher(servletContext, new HashMap<>());
         dispatcher.init();
         Dispatcher.setInstance(dispatcher);
 
         stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
-        stack.getContext().put(ActionContext.CONTAINER, dispatcher.getContainer());
-        ActionContext context = new ActionContext(stack.getContext());
-        ActionContext.setContext(context);
 
-    	mockActionInvocation  = mock(ActionInvocation.class);
+        mockActionInvocation = mock(ActionInvocation.class);
         mockActionProxy = mock(ActionProxy.class);
         mockHttpReq = mock(HttpServletRequest.class);
         mockHttpRes = mock(HttpServletResponse.class);
@@ -106,27 +107,27 @@ public class PortletUrlTagTest extends MockObjectTestCase {
 
         mockActionProxy.stubs().method("getNamespace").will(returnValue("/view"));
         mockActionInvocation.stubs().method("getProxy").will(returnValue(
-                mockActionProxy.proxy()));
+            mockActionProxy.proxy()));
         mockPageCtx.stubs().method("getRequest").will(
-                returnValue(mockHttpReq.proxy()));
+            returnValue(mockHttpReq.proxy()));
         mockPageCtx.stubs().method("getResponse").will(
-                returnValue(mockHttpRes.proxy()));
+            returnValue(mockHttpRes.proxy()));
         mockPageCtx.stubs().method("getOut").will(returnValue(mockJspWriter));
 
         mockHttpReq.stubs().method("getScheme").will(returnValue("http"));
         mockHttpReq.stubs().method("getAttribute").with(
-                eq("struts.valueStack")).will(returnValue(stack));
+            eq("struts.valueStack")).will(returnValue(stack));
         mockHttpReq.stubs().method("getAttribute").with(
-                eq("javax.portlet.response")).will(
-                returnValue(mockPortletRes.proxy()));
+            eq("javax.portlet.response")).will(
+            returnValue(mockPortletRes.proxy()));
         mockHttpReq.stubs().method("getAttribute").with(
-                eq("javax.portlet.request")).will(
-                returnValue(mockPortletReq.proxy()));
+            eq("javax.portlet.request")).will(
+            returnValue(mockPortletReq.proxy()));
         mockHttpReq.stubs().method("getAttribute").with(
-                eq("javax.servlet.include.servlet_path")).will(
-                returnValue("/servletPath"));
+            eq("javax.servlet.include.servlet_path")).will(
+            returnValue("/servletPath"));
         mockHttpReq.stubs().method("getParameterMap").will(
-                returnValue(Collections.emptyMap()));
+            returnValue(Collections.emptyMap()));
 
         mockPortletReq.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
         mockPortletReq.stubs().method("getWindowState").will(returnValue(WindowState.NORMAL));
@@ -134,30 +135,30 @@ public class PortletUrlTagTest extends MockObjectTestCase {
 
         tag.setPageContext((PageContext) mockPageCtx.proxy());
 
-        Map modeMap = new HashMap();
+        Map<PortletMode, String> modeMap = new HashMap<>();
         modeMap.put(PortletMode.VIEW, "/view");
         modeMap.put(PortletMode.HELP, "/help");
         modeMap.put(PortletMode.EDIT, "/edit");
-        Map<PortletMode,ActionMapping> actionMap = new HashMap<PortletMode,ActionMapping>();
-        actionMap.put(PortletMode.VIEW, new ActionMapping("defaultView", "/view", "execute", new HashMap<String,Object>()));
-        actionMap.put(PortletMode.HELP, new ActionMapping("defaultHelp", "/help", "execute", new HashMap<String,Object>()));
-        actionMap.put(PortletMode.EDIT, new ActionMapping("defaultEdit", "/edit", "execute", new HashMap<String,Object>()));
-        Map sessionMap = new HashMap();
-        Map contextMap = new HashMap();
-        contextMap.put(ActionContext.SESSION, sessionMap);
+        Map<PortletMode, ActionMapping> actionMap = new HashMap<>();
+        actionMap.put(PortletMode.VIEW, new ActionMapping("defaultView", "/view", "execute", new HashMap<>()));
+        actionMap.put(PortletMode.HELP, new ActionMapping("defaultHelp", "/help", "execute", new HashMap<>()));
+        actionMap.put(PortletMode.EDIT, new ActionMapping("defaultEdit", "/edit", "execute", new HashMap<>()));
+
+        Map<String, Object> contextMap = stack.getContext();
+        contextMap.put(ActionContext.SESSION, new HashMap<String, Object>());
         contextMap.put(PortletConstants.REQUEST, mockPortletReq.proxy());
         contextMap.put(PortletConstants.RESPONSE, mockPortletRes.proxy());
         contextMap.put(PortletConstants.PHASE, PortletPhase.RENDER_PHASE);
         contextMap.put(PortletConstants.MODE_NAMESPACE_MAP, modeMap);
         contextMap.put(PortletConstants.DEFAULT_ACTION_MAP, actionMap);
         contextMap.put(STRUTS_PORTLET_CONTEXT, mockCtx.proxy());
-        
-        ActionContext ctx = new ActionContext(contextMap);
+
+        ActionContext ctx = ActionContext.ofAndBound(contextMap);
         ctx.setValueStack(stack);
-        ActionInvocation ai = (ActionInvocation)mockActionInvocation.proxy();
-    	stack.getContext().put(ActionContext.ACTION_INVOCATION, ai);
-        ActionContext.setContext(ctx);
-        
+        ctx.setContainer(dispatcher.getContainer());
+
+        ActionInvocation ai = (ActionInvocation) mockActionInvocation.proxy();
+        ctx.setActionInvocation(ai);
     }
 
     public void tearDown() throws Exception {
@@ -169,13 +170,13 @@ public class PortletUrlTagTest extends MockObjectTestCase {
     }
 
     public void testEnsureParamsAreStringArrays() {
-        Map params = new HashMap();
+        Map<String, Object> params = new HashMap<>();
         params.put("param1", "Test1");
-        params.put("param2", new String[] { "Test2" });
+        params.put("param2", new String[]{"Test2"});
 
-        Map result = PortletUrlHelper.ensureParamsAreStringArrays(params);
+        Map<String, String[]> result = PortletUrlHelper.ensureParamsAreStringArrays(params);
         assertEquals(2, result.size());
-        assertTrue(result.get("param1") instanceof String[]);
+        assertNotNull(result.get("param1"));
     }
 
     public void testSetWindowState() throws Exception {
@@ -185,10 +186,10 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
 
         mockPortletRes.expects(once()).method("createRenderURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
-        Map paramMap = new HashMap();
+        Map<String, Object> paramMap = new HashMap<>();
         paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/view/testAction"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
 
@@ -200,20 +201,19 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         tag.setWindowState("maximized");
         tag.doStartTag();
         tag.doEndTag();
-
     }
 
-    public void testSetPortletMode() throws Exception  {
+    public void testSetPortletMode() throws Exception {
 
         PortletMode mode = PortletMode.HELP;
 
         mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
 
         mockPortletRes.expects(once()).method("createRenderURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
-        Map paramMap = new HashMap();
+        Map<String, Object> paramMap = new HashMap<>();
         paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/help/testAction"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
 
@@ -227,18 +227,18 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         tag.doStartTag();
         tag.doEndTag();
     }
-    
+
     public void testWhenPortletModeDiffersFromCurrentAndNoParametersAreSetRenderTheDefaults()
-    		 throws Exception {
+        throws Exception {
         PortletMode mode = PortletMode.HELP;
 
         mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
 
         mockPortletRes.expects(once()).method("createRenderURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
-        Map paramMap = new HashMap();
+        Map<String, Object> paramMap = new HashMap<>();
         paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/help/defaultHelp"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
 
@@ -258,10 +258,10 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
 
         mockPortletRes.expects(once()).method("createRenderURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
-        Map paramMap = new HashMap();
+        Map<String, Object> paramMap = new HashMap<>();
         paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/view/testAction"});
         paramMap.put("testParam1", new String[]{"testValue1"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
@@ -282,10 +282,10 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
 
         mockPortletRes.expects(once()).method("createActionURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
-        Map paramMap = new HashMap();
+        Map<String, Object> paramMap = new HashMap<>();
         paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/view/testAction"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
 
@@ -318,7 +318,7 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
         ParamTag paramTag = new ParamTag();
-        paramTag.setPageContext((PageContext)mockPageCtx.proxy());
+        paramTag.setPageContext((PageContext) mockPageCtx.proxy());
         paramTag.setParent(tag);
         paramTag.setName("testParam1");
         paramTag.setValue("'testValue1'");
@@ -337,12 +337,12 @@ public class PortletUrlTagTest extends MockObjectTestCase {
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
 
         ParamTag paramTag = new ParamTag();
-        paramTag.setPageContext((PageContext)mockPageCtx.proxy());
+        paramTag.setPageContext((PageContext) mockPageCtx.proxy());
         paramTag.setParent(tag);
         paramTag.setName("testParam1");
         paramTag.setValue("'testValue1'");
         ParamTag paramTag2 = new ParamTag();
-        paramTag2.setPageContext((PageContext)mockPageCtx.proxy());
+        paramTag2.setPageContext((PageContext) mockPageCtx.proxy());
         paramTag2.setParent(tag);
         paramTag2.setName("testParam2");
         paramTag2.setValue("'testValue2'");
@@ -357,51 +357,49 @@ public class PortletUrlTagTest extends MockObjectTestCase {
     }
 
     public void testUrlWithMethod() throws Exception {
-    	PortletMode mode = PortletMode.VIEW;
-    	mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
+        PortletMode mode = PortletMode.VIEW;
+        mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
         mockPortletRes.expects(once()).method("createRenderURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
-    	tag.setAction("testAction");
-    	Map paramMap = new HashMap();
+        tag.setAction("testAction");
+        Map<String, Object> paramMap = new HashMap<>();
         paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/view/testAction!input"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
         mockPortletUrl.expects(once()).method("setParameters").with(new ParamMapConstraint(paramMap));
         mockPortletUrl.expects(once()).method("setPortletMode").with(eq(PortletMode.VIEW));
         mockPortletUrl.expects(once()).method("setWindowState").with(eq(WindowState.NORMAL));
-    	tag.setMethod("input");
-    	tag.doStartTag();
-    	tag.doEndTag();
+        tag.setMethod("input");
+        tag.doStartTag();
+        tag.doEndTag();
     }
-    
+
     public void testUrlWithNoActionOrMethod() throws Exception {
-    	PortletMode mode = PortletMode.VIEW;
-    	mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
+        PortletMode mode = PortletMode.VIEW;
+        mockHttpReq.stubs().method("getQueryString").will(returnValue(""));
         mockPortletRes.expects(once()).method("createRenderURL").will(
-                returnValue(mockPortletUrl.proxy()));
+            returnValue(mockPortletUrl.proxy()));
         mockCtx.expects(atLeastOnce()).method("getMajorVersion").will(returnValue(1));
-    	Map paramMap = new HashMap();
-    	
-    	
-    	mockActionProxy.stubs().method("getActionName").will(returnValue("currentExecutingAction"));
-    	
-    	
-    	paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/view/currentExecutingAction"});
+        Map<String, Object> paramMap = new HashMap<>();
+
+        mockActionProxy.stubs().method("getActionName").will(returnValue("currentExecutingAction"));
+
+        paramMap.put(PortletConstants.ACTION_PARAM, new String[]{"/view/currentExecutingAction"});
         paramMap.put(PortletConstants.MODE_PARAM, new String[]{mode.toString()});
         mockPortletUrl.expects(once()).method("setParameters").with(new ParamMapConstraint(paramMap));
         mockPortletUrl.expects(once()).method("setPortletMode").with(eq(PortletMode.VIEW));
         mockPortletUrl.expects(once()).method("setWindowState").with(eq(WindowState.NORMAL));
-    	tag.doStartTag();
-    	tag.doEndTag();    	
+        tag.doStartTag();
+        tag.doEndTag();
     }
-    
+
     private static class ParamMapConstraint implements Constraint {
 
-        private Map myExpectedMap = null;
-        private Map myActualMap = null;
+        private Map<String, Object> myExpectedMap;
+        private Map<String, Object> myActualMap = null;
 
-        public ParamMapConstraint(Map expectedMap) {
-            if(expectedMap == null) {
+        public ParamMapConstraint(Map<String, Object> expectedMap) {
+            if (expectedMap == null) {
                 throw new IllegalArgumentException("Use an isNull constraint instead!");
             }
             myExpectedMap = expectedMap;
@@ -411,22 +409,21 @@ public class PortletUrlTagTest extends MockObjectTestCase {
          * @see org.jmock.core.Constraint#eval(java.lang.Object)
          */
         public boolean eval(Object val) {
-            myActualMap = (Map)val;
+            myActualMap = (Map<String, Object>) val;
             boolean result = false;
-            if(val != null) {
-                if(myExpectedMap.size() == myActualMap.size()) {
-                    Iterator keys = myExpectedMap.keySet().iterator();
+            if (val != null) {
+                if (myExpectedMap.size() == myActualMap.size()) {
+                    Iterator<String> keys = myExpectedMap.keySet().iterator();
                     boolean allSame = true;
-                    while(keys.hasNext()) {
+                    while (keys.hasNext()) {
                         Object key = keys.next();
-                        if(!myActualMap.containsKey(key)) {
+                        if (!myActualMap.containsKey(key)) {
                             allSame = false;
                             break;
-                        }
-                        else {
-                            String[] expected = (String[])myExpectedMap.get(key);
-                            String[] actual = (String[])myActualMap.get(key);
-                            if(!Arrays.equals(expected, actual)) {
+                        } else {
+                            String[] expected = (String[]) myExpectedMap.get(key);
+                            String[] actual = (String[]) myActualMap.get(key);
+                            if (!Arrays.equals(expected, actual)) {
                                 allSame = false;
                                 break;
                             }
@@ -442,30 +439,29 @@ public class PortletUrlTagTest extends MockObjectTestCase {
          * @see org.jmock.core.SelfDescribing#describeTo(java.lang.StringBuffer)
          */
         public StringBuffer describeTo(StringBuffer sb) {
-        	sb.append("\n Expected: ");
-        	describeTo(myExpectedMap, sb);
-        	sb.append("\n Actual: ");
-        	describeTo(myActualMap, sb);
-        	
+            sb.append("\n Expected: ");
+            describeTo(myExpectedMap, sb);
+            sb.append("\n Actual: ");
+            describeTo(myActualMap, sb);
+
             return sb;
         }
-        
-        private StringBuffer describeTo(Map map,StringBuffer sb) {
-        	Iterator<String> it = map.keySet().iterator();
-        	while(it.hasNext()) {
-        		String key = it.next();
-        		sb.append(key).append("=");
-        		String[] value = (String[])map.get(key);
-        		sb.append(value[0]);
-        		if(it.hasNext()) {
-        			sb.append(", ");
-        		}
-        	}
+
+        private StringBuffer describeTo(Map<String, Object> map, StringBuffer sb) {
+            Iterator<String> it = map.keySet().iterator();
+            while (it.hasNext()) {
+                String key = it.next();
+                sb.append(key).append("=");
+                String[] value = (String[]) map.get(key);
+                sb.append(value[0]);
+                if (it.hasNext()) {
+                    sb.append(", ");
+                }
+            }
             return sb;
         }
 
 
-
     }
 
 }
diff --git a/plugins/rest/src/test/java/org/apache/struts2/rest/ContentTypeHandlerManagerTest.java b/plugins/rest/src/test/java/org/apache/struts2/rest/ContentTypeHandlerManagerTest.java
index 2629455..567f64a 100644
--- a/plugins/rest/src/test/java/org/apache/struts2/rest/ContentTypeHandlerManagerTest.java
+++ b/plugins/rest/src/test/java/org/apache/struts2/rest/ContentTypeHandlerManagerTest.java
@@ -27,7 +27,6 @@ import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
 import com.opensymphony.xwork2.mock.MockActionProxy;
 import junit.framework.TestCase;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.rest.handler.AbstractContentTypeHandler;
 import org.apache.struts2.rest.handler.ContentTypeHandler;
 import org.apache.struts2.rest.handler.FormUrlEncodedHandler;
@@ -37,6 +36,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.Writer;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -58,9 +58,9 @@ public class ContentTypeHandlerManagerTest extends TestCase {
         mockResponse = new MockHttpServletResponse();
         mockRequest = new MockHttpServletRequest();
         mockRequest.setMethod("GET");
-        ActionContext.setContext(new ActionContext(new HashMap()));
-        ServletActionContext.setRequest(mockRequest);
-        ServletActionContext.setResponse(mockResponse);
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setServletRequest(mockRequest);
+        actionContext.setServletResponse(mockResponse);
 
         invocation = new MockActionInvocation();
         invocation.setProxy(new MockActionProxy());
@@ -69,7 +69,7 @@ public class ContentTypeHandlerManagerTest extends TestCase {
     @Override
     public void tearDown() {
         mockRequest = null;
-        mockRequest = null;
+        mockResponse = null;
         mgr = null;
     }
 
@@ -106,7 +106,7 @@ public class ContentTypeHandlerManagerTest extends TestCase {
         assertEquals(0, mockResponse.getContentLength());
     }
 
-    public void testHandleValidationError() throws Exception {
+    public void testHandleValidationError() {
         mockRequest.setMethod("PUT");
 
     }
@@ -129,7 +129,7 @@ public class ContentTypeHandlerManagerTest extends TestCase {
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("xmlOverride")), mockHandlerXmlOverride.proxy());
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("xml")), mockHandlerXml.proxy());
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("json")), mockHandlerJson.proxy());
-        mockContainer.expectAndReturn("getInstanceNames", C.args(C.eq(ContentTypeHandler.class)), new HashSet(Arrays.asList("xml", "xmlOverride", "json")));
+        mockContainer.expectAndReturn("getInstanceNames", C.args(C.eq(ContentTypeHandler.class)), new HashSet<>(Arrays.asList("xml", "xmlOverride", "json")));
 
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(String.class),
                 C.eq(ContentTypeHandlerManager.STRUTS_REST_HANDLER_OVERRIDE_PREFIX+"xml")), "xmlOverride");
@@ -147,7 +147,7 @@ public class ContentTypeHandlerManagerTest extends TestCase {
     }
 
     /** Assert that the request content-type and differ from the response content type */
-    public void testHandleRequestContentType() throws IOException {
+    public void testHandleRequestContentType() {
 
         Mock mockHandlerForm = new Mock(ContentTypeHandler.class);
         mockHandlerForm.matchAndReturn("getExtension", null);
@@ -163,10 +163,10 @@ public class ContentTypeHandlerManagerTest extends TestCase {
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("x-www-form-urlencoded")), mockHandlerForm.proxy());
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("json")), mockHandlerJson.proxy());
         mockContainer.matchAndReturn("getInstance", C.args(C.eq(String.class), C.eq("struts.rest.handlerOverride.json")), null);
-        mockContainer.expectAndReturn("getInstanceNames", C.args(C.eq(ContentTypeHandler.class)), new HashSet(Arrays.asList("x-www-form-urlencoded", "json")));
+        mockContainer.expectAndReturn("getInstanceNames", C.args(C.eq(ContentTypeHandler.class)), new HashSet<>(Arrays.asList("x-www-form-urlencoded", "json")));
 
         mockRequest.setContentType(FormUrlEncodedHandler.CONTENT_TYPE);
-        mockRequest.setContent("a=1&b=2".getBytes("UTF-8"));
+        mockRequest.setContent("a=1&b=2".getBytes(StandardCharsets.UTF_8));
         mgr.setContainer((Container) mockContainer.proxy());
         ContentTypeHandler handler = mgr.getHandlerForRequest(mockRequest);
 
diff --git a/plugins/rest/src/test/java/org/apache/struts2/rest/RestWorkflowInterceptorTest.java b/plugins/rest/src/test/java/org/apache/struts2/rest/RestWorkflowInterceptorTest.java
index 5b3a09d..c291dce 100644
--- a/plugins/rest/src/test/java/org/apache/struts2/rest/RestWorkflowInterceptorTest.java
+++ b/plugins/rest/src/test/java/org/apache/struts2/rest/RestWorkflowInterceptorTest.java
@@ -25,7 +25,6 @@ import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ActionProxy;
 import com.opensymphony.xwork2.ActionSupport;
 import junit.framework.TestCase;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
 
 import java.util.HashMap;
@@ -52,9 +51,9 @@ public class RestWorkflowInterceptorTest extends TestCase {
         }, null);
         wf.setContentTypeHandlerManager((ContentTypeHandlerManager) mockContentTypeHandlerManager.proxy());
 
-        ActionContext.setContext(new ActionContext(new HashMap<String, Object>() {{
-            put(ServletActionContext.ACTION_MAPPING, new ActionMapping());
-        }}));
+        ActionContext actionContext = ActionContext.ofAndBound(new HashMap<>());
+        actionContext.setActionMapping(new ActionMapping());
+
         wf.doIntercept((ActionInvocation) mockActionInvocation.proxy());
         mockContentTypeHandlerManager.verify();
         mockActionInvocation.verify();
diff --git a/plugins/rest/src/test/java/org/apache/struts2/rest/handler/JuneauXmlHandlerTest.java b/plugins/rest/src/test/java/org/apache/struts2/rest/handler/JuneauXmlHandlerTest.java
index c51ba6a..52e3d5f 100644
--- a/plugins/rest/src/test/java/org/apache/struts2/rest/handler/JuneauXmlHandlerTest.java
+++ b/plugins/rest/src/test/java/org/apache/struts2/rest/handler/JuneauXmlHandlerTest.java
@@ -51,7 +51,7 @@ public class JuneauXmlHandlerTest extends XWorkTestCase {
             "</object>";
         handler = new JuneauXmlHandler();
         ai = new MockActionInvocation();
-        ActionContext context = new ActionContext(new HashMap<String, Object>());
+        ActionContext context = ActionContext.ofAndBound(new HashMap<>());
         context.setLocale(Locale.US);
         ((MockActionInvocation) ai).setInvocationContext(context);
     }
diff --git a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/FreemarkerDecoratorServlet.java b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/FreemarkerDecoratorServlet.java
index e3760cf..85ef896 100644
--- a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/FreemarkerDecoratorServlet.java
+++ b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/FreemarkerDecoratorServlet.java
@@ -66,7 +66,7 @@ public class FreemarkerDecoratorServlet extends freemarker.ext.servlet.Freemarke
 
     public void init() throws ServletException {
         try {
-            Dispatcher dispatcher = (Dispatcher) getServletContext().getAttribute(StrutsStatics.SERVLET_DISPATCHER);
+            Dispatcher dispatcher = Dispatcher.getInstance(getServletContext());
             if (dispatcher == null) {
                 throw new IllegalStateException("Unable to find the Dispatcher in the Servlet Context. Is '" + StrutsListener.class.getName() + "' missing in web.xml?");
             }
diff --git a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java
index b172eb4..9cdc9fc 100644
--- a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java
+++ b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java
@@ -95,7 +95,7 @@ public abstract class OldDecorator2NewStrutsDecorator extends BaseWebAppDecorato
             // ok, one isn't associated with the request, so let's create one using the current Dispatcher
             ValueStack vs = Dispatcher.getInstance().getContainer().getInstance(ValueStackFactory.class).createValueStack();
             vs.getContext().putAll(Dispatcher.getInstance().createContextMap(request, response, null));
-            ctx = new ActionContext(vs.getContext());
+            ctx = ActionContext.of(vs.getContext());
             if (ctx.getActionInvocation() == null) {
                 // put in a dummy ActionSupport so basic functionality still works
                 ActionSupport action = new ActionSupport();
diff --git a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/VelocityDecoratorServlet.java b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/VelocityDecoratorServlet.java
index 7fd2265..4859c4f 100644
--- a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/VelocityDecoratorServlet.java
+++ b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/VelocityDecoratorServlet.java
@@ -74,7 +74,7 @@ public class VelocityDecoratorServlet extends VelocityViewServlet {
      */
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
-        Dispatcher dispatcher = (Dispatcher) getServletContext().getAttribute(StrutsStatics.SERVLET_DISPATCHER);
+        Dispatcher dispatcher = Dispatcher.getInstance(getServletContext());
         if (dispatcher == null) {
             throw new IllegalStateException("Unable to find the Dispatcher in the Servlet Context. Is '" + StrutsListener.class.getName() + "' missing in web.xml?");
         }