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/03/22 13:21:35 UTC

[struts] branch action-context-boost updated (3cccede -> 3739b66)

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

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


 discard 3cccede  WW-4789 WW-3788 ActionContext refactoring
     new 3739b66  WW-4789 WW-3788 ActionContext refactoring

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (3cccede)
            \
             N -- N -- N   refs/heads/action-context-boost (3739b66)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../com/opensymphony/xwork2/ActionContext.java     | 22 +++++++++++++---------
 .../xwork2/DefaultActionInvocation.java            |  2 +-
 .../opensymphony/xwork2/DefaultActionProxy.java    |  4 ++--
 .../xwork2/config/impl/DefaultConfiguration.java   |  2 +-
 .../opensymphony/xwork2/ognl/OgnlValueStack.java   |  2 +-
 .../xwork2/util/XWorkTestCaseHelper.java           |  4 ++--
 .../org/apache/struts2/ServletActionContext.java   |  2 +-
 .../struts2/dispatcher/PrepareOperations.java      |  4 ++--
 .../apache/struts2/factory/StrutsActionProxy.java  |  4 ++--
 .../struts2/interceptor/BackgroundProcess.java     |  2 +-
 .../struts2/util/InvocationSessionStore.java       |  3 +--
 .../apache/struts2/util/StrutsTestCaseHelper.java  |  2 +-
 .../org/apache/struts2/views/jsp/TagUtils.java     |  5 ++---
 .../com/opensymphony/xwork2/ActionContextTest.java |  4 ++--
 .../xwork2/ActionContextThreadLocalTest.java       |  2 +-
 .../com/opensymphony/xwork2/ActionSupportTest.java |  2 +-
 .../com/opensymphony/xwork2/ChainResultTest.java   |  2 +-
 .../xwork2/DefaultTextProviderTest.java            |  2 +-
 .../com/opensymphony/xwork2/LocaleAwareTest.java   |  2 +-
 .../com/opensymphony/xwork2/StubValueStack.java    |  2 +-
 .../interceptor/ChainingInterceptorTest.java       |  2 +-
 .../ConversionErrorInterceptorTest.java            |  4 ++--
 .../DefaultWorkflowInterceptorTest.java            |  2 +-
 .../ExceptionMappingInterceptorTest.java           |  2 +-
 .../ParameterRemoverInterceptorTest.java           |  2 +-
 .../interceptor/ValidationErrorAwareTest.java      |  2 +-
 ...ationInterceptorPrefixMethodInvocationTest.java |  2 +-
 .../AnnotationParameterFilterInterceptorTest.java  |  8 ++++----
 .../ActionAutowiringInterceptorTest.java           |  4 ++--
 .../ConversionErrorFieldValidatorTest.java         |  2 +-
 .../DefaultActionValidatorManagerTest.java         |  2 +-
 .../validator/SimpleActionValidationTest.java      |  4 ++--
 .../apache/struts2/ServletActionContextTest.java   |  2 +-
 .../interceptor/ClearSessionInterceptorTest.java   |  5 +----
 .../struts2/interceptor/I18nInterceptorTest.java   |  2 +-
 .../interceptor/MessageStoreInterceptorTest.java   | 12 ++++++------
 .../MessageStorePreResultListenerTest.java         |  8 ++++----
 .../StrutsConversionErrorInterceptorTest.java      |  2 +-
 .../struts2/interceptor/TokenInterceptorTest.java  |  2 +-
 .../apache/struts2/result/PlainTextResultTest.java |  2 +-
 .../struts2/result/ServletRedirectResultTest.java  |  2 +-
 .../struts2/util/InvocationSessionStoreTest.java   |  7 +++----
 .../org/apache/struts2/util/TokenHelperTest.java   |  2 +-
 .../views/freemarker/FreeMarkerResultTest.java     |  2 +-
 .../freemarker/FreemarkerResultMockedTest.java     |  2 +-
 .../apache/struts2/views/jsp/AbstractTagTest.java  |  2 +-
 .../org/apache/struts2/views/jsp/TextTagTest.java  |  2 +-
 .../org/apache/struts2/views/jsp/URLTagTest.java   |  2 +-
 .../PackageBasedActionConfigBuilderTest.java       |  4 ++--
 .../org/apache/struts2/EmbeddedJSPResultTest.java  |  4 ++--
 .../struts2/views/java/simple/AbstractTest.java    |  2 +-
 .../struts2/views/java/simple/TokenTest.java       |  3 +--
 .../org/apache/struts2/StrutsJUnit4TestCase.java   |  4 +---
 .../org/apache/struts2/StrutsRestTestCase.java     |  2 +-
 .../java/org/apache/struts2/StrutsTestCase.java    |  4 +---
 .../struts2/osgi/OsgiConfigurationProvider.java    |  2 +-
 .../portlet/context/PortletActionContextTest.java  |  2 +-
 .../interceptor/PortletAwareInterceptorTest.java   |  6 +++---
 .../interceptor/PortletStateInterceptorTest.java   |  6 +++---
 .../struts2/portlet/result/PortletResultTest.java  |  5 +----
 .../struts2/portlet/util/PortletUrlHelperTest.java |  2 +-
 .../struts2/views/jsp/PortletUrlTagTest.java       |  3 +--
 .../rest/ContentTypeHandlerManagerTest.java        |  2 +-
 .../struts2/rest/RestWorkflowInterceptorTest.java  |  2 +-
 .../struts2/rest/handler/JuneauXmlHandlerTest.java |  2 +-
 .../sitemesh/OldDecorator2NewStrutsDecorator.java  |  2 +-
 66 files changed, 106 insertions(+), 117 deletions(-)


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

Posted by lu...@apache.org.
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 3739b66821d062a8b3f2c430b297676bac4203aa
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 fe9daf5..0ec3262 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 StrutsTestCase {
 
-	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?");
         }