Merge branch 'master' into action-context-boost
diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatAuthenticationInterceptor.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatAuthenticationInterceptor.java
index fccaad7..e820f55 100644
--- a/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatAuthenticationInterceptor.java
+++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatAuthenticationInterceptor.java
@@ -25,27 +25,28 @@
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
-import org.apache.struts2.dispatcher.SessionMap;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
 
 public class ChatAuthenticationInterceptor extends AbstractInterceptor {
 
-	private static final long serialVersionUID = 1L;
-	private static final Logger LOG = LogManager.getLogger(ChatAuthenticationInterceptor.class);
-	public static final String USER_SESSION_KEY = "chatUserSessionKey";
+    private static final long serialVersionUID = 1L;
+    private static final Logger LOG = LogManager.getLogger(ChatAuthenticationInterceptor.class);
+    public static final String USER_SESSION_KEY = "chatUserSessionKey";
 
-	public String intercept(ActionInvocation invocation) throws Exception {
+    public String intercept(ActionInvocation invocation) throws Exception {
 
-		LOG.debug("Authenticating chat user");
+        LOG.debug("Authenticating chat user");
 
-		SessionMap session = (SessionMap) ActionContext.getContext().get(ActionContext.SESSION);
-		User user = (User) session.get(USER_SESSION_KEY);
+        Map<String, Object> session = ActionContext.getContext().getSession();
+        User user = (User) session.get(USER_SESSION_KEY);
 
-		if (user == null) {
-			return Action.LOGIN;
-		}
-		return invocation.invoke();
-	}
+        if (user == null) {
+            return Action.LOGIN;
+        }
+        return invocation.invoke();
+    }
 
 }
diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatInterceptor.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatInterceptor.java
index 4497709..324226b 100644
--- a/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatInterceptor.java
+++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/chat/ChatInterceptor.java
@@ -24,31 +24,31 @@
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
-import javax.servlet.http.HttpSession;
+import java.util.Map;
 
 /**
  * Authenticate showcase chat example, make sure everyone have a username.
  */
 public class ChatInterceptor extends AbstractInterceptor {
 
-	private static final Logger LOG = LogManager.getLogger(ChatInterceptor.class);
+    private static final Logger LOG = LogManager.getLogger(ChatInterceptor.class);
 
-	private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 1L;
 
-	public static final String CHAT_USER_SESSION_KEY = "ChatUserSessionKey";
+    public static final String CHAT_USER_SESSION_KEY = "ChatUserSessionKey";
 
-	public String intercept(ActionInvocation invocation) throws Exception {
-		HttpSession session = (HttpSession) ActionContext.getContext().get(ActionContext.SESSION);
-		User chatUser = (User) session.getAttribute(CHAT_USER_SESSION_KEY);
-		if (chatUser == null) {
-			LOG.debug("Chat user not logged in");
-			return Action.LOGIN;
-		}
-		return invocation.invoke();
-	}
+    public String intercept(ActionInvocation invocation) throws Exception {
+        Map<String, Object> session = ActionContext.getContext().getSession();
+        User chatUser = (User) session.get(CHAT_USER_SESSION_KEY);
+        if (chatUser == null) {
+            LOG.debug("Chat user not logged in");
+            return Action.LOGIN;
+        }
+        return invocation.invoke();
+    }
 }
 
 
diff --git a/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java b/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java
index 9261e61..06ab021 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 @@
      * @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,15 +216,16 @@
             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(CHAIN_HISTORY, ActionChainResult.getChainHistory());
+        Map<String, Object> extraContext = ActionContext.of(new HashMap<>())
+            .withValueStack(invocation.getInvocationContext().getValueStack())
+            .withParameters(invocation.getInvocationContext().getParameters())
+            .with(CHAIN_HISTORY, ActionChainResult.getChainHistory())
+            .getContextMap();
 
         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..5fd676b 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.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;
@@ -56,71 +62,167 @@
 
     /**
      * Constant for the name of the action being executed.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
 
     /**
      * Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String VALUE_STACK = ValueStack.VALUE_STACK;
 
     /**
      * Constant for the action's session.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
 
     /**
      * Constant for the action's application context.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
 
     /**
      * Constant for the action's parameters.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
 
     /**
      * Constant for the action's locale.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
 
     /**
      * Constant for the action's {@link com.opensymphony.xwork2.ActionInvocation invocation} context.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
 
     /**
      * Constant for the map of type conversion errors.
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
 
-
     /**
      * Constant for the container
+     *
+     * @deprecated scope will be narrowed to "private", use helper methods instead
      */
+    @Deprecated
     public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
-    
-    private Map<String, Object> context;
+
+    private final Map<String, Object> context;
 
     /**
      * Creates a new ActionContext initialized with another context.
      *
      * @param context a context map.
      */
-    public ActionContext(Map<String, Object> context) {
+    protected 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) {
+        if (context == null) {
+            throw new IllegalArgumentException("Context cannot be null!");
+        }
+        return new ActionContext(context);
+    }
+
+    /**
+     * Binds the provided context with the current thread
+     *
+     * @param actionContext context to bind to the thread
+     * @return context which was bound to the thread
+     */
+    public static ActionContext bind(ActionContext actionContext) {
+        ActionContext.setContext(actionContext);
+        return ActionContext.getContext();
+    }
+
+    public static boolean containsValueStack(Map<String, Object> context) {
+        return context != null && context.containsKey(VALUE_STACK);
+    }
+
+    /**
+     * Binds this context with the current thread
+     *
+     * @return this context which was bound to the thread
+     */
+    public ActionContext bind() {
+        ActionContext.setContext(this);
+        return ActionContext.getContext();
+    }
+
+    /**
+     * 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).
      *
      * @param actionInvocation the action execution state.
+     * @deprecated use {@link #withActionInvocation(ActionInvocation)} instead
      */
+    @Deprecated
     public void setActionInvocation(ActionInvocation actionInvocation) {
         put(ACTION_INVOCATION, actionInvocation);
     }
 
+    public ActionContext withActionInvocation(ActionInvocation actionInvocation) {
+        put(ACTION_INVOCATION, actionInvocation);
+        return this;
+    }
+
     /**
      * Gets the action invocation (the execution state).
      *
@@ -134,68 +236,65 @@
      * Sets the action's application context.
      *
      * @param application the action's application context.
+     * @deprecated use {@link #withApplication(Map)} instead
      */
+    @Deprecated
     public void setApplication(Map<String, Object> application) {
         put(APPLICATION, application);
     }
 
+    public ActionContext withApplication(Map<String, Object> application) {
+        put(APPLICATION, application);
+        return this;
+    }
+
     /**
      * Returns a Map of the ServletContext when in a servlet environment or a generic application level Map otherwise.
      *
      * @return a Map of ServletContext or generic application level Map
      */
+    @SuppressWarnings("unchecked")
     public Map<String, Object> getApplication() {
         return (Map<String, Object>) get(APPLICATION);
     }
 
     /**
-     * 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.
      */
     public Map<String, Object> getContextMap() {
-        return getContext().context;
+        return context;
     }
 
     /**
      * Sets conversion errors which occurred when executing the action.
      *
      * @param conversionErrors a Map of errors which occurred when executing the action.
+     * @deprecated use {@link #withConversionErrors(Map)} instead
      */
+    @Deprecated
     public void setConversionErrors(Map<String, ConversionData> conversionErrors) {
         put(CONVERSION_ERRORS, conversionErrors);
     }
 
+    public ActionContext withConversionErrors(Map<String, ConversionData> conversionErrors) {
+        put(CONVERSION_ERRORS, conversionErrors);
+        return this;
+    }
+
     /**
      * 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.
      */
+    @SuppressWarnings("unchecked")
     public Map<String, ConversionData> getConversionErrors() {
-        Map<String, ConversionData> errors = (Map) get(CONVERSION_ERRORS);
+        Map<String, ConversionData> errors = (Map<String, ConversionData>) get(CONVERSION_ERRORS);
 
         if (errors == null) {
-            errors = new HashMap<>();
-            setConversionErrors(errors);
+            errors = withConversionErrors(new HashMap<>()).getConversionErrors();
         }
 
         return errors;
@@ -205,11 +304,18 @@
      * Sets the Locale for the current action.
      *
      * @param locale the Locale for the current action.
+     * @deprecated use {@link #withLocale(Locale)} instead
      */
+    @Deprecated
     public void setLocale(Locale locale) {
         put(LOCALE, locale);
     }
 
+    public ActionContext withLocale(Locale locale) {
+        put(LOCALE, locale);
+        return this;
+    }
+
     /**
      * Gets the Locale of the current action. If no locale was ever specified the platform's
      * {@link java.util.Locale#getDefault() default locale} is used.
@@ -231,11 +337,18 @@
      * Sets the name of the current Action in the ActionContext.
      *
      * @param name the name of the current action.
+     * @deprecated use {@link #withActionName(String)} instead
      */
+    @Deprecated
     public void setName(String name) {
         put(ACTION_NAME, name);
     }
 
+    public ActionContext withActionName(String actionName) {
+        put(ACTION_NAME, actionName);
+        return this;
+    }
+
     /**
      * Gets the name of the current Action.
      *
@@ -246,6 +359,15 @@
     }
 
     /**
+     * Gets the name of the current Action.
+     *
+     * @return the name of the current action.
+     */
+    public String getActionName() {
+        return (String) get(ACTION_NAME);
+    }
+
+    /**
      * Sets the action parameters.
      *
      * @param parameters the parameters for the current action.
@@ -254,12 +376,17 @@
         put(PARAMETERS, parameters);
     }
 
+    public ActionContext withParameters(HttpParameters parameters) {
+        put(PARAMETERS, parameters);
+        return this;
+    }
+
     /**
      * Returns a Map of the HttpServletRequest parameters when in a servlet environment or a generic Map of
      * 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,17 +395,25 @@
     /**
      * Sets a map of action session values.
      *
-     * @param session  the session values.
+     * @param session the session values.
+     * @deprecated use {@link #withSession(Map)} instead
      */
+    @Deprecated
     public void setSession(Map<String, Object> session) {
         put(SESSION, session);
     }
 
+    public ActionContext withSession(Map<String, Object> session) {
+        put(SESSION, session);
+        return this;
+    }
+
     /**
      * Gets the Map of HttpSession values when in a servlet environment or a generic session map otherwise.
      *
      * @return the Map of HttpSession values when in a servlet environment or a generic session map otherwise.
      */
+    @SuppressWarnings("unchecked")
     public Map<String, Object> getSession() {
         return (Map<String, Object>) get(SESSION);
     }
@@ -287,11 +422,18 @@
      * Sets the OGNL value stack.
      *
      * @param stack the OGNL value stack.
+     * @deprecated Use {@link #withValueStack(ValueStack)} instead
      */
+    @Deprecated
     public void setValueStack(ValueStack stack) {
         put(VALUE_STACK, stack);
     }
 
+    public ActionContext withValueStack(ValueStack valueStack) {
+        put(VALUE_STACK, valueStack);
+        return this;
+    }
+
     /**
      * Gets the OGNL value stack.
      *
@@ -300,25 +442,32 @@
     public ValueStack getValueStack() {
         return (ValueStack) get(VALUE_STACK);
     }
-    
+
     /**
      * Gets the container for this request
-     * 
+     *
      * @param cont The container
+     * @deprecated use {@link #withContainer(Container)} instead
      */
+    @Deprecated
     public void setContainer(Container cont) {
         put(CONTAINER, cont);
     }
-    
+
+    public ActionContext withContainer(Container container) {
+        put(CONTAINER, container);
+        return this;
+    }
+
     /**
      * 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 +496,129 @@
     public void put(String key, Object value) {
         context.put(key, value);
     }
+
+    /**
+     * Gets ServletContext associated with current action
+     *
+     * @return current ServletContext
+     */
+    public ServletContext getServletContext() {
+        return (ServletContext) get(StrutsStatics.SERVLET_CONTEXT);
+    }
+
+    /**
+     * Assigns ServletContext to action context
+     *
+     * @param servletContext associated with current request
+     * @return ActionContext
+     */
+    public ActionContext withServletContext(ServletContext servletContext) {
+        put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+        return this;
+    }
+
+    /**
+     * Gets ServletRequest associated with current action
+     *
+     * @return current ServletRequest
+     */
+    public HttpServletRequest getServletRequest() {
+        return (HttpServletRequest) get(StrutsStatics.HTTP_REQUEST);
+    }
+
+    /**
+     * Assigns ServletRequest to action context
+     *
+     * @param request associated with current request
+     * @return ActionContext
+     */
+    public ActionContext withServletRequest(HttpServletRequest request) {
+        put(StrutsStatics.HTTP_REQUEST, request);
+        return this;
+    }
+
+    /**
+     * Gets ServletResponse associated with current action
+     *
+     * @return current ServletResponse
+     */
+    public HttpServletResponse getServletResponse() {
+        return (HttpServletResponse) get(StrutsStatics.HTTP_RESPONSE);
+    }
+
+    /**
+     * Assigns ServletResponse to action context
+     *
+     * @param response associated with current request
+     * @return ActionContext
+     */
+    public ActionContext withServletResponse(HttpServletResponse response) {
+        put(StrutsStatics.HTTP_RESPONSE, response);
+        return this;
+    }
+
+    /**
+     * Gets PageContext associated with current action
+     *
+     * @return current PageContext
+     */
+    public PageContext getPageContext() {
+        return (PageContext) get(StrutsStatics.PAGE_CONTEXT);
+    }
+
+    /**
+     * Assigns PageContext to action context
+     *
+     * @param pageContext associated with current request
+     * @return ActionContext
+     */
+    public ActionContext withPageContext(PageContext pageContext) {
+        put(StrutsStatics.PAGE_CONTEXT, pageContext);
+        return this;
+    }
+
+    /**
+     * Gets ActionMapping associated with current action
+     *
+     * @return current ActionMapping
+     */
+    public ActionMapping getActionMapping() {
+        return (ActionMapping) get(StrutsStatics.ACTION_MAPPING);
+    }
+
+    /**
+     * Assigns ActionMapping to action context
+     *
+     * @param actionMapping associated with current request
+     * @return ActionContext
+     */
+    public ActionContext withActionMapping(ActionMapping actionMapping) {
+        put(StrutsStatics.ACTION_MAPPING, actionMapping);
+        return this;
+    }
+
+    /**
+     * Assigns an extra context map to action context
+     *
+     * @param extraContext to add to the current action context
+     * @return ActionContext
+     */
+    public ActionContext withExtraContext(Map<String, Object> extraContext) {
+        if (extraContext != null) {
+            context.putAll(extraContext);
+        }
+        return this;
+    }
+
+    /**
+     * Adds arbitrary key to action context
+     *
+     * @param key   a string
+     * @param value an object
+     * @return ActionContext
+     */
+    public ActionContext with(String key, Object value) {
+        put(key, value);
+        return this;
+    }
 }
diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
index caa8d0f..ea2076d 100644
--- a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
+++ b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
@@ -324,36 +324,31 @@
     }
 
     protected Map<String, Object> createContextMap() {
-        Map<String, Object> contextMap;
+        ActionContext actionContext;
 
-        if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {
+        if (ActionContext.containsValueStack(extraContext)) {
             // In case the ValueStack was passed in
-            stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);
+            stack = ActionContext.of(extraContext).getValueStack();
 
             if (stack == null) {
                 throw new IllegalStateException("There was a null Stack set into the extra params.");
             }
 
-            contextMap = stack.getContext();
+            actionContext = stack.getActionContext();
         } else {
             // create the value stack
             // this also adds the ValueStack to its context
             stack = valueStackFactory.createValueStack();
 
             // create the action context
-            contextMap = stack.getContext();
+            actionContext = stack.getActionContext();
         }
 
-        // put extraContext in
-        if (extraContext != null) {
-            contextMap.putAll(extraContext);
-        }
-
-        //put this DefaultActionInvocation into the context map
-        contextMap.put(ActionContext.ACTION_INVOCATION, this);
-        contextMap.put(ActionContext.CONTAINER, container);
-
-        return contextMap;
+        return actionContext
+            .withExtraContext(extraContext)
+            .withActionInvocation(this)
+            .withContainer(container)
+            .getContextMap();
     }
 
     /**
@@ -385,7 +380,7 @@
         ActionContext actionContext = ActionContext.getContext();
 
         if (actionContext != null) {
-            actionContext.setActionInvocation(this);
+            actionContext.withActionInvocation(this);
         }
 
         createAction(contextMap);
@@ -395,8 +390,8 @@
             contextMap.put("action", action);
         }
 
-        invocationContext = new ActionContext(contextMap);
-        invocationContext.setName(proxy.getActionName());
+        invocationContext = ActionContext.of(contextMap)
+            .withActionName(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..678a830 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 String execute() throws Exception {
         ActionContext nestedContext = ActionContext.getContext();
-        ActionContext.setContext(invocation.getInvocationContext());
+        ActionContext.bind(invocation.getInvocationContext());
 
         String retCode = null;
 
@@ -153,7 +153,7 @@
             retCode = invocation.invoke();
         } finally {
             if (cleanupContext) {
-                ActionContext.setContext(nestedContext);
+                ActionContext.bind(nestedContext);
             }
         }
 
diff --git a/core/src/main/java/com/opensymphony/xwork2/TextProviderSupport.java b/core/src/main/java/com/opensymphony/xwork2/TextProviderSupport.java
index abb3249..7f8afbc 100644
--- a/core/src/main/java/com/opensymphony/xwork2/TextProviderSupport.java
+++ b/core/src/main/java/com/opensymphony/xwork2/TextProviderSupport.java
@@ -254,7 +254,7 @@
         if (stack == null){
         	locale = getLocale();
         }else{
-        	locale = (Locale) stack.getContext().get(ActionContext.LOCALE);
+        	locale = stack.getActionContext().getLocale();
         }
         if (locale == null) {
             locale = getLocale();
@@ -284,7 +284,7 @@
         if (stack == null){
         	locale = getLocale();
         }else{
-        	locale = (Locale) stack.getContext().get(ActionContext.LOCALE);
+        	locale = stack.getActionContext().getLocale();
         }
         if (locale == null) {
             locale = getLocale();
diff --git a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
index bc6b0a8..330e964 100644
--- a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
+++ b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
@@ -29,6 +29,10 @@
 import junit.framework.TestCase;
 import org.apache.commons.lang3.ClassUtils;
 
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
 /**
  * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses 
  * the generic test setup for logic.
@@ -96,5 +100,11 @@
             }
         });
     }
-    
+
+    protected Map<String, Object> createContextWithLocale(Locale locale) {
+        return ActionContext.of(new HashMap<>())
+            .withLocale(locale)
+            .getContextMap();
+    }
+
 }
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..3a714b8 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 @@
             rebuildRuntimeConfiguration();
         } finally {
             if (oldContext == null) {
-                ActionContext.setContext(null);
+                ActionContext.clear();
             }
         }
         return packageProviders;
@@ -223,8 +223,7 @@
         ActionContext context = ActionContext.getContext();
         if (context == null) {
             ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
-            context = new ActionContext(vs.getContext());
-            ActionContext.setContext(context);
+            context = ActionContext.of(vs.getContext()).bind();
         }
         return context;
     }
diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java
index 731b8f5..6665654 100644
--- a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java
+++ b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java
@@ -324,7 +324,7 @@
     protected Locale getLocale(Map<String, Object> context) {
         Locale locale = null;
         if (context != null) {
-            locale = (Locale) context.get(ActionContext.LOCALE);
+            locale = ActionContext.of(context).getLocale();
         }
         if (locale == null) {
             LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java
index 2a8df11..3cda3e3 100644
--- a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java
+++ b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java
@@ -442,11 +442,12 @@
                 realProperty = fullName;
             }
 
-            Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) context.get(ActionContext.CONVERSION_ERRORS);
+            ActionContext actionContext = ActionContext.of(context);
+            Map<String, ConversionData> conversionErrors = actionContext.getConversionErrors();
 
             if (conversionErrors == null) {
                 conversionErrors = new HashMap<>();
-                context.put(ActionContext.CONVERSION_ERRORS, conversionErrors);
+                actionContext.withConversionErrors(conversionErrors);
             }
 
             conversionErrors.put(realProperty, new ConversionData(value, toClass));
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java
index bc156e4..992bf94 100644
--- a/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java
@@ -158,7 +158,7 @@
                     ReflectionContextState.setReportingConversionErrors(context, true);
 
                     //keep locale from original context
-                    context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));
+                    newStack.getActionContext().withLocale(stack.getActionContext().getLocale());
                 }
 
                 // override
@@ -193,8 +193,9 @@
                     }
                 }
 
-                if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
-                    stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));
+                if (clearableStack) {
+                    stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
+                }
             } else {
                 LOG.debug("invalid alias expression: {}", aliasesKey);
             }
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
index aa8c441..1310588 100644
--- a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
@@ -192,7 +192,7 @@
             ReflectionContextState.setReportingConversionErrors(context, true);
 
             //keep locale from original context
-            context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));
+            newStack.getActionContext().withLocale(stack.getActionContext().getLocale());
         }
 
         boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
@@ -216,8 +216,9 @@
             }
         }
 
-        if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
-            stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));
+        if (clearableStack) {
+            stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
+        }
 
         addParametersToContext(ActionContext.getContext(), acceptableParameters);
     }
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java
index 2d05e9c..9f0f117 100644
--- a/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java
@@ -164,7 +164,7 @@
                     ReflectionContextState.setReportingConversionErrors(context, true);
 
                     //keep locale from original context
-                    context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));
+                    newStack.getActionContext().withLocale(stack.getActionContext().getLocale());
                 }
 
                 for (Map.Entry<String, String> entry : parameters.entrySet()) {
@@ -188,8 +188,9 @@
                     }
                 }
 
-                 if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
-                    stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));
+                 if (clearableStack) {
+                     stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
+                 }
 
                 if (merge)
                     addParametersToContext(ac, parameters);
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 @@
         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/ognl/OgnlValueStackFactory.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStackFactory.java
index bf4e571..267a3c8 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStackFactory.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStackFactory.java
@@ -18,7 +18,6 @@
  */
 package com.opensymphony.xwork2.ognl;
 
-import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.TextProvider;
 import com.opensymphony.xwork2.conversion.NullHandler;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
@@ -34,17 +33,17 @@
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.apache.struts2.StrutsConstants;
 
 import java.util.Map;
 import java.util.Set;
-import org.apache.struts2.StrutsConstants;
 
 /**
  * Creates an Ognl value stack
  */
 public class OgnlValueStackFactory implements ValueStackFactory {
-    
-	private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
+
+    private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
 
     protected XWorkConverter xworkConverter;
     protected CompoundRootAccessor compoundRootAccessor;
@@ -55,7 +54,7 @@
     protected void setXWorkConverter(XWorkConverter converter) {
         this.xworkConverter = converter;
     }
-    
+
     @Inject("system")
     protected void setTextProvider(TextProvider textProvider) {
         this.textProvider = textProvider;
@@ -63,20 +62,24 @@
 
     public ValueStack createValueStack() {
         ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider,
-                containerAllowsStaticMethodAccess(), containerAllowsStaticFieldAccess());
+            containerAllowsStaticMethodAccess(), containerAllowsStaticFieldAccess());
         container.inject(stack);
-        stack.getContext().put(ActionContext.CONTAINER, container);
-        return stack;
+        return stack.getActionContext()
+            .withContainer(container)
+            .withValueStack(stack)
+            .getValueStack();
     }
 
     public ValueStack createValueStack(ValueStack stack) {
         ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor,
-                containerAllowsStaticMethodAccess(), containerAllowsStaticFieldAccess());
+            containerAllowsStaticMethodAccess(), containerAllowsStaticFieldAccess());
         container.inject(result);
-        stack.getContext().put(ActionContext.CONTAINER, container);
-        return result;
+        return result.getActionContext()
+            .withContainer(container)
+            .withValueStack(result)
+            .getValueStack();
     }
-    
+
     @Inject
     protected void setContainer(Container container) throws ClassNotFoundException {
         Set<String> names = container.getInstanceNames(PropertyAccessor.class);
@@ -116,7 +119,7 @@
 
     /**
      * Retrieve allowsStaticMethodAccess state from the container (allows for lazy fetching)
-     * 
+     *
      * @return
      */
     protected boolean containerAllowsStaticMethodAccess() {
@@ -125,7 +128,7 @@
 
     /**
      * Retrieve allowStaticFieldAccess state from the container (allows for lazy fetching)
-     * 
+     *
      * @return
      */
     protected boolean containerAllowsStaticFieldAccess() {
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 @@
  * @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 @@
         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/TextParseUtil.java b/core/src/main/java/com/opensymphony/xwork2/util/TextParseUtil.java
index f22a255..c054856 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/TextParseUtil.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/TextParseUtil.java
@@ -164,7 +164,7 @@
             }
         };
 
-        TextParser parser = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(TextParser.class);
+        TextParser parser = stack.getActionContext().getContainer().getInstance(TextParser.class);
 
         return parser.evaluate(openChars, expression, ognlEval, maxLoopCount);
     }
@@ -205,8 +205,8 @@
             }
         };
 
-        Map<String, Object> context = stack.getContext();
-        TextParser parser = ((Container)context.get(ActionContext.CONTAINER)).getInstance(TextParser.class);
+        ActionContext actionContext = stack.getActionContext();
+        TextParser parser = actionContext.getContainer().getInstance(TextParser.class);
 
         Object result = parser.evaluate(openChars, expression, ognlEval, maxLoopCount);
 
@@ -216,10 +216,10 @@
             Collection<Object> casted = (Collection<Object>)result;
             resultCol = new ArrayList<>();
 
-            XWorkConverter conv = ((Container)context.get(ActionContext.CONTAINER)).getInstance(XWorkConverter.class);
+            XWorkConverter conv = actionContext.getContainer().getInstance(XWorkConverter.class);
 
             for (Object element : casted) {
-                String stringElement = (String)conv.convertValue(context, element, String.class);
+                String stringElement = (String) conv.convertValue(actionContext.getContextMap(), element, String.class);
                 if (shallBeIncluded(stringElement, excludeEmptyElements)) {
                     if (evaluator != null) {
                         stringElement = evaluator.evaluate(stringElement).toString();
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 @@
  */
 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 @@
      *
      * @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 @@
      * @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 @@
      * @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 @@
      * @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 @@
      * @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 @@
      * @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 @@
      * @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 @@
      * @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 @@
      * @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..bde73ca 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/XWorkTestCaseHelper.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/XWorkTestCaseHelper.java
@@ -39,14 +39,8 @@
         
         // Reset the value stack
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
-        stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
-    
-        // clear out localization
-        //container.getInstance(LocalizedTextUtil.class).reset();
-        
-    
-        //ObjectFactory.setObjectFactory(container.getInstance(ObjectFactory.class));
+        stack.getActionContext().withContainer(container).withValueStack(stack).bind();
+
         return configurationManager;
     }
 
@@ -79,18 +73,16 @@
         
         // Reset the value stack
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
-        stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
-        
+        stack.getActionContext().withContainer(container).withValueStack(stack).bind();
+
         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..24a4422 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.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 static ActionContext getActionContext(HttpServletRequest req) {
         ValueStack vs = getValueStack(req);
         if (vs != null) {
-            return new ActionContext(vs.getContext());
+            return ActionContext.of(vs.getContext()).bind();
         } 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 @@
      * @return The action mapping
      */
     public static ActionMapping getActionMapping() {
-        return (ActionMapping) ActionContext.getContext().get(ACTION_MAPPING);
+        return ActionContext.getContext().getActionMapping();
     }
 
     /**
@@ -85,7 +93,7 @@
      * @return the HTTP page context.
      */
     public static PageContext getPageContext() {
-        return (PageContext) ActionContext.getContext().get(PAGE_CONTEXT);
+        return ActionContext.getContext().getPageContext();
     }
 
     /**
@@ -94,7 +102,7 @@
      * @param request the HTTP servlet request object.
      */
     public static void setRequest(HttpServletRequest request) {
-        ActionContext.getContext().put(HTTP_REQUEST, request);
+        ActionContext.getContext().withServletRequest(request);
     }
 
     /**
@@ -103,7 +111,7 @@
      * @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 @@
      * @param response the HTTP servlet response object.
      */
     public static void setResponse(HttpServletResponse response) {
-        ActionContext.getContext().put(HTTP_RESPONSE, response);
+        ActionContext.getContext().withServletResponse(response);
     }
 
     /**
@@ -121,7 +129,7 @@
      * @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 @@
      * @return the servlet context.
      */
     public static ServletContext getServletContext() {
-        return (ServletContext) ActionContext.getContext().get(SERVLET_CONTEXT);
+        return ActionContext.getContext().getServletContext();
     }
 
     /**
@@ -139,6 +147,6 @@
      * @param servletContext The servlet context to use
      */
     public static void setServletContext(ServletContext servletContext) {
-        ActionContext.getContext().put(SERVLET_CONTEXT, servletContext);
+        ActionContext.getContext().withServletContext(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 @@
     /**
      * 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..98251ba 100644
--- a/core/src/main/java/org/apache/struts2/components/ActionComponent.java
+++ b/core/src/main/java/org/apache/struts2/components/ActionComponent.java
@@ -175,16 +175,17 @@
         return end;
     }
 
-    protected Map createExtraContext() {
+    protected Map<String, Object> 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),
+        Map<String, Object> extraContext = du.createContextMap(
+            new RequestMap(req),
                 newParams,
                 session,
                 application,
@@ -192,12 +193,12 @@
                 res);
 
         ValueStack newStack = valueStackFactory.createValueStack(stack);
-        extraContext.put(ActionContext.VALUE_STACK, newStack);
 
-        // add page context, such that ServletDispatcherResult will do an include
-        extraContext.put(ServletActionContext.PAGE_CONTEXT, pageContext);
-
-        return extraContext;
+        return ActionContext.of(extraContext)
+            .withValueStack(newStack)
+            // add page context, such that ServletDispatcherResult will do an include
+            .withPageContext(pageContext)
+            .getContextMap();
     }
 
     /**
@@ -210,7 +211,7 @@
         HttpParameters parentParams = null;
 
         if (!ignoreContextParams) {
-            parentParams = new ActionContext(getStack().getContext()).getParameters();
+            parentParams = getStack().getActionContext().getParameters();
         }
 
         HttpParameters.Builder builder = HttpParameters.create();
@@ -293,7 +294,7 @@
             // set the old stack back on the request
             req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
             if (inv != null) {
-                ActionContext.getContext().setActionInvocation(inv);
+                ActionContext.getContext().withActionInvocation(inv);
             }
         }
 
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..b8b7a3c 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 @@
         }
 
         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) {
@@ -137,8 +137,7 @@
         } else {
             // no action supplied? ok, then default to the current request
             // (action or general URL)
-            ActionInvocation ai = (ActionInvocation) formComponent.getStack().getContext().get(
-                    ActionContext.ACTION_INVOCATION);
+            ActionInvocation ai = formComponent.getStack().getActionContext().getActionInvocation();
             if (ai != null) {
                 action = ai.getProxy().getActionName();
                 namespace = ai.getProxy().getNamespace();
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 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..b2951ad 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.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.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.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 @@
      * 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 @@
 
     /**
      * 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 @@
 
     /**
      * Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
+     *
      * @param val New setting
      */
     @Inject(StrutsConstants.STRUTS_MULTIPART_SAVEDIR)
@@ -314,17 +343,15 @@
      * 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 @@
 
         // 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 @@
             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 @@
     private void init_DefaultProperties() {
         configurationManager.addContainerProvider(new DefaultPropertiesProvider());
     }
-    
+
     private void init_LegacyStrutsProperties() {
         configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
     }
@@ -443,17 +471,17 @@
             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 @@
             paramsWorkaroundEnabled = true;
         } else {
             paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,
-                    StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
+                StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
         }
     }
 
@@ -504,10 +532,9 @@
      * 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 @@
             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 @@
             }
             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 @@
      * @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);
 
@@ -577,7 +606,9 @@
             }
         }
         if (stack != null) {
-            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
+            extraContext = ActionContext.of(extraContext)
+                .withValueStack(valueStackFactory.createValueStack(stack))
+                .getContextMap();
         }
 
         try {
@@ -591,7 +622,7 @@
             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 +660,7 @@
      * 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 +678,14 @@
     /**
      * 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 +699,7 @@
         // 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,31 +718,28 @@
      * @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) {
-        HashMap<String, Object> extraContext = new HashMap<>();
-        extraContext.put(ActionContext.PARAMETERS, parameters);
-        extraContext.put(ActionContext.SESSION, sessionMap);
-        extraContext.put(ActionContext.APPLICATION, applicationMap);
-
-        extraContext.put(ActionContext.LOCALE, getLocale(request));
-
-        extraContext.put(StrutsStatics.HTTP_REQUEST, request);
-        extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
-        extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
-
-        // helpers to get access to request/session/application scope
-        extraContext.put("request", requestMap);
-        extraContext.put("session", sessionMap);
-        extraContext.put("application", applicationMap);
-        extraContext.put("parameters", parameters);
+    public Map<String, Object> createContextMap(Map<String, Object> requestMap,
+                                                HttpParameters parameters,
+                                                Map<String, Object> sessionMap,
+                                                Map<String, Object> applicationMap,
+                                                HttpServletRequest request,
+                                                HttpServletResponse response) {
+        Map<String, Object> extraContext = ActionContext.of(new HashMap<>())
+            .withParameters(parameters)
+            .withSession(sessionMap)
+            .withApplication(applicationMap)
+            .withLocale(getLocale(request))
+            .withServletRequest(request)
+            .withServletResponse(response)
+            .withServletContext(servletContext)
+            // helpers to get access to request/session/application scope
+            .with("request", requestMap)
+            .with("session", sessionMap)
+            .with("application", applicationMap)
+            .with("parameters", parameters)
+            .getContextMap();
 
         AttributeMap attrMap = new AttributeMap(extraContext);
         extraContext.put("attr", attrMap);
@@ -727,7 +754,7 @@
                 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 @@
 
         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 @@
     /**
      * 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 @@
      *
      * @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 @@
             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 @@
 
     /**
      * 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 @@
      *
      * @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 @@
         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 @@
                 mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
             }
         }
-        if (mpr == null ) {
+        if (mpr == null) {
             mpr = getContainer().getInstance(MultiPartRequest.class);
         }
         return mpr;
@@ -935,7 +959,6 @@
      * @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 @@
 
     /**
      * Expose the dependency injection container.
+     *
      * @return Our dependency injection container
      */
     public Container getContainer() {
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/HttpParameters.java b/core/src/main/java/org/apache/struts2/dispatcher/HttpParameters.java
index d17b71c..7746f2d 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/HttpParameters.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/HttpParameters.java
@@ -26,6 +26,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
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 @@
      */
     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 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..5ccc9d6 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 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 @@
         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.of(new HashMap<>(oldContext.getContextMap())).bind();
         } 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.of(stack.getContext()).bind();
             }
         }
         request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
-        ActionContext.setContext(ctx);
         return ctx;
     }
 
@@ -110,7 +109,7 @@
         try {
             dispatcher.cleanUpRequest(request);
         } finally {
-            ActionContext.setContext(null);
+            ActionContext.clear();
             Dispatcher.setInstance(null);
             devModeOverride.remove();
         }
@@ -208,7 +207,7 @@
             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 @@
             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 @@
      * @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 @@
             // 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..8b70952 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 String execute() throws Exception {
         ActionContext previous = ActionContext.getContext();
-        ActionContext.setContext(invocation.getInvocationContext());
+        ActionContext.bind(invocation.getInvocationContext());
         try {
 // This is for the new API:
 //            return RequestContextImpl.callInContext(invocation, new Callable<String>() {
@@ -48,7 +48,7 @@
             return invocation.invoke();
         } finally {
             if (cleanupContext)
-                ActionContext.setContext(previous);
+                ActionContext.bind(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 @@
      */
     @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..eed1811 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 @@
      * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
      */
     protected void beforeInvocation() throws Exception {
-        ActionContext.setContext(invocation.getInvocationContext());
+        ActionContext.bind(invocation.getInvocationContext());
     }
 
     /**
@@ -87,7 +87,7 @@
      * @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 @@
             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..b927c13 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.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.SessionMap;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 /**
@@ -86,11 +88,12 @@
      * @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().withSession(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.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 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 @@
             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/ScopeInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ScopeInterceptor.java
index b83c7e9..e9d09dc 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ScopeInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ScopeInterceptor.java
@@ -232,7 +232,7 @@
         return o;
     }
 
-    private static Map locks = new IdentityHashMap();
+    private static Map<Object, Object> locks = new IdentityHashMap<>();
 
     static void lock(Object o, ActionInvocation invocation) throws Exception {
         synchronized (o) {
@@ -262,34 +262,34 @@
     }
 
     protected void after(ActionInvocation invocation, String result) throws Exception {
-        Map ses = ActionContext.getContext().getSession();
-        if ( ses != null) {
-            unlock(ses);
+        Map<String, Object> session = ActionContext.getContext().getSession();
+        if ( session != null) {
+            unlock(session);
         }
     }
 
 
     protected void before(ActionInvocation invocation) throws Exception {
         invocation.addPreResultListener(this);
-        Map ses = ActionContext.getContext().getSession();
-        if (ses == null && autoCreateSession) {
-            ses = new SessionMap(ServletActionContext.getRequest());
-            ActionContext.getContext().setSession(ses);
+        Map<String, Object> session = ActionContext.getContext().getSession();
+        if (session == null && autoCreateSession) {
+            session = new SessionMap<>(ServletActionContext.getRequest());
+            ActionContext.getContext().withSession(session);
         }
 
-        if ( ses != null) {
-            lock(ses, invocation);
+        if ( session != null) {
+            lock(session, invocation);
         }
 
         String key = getKey(invocation);
-        Map app = ActionContext.getContext().getApplication();
+        Map<String, Object> application = ActionContext.getContext().getApplication();
         final ValueStack stack = ActionContext.getContext().getValueStack();
 
         LOG.debug("scope interceptor before");
 
-        if (application != null)
-            for (String string : application) {
-                Object attribute = app.get(key + string);
+        if (this.application != null)
+            for (String string : this.application) {
+                Object attribute = application.get(key + string);
                 if (attribute != null) {
                     LOG.debug("Application scoped variable set {} = {}", string, String.valueOf(attribute));
                     stack.setValue(string, nullConvert(attribute));
@@ -304,14 +304,14 @@
             return;
         }
 
-        if (ses == null) {
+        if (session == null) {
             LOG.debug("No HttpSession created... Cannot set session scoped variables");
             return;
         }
 
-        if (session != null && (!"start".equals(type))) {
-            for (String string : session) {
-                Object attribute = ses.get(key + string);
+        if (this.session != null && (!"start".equals(type))) {
+            for (String string : this.session) {
+                Object attribute = session.get(key + string);
                 if (attribute != null) {
                     LOG.debug("Session scoped variable set {} = {}", string, String.valueOf(attribute));
                     stack.setValue(string, nullConvert(attribute));
@@ -329,38 +329,38 @@
      */
     public void beforeResult(ActionInvocation invocation, String resultCode) {
         String key = getKey(invocation);
-        Map app = ActionContext.getContext().getApplication();
+        Map<String, Object> application = ActionContext.getContext().getApplication();
         final ValueStack stack = ActionContext.getContext().getValueStack();
 
-        if (application != null)
-            for (String string : application) {
+        if (this.application != null)
+            for (String string : this.application) {
                 Object value = stack.findValue(string);
                 LOG.debug("Application scoped variable saved {} = {}", string, String.valueOf(value));
 
                 //if( value != null)
-                app.put(key + string, nullConvert(value));
+                application.put(key + string, nullConvert(value));
             }
 
         boolean ends = "end".equals(type);
 
-        Map ses = ActionContext.getContext().getSession();
-        if (ses != null) {
+        Map<String, Object> session = ActionContext.getContext().getSession();
+        if (session != null) {
 
-            if (session != null) {
-                for (String string : session) {
+            if (this.session != null) {
+                for (String string : this.session) {
                     if (ends) {
-                        ses.remove(key + string);
+                        session.remove(key + string);
                     } else {
                         Object value = stack.findValue(string);
                         LOG.debug("Session scoped variable saved {} = {}", string, String.valueOf(value));
 
                         // Null value should be scoped too
                         //if( value != null)
-                        ses.put(key + string, nullConvert(value));
+                        session.put(key + string, nullConvert(value));
                     }
                 }
             }
-            unlock(ses);
+            unlock(session);
         } else {
             LOG.debug("No HttpSession created... Cannot save session scoped variables.");
         }
@@ -406,15 +406,15 @@
      * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
      */
     public String intercept(ActionInvocation invocation) throws Exception {
-        String result = null;
-        Map ses = ActionContext.getContext().getSession();
+        String result;
+        Map<String, Object> session = ActionContext.getContext().getSession();
         before(invocation);
         try {
             result = invocation.invoke();
             after(invocation, result);
         } finally {
-            if (ses != null) {
-                unlock(ses);
+            if (session != null) {
+                unlock(session);
             }
         }
 
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 @@
         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 @@
         }
 
         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 @@
         }
 
         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 @@
         }
 
         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 @@
     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 @@
 
     /**
      * 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..62f8716 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 @@
                 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 @@
                                 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 @@
             } 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 {
@@ -304,14 +304,12 @@
         writer.startNode(DEBUG_PARAM);
         serializeIt(ctx.getParameters(), "parameters", writer, new ArrayList<>());
         writer.startNode("context");
-        String key;
-        Map ctxMap = ctx.getContextMap();
-        for (Object o : ctxMap.keySet()) {
-            key = o.toString();
+        Map<String, Object> ctxMap = ctx.getContextMap();
+        for (String key : ctxMap.keySet()) {
             boolean print = !ignoreKeys.contains(key);
 
-            for (String ignorePrefixe : ignorePrefixes) {
-                if (key.startsWith(ignorePrefixe)) {
+            for (String ignorePrefix : ignorePrefixes) {
+                if (key.startsWith(ignorePrefix)) {
                     print = false;
                     break;
                 }
@@ -321,11 +319,11 @@
             }
         }
         writer.endNode();
-        Map requestMap = (Map) ctx.get("request");
+        Map<String, Object> requestMap = (Map<String, Object>) ctx.get("request");
         serializeIt(requestMap, "request", writer, filterValueStack(requestMap));
         serializeIt(ctx.getSession(), "session", writer, new ArrayList<>());
 
-        ValueStack stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
+        ValueStack stack = ctx.getValueStack();
         serializeIt(stack.getRoot(), "valueStack", writer, new ArrayList<>());
         writer.endNode();
     }
diff --git a/core/src/main/java/org/apache/struts2/result/PlainTextResult.java b/core/src/main/java/org/apache/struts2/result/PlainTextResult.java
index f2dda71..7baaa05 100644
--- a/core/src/main/java/org/apache/struts2/result/PlainTextResult.java
+++ b/core/src/main/java/org/apache/struts2/result/PlainTextResult.java
@@ -31,16 +31,9 @@
 import java.nio.charset.Charset;
 
 /**
- * <!-- START SNIPPET: description -->
- *
  * A result that send the content out as plain text. Useful typically when needed
  * to display the raw content of a JSP or Html file for example.
  *
- * <!-- END SNIPPET: description -->
- *
- *
- * <!-- START SNIPPET: params -->
- *
  * <ul>
  *  <li>location (default) = location of the file (jsp/html) to be displayed as plain text.</li>
  *  <li>charSet (optional) = character set to be used. This character set will be used to set the
@@ -48,27 +41,18 @@
  *  using a Reader. Some example of charSet would be UTF-8, ISO-8859-1 etc.
  * </ul>
  *
- * <!-- END SNIPPET: params -->
- *
- *
- * <pre>
- * <!-- START SNIPPET: example -->
- *
+ *  <pre>
  * &lt;action name="displayJspRawContent" &gt;
  *   &lt;result type="plainText"&gt;/myJspFile.jsp&lt;/result&gt;
  * &lt;/action&gt;
  *
- *
  * &lt;action name="displayJspRawContent" &gt;
  *   &lt;result type="plainText"&gt;
  *      &lt;param name="location"&gt;/myJspFile.jsp&lt;/param&gt;
  *      &lt;param name="charSet"&gt;UTF-8&lt;/param&gt;
  *   &lt;/result&gt;
  * &lt;/action&gt;
- *
- * <!-- END SNIPPET: example -->
  * </pre>
- *
  */
 public class PlainTextResult extends StrutsResultSupport {
 
@@ -113,22 +97,22 @@
         // verify charset
         Charset charset = readCharset();
 
-        HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(HTTP_RESPONSE);
+        HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
 
         applyCharset(charset, response);
         applyAdditionalHeaders(response);
         String location = adjustLocation(finalLocation);
 
         try (PrintWriter writer = response.getWriter();
-                InputStream resourceAsStream = readStream(invocation, location);
-                InputStreamReader reader = new InputStreamReader(resourceAsStream, charset == null ? Charset.defaultCharset() : charset)) {
+             InputStream resourceAsStream = readStream(invocation, location);
+             InputStreamReader reader = new InputStreamReader(resourceAsStream, charset == null ? Charset.defaultCharset() : charset)) {
             logWrongStream(finalLocation, resourceAsStream);
             sendStream(writer, reader);
         }
     }
 
     protected InputStream readStream(ActionInvocation invocation, String location) {
-        ServletContext servletContext = (ServletContext) invocation.getInvocationContext().get(SERVLET_CONTEXT);
+        ServletContext servletContext = invocation.getInvocationContext().getServletContext();
         return servletContext.getResourceAsStream(location);
     }
 
@@ -141,7 +125,7 @@
     protected void sendStream(PrintWriter writer, InputStreamReader reader) throws IOException {
         char[] buffer = new char[BUFFER_SIZE];
         int charRead;
-        while((charRead = reader.read(buffer)) != -1) {
+        while ((charRead = reader.read(buffer)) != -1) {
             writer.write(buffer, 0, charRead);
         }
     }
diff --git a/core/src/main/java/org/apache/struts2/result/PostbackResult.java b/core/src/main/java/org/apache/struts2/result/PostbackResult.java
index 1a275d5..261404d 100644
--- a/core/src/main/java/org/apache/struts2/result/PostbackResult.java
+++ b/core/src/main/java/org/apache/struts2/result/PostbackResult.java
@@ -21,7 +21,6 @@
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.inject.Inject;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.mapper.ActionMapper;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
 
@@ -75,7 +74,7 @@
 public class PostbackResult extends StrutsResultSupport {
 
     private static final long serialVersionUID = -2283504349296877429L;
-    
+
     private String actionName;
     private String namespace;
     private String method;
@@ -87,8 +86,8 @@
     @Override
     protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
         ActionContext ctx = invocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
-        HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);
+        HttpServletRequest request = ctx.getServletRequest();
+        HttpServletResponse response = ctx.getServletResponse();
 
         // Cache?
         if (!cache) {
@@ -99,7 +98,7 @@
 
         //set contenttype @see ww-4564
         response.setContentType("text/html");
-        
+
         // Render
         PrintWriter pw = new PrintWriter(response.getOutputStream());
         pw.write("<!DOCTYPE html><html><body><form action=\"" + finalLocation + "\" method=\"POST\">");
@@ -119,7 +118,7 @@
     /**
      * Determines if the specified form input element should be included.
      *
-     * @param name the input element name
+     * @param name   the input element name
      * @param values the input element values
      * @return {@code true} if included; otherwise {@code false}
      */
@@ -129,7 +128,7 @@
 
     protected String makePostbackUri(ActionInvocation invocation) {
         ActionContext ctx = invocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
+        HttpServletRequest request = ctx.getServletRequest();
         String postbackUri;
 
         if (actionName != null) {
@@ -178,6 +177,7 @@
     /**
      * Stores the option to cache the rendered intermediate page. The default
      * is {@code true}.
+     *
      * @param cache enable/disable cache
      */
     public final void setCache(boolean cache) {
diff --git a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java
index defb5b5..9d7b461 100644
--- a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java
+++ b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java
@@ -26,7 +26,6 @@
 import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.Dispatcher;
 import org.apache.struts2.dispatcher.mapper.ActionMapper;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
@@ -38,13 +37,15 @@
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 import static javax.servlet.http.HttpServletResponse.SC_FOUND;
 
 /**
- * <!-- START SNIPPET: description -->
- * <p>
  * Calls the {@link HttpServletResponse#sendRedirect(String) sendRedirect}
  * method to the location specified. The response is told to redirect the
  * browser to the specified location (a new request from the client). The
@@ -53,33 +54,19 @@
  * available. This is because actions are built on a single-thread model. The
  * only way to pass data is through the session or with web parameters
  * (url?name=value) which can be OGNL expressions.
- * </p>
- * <!-- END SNIPPET: description -->
- * <p>
+ *
  * <b>This result type takes the following parameters:</b>
- * </p>
- * <!-- START SNIPPET: params -->
- * 
+ *
  * <ul>
- * 
  * <li><b>location (default)</b> - the location to go to after execution.</li>
- * 
  * <li><b>parse</b> - true by default. If set to false, the location param will
  * not be parsed for Ognl expressions.</li>
- * 
- * <li><b>anchor</b> - Optional.  Also known as "fragment" or colloquially as 
+ * <li><b>anchor</b> - Optional.  Also known as "fragment" or colloquially as
  * "hash".  You can specify an anchor for a result.</li>
  * </ul>
- * 
- * <p>
  * This result follows the same rules from {@link StrutsResultSupport}.
- * </p>
- * 
- * <!-- END SNIPPET: params -->
- * <p>
- * <b>Example:</b>
- * </p>
  *
+ * <b>Example:</b>
  * <pre>
  * <!-- START SNIPPET: example -->
  * &lt;!--
@@ -93,7 +80,6 @@
  * &lt;/result&gt;
  * <!-- END SNIPPET: example -->
  * </pre>
- * 
  */
 public class ServletRedirectResult extends StrutsResultSupport implements ReflectionExceptionHandler, Redirectable {
 
@@ -139,7 +125,7 @@
 
     /**
      * Set the optional anchor value.
-     * 
+     *
      * @param anchor the anchor value
      */
     public void setAnchor(String anchor) {
@@ -149,7 +135,7 @@
     /**
      * Sets whether or not to prepend the servlet context path to the redirected
      * URL.
-     * 
+     *
      * @param prependServletContext <tt>true</tt> to prepend the location with the servlet context path, <tt>false</tt> otherwise.
      */
     public void setPrependServletContext(boolean prependServletContext) {
@@ -166,15 +152,15 @@
     /**
      * Redirects to the location specified by calling
      * {@link HttpServletResponse#sendRedirect(String)}.
-     * 
+     *
      * @param finalLocation the location to redirect to.
-     * @param invocation an encapsulation of the action execution state.
+     * @param invocation    an encapsulation of the action execution state.
      * @throws Exception if an error occurs when redirecting.
      */
     protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
         ActionContext ctx = invocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
-        HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);
+        HttpServletRequest request = ctx.getServletRequest();
+        HttpServletResponse response = ctx.getServletResponse();
 
         if (isPathUrl(finalLocation)) {
             if (!finalLocation.startsWith("/")) {
@@ -228,24 +214,24 @@
 
     protected List<String> getProhibitedResultParams() {
         return Arrays.asList(
-                DEFAULT_PARAM,
-                "namespace",
-                "method",
-                "encode",
-                "parse",
-                "location",
-                "prependServletContext",
-                "suppressEmptyParameters",
-                "anchor",
-                "statusCode"
+            DEFAULT_PARAM,
+            "namespace",
+            "method",
+            "encode",
+            "parse",
+            "location",
+            "prependServletContext",
+            "suppressEmptyParameters",
+            "anchor",
+            "statusCode"
         );
     }
 
     /**
      * Sends the redirection. Can be overridden to customize how the redirect is
      * handled (i.e. to use a different status code)
-     * 
-     * @param response The response
+     *
+     * @param response      The response
      * @param finalLocation The location URI
      * @throws IOException in case of IO errors
      */
@@ -304,7 +290,7 @@
 
     /**
      * Sets the suppressEmptyParameters option
-     * 
+     *
      * @param suppressEmptyParameters The new value for this option
      */
     public void setSuppressEmptyParameters(boolean suppressEmptyParameters) {
@@ -313,10 +299,9 @@
 
     /**
      * Adds a request parameter to be added to the redirect url
-     * 
-     * @param key The parameter name
-     * @param value The parameter value
      *
+     * @param key   The parameter name
+     * @param value The parameter value
      * @return the servlet redirect result
      */
     public ServletRedirectResult addParameter(String key, Object value) {
diff --git a/core/src/main/java/org/apache/struts2/result/StreamResult.java b/core/src/main/java/org/apache/struts2/result/StreamResult.java
index 554b1a9..711929f 100644
--- a/core/src/main/java/org/apache/struts2/result/StreamResult.java
+++ b/core/src/main/java/org/apache/struts2/result/StreamResult.java
@@ -27,61 +27,46 @@
 import java.io.OutputStream;
 
 /**
- * <!-- START SNIPPET: description -->
- * <p>
  * A custom Result type for sending raw data (via an InputStream) directly to the
  * HttpServletResponse. Very useful for allowing users to download content.
- * </p>
- * <!-- END SNIPPET: description -->
- * <p>
+ *
  * <b>This result type takes the following parameters:</b>
- * </p>
- * <!-- START SNIPPET: params -->
  *
  * <ul>
- *
  * <li><b>contentType</b> - the stream mime-type as sent to the web browser
  * (default = <code>text/plain</code>).</li>
- *
  * <li><b>contentLength</b> - the stream length in bytes (the browser displays a
  * progress bar).</li>
- *
  * <li><b>contentDisposition</b> - the content disposition header value for
  * specifing the file name (default = <code>inline</code>, values are typically
  * <i>attachment;filename="document.pdf"</i>.</li>
- *
  * <li><b>inputName</b> - the name of the InputStream property from the chained
  * action (default = <code>inputStream</code>).</li>
- *
  * <li><b>bufferSize</b> - the size of the buffer to copy from input to output
  * (default = <code>1024</code>).</li>
- *
  * <li><b>allowCaching</b> if set to 'false' it will set the headers 'Pragma' and 'Cache-Control'
  * to 'no-cahce', and prevent client from caching the content. (default = <code>true</code>)
- *
  * <li><b>contentCharSet</b> if set to a string, ';charset=value' will be added to the
  * content-type header, where value is the string set. If set to an expression, the result
  * of evaluating the expression will be used. If not set, then no charset will be set on
  * the header</li>
  * </ul>
- * 
- * <p>These parameters can also be set by exposing a similarly named getter method on your Action.  For example, you can
- * provide <code>getContentType()</code> to override that parameter for the current action.</p>
  *
- * <!-- END SNIPPET: params -->
  * <p>
- * <b>Example:</b>
+ * These parameters can also be set by exposing a similarly named getter method on your Action.  For example, you can
+ * provide <code>getContentType()</code> to override that parameter for the current action.
  * </p>
  *
- * <pre><!-- START SNIPPET: example -->
+ * <b>Example:</b>
+ *
+ * <pre>
  * &lt;result name="success" type="stream"&gt;
  *   &lt;param name="contentType"&gt;image/jpeg&lt;/param&gt;
  *   &lt;param name="inputName"&gt;imageStream&lt;/param&gt;
  *   &lt;param name="contentDisposition"&gt;attachment;filename="document.pdf"&lt;/param&gt;
  *   &lt;param name="bufferSize"&gt;1024&lt;/param&gt;
  * &lt;/result&gt;
- * <!-- END SNIPPET: example --></pre>
- *
+ * </pre>
  */
 public class StreamResult extends StrutsResultSupport {
 
@@ -94,7 +79,7 @@
     protected String contentType = "text/plain";
     protected String contentLength;
     protected String contentDisposition = "inline";
-    protected String contentCharSet ;
+    protected String contentCharSet;
     protected String inputName = "inputStream";
     protected InputStream inputStream;
     protected int bufferSize = 1024;
@@ -108,7 +93,7 @@
         this.inputStream = in;
     }
 
-     /**
+    /**
      * @return Returns the whether or not the client should be requested to allow caching of the data stream.
      */
     public boolean getAllowCaching() {
@@ -232,11 +217,11 @@
             }
 
 
-            HttpServletResponse oResponse = (HttpServletResponse) invocation.getInvocationContext().get(HTTP_RESPONSE);
+            HttpServletResponse oResponse = invocation.getInvocationContext().getServletResponse();
 
             LOG.debug("Set the content type: {};charset{}", contentType, contentCharSet);
-            if (contentCharSet != null && ! contentCharSet.equals("")) {
-                oResponse.setContentType(conditionalParse(contentType, invocation)+";charset="+conditionalParse(contentCharSet, invocation));
+            if (contentCharSet != null && !contentCharSet.equals("")) {
+                oResponse.setContentType(conditionalParse(contentType, invocation) + ";charset=" + conditionalParse(contentCharSet, invocation));
             } else {
                 oResponse.setContentType(conditionalParse(contentType, invocation));
             }
@@ -250,7 +235,7 @@
                     if (_contentLengthAsInt >= 0) {
                         oResponse.setContentLength(_contentLengthAsInt);
                     }
-                } catch(NumberFormatException e) {
+                } catch (NumberFormatException e) {
                     LOG.warn("failed to recognize {} as a number, contentLength header will not be set", _contentLength, e);
                 }
             }
@@ -269,16 +254,16 @@
             oOutput = oResponse.getOutputStream();
 
             LOG.debug("Streaming result [{}] type=[{}] length=[{}] content-disposition=[{}] charset=[{}]",
-                    inputName, contentType, contentLength, contentDisposition, contentCharSet);
+                inputName, contentType, contentLength, contentDisposition, contentCharSet);
 
-        	LOG.debug("Streaming to output buffer +++ START +++");
+            LOG.debug("Streaming to output buffer +++ START +++");
             byte[] oBuff = new byte[bufferSize];
             int iSize;
             while (-1 != (iSize = inputStream.read(oBuff))) {
                 LOG.debug("Sending stream ... {}", iSize);
                 oOutput.write(oBuff, 0, iSize);
             }
-        	LOG.debug("Streaming to output buffer +++ END +++");
+            LOG.debug("Streaming to output buffer +++ END +++");
 
             // Flush
             oOutput.flush();
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..c9fe66e 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 @@
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
-import org.apache.struts2.ServletActionContext;
 
 import java.io.Serializable;
 import java.util.HashMap;
@@ -61,15 +60,13 @@
             // WW-5026 - Preserve the previous PageContext (even if null) and restore it to the
             // ActionContext after loading the savedInvocation context.  The saved context's PageContext
             // 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);
-            savedActionContext.setValueStack(savedInvocation.getStack());
-            if (previousActionContext != null) {
-                savedActionContext.put(ServletActionContext.PAGE_CONTEXT, previousActionContext.get(ServletActionContext.PAGE_CONTEXT));
-            } else {
-                savedActionContext.put(ServletActionContext.PAGE_CONTEXT, null);
-            }
+
+            savedInvocation
+                .getInvocationContext()
+                .withPageContext(previousActionContext.getPageContext())
+                .withValueStack(savedInvocation.getStack())
+                .bind();
         }
 
         return savedInvocation;
@@ -85,13 +82,13 @@
      */
     public static void storeInvocation(String key, String token, ActionInvocation invocation) {
         InvocationContext invocationContext = new InvocationContext(invocation, token);
-        Map invocationMap = getInvocationMap();
+        Map<String, Object> invocationMap = getInvocationMap();
         invocationMap.put(key, invocationContext);
         setInvocationMap(invocationMap);
     }
 
-    static void setInvocationMap(Map invocationMap) {
-        Map session = ActionContext.getContext().getSession();
+    static void setInvocationMap(Map<String, Object> invocationMap) {
+        Map<String, Object> session = ActionContext.getContext().getSession();
 
         if (session == null) {
             throw new IllegalStateException("Unable to access the session.");
@@ -100,17 +97,17 @@
         session.put(INVOCATION_MAP_KEY, invocationMap);
     }
 
-    static Map getInvocationMap() {
-        Map session = ActionContext.getContext().getSession();
+    static Map<String, Object> getInvocationMap() {
+        Map<String, Object> session = ActionContext.getContext().getSession();
 
         if (session == null) {
             throw new IllegalStateException("Unable to access the session.");
         }
 
-        Map invocationMap = (Map) session.get(INVOCATION_MAP_KEY);
+        Map<String, Object> invocationMap = (Map<String, Object>) session.get(INVOCATION_MAP_KEY);
 
         if (invocationMap == null) {
-            invocationMap = new HashMap();
+            invocationMap = new HashMap<>();
             setInvocationMap(invocationMap);
         }
 
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..42e107f 100644
--- a/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
+++ b/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
@@ -47,15 +47,14 @@
         // Reset the value stack
         Container container = du.getContainer();
         ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
-        stack.getContext().put(ActionContext.CONTAINER, container);
-        ActionContext.setContext(new ActionContext(stack.getContext()));
-        
+        stack.getActionContext().withContainer(container).withValueStack(stack).bind();
+
         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/StrutsUtil.java b/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
index 647a743..2d95314 100644
--- a/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
+++ b/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
@@ -61,9 +61,9 @@
         this.stack = stack;
         this.request = request;
         this.response = response;
-        this.ognl = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(OgnlTool.class);
-        this.urlHelper = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(UrlHelper.class);
-        this.objectFactory = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(ObjectFactory.class);
+        this.ognl = stack.getActionContext().getContainer().getInstance(OgnlTool.class);
+        this.urlHelper = stack.getActionContext().getContainer().getInstance(UrlHelper.class);
+        this.objectFactory = stack.getActionContext().getContainer().getInstance(ObjectFactory.class);
     }
 
     public Object bean(Object aName) throws Exception {
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 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..7d59456 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
@@ -23,8 +23,6 @@
 import com.opensymphony.xwork2.LocaleProvider;
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.util.ValueStack;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.LogManager;
 import freemarker.template.Configuration;
 import freemarker.template.ObjectWrapper;
 import freemarker.template.Template;
@@ -33,6 +31,8 @@
 import freemarker.template.TemplateModel;
 import freemarker.template.TemplateModelException;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.result.StrutsResultSupport;
@@ -68,7 +68,7 @@
      */
     protected String location;
     private String pContentType = "text/html";
-    private static final String PARENT_TEMPLATE_WRITER = FreemarkerResult.class.getName() +  ".parentWriter";
+    private static final String PARENT_TEMPLATE_WRITER = FreemarkerResult.class.getName() + ".parentWriter";
 
     public FreemarkerResult() {
         super();
@@ -77,7 +77,7 @@
     public FreemarkerResult(String location) {
         super(location);
     }
-    
+
     @Inject
     public void setFreemarkerManager(FreemarkerManager mgr) {
         this.freemarkerManager = mgr;
@@ -109,9 +109,8 @@
      * </p>
      *
      * @param locationArg location argument
-     * @param invocation the action invocation
-     *
-     * @throws IOException in case of IO errors
+     * @param invocation  the action invocation
+     * @throws IOException       in case of IO errors
      * @throws TemplateException in case of freemarker template errors
      */
     public void doExecute(String locationArg, ActionInvocation invocation) throws IOException, TemplateException {
@@ -121,12 +120,12 @@
         this.wrapper = getObjectWrapper();
 
         ActionContext ctx = invocation.getInvocationContext();
-        HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
+        HttpServletRequest req = ctx.getServletRequest();
 
         String absoluteLocation;
         if (location.startsWith("/")) {
-            absoluteLocation = location; 
-        } else { 
+            absoluteLocation = location;
+        } else {
             String namespace = invocation.getProxy().getNamespace();
             if (namespace == null || namespace.length() == 0 || namespace.equals("/")) {
                 absoluteLocation = "/" + location;
@@ -152,7 +151,7 @@
 
                 // Process the template
                 Writer writer = getWriter();
-                if (willWriteIfCompleted){
+                if (willWriteIfCompleted) {
                     CharArrayWriter parentCharArrayWriter = (CharArrayWriter) req.getAttribute(PARENT_TEMPLATE_WRITER);
                     boolean isTopTemplate;
                     if (isTopTemplate = (parentCharArrayWriter == null)) {
@@ -242,7 +241,7 @@
      * @throws IOException in case of IO errors
      */
     protected Writer getWriter() throws IOException {
-        if(writer != null) {
+        if (writer != null) {
             return writer;
         }
         return ServletActionContext.getResponse().getWriter();
@@ -275,10 +274,10 @@
         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
+        if (invocation != null) action = invocation.getAction(); //Added for NullPointException
         return freemarkerManager.buildTemplateModel(stack, action, servletContext, request, response, wrapper);
     }
 
@@ -301,8 +300,7 @@
      * the default implementation of postTemplateProcess applies the contentType parameter
      *
      * @param template the freemarker template
-     * @param model the template model
-     *
+     * @param model    the template model
      * @throws IOException in case of IO errors
      */
     protected void postTemplateProcess(Template template, TemplateModel model) throws IOException {
@@ -316,7 +314,7 @@
      * objects into the model root
      *
      * @param template the freemarker template
-     * @param model the template model
+     * @param model    the template model
      * @return true to process the template, false to suppress template processing.
      * @throws IOException in case of IO errors
      */
@@ -342,8 +340,8 @@
 
                 response.setContentType(contentType);
             }
-        } else if(isInsideActionTag()){
-             //trigger com.opensymphony.module.sitemesh.filter.PageResponseWrapper.deactivateSiteMesh()
+        } else if (isInsideActionTag()) {
+            //trigger com.opensymphony.module.sitemesh.filter.PageResponseWrapper.deactivateSiteMesh()
             response.setContentType(response.getContentType());
         }
 
diff --git a/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java b/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java
index 5d307a2..1124574 100644
--- a/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java
+++ b/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java
@@ -55,7 +55,7 @@
     public Writer getWriter(Writer writer, Map params)
         throws TemplateModelException, IOException {
         Component bean = getBean();
-        Container container = (Container) stack.getContext().get(ActionContext.CONTAINER);
+        Container container = stack.getActionContext().getContainer();
         container.inject(bean);
 
         Map unwrappedParameters = unwrapParameters(params);
diff --git a/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java b/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java
index f39d581..9dd463c 100644
--- a/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java
+++ b/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java
@@ -44,7 +44,7 @@
     public int doStartTag() throws JspException {
         ValueStack stack = getStack();
         component = getBean(stack, (HttpServletRequest) pageContext.getRequest(), (HttpServletResponse) pageContext.getResponse());
-        Container container = (Container) stack.getContext().get(ActionContext.CONTAINER);
+        Container container = stack.getActionContext().getContainer();
         container.inject(component);
         
         populateParams();
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..e4c1645 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 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 @@
 
             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 @@
             req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
 
             // also tie this stack/context to the ThreadLocal
-            ActionContext.setContext(new ActionContext(stack.getContext()));
+            ActionContext.of(stack.getContext()).bind();
         } 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 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/java/org/apache/struts2/views/util/ContextUtil.java b/core/src/main/java/org/apache/struts2/views/util/ContextUtil.java
index 8d493f6..7cc1aab 100644
--- a/core/src/main/java/org/apache/struts2/views/util/ContextUtil.java
+++ b/core/src/main/java/org/apache/struts2/views/util/ContextUtil.java
@@ -18,26 +18,20 @@
  */
 package org.apache.struts2.views.util;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
 import org.apache.struts2.StrutsConstants;
 import org.apache.struts2.util.StrutsUtil;
 import org.apache.struts2.views.jsp.ui.OgnlTool;
 
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.inject.Inject;
-import com.opensymphony.xwork2.util.ValueStack;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Value Stack's Context related Utilities.
- *
  */
 public class ContextUtil {
     public static final String REQUEST = "request";
@@ -48,7 +42,7 @@
     public static final String OGNL = "ognl";
     public static final String STRUTS = "struts";
     public static final String ACTION = "action";
-    
+
     public static Map<String, Object> getStandardContext(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
         HashMap<String, Object> map = new HashMap<>();
         map.put(REQUEST, req);
@@ -56,10 +50,10 @@
         map.put(SESSION, req.getSession(false));
         map.put(BASE, req.getContextPath());
         map.put(STACK, stack);
-        map.put(OGNL, ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(OgnlTool.class));
+        map.put(OGNL, stack.getActionContext().getContainer().getInstance(OgnlTool.class));
         map.put(STRUTS, new StrutsUtil(stack, req, res));
 
-        ActionInvocation invocation = (ActionInvocation) stack.getContext().get(ActionContext.ACTION_INVOCATION);
+        ActionInvocation invocation = stack.getActionContext().getActionInvocation();
         if (invocation != null) {
             map.put(ACTION, invocation.getAction());
         }
@@ -68,25 +62,31 @@
 
     /**
      * Return true if either Configuration's altSyntax is on or the stack context's useAltSyntax is on
+     *
      * @param context stack's context
      * @return boolean
      */
-    public static boolean isUseAltSyntax(Map context) {
+    public static boolean isUseAltSyntax(Map<String, Object> context) {
         // We didn't make altSyntax static cause, if so, struts.configuration.xml.reload will not work
         // plus the Configuration implementation should cache the properties, which the framework's
         // configuration implementation does
-        return "true".equals(((Container)context.get(ActionContext.CONTAINER)).getInstance(String.class, StrutsConstants.STRUTS_TAG_ALTSYNTAX)) ||(
-                (context.containsKey("useAltSyntax") &&
-                        context.get("useAltSyntax") != null &&
-                        "true".equals(context.get("useAltSyntax").toString())));
+        String tagAltSytnax = ActionContext.of(context).getContainer().getInstance(String.class, StrutsConstants.STRUTS_TAG_ALTSYNTAX);
+
+        return "true".equals(tagAltSytnax) || (
+            (context.containsKey("useAltSyntax") &&
+                context.get("useAltSyntax") != null &&
+                "true".equals(context.get("useAltSyntax").toString()))
+        );
     }
-    
+
     /**
      * Returns a String for overriding the default templateSuffix if templateSuffix is on the stack
+     *
      * @param context stack's context
      * @return String
      */
-    public static String getTemplateSuffix(Map context) {
+    public static String getTemplateSuffix(Map<String, Object> context) {
         return context.containsKey("templateSuffix") ? (String) context.get("templateSuffix") : 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..f5f9af2 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ActionContextTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ActionContextTest.java
@@ -34,31 +34,31 @@
  */
 public class ActionContextTest extends XWorkTestCase {
 
-    private static final String APPLICATION_KEY = "com.opensymphony.xwork2.ActionContextTest.application";
-    private static final String SESSION_KEY = "com.opensymphony.xwork2.ActionContextTest.session";
-    private static final String PARAMETERS_KEY = "com.opensymphony.xwork2.ActionContextTest.params";
-    private static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContextTest.actionName";
+    private static final String APPLICATION_KEY = ActionContextTest.class.getName() + ".application";
+    private static final String SESSION_KEY = ActionContextTest.class.getName() + ".session";
+    private static final String PARAMETERS_KEY = ActionContextTest.class.getName() + ".params";
+    private static final String ACTION_NAME = ActionContextTest.class.getName() + ".actionName";
 
     private ActionContext context;
+    private Map<String, Object> application = new HashMap<>();
+    private Map<String, Object> session = new HashMap<>();
+    private Map<String, Object> params = new HashMap<>();
 
     @Override public void setUp() throws Exception {
         super.setUp();
         ValueStack valueStack = container.getInstance(ValueStackFactory.class).createValueStack();
         Map<String, Object> extraContext = valueStack.getContext();
-        Map<String, Object> application = new HashMap<>();
+
         application.put(APPLICATION_KEY, APPLICATION_KEY);
-
-        Map<String, Object> session = new HashMap<>();
         session.put(SESSION_KEY, SESSION_KEY);
-
-        Map<String, Object> params = new HashMap<>();
         params.put(PARAMETERS_KEY, PARAMETERS_KEY);
-        extraContext.put(ActionContext.APPLICATION, application);
-        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.of(extraContext)
+            .withApplication(application)
+            .withSession(session)
+            .withParameters(HttpParameters.create(params).build())
+            .withActionName(ACTION_NAME)
+            .bind();
     }
 
     public void testContextParams() {
@@ -81,21 +81,19 @@
     }
 
     public void testApplication() {
-        Map<String, Object> app = new HashMap<>();
-        context.setApplication(app);
-        assertEquals(app, context.getApplication());
+        assertEquals(application, context.getApplication());
     }
 
     public void testContextMap() {
         Map<String, Object> map = new HashMap<>();
-        ActionContext.setContext(new ActionContext(map));
+        ActionContext.of(map).bind();
 
         assertEquals(map, ActionContext.getContext().getContextMap());
     }
 
     public void testParameters() {
-        context.setParameters(HttpParameters.create().build());
-        assertEquals(0, context.getParameters().keySet().size());
+        assertEquals(1, context.getParameters().keySet().size());
+        assertEquals(PARAMETERS_KEY, context.getParameters().get(PARAMETERS_KEY).getValue());
     }
 
     public void testConversionErrors() {
@@ -104,17 +102,16 @@
         assertEquals(0, errors.size());
 
         Map<String, ConversionData> errors2 = new HashMap<>();
-        context.setConversionErrors(errors);
+        context.withConversionErrors(errors);
         assertEquals(errors2, context.getConversionErrors());
     }
 
     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..de0b5c1 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 @@
 
 /**
  * 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.of(new HashMap<>()).bind();
+
+        assertEquals(context, ActionContext.getContext());
+    }
+
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/ActionNestingTest.java b/core/src/test/java/com/opensymphony/xwork2/ActionNestingTest.java
index 3344ee5..cc4f017 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ActionNestingTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ActionNestingTest.java
@@ -29,13 +29,14 @@
 import com.opensymphony.xwork2.util.location.LocatableProperties;
 
 import java.util.HashMap;
+import java.util.Map;
 
 
 /**
  * ActionNestingTest
  *
  * @author Jason Carreira
- *         Created Mar 5, 2003 2:02:01 PM
+ * Created Mar 5, 2003 2:02:01 PM
  */
 public class ActionNestingTest extends XWorkTestCase {
 
@@ -56,7 +57,8 @@
         return VALUE;
     }
 
-    @Override public void setUp() throws Exception {
+    @Override
+    public void setUp() throws Exception {
         super.setUp();
         loadConfigurationProviders(new NestedTestConfigurationProvider());
 
@@ -64,7 +66,8 @@
         context.getValueStack().push(this);
     }
 
-    @Override protected void tearDown() throws Exception {
+    @Override
+    protected void tearDown() throws Exception {
         super.tearDown();
     }
 
@@ -90,8 +93,9 @@
         ValueStack stack = ActionContext.getContext().getValueStack();
         assertEquals(VALUE, stack.findValue(KEY));
 
-        HashMap<String, Object> extraContext = new HashMap<>();
-        extraContext.put(ActionContext.VALUE_STACK, stack);
+        Map<String, Object> extraContext = ActionContext.of(new HashMap<>())
+            .withValueStack(stack)
+            .getContextMap();
 
         ActionProxy proxy = actionProxyFactory.createActionProxy(NAMESPACE, STACK_ACTION_NAME, null, extraContext);
         proxy.execute();
@@ -105,30 +109,32 @@
 
     class NestedTestConfigurationProvider implements ConfigurationProvider {
         private Configuration configuration;
+
         public void destroy() {
         }
+
         public void init(Configuration configuration) {
             this.configuration = configuration;
         }
 
         public void register(ContainerBuilder builder, LocatableProperties props) {
         }
-        
+
         public void loadPackages() {
-            
+
             PackageConfig packageContext = new PackageConfig.Builder("nestedActionTest")
                 .addActionConfig(SIMPLE_ACTION_NAME, new ActionConfig.Builder("nestedActionTest", SIMPLE_ACTION_NAME, SimpleAction.class.getName())
-                        .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, MockResult.class.getName()).build())
-                        .addResultConfig(new ResultConfig.Builder(Action.ERROR, MockResult.class.getName()).build())
-                        .build())
+                    .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, MockResult.class.getName()).build())
+                    .addResultConfig(new ResultConfig.Builder(Action.ERROR, MockResult.class.getName()).build())
+                    .build())
                 .addActionConfig(NO_STACK_ACTION_NAME, new ActionConfig.Builder("nestedActionTest", NO_STACK_ACTION_NAME, NestedAction.class.getName())
-                        .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, MockResult.class.getName()).build())
-                        .methodName("noStack")
-                        .build())
+                    .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, MockResult.class.getName()).build())
+                    .methodName("noStack")
+                    .build())
                 .addActionConfig(STACK_ACTION_NAME, new ActionConfig.Builder("nestedActionTest", STACK_ACTION_NAME, NestedAction.class.getName())
-                        .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, MockResult.class.getName()).build())
-                        .methodName("stack")
-                        .build())
+                    .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, MockResult.class.getName()).build())
+                    .methodName("stack")
+                    .build())
                 .namespace(NAMESPACE)
                 .build();
             configuration.addPackageConfig("nestedActionTest", packageContext);
diff --git a/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java b/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java
index f712ee7..0a357e8 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ActionSupportTest.java
@@ -39,7 +39,7 @@
         as = new ActionSupport();
         container.inject(as);
 
-        ActionContext.getContext().setLocale(new Locale("da"));
+        ActionContext.getContext().withLocale(new Locale("da"));
 
         mas = new MyActionSupport();
         container.inject(mas);
@@ -52,19 +52,19 @@
     }
 
     public void testNothingDoneOnActionSupport() throws Exception {
-        assertEquals(false, as.hasErrors());
+        assertFalse(as.hasErrors());
 
         assertNotNull(as.getActionErrors());
         assertEquals(0, as.getActionErrors().size());
-        assertEquals(false, as.hasActionErrors());
+        assertFalse(as.hasActionErrors());
 
         assertNotNull(as.getActionMessages());
         assertEquals(0, as.getActionMessages().size());
-        assertEquals(false, as.hasActionMessages());
+        assertFalse(as.hasActionMessages());
 
         assertNotNull(as.getFieldErrors());
         assertEquals(0, as.getFieldErrors().size());
-        assertEquals(false, as.hasFieldErrors());
+        assertFalse(as.hasFieldErrors());
 
         assertNull(as.getText(null));
 
@@ -84,16 +84,16 @@
         }
 
 
-        assertNull(as.getText(null, (List) null));
+        assertNull(as.getText(null, (List<?>) null));
         assertNull(as.getText(null, (String) null));
         assertNull(as.getText(null, (String[]) null));
 
-        assertNull(as.getText(null, (String) null, (List) null));
-        assertNull(as.getText(null, (String) null, (String) null));
-        assertNull(as.getText(null, (String) null, (String[]) null));
+        assertNull(as.getText(null, null, (List<?>) null));
+        assertNull(as.getText(null, null, (String) null));
+        assertNull(as.getText(null, null, (String[]) null));
 
-        assertNull(as.getText(null, (String) null, (List) null, (ValueStack) null));
-        assertNull(as.getText(null, (String) null, (String[]) null, (ValueStack) null));
+        assertNull(as.getText(null, null, (List<?>) null, null));
+        assertNull(as.getText(null, null, (String[]) null, null));
 
         assertNotNull(as.getLocale());
         assertEquals(ActionContext.getContext().getLocale(), as.getLocale());
@@ -103,63 +103,63 @@
     }
 
     public void testActionErrors() {
-        assertEquals(false, as.hasActionErrors());
+        assertFalse(as.hasActionErrors());
         assertEquals(0, as.getActionErrors().size());
         as.addActionError("Damm");
         assertEquals(1, as.getActionErrors().size());
         assertEquals("Damm", as.getActionErrors().iterator().next());
-        assertEquals(true, as.hasActionErrors());
-        assertEquals(true, as.hasErrors());
+        assertTrue(as.hasActionErrors());
+        assertTrue(as.hasErrors());
 
         as.clearErrorsAndMessages();
-        assertEquals(false, as.hasActionErrors());
-        assertEquals(false, as.hasErrors());
+        assertFalse(as.hasActionErrors());
+        assertFalse(as.hasErrors());
     }
 
     public void testActionMessages() {
-        assertEquals(false, as.hasActionMessages());
+        assertFalse(as.hasActionMessages());
         assertEquals(0, as.getActionMessages().size());
         as.addActionMessage("Killroy was here");
         assertEquals(1, as.getActionMessages().size());
         assertEquals("Killroy was here", as.getActionMessages().iterator().next());
-        assertEquals(true, as.hasActionMessages());
+        assertTrue(as.hasActionMessages());
 
-        assertEquals(false, as.hasActionErrors()); // does not count as a error
-        assertEquals(false, as.hasErrors()); // does not count as a error
+        assertFalse(as.hasActionErrors()); // does not count as a error
+        assertFalse(as.hasErrors()); // does not count as a error
 
         as.clearErrorsAndMessages();
-        assertEquals(false, as.hasActionMessages());
-        assertEquals(false, as.hasErrors());
+        assertFalse(as.hasActionMessages());
+        assertFalse(as.hasErrors());
     }
 
     public void testFieldErrors() {
-        assertEquals(false, as.hasFieldErrors());
+        assertFalse(as.hasFieldErrors());
         assertEquals(0, as.getFieldErrors().size());
         as.addFieldError("username", "Admin is not allowed as username");
         List<String> errors = as.getFieldErrors().get("username");
         assertEquals(1, errors.size());
         assertEquals("Admin is not allowed as username", errors.get(0));
 
-        assertEquals(true, as.hasFieldErrors());
-        assertEquals(true, as.hasErrors());
+        assertTrue(as.hasFieldErrors());
+        assertTrue(as.hasErrors());
 
         as.clearErrorsAndMessages();
-        assertEquals(false, as.hasFieldErrors());
-        assertEquals(false, as.hasErrors());
+        assertFalse(as.hasFieldErrors());
+        assertFalse(as.hasErrors());
     }
 
     public void testLocale() {
         Locale defLocale = Locale.getDefault();
-        ActionContext.getContext().setLocale(null);
+        ActionContext.getContext().withLocale(null);
 
         // will never return null, if no locale is set then default is returned
         assertNotNull(as.getLocale());
         assertEquals(defLocale, as.getLocale());
 
-        ActionContext.getContext().setLocale(Locale.ITALY);
+        ActionContext.getContext().withLocale(Locale.ITALY);
         assertEquals(Locale.ITALY, as.getLocale());
 
-        ActionContext.setContext(new ActionContext(new HashMap<String, Object>()));
+        ActionContext.of(new HashMap<>()).bind();
         assertEquals(defLocale, as.getLocale()); // ActionContext will create a new context, when it was set to null before
     }
 
@@ -167,17 +167,17 @@
         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 {
+    public void testSimpleGetTexts() {
         checkGetTexts(mas);
     }
 
-    public void testSimpleGetTextsWithInjectedTextProvider() throws Exception {
-        ActionContext.getContext().setLocale(new Locale("da"));
+    public void testSimpleGetTextsWithInjectedTextProvider() {
+        ActionContext.getContext().withLocale(new Locale("da"));
         MyActionSupport mas = new MyActionSupport();
 
         TextProvider textProvider = container.getInstance(TextProvider.class, "system");
@@ -196,14 +196,12 @@
         assertEquals("Hello World", mas.getText("hello", "this is default"));
         assertEquals("this is default", mas.getText("not.in.bundle", "this is default"));
 
-        List nullList = null;
-        assertEquals("Hello World", mas.getText("hello", nullList));
+        assertEquals("Hello World", mas.getText("hello", (List<?>) null));
 
-        String[] nullStrings = null;
-        assertEquals("Hello World", mas.getText("hello", nullStrings));
+        assertEquals("Hello World", mas.getText("hello", (String[]) null));
     }
 
-    public void testGetTextsWithArgs() throws Exception {
+    public void testGetTextsWithArgs() {
         assertEquals("Hello World", mas.getText("hello", "this is default", "from me")); // no args in bundle
         assertEquals("Hello World from me", mas.getText("hello.0", "this is default", "from me"));
         assertEquals("this is default", mas.getText("not.in.bundle", "this is default", "from me"));
@@ -212,7 +210,7 @@
         assertEquals("not.in.bundle", mas.getText("not.in.bundle"));
     }
 
-    public void testGetTextsWithListArgs() throws Exception {
+    public void testGetTextsWithListArgs() {
         List<Object> args = new ArrayList<>();
         args.add("Santa");
         args.add("loud");
@@ -230,11 +228,11 @@
 
         assertEquals("not.in.bundle", mas.getText("not.in.bundle", args));
 
-        assertEquals("Hello World", mas.getText("hello", "this is default", (List) null));
-        assertEquals("this is default", mas.getText("not.in.bundle", "this is default", (List) null));
+        assertEquals("Hello World", mas.getText("hello", "this is default", (List<?>) null));
+        assertEquals("this is default", mas.getText("not.in.bundle", "this is default", (List<?>) null));
     }
 
-    public void testGetTextsWithArrayArgs() throws Exception {
+    public void testGetTextsWithArrayArgs() {
         String[] args = {"Santa", "loud"};
         assertEquals("Hello World", mas.getText("hello", "this is default", args)); // no args in bundle
         assertEquals("Hello World Santa", mas.getText("hello.0", "this is default", args)); // only 1 arg in bundle
@@ -254,8 +252,8 @@
         assertEquals("this is default", mas.getText("not.in.bundle", "this is default", (String[]) null));
     }
 
-    public void testGetTextsWithListAndStack() throws Exception {
-        ActionContext.getContext().setLocale(new Locale("da"));
+    public void testGetTextsWithListAndStack() {
+        ActionContext.getContext().withLocale(new Locale("da"));
         MyActionSupport mas = container.inject(MyActionSupport.class);
 
         ValueStack stack = ActionContext.getContext().getValueStack();
@@ -272,8 +270,8 @@
         assertEquals("this is default Santa speaking loud", mas.getText("not.in.bundle", "this is default {0} speaking {1}", args, stack));
     }
 
-    public void testGetTextsWithArrayAndStack() throws Exception {
-        ActionContext.getContext().setLocale(new Locale("da"));
+    public void testGetTextsWithArrayAndStack() {
+        ActionContext.getContext().withLocale(new Locale("da"));
         MyActionSupport mas = container.inject(MyActionSupport.class);
 
         ValueStack stack = ActionContext.getContext().getValueStack();
@@ -288,12 +286,12 @@
         assertEquals("this is default Santa speaking loud", mas.getText("not.in.bundle", "this is default {0} speaking {1}", args, stack));
     }
 
-    public void testGetBundle() throws Exception {
+    public void testGetBundle() {
         ResourceBundle rb = ResourceBundle.getBundle(MyActionSupport.class.getName(), new Locale("da"));
         assertEquals(rb, mas.getTexts(MyActionSupport.class.getName()));
     }
 
-    public void testFormattingSupport() throws Exception {
+    public void testFormattingSupport() {
         ActionContext.getContext().getValueStack().push(mas);
 
         mas.setVal(234d);
@@ -303,9 +301,9 @@
         assertEquals("234,0", formatted);
     }
 
-    public void testFormattingSupportWithConversionError() throws Exception {
+    public void testFormattingSupportWithConversionError() {
         ActionContext.getContext().getConversionErrors().put("val", new ConversionData(new String[]{"4567def"}, Double.class));
-        ActionContext.getContext().setLocale(new Locale("da"));
+        ActionContext.getContext().withLocale(new Locale("da"));
         MyActionSupport mas = new MyActionSupport();
         container.inject(mas);
         ActionContext.getContext().getValueStack().push(mas);
diff --git a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
index 2d6ac54..f806caf 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 @@
         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 @@
 
         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.bind(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/CompositeTextProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/CompositeTextProviderTest.java
index 37f0073..0237b65 100644
--- a/core/src/test/java/com/opensymphony/xwork2/CompositeTextProviderTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/CompositeTextProviderTest.java
@@ -97,7 +97,7 @@
 
         TextProviderFactory tpf = container.getInstance(TextProviderFactory.class);
 
-        ActionContext.getContext().setLocale(Locale.ENGLISH);
+        ActionContext.getContext().withLocale(Locale.ENGLISH);
 
         textProvider = new CompositeTextProvider(new TextProvider[]{
                 tpf.createInstance(ResourceBundle.getBundle("com.opensymphony.xwork2.validator.CompositeTextProviderTestResourceBundle1")),
diff --git a/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java
index c407dc4..39c598a 100644
--- a/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/DefaultTextProviderTest.java
@@ -129,9 +129,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        ActionContext ctx = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(ctx);
-        ctx.setLocale(Locale.CANADA);
+        ActionContext.of(new HashMap<>()).withLocale(Locale.CANADA).bind();
 
         container.getInstance(LocalizedTextProvider.class).addDefaultResourceBundle(DefaultTextProviderTest.class.getName());
 
@@ -140,7 +138,8 @@
 
     @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..f7c9506 100644
--- a/core/src/test/java/com/opensymphony/xwork2/LocaleAwareTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/LocaleAwareTest.java
@@ -37,7 +37,7 @@
     public void testGetText() {
         try {
             ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.FOO_ACTION_NAME, null, null);
-            ActionContext.getContext().setLocale(Locale.US);
+            ActionContext.getContext().withLocale(Locale.US);
 
             TextProvider localeAware = (TextProvider) proxy.getAction();
             assertEquals("Foo Range Message", localeAware.getText("foo.range"));
@@ -50,7 +50,7 @@
     public void testLocaleGetText() {
         try {
             ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.FOO_ACTION_NAME, null, null);
-            ActionContext.getContext().setLocale(Locale.GERMANY);
+            ActionContext.getContext().withLocale(Locale.GERMANY);
 
             TextProvider localeAware = (TextProvider) proxy.getAction();
             assertEquals("I don't know German", localeAware.getText("foo.range"));
@@ -68,7 +68,7 @@
         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().withContainer(container);
+        ActionContext.of(stack.getContext()).bind();
     }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/StubValueStack.java b/core/src/test/java/com/opensymphony/xwork2/StubValueStack.java
index ea475f0..4673734 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 @@
         return ctx;
     }
 
+    @Override
+    public ActionContext getActionContext() {
+        return ActionContext.of(ctx).bind();
+    }
+
     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 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..e9b0247 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
@@ -24,8 +24,6 @@
 import com.opensymphony.xwork2.util.Bar;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
-import ognl.Ognl;
-import ognl.OgnlException;
 import ognl.OgnlRuntime;
 
 import java.math.BigDecimal;
@@ -35,6 +33,8 @@
 import java.text.SimpleDateFormat;
 import java.util.*;
 
+import static org.junit.Assert.assertArrayEquals;
+
 
 /**
  * @author $Author$
@@ -76,11 +76,11 @@
 
     public void testArrayToNumberConversion() {
         String[] value = new String[]{"12345"};
-        assertEquals(new Integer(12345), converter.convertValue(context, null, null, null, value, Integer.class));
-        assertEquals(new Long(12345), converter.convertValue(context, null, null, null, value, Long.class));
+        assertEquals(12345, converter.convertValue(context, null, null, null, value, Integer.class));
+        assertEquals(12345L, converter.convertValue(context, null, null, null, value, Long.class));
         value[0] = "123.45";
-        assertEquals(new Float(123.45), converter.convertValue(context, null, null, null, value, Float.class));
-        assertEquals(new Double(123.45), converter.convertValue(context, null, null, null, value, Double.class));
+        assertEquals(123.45f, converter.convertValue(context, null, null, null, value, Float.class));
+        assertEquals(123.45, converter.convertValue(context, null, null, null, value, Double.class));
         value[0] = "1234567890123456789012345678901234567890";
         assertEquals(new BigInteger(value[0]), converter.convertValue(context, null, null, null, value, BigInteger.class));
         value[0] = "1234567890123456789.012345678901234567890";
@@ -91,7 +91,7 @@
         java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
         assertEquals(sqlDate, converter.convertValue(context, null, null, null, sqlDate, Date.class));
 
-        SimpleDateFormat format = new SimpleDateFormat("mm/dd/yyyy hh:mm:ss");
+        SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss");
         Date date = format.parse("01/10/2001 00:00:00");
         String dateStr = (String) converter.convertValue(context, null, null, null, date, String.class);
         Date date2 = (Date) converter.convertValue(context, null, null, null, dateStr, Date.class);
@@ -113,9 +113,9 @@
         assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action.getBean(), null, "birth", value, Date.class));
         stack.pop();
 
-        Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertNotNull(conversionErrors);
-        assertTrue(conversionErrors.size() == 1);
+        assertEquals(1, conversionErrors.size());
         assertEquals(value, conversionErrors.get("bean.birth").getValue());
     }
 
@@ -133,7 +133,7 @@
         assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action, null, "date", value, Date.class));
         stack.pop();
 
-        Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = ActionContext.of(ognlStackContext).getConversionErrors();
         assertNotNull(conversionErrors);
         assertEquals(1, conversionErrors.size());
         assertNotNull(conversionErrors.get("date"));
@@ -154,7 +154,7 @@
         stack.pop();
         stack.pop();
 
-        Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = ActionContext.of(ognlStackContext).getConversionErrors();
         assertNotNull(conversionErrors);
         assertEquals(1, conversionErrors.size());
         assertNotNull(conversionErrors.get("birth"));
@@ -204,12 +204,12 @@
         assertEquals(value2, b2.getTitle() + ":" + b2.getSomethingElse());
     }
 
-    public void testLocalizedDateConversion() throws Exception {
+    public void testLocalizedDateConversion() {
         Date date = new Date(System.currentTimeMillis());
         Locale locale = Locale.GERMANY;
         DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
         String dateString = df.format(date);
-        context.put(ActionContext.LOCALE, locale);
+        context = ActionContext.of(context).withLocale(locale).getContextMap();
         assertEquals(dateString, converter.convertValue(context, null, null, null, date, String.class));
     }
 
@@ -224,7 +224,7 @@
     }
 
     public void testStringArrayToList() {
-        List<String> list = new ArrayList<String>();
+        List<String> list = new ArrayList<>();
         list.add("foo");
         list.add("bar");
         list.add("baz");
@@ -238,63 +238,63 @@
                 "123", "456"
         }, Long[].class);
         assertNotNull(longs);
-        assertTrue(Arrays.equals(new Long[]{123L, 456L}, longs));
+        assertArrayEquals(new Long[]{123L, 456L}, longs);
 
         Integer[] ints = (Integer[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, Integer[].class);
         assertNotNull(ints);
-        assertTrue(Arrays.equals(new Integer[]{123, 456}, ints));
+        assertArrayEquals(new Integer[]{123, 456}, ints);
 
         Double[] doubles = (Double[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, Double[].class);
         assertNotNull(doubles);
-        assertTrue(Arrays.equals(new Double[]{123D, 456D}, doubles));
+        assertArrayEquals(new Double[]{123D, 456D}, doubles);
 
         Float[] floats = (Float[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, Float[].class);
         assertNotNull(floats);
-        assertTrue(Arrays.equals(new Float[]{123F, 456F}, floats));
+        assertArrayEquals(new Float[]{123F, 456F}, floats);
 
         Boolean[] booleans = (Boolean[]) converter.convertValue(context, null, null, null, new String[]{
                 "true", "false"
         }, Boolean[].class);
         assertNotNull(booleans);
-        assertTrue(Arrays.equals(new Boolean[]{Boolean.TRUE, Boolean.FALSE}, booleans));
+        assertArrayEquals(new Boolean[]{Boolean.TRUE, Boolean.FALSE}, booleans);
     }
 
-    public void testStringArrayToPrimitives() throws OgnlException {
+    public void testStringArrayToPrimitives() {
         long[] longs = (long[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, long[].class);
         assertNotNull(longs);
-        assertTrue(Arrays.equals(new long[]{123, 456}, longs));
+        assertArrayEquals(new long[]{123, 456}, longs);
 
         int[] ints = (int[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, int[].class);
         assertNotNull(ints);
-        assertTrue(Arrays.equals(new int[]{123, 456}, ints));
+        assertArrayEquals(new int[]{123, 456}, ints);
 
         double[] doubles = (double[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, double[].class);
         assertNotNull(doubles);
-        assertTrue(Arrays.equals(new double[]{123, 456}, doubles));
+        assertArrayEquals(new double[]{123, 456}, doubles, 0.0);
 
         float[] floats = (float[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, float[].class);
         assertNotNull(floats);
-        assertTrue(Arrays.equals(new float[]{123, 456}, floats));
+        assertArrayEquals(new float[]{123, 456}, floats, 0.0f);
 
         boolean[] booleans = (boolean[]) converter.convertValue(context, null, null, null, new String[]{
                 "true", "false"
         }, boolean[].class);
         assertNotNull(booleans);
-        assertTrue(Arrays.equals(new boolean[]{true, false}, booleans));
+        assertArrayEquals(new boolean[]{true, false}, booleans);
     }
 
     public void testStringArrayToSet() {
@@ -338,22 +338,22 @@
     }
 
     public void testStringToPrimitiveWrappers() {
-        assertEquals(new Long(123), converter.convertValue(context, null, null, null, "123", Long.class));
-        assertEquals(new Integer(123), converter.convertValue(context, null, null, null, "123", Integer.class));
-        assertEquals(new Double(123.5), converter.convertValue(context, null, null, null, "123.5", Double.class));
-        assertEquals(new Float(123.5), converter.convertValue(context, null, null, null, "123.5", float.class));
+        assertEquals(123L, converter.convertValue(context, null, null, null, "123", Long.class));
+        assertEquals(123, converter.convertValue(context, null, null, null, "123", Integer.class));
+        assertEquals(123.5, converter.convertValue(context, null, null, null, "123.5", Double.class));
+        assertEquals(123.5f, converter.convertValue(context, null, null, null, "123.5", float.class));
         assertEquals(false, converter.convertValue(context, null, null, null, "false", Boolean.class));
         assertEquals(true, converter.convertValue(context, null, null, null, "true", Boolean.class));
     }
 
     public void testStringToPrimitives() {
-        assertEquals(new Long(123), converter.convertValue(context, null, null, null, "123", long.class));
-        assertEquals(new Integer(123), converter.convertValue(context, null, null, null, "123", int.class));
-        assertEquals(new Double(123.5), converter.convertValue(context, null, null, null, "123.5", double.class));
-        assertEquals(new Float(123.5), converter.convertValue(context, null, null, null, "123.5", float.class));
+        assertEquals(123L, converter.convertValue(context, null, null, null, "123", long.class));
+        assertEquals(123, converter.convertValue(context, null, null, null, "123", int.class));
+        assertEquals(123.5, converter.convertValue(context, null, null, null, "123.5", double.class));
+        assertEquals(123.5f, converter.convertValue(context, null, null, null, "123.5", float.class));
         assertEquals(false, converter.convertValue(context, null, null, null, "false", boolean.class));
         assertEquals(true, converter.convertValue(context, null, null, null, "true", boolean.class));
-        assertEquals(new BigDecimal(123.5), converter.convertValue(context, null, null, null, "123.5", BigDecimal.class));
+        assertEquals(new BigDecimal("123.5"), converter.convertValue(context, null, null, null, "123.5", BigDecimal.class));
         assertEquals(new BigInteger("123"), converter.convertValue(context, null, null, null, "123", BigInteger.class));
     }
 
@@ -373,8 +373,8 @@
         stack.setValue("doubles", value);
         assertEquals(2, gb.getDoubles().size());
         assertEquals(Double.class, gb.getDoubles().get(0).getClass());
-        assertEquals(new Double(123.12), gb.getDoubles().get(0));
-        assertEquals(new Double(123.45), gb.getDoubles().get(1));
+        assertEquals(123.12, gb.getDoubles().get(0));
+        assertEquals(123.45, gb.getDoubles().get(1));
     }
 
     public void testGenericPropertiesFromField() {
@@ -422,8 +422,8 @@
 
         assertEquals(1, gb.getGetterList().size());
         assertEquals("42.42", stack.findValue("getterList.get(0).toString()"));
-        assertEquals(new Double(42.42), stack.findValue("getterList.get(0)"));
-        assertEquals(new Double(42.42), gb.getGetterList().get(0));
+        assertEquals(42.42, stack.findValue("getterList.get(0)"));
+        assertEquals(42.42, gb.getGetterList().get(0));
 
     }
 
@@ -471,13 +471,13 @@
         super.setUp();
         converter = container.getInstance(XWorkConverter.class);
 
-        ac = ActionContext.getContext();
-        ac.setLocale(Locale.US);
+        ac = ActionContext.getContext().withLocale(Locale.US);
         context = ac.getContextMap();
     }
 
     @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/NumberConverterTest.java b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/NumberConverterTest.java
index 049e740..b0271ea 100644
--- a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/NumberConverterTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/NumberConverterTest.java
@@ -18,19 +18,17 @@
  */
 package com.opensymphony.xwork2.conversion.impl;
 
-import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.SimpleFooAction;
 import com.opensymphony.xwork2.XWorkTestCase;
 import org.apache.struts2.conversion.TypeConversionException;
 import org.apache.commons.lang3.StringUtils;
 
 import java.math.BigDecimal;
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
 public class NumberConverterTest extends XWorkTestCase {
-    
+
     private final static String FLOAT_OUT_OF_RANGE = "3.5028235E38";
     private final static String DOUBLE_OUT_OF_RANGE = "1.7976931348623157E309";
     private final static String INTEGER_OUT_OF_RANGE = "2147483648";
@@ -44,8 +42,7 @@
     public void testStringToNumberConversionPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         SimpleFooAction foo = new SimpleFooAction();
 
@@ -59,8 +56,7 @@
     public void testStringToNumberConversionUS() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("en", "US"));
+        Map<String, Object> context = createContextWithLocale(new Locale("en", "US"));
 
         SimpleFooAction foo = new SimpleFooAction();
 
@@ -74,12 +70,11 @@
     public void testStringToBigDecimalConversionPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when a bit bigger than double
         String aBitBiggerThanDouble = "17976931348623157" + StringUtils.repeat('0', 291) + "1,"
-                + StringUtils.repeat('0', 324) + "49";
+            + StringUtils.repeat('0', 324) + "49";
         Object value = converter.convertValue(context, null, null, null, aBitBiggerThanDouble, BigDecimal.class);
 
         // then does not lose integer and fraction digits
@@ -89,8 +84,7 @@
     public void testStringToBigDecimalConversionWithDotsPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, "1 234,4", BigDecimal.class);
@@ -98,12 +92,11 @@
         // then
         assertEquals(BigDecimal.valueOf(1234.4), value);
     }
-    
+
     public void testStringToBigDecimalConversionWithCommasEN() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("en", "US"));
+        Map<String, Object> context = createContextWithLocale(new Locale("en", "US"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, "100,234.4", BigDecimal.class);
@@ -115,19 +108,18 @@
     public void testStringToDoubleConversionPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when has max fraction digits
         Object value = converter.convertValue(context, null, null, null,
-                "0," + StringUtils.repeat('0', 323) + "49", Double.class);
+            "0," + StringUtils.repeat('0', 323) + "49", Double.class);
 
         // then does not lose fraction digits
         assertEquals(Double.MIN_VALUE, value);
 
         // when has max integer digits
         value = converter.convertValue(context, null, null, null,
-                "17976931348623157" + StringUtils.repeat('0', 292) + ",0", Double.class);
+            "17976931348623157" + StringUtils.repeat('0', 292) + ",0", Double.class);
 
         // then does not lose integer digits
         assertEquals(Double.MAX_VALUE, value);
@@ -136,8 +128,7 @@
     public void testStringToDoubleConversionWithDotsPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, "1 234,4", Double.class);
@@ -149,19 +140,18 @@
     public void testStringToFloatConversionPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when has max fraction digits
         Object value = converter.convertValue(context, null, null, null,
-                "0," + StringUtils.repeat('0', 44) + "1401298464324817", Float.class);
+            "0," + StringUtils.repeat('0', 44) + "1401298464324817", Float.class);
 
         // then does not lose fraction digits
         assertEquals(Float.MIN_VALUE, value);
 
         // when has max integer digits
         value = converter.convertValue(context, null, null, null,
-                "34028234663852886" + StringUtils.repeat('0', 22) + ",0", Float.class);
+            "34028234663852886" + StringUtils.repeat('0', 22) + ",0", Float.class);
 
         // then does not lose integer digits
         assertEquals(Float.MAX_VALUE, value);
@@ -170,8 +160,7 @@
     public void testStringToFloatConversionWithDotsPL() throws Exception {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, "1 234,4", Float.class);
@@ -179,13 +168,12 @@
         // then
         assertEquals(1234.4F, value);
     }
-    
+
     public void testExceptionWhenPrimitiveIsOutOfRange() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, INTEGER_OUT_OF_RANGE, int.class);
@@ -196,14 +184,13 @@
             assertTrue(ex.getMessage().startsWith(MSG_OUT_OF_RANGE_CASTING));
         }
     }
-    
+
     public void testExceptionWhenANotPrimitiveIsUnparsable() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
         String strValue = "1.2";
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, strValue, Byte.class);
@@ -214,14 +201,13 @@
             assertTrue(ex.getMessage().startsWith(MSG_UNPARSEABLE_NUMBER));
         }
     }
-    
+
     public void testExceptionWhenANotPrimitiveIsOutOfRange() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
         String strValue = "129";
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, strValue, Byte.class);
@@ -232,14 +218,13 @@
             assertTrue(ex.getMessage().startsWith(MSG_OUT_OF_RANGE_CASTING));
         }
     }
-    
+
     public void testExceptionWhenUnparseableInConvertToBigDecimal() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
         String strValue = "1-23";
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, strValue, BigDecimal.class);
@@ -250,14 +235,13 @@
             assertTrue(ex.getMessage().startsWith(MSG_UNPARSEABLE_NUMBER));
         }
     }
-    
+
     public void testExceptionWhenUnparseableInConvertToDouble() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
         String strValue = "1-23";
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, strValue, Double.class);
@@ -268,13 +252,12 @@
             assertTrue(ex.getMessage().startsWith(MSG_UNPARSEABLE_NUMBER));
         }
     }
-    
+
     public void testExceptionWhenOutOfRangeInConvertToDouble() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, DOUBLE_OUT_OF_RANGE, Double.class);
@@ -285,13 +268,12 @@
             assertTrue(ex.getMessage().startsWith(MSG_OUT_OF_RANGE_CONVERTING));
         }
     }
-    
+
     public void testExceptionWhenOutOfRangeInConvertToFloat() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, FLOAT_OUT_OF_RANGE, Float.class);
@@ -302,14 +284,13 @@
             assertTrue(ex.getMessage().startsWith(MSG_OUT_OF_RANGE_CONVERTING));
         }
     }
-    
+
     public void testExceptionWhenUnparseableInConvertToFloat() {
         // given
         NumberConverter converter = new NumberConverter();
-        Map<String, Object> context = new HashMap<>();
+        Map<String, Object> context = createContextWithLocale(LOCALE_MEXICO);
         String strValue = "1-23";
-        context.put(ActionContext.LOCALE, LOCALE_MEXICO);
-        
+
         // when
         try {
             Object value = converter.convertValue(context, null, null, null, strValue, Float.class);
@@ -320,5 +301,5 @@
             assertTrue(ex.getMessage().startsWith(MSG_UNPARSEABLE_NUMBER));
         }
     }
-    
+
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
index b565dd1..8eef6d1 100644
--- a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
@@ -32,8 +32,7 @@
     public void testIntegerToStringConversionPL() throws Exception {
         // given
         StringConverter converter = new StringConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, Integer.MIN_VALUE, null);
@@ -45,8 +44,7 @@
     public void testDoubleToStringConversionPL() throws Exception {
         // given
         StringConverter converter = new StringConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when has max fraction digits
         Object value = converter.convertValue(context, null, null, null, Double.MIN_VALUE, null);
@@ -70,8 +68,7 @@
     public void testFloatToStringConversionPL() throws Exception {
         // given
         StringConverter converter = new StringConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when has max fraction digits
         Object value = converter.convertValue(context, null, null, null, Float.MIN_VALUE, null);
@@ -95,8 +92,7 @@
     public void testBigDecimalToStringConversionPL() throws Exception {
         // given
         StringConverter converter = new StringConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when a bit bigger than double
         String aBitBiggerThanDouble = "17976931348623157" + StringUtils.repeat('0', 291) + "1."
@@ -111,8 +107,7 @@
     public void testStringArrayToStringConversion() {
         // given
         StringConverter converter = new StringConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, new String[] {"foo", "baz"}, null);
@@ -124,8 +119,7 @@
     public void testArrayOfNullToStringConversion() {
         // given
         StringConverter converter = new StringConverter();
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
 
         // when
         Object value = converter.convertValue(context, null, null, null, new String[] {null}, null);
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 33fea83..4209da1 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
@@ -66,12 +66,11 @@
 
     public void testDateWithLocalePoland() throws Exception {
 
-        Map<String, Object> map = new HashMap<>();
         Locale locale = new Locale("pl", "PL");
-        map.put(ActionContext.LOCALE, locale);
+        Map<String, Object> context = createContextWithLocale(locale);
 
         String reference = "2009-01-09";
-        Object convertedObject = basicConverter.convertValue(map, null, null, null, reference, Date.class);
+        Object convertedObject = basicConverter.convertValue(context, null, null, null, reference, Date.class);
 
         assertNotNull(convertedObject);
 
@@ -79,13 +78,11 @@
     }
 
     public void testDateWithLocaleFrance() throws Exception {
-
-        Map<String, Object> map = new HashMap<>();
         Locale locale = new Locale("fr", "FR");
-        map.put(ActionContext.LOCALE, locale);
+        Map<String, Object> context = createContextWithLocale(locale);
 
         String reference = "09/01/2009";
-        Object convertedObject = basicConverter.convertValue(map, null, null, null, reference, Date.class);
+        Object convertedObject = basicConverter.convertValue(context, null, null, null, reference, Date.class);
 
         assertNotNull(convertedObject);
 
@@ -93,13 +90,11 @@
     }
 
     public void testDateWithLocaleUK() throws Exception {
-
-        Map<String, Object> map = new HashMap<>();
         Locale locale = new Locale("en", "US");
-        map.put(ActionContext.LOCALE, locale);
+        Map<String, Object> context = createContextWithLocale(locale);
 
         String reference = "01/09/2009";
-        Object convertedObject = basicConverter.convertValue(map, null, null, null, reference, Date.class);
+        Object convertedObject = basicConverter.convertValue(context, null, null, null, reference, Date.class);
 
         assertNotNull(convertedObject);
 
@@ -139,9 +134,7 @@
 
     public void testXW490ConvertStringToDouble() throws Exception {
         Locale locale = new Locale("DA"); // let's use a not common locale such as Denmark
-
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, locale);
+        Map<String, Object> context = createContextWithLocale(locale);
 
         // decimal seperator is , in Denmark so we should write 123,99 as input
         Double value = (Double) basicConverter.convertValue(context, null, null, null, "123,99", Double.class);
@@ -153,9 +146,7 @@
 
     public void testXW49ConvertDoubleToString() throws Exception {
         Locale locale = new Locale("DA"); // let's use a not common locale such as Denmark
-
-        Map<String, Object> context = new HashMap<>();
-        context.put(ActionContext.LOCALE, locale);
+        Map<String, Object> context = createContextWithLocale(locale);
 
         // decimal seperator is , in Denmark so we should write 123,99 as input
         String value = (String) basicConverter.convertValue(context, null, null, null, new Double("123.99"), String.class);
@@ -391,7 +382,7 @@
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
     
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java
index 1b2113c..3465737 100644
--- a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java
@@ -27,7 +27,6 @@
 import com.opensymphony.xwork2.util.Foo;
 import com.opensymphony.xwork2.util.FurColor;
 import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
-import ognl.OgnlException;
 import ognl.OgnlRuntime;
 
 import java.io.IOException;
@@ -40,6 +39,8 @@
 import java.text.SimpleDateFormat;
 import java.util.*;
 
+import static org.junit.Assert.assertArrayEquals;
+
 
 /**
  * @author $Author$
@@ -80,11 +81,11 @@
 
     public void testArrayToNumberConversion() {
         String[] value = new String[]{"12345"};
-        assertEquals(new Integer(12345), converter.convertValue(context, null, null, null, value, Integer.class));
-        assertEquals(new Long(12345), converter.convertValue(context, null, null, null, value, Long.class));
+        assertEquals(12345, converter.convertValue(context, null, null, null, value, Integer.class));
+        assertEquals(12345L, converter.convertValue(context, null, null, null, value, Long.class));
         value[0] = "123.45";
-        assertEquals(new Float(123.45), converter.convertValue(context, null, null, null, value, Float.class));
-        assertEquals(new Double(123.45), converter.convertValue(context, null, null, null, value, Double.class));
+        assertEquals(123.45f, converter.convertValue(context, null, null, null, value, Float.class));
+        assertEquals(123.45, converter.convertValue(context, null, null, null, value, Double.class));
         value[0] = "1234567890123456789012345678901234567890";
         assertEquals(new BigInteger(value[0]), converter.convertValue(context, null, null, null, value, BigInteger.class));
         value[0] = "1234567890123456789.012345678901234567890";
@@ -135,9 +136,9 @@
         assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action.getBean(), null, "birth", value, Date.class));
         stack.pop();
 
-        Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertNotNull(conversionErrors);
-        assertTrue(conversionErrors.size() == 1);
+        assertEquals(1, conversionErrors.size());
         assertEquals(value, conversionErrors.get("bean.birth").getValue());
     }
 
@@ -154,7 +155,7 @@
         assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action, null, "date", value, Date.class));
         stack.pop();
 
-        Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = ActionContext.of(ognlStackContext).getConversionErrors();
         assertNotNull(conversionErrors);
         assertEquals(1, conversionErrors.size());
         assertNotNull(conversionErrors.get("date"));
@@ -174,7 +175,7 @@
         stack.pop();
         stack.pop();
 
-        Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = ActionContext.of(ognlStackContext).getConversionErrors();
         assertNotNull(conversionErrors);
         assertEquals(1, conversionErrors.size());
         assertNotNull(conversionErrors.get("birth"));
@@ -311,12 +312,13 @@
         assertEquals("Custom error message for java.util.Date.", message);
     }
 
-    public void testLocalizedDateConversion() throws Exception {
+    public void testLocalizedDateConversion() {
         Date date = new Date(System.currentTimeMillis());
         Locale locale = Locale.GERMANY;
         DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
         String dateString = df.format(date);
-        context.put(ActionContext.LOCALE, locale);
+        context = ActionContext.of(context).withLocale(locale).getContextMap();
+
         assertEquals(dateString, converter.convertValue(context, null, null, null, date, String.class));
     }
 
@@ -332,9 +334,9 @@
         assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action.getBean(), null, "count", "111.1", int.class));
         stack.pop();
 
-        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertNotNull(conversionErrors);
-        assertTrue(conversionErrors.size() == 1);
+        assertEquals(1, conversionErrors.size());
     }
 
     public void testStringArrayToCollection() {
@@ -362,63 +364,63 @@
                 "123", "456"
         }, Long[].class);
         assertNotNull(longs);
-        assertTrue(Arrays.equals(new Long[]{123L, 456L}, longs));
+        assertArrayEquals(new Long[]{123L, 456L}, longs);
 
         Integer[] ints = (Integer[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, Integer[].class);
         assertNotNull(ints);
-        assertTrue(Arrays.equals(new Integer[]{123, 456}, ints));
+        assertArrayEquals(new Integer[]{123, 456}, ints);
 
         Double[] doubles = (Double[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, Double[].class);
         assertNotNull(doubles);
-        assertTrue(Arrays.equals(new Double[]{123D, 456D}, doubles));
+        assertArrayEquals(new Double[]{123D, 456D}, doubles);
 
         Float[] floats = (Float[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, Float[].class);
         assertNotNull(floats);
-        assertTrue(Arrays.equals(new Float[]{123F, 456F}, floats));
+        assertArrayEquals(new Float[]{123F, 456F}, floats);
 
         Boolean[] booleans = (Boolean[]) converter.convertValue(context, null, null, null, new String[]{
                 "true", "false"
         }, Boolean[].class);
         assertNotNull(booleans);
-        assertTrue(Arrays.equals(new Boolean[]{Boolean.TRUE, Boolean.FALSE}, booleans));
+        assertArrayEquals(new Boolean[]{Boolean.TRUE, Boolean.FALSE}, booleans);
     }
 
-    public void testStringArrayToPrimitives() throws OgnlException {
+    public void testStringArrayToPrimitives() {
         long[] longs = (long[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, long[].class);
         assertNotNull(longs);
-        assertTrue(Arrays.equals(new long[]{123, 456}, longs));
+        assertArrayEquals(new long[]{123, 456}, longs);
 
         int[] ints = (int[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, int[].class);
         assertNotNull(ints);
-        assertTrue(Arrays.equals(new int[]{123, 456}, ints));
+        assertArrayEquals(new int[]{123, 456}, ints);
 
         double[] doubles = (double[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, double[].class);
         assertNotNull(doubles);
-        assertTrue(Arrays.equals(new double[]{123, 456}, doubles));
+        assertArrayEquals(new double[]{123, 456}, doubles, 0.0);
 
         float[] floats = (float[]) converter.convertValue(context, null, null, null, new String[]{
                 "123", "456"
         }, float[].class);
         assertNotNull(floats);
-        assertTrue(Arrays.equals(new float[]{123, 456}, floats));
+        assertArrayEquals(new float[]{123, 456}, floats, 0.0f);
 
         boolean[] booleans = (boolean[]) converter.convertValue(context, null, null, null, new String[]{
                 "true", "false"
         }, boolean[].class);
         assertNotNull(booleans);
-        assertTrue(Arrays.equals(new boolean[]{true, false}, booleans));
+        assertArrayEquals(new boolean[]{true, false}, booleans);
     }
 
     public void testStringArrayToSet() {
@@ -500,21 +502,21 @@
     }
 
     public void testStringToPrimitiveWrappers() {
-        assertEquals(new Long(123), converter.convertValue(context, null, null, null, "123", Long.class));
-        assertEquals(new Integer(123), converter.convertValue(context, null, null, null, "123", Integer.class));
-        assertEquals(new Double(123.5), converter.convertValue(context, null, null, null, "123.5", Double.class));
-        assertEquals(new Float(123.5), converter.convertValue(context, null, null, null, "123.5", float.class));
+        assertEquals(123L, converter.convertValue(context, null, null, null, "123", Long.class));
+        assertEquals(123, converter.convertValue(context, null, null, null, "123", Integer.class));
+        assertEquals(123.5, converter.convertValue(context, null, null, null, "123.5", Double.class));
+        assertEquals(123.5f, converter.convertValue(context, null, null, null, "123.5", float.class));
         assertEquals(false, converter.convertValue(context, null, null, null, "false", Boolean.class));
         assertEquals(true, converter.convertValue(context, null, null, null, "true", Boolean.class));
     }
 
     public void testStringToPrimitives() {
-        assertEquals(new Long(123), converter.convertValue(context, null, null, null, "123", long.class));
-        assertEquals(new Double(123.5), converter.convertValue(context, null, null, null, "123.5", double.class));
-        assertEquals(new Float(123.5), converter.convertValue(context, null, null, null, "123.5", float.class));
+        assertEquals(123L, converter.convertValue(context, null, null, null, "123", long.class));
+        assertEquals(123.5, converter.convertValue(context, null, null, null, "123.5", double.class));
+        assertEquals(123.5f, converter.convertValue(context, null, null, null, "123.5", float.class));
         assertEquals(false, converter.convertValue(context, null, null, null, "false", boolean.class));
         assertEquals(true, converter.convertValue(context, null, null, null, "true", boolean.class));
-        assertEquals(new BigDecimal(123.5), converter.convertValue(context, null, null, null, "123.5", BigDecimal.class));
+        assertEquals(new BigDecimal("123.5"), converter.convertValue(context, null, null, null, "123.5", BigDecimal.class));
         assertEquals(new BigInteger("123"), converter.convertValue(context, null, null, null, "123", BigInteger.class));
     }
 
@@ -551,8 +553,8 @@
     }
 
     public void testStringToInt() {
-        assertEquals(new Integer(123), converter.convertValue(context, null, null, null, "123", int.class));
-        context.put(ActionContext.LOCALE, Locale.US);
+        assertEquals(123, converter.convertValue(context, null, null, null, "123", int.class));
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123.12", int.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", int.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", int.class));
@@ -561,7 +563,7 @@
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234.12", int.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234", int.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234,12", int.class));
-        context.put(ActionContext.LOCALE, Locale.GERMANY);
+        context = ActionContext.of(context).withLocale(Locale.GERMANY).getContextMap();
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123.12", int.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", int.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", int.class));
@@ -574,22 +576,22 @@
 
 
     public void testStringToInteger() {
-        assertEquals(new Integer(123), converter.convertValue(context, null, null, null, "123", Integer.class));
-        context.put(ActionContext.LOCALE, Locale.US);
+        assertEquals(123, converter.convertValue(context, null, null, null, "123", Integer.class));
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123.12", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", Integer.class));
-        assertEquals(new Integer(1234), converter.convertValue(context, null, null, null, "1,234", Integer.class));
+        assertEquals(1234, converter.convertValue(context, null, null, null, "1,234", Integer.class));
         // WRONG: locale separator is wrongly placed
-        assertEquals(new Integer(123), converter.convertValue(context, null, null, null, "1,23", Integer.class));
+        assertEquals(123, converter.convertValue(context, null, null, null, "1,23", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234.12", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234,12", Integer.class));
 
-        context.put(ActionContext.LOCALE, Locale.GERMANY);
+        context = ActionContext.of(context).withLocale(Locale.GERMANY).getContextMap();
         // WRONG: locale separator is wrongly placed
-        assertEquals(new Integer(12312), converter.convertValue(context, null, null, null, "123.12", Integer.class));
-        assertEquals(new Integer(1234), converter.convertValue(context, null, null, null, "1.234", Integer.class));
+        assertEquals(12312, converter.convertValue(context, null, null, null, "123.12", Integer.class));
+        assertEquals(1234, converter.convertValue(context, null, null, null, "1.234", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", Integer.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234", Integer.class));
@@ -600,7 +602,7 @@
 
     public void testStringToPrimitiveDouble() {
         assertEquals(123d, converter.convertValue(context, null, null, null, "123", double.class));
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         assertEquals(123.12, converter.convertValue(context, null, null, null, "123.12", double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", double.class));
@@ -610,7 +612,7 @@
         assertEquals(1.234, converter.convertValue(context, null, null, null, "1.234", double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234,12", double.class));
 
-        context.put(ActionContext.LOCALE, Locale.GERMANY);
+        context = ActionContext.of(context).withLocale(Locale.GERMANY).getContextMap();
         assertEquals(12312d, converter.convertValue(context, null, null, null, "123.12", double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", double.class));
@@ -623,27 +625,27 @@
 
     public void testStringToDouble() {
         assertEquals(123d, converter.convertValue(context, null, null, null, "123", Double.class));
-        context.put(ActionContext.LOCALE, Locale.US);
-        assertEquals(new Double(123.12), converter.convertValue(context, null, null, null, "123.12", Double.class));
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
+        assertEquals(123.12, converter.convertValue(context, null, null, null, "123.12", Double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", Double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", Double.class));
-        assertEquals(new Double(1234), converter.convertValue(context, null, null, null, "1,234", Double.class));
-        assertEquals(new Double(1234.12), converter.convertValue(context, null, null, null, "1,234.12", Double.class));
+        assertEquals(1234d, converter.convertValue(context, null, null, null, "1,234", Double.class));
+        assertEquals(1234.12, converter.convertValue(context, null, null, null, "1,234.12", Double.class));
         // WRONG: locale separator is wrongly placed 
-        assertEquals(new Double(123), converter.convertValue(context, null, null, null, "1,23", Double.class));
-        assertEquals(new Double(1.234), converter.convertValue(context, null, null, null, "1.234", Double.class));
+        assertEquals(123d, converter.convertValue(context, null, null, null, "1,23", Double.class));
+        assertEquals(1.234, converter.convertValue(context, null, null, null, "1.234", Double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234,12", Double.class));
 
-        context.put(ActionContext.LOCALE, Locale.GERMANY);
+        context = ActionContext.of(context).withLocale(Locale.GERMANY).getContextMap();
         // WRONG: locale separator is wrongly placed
-        assertEquals(new Double(12312), converter.convertValue(context, null, null, null, "123.12", Double.class));
+        assertEquals(12312d, converter.convertValue(context, null, null, null, "123.12", Double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", Double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", Double.class));
-        assertEquals(new Double(1.234), converter.convertValue(context, null, null, null, "1,234", Double.class));
+        assertEquals(1.234, converter.convertValue(context, null, null, null, "1,234", Double.class));
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234.12", Double.class));
-        assertEquals(new Double(1.23), converter.convertValue(context, null, null, null, "1,23", Double.class));
-        assertEquals(new Double(1234), converter.convertValue(context, null, null, null, "1.234", Double.class));
-        assertEquals(new Double(1234.12), converter.convertValue(context, null, null, null, "1.234,12", Double.class));
+        assertEquals(1.23, converter.convertValue(context, null, null, null, "1,23", Double.class));
+        assertEquals(1234d, converter.convertValue(context, null, null, null, "1.234", Double.class));
+        assertEquals(1234.12, converter.convertValue(context, null, null, null, "1.234,12", Double.class));
 
     }
     
@@ -655,50 +657,50 @@
 
     // Testing for null result on non-primitive Number types supplied as empty String or 
     public void testNotPrimitiveDefaultsToNull() {
-        assertEquals(null, converter.convertValue(context, null, null, null, null, Double.class));
-        assertEquals(null, converter.convertValue(context, null, null, null, "", Double.class));
+        assertNull(converter.convertValue(context, null, null, null, null, Double.class));
+        assertNull(converter.convertValue(context, null, null, null, "", Double.class));
 
-        assertEquals(null, converter.convertValue(context, null, null, null, null, Integer.class));
-        assertEquals(null, converter.convertValue(context, null, null, null, "", Integer.class));
+        assertNull(converter.convertValue(context, null, null, null, null, Integer.class));
+        assertNull(converter.convertValue(context, null, null, null, "", Integer.class));
 
-        assertEquals(null, converter.convertValue(context, null, null, null, null, Float.class));
-        assertEquals(null, converter.convertValue(context, null, null, null, "", Float.class));
+        assertNull(converter.convertValue(context, null, null, null, null, Float.class));
+        assertNull(converter.convertValue(context, null, null, null, "", Float.class));
 
-        assertEquals(null, converter.convertValue(context, null, null, null, null, Character.class));
-        assertEquals(null, converter.convertValue(context, null, null, null, "", Character.class));
+        assertNull(converter.convertValue(context, null, null, null, null, Character.class));
+        assertNull(converter.convertValue(context, null, null, null, "", Character.class));
 
-        assertEquals(null, converter.convertValue(context, null, null, null, null, Long.class));
-        assertEquals(null, converter.convertValue(context, null, null, null, "", Long.class));
+        assertNull(converter.convertValue(context, null, null, null, null, Long.class));
+        assertNull(converter.convertValue(context, null, null, null, "", Long.class));
 
-        assertEquals(null, converter.convertValue(context, null, null, null, null, Short.class));
-        assertEquals(null, converter.convertValue(context, null, null, null, "", Short.class));
+        assertNull(converter.convertValue(context, null, null, null, null, Short.class));
+        assertNull(converter.convertValue(context, null, null, null, "", Short.class));
 
     }
 
     public void testConvertChar() {
-        assertEquals(new Character('A'), converter.convertValue(context, "A", char.class));
-        assertEquals(new Character('Z'), converter.convertValue(context, "Z", char.class));
-        assertEquals(new Character('A'), converter.convertValue(context, "A", Character.class));
-        assertEquals(new Character('Z'), converter.convertValue(context, "Z", Character.class));
+        assertEquals('A', converter.convertValue(context, "A", char.class));
+        assertEquals('Z', converter.convertValue(context, "Z", char.class));
+        assertEquals('A', converter.convertValue(context, "A", Character.class));
+        assertEquals('Z', converter.convertValue(context, "Z", Character.class));
 
-        assertEquals(new Character('A'), converter.convertValue(context, new Character('A'), char.class));
-        assertEquals(new Character('Z'), converter.convertValue(context, new Character('Z'), char.class));
-        assertEquals(new Character('A'), converter.convertValue(context, new Character('A'), Character.class));
-        assertEquals(new Character('Z'), converter.convertValue(context, new Character('Z'), Character.class));
+        assertEquals('A', converter.convertValue(context, 'A', char.class));
+        assertEquals('Z', converter.convertValue(context, 'Z', char.class));
+        assertEquals('A', converter.convertValue(context, 'A', Character.class));
+        assertEquals('Z', converter.convertValue(context, 'Z', Character.class));
 
-        assertEquals(new Character('D'), converter.convertValue(context, "DEF", char.class));
-        assertEquals(new Character('X'), converter.convertValue(context, "XYZ", Character.class));
-        assertEquals(new Character(' '), converter.convertValue(context, " ", Character.class));
-        assertEquals(new Character(' '), converter.convertValue(context, "   ", char.class));
+        assertEquals('D', converter.convertValue(context, "DEF", char.class));
+        assertEquals('X', converter.convertValue(context, "XYZ", Character.class));
+        assertEquals(' ', converter.convertValue(context, " ", Character.class));
+        assertEquals(' ', converter.convertValue(context, "   ", char.class));
 
-        assertEquals(null, converter.convertValue(context, "", char.class));
+        assertNull(converter.convertValue(context, "", char.class));
     }
 
     public void testConvertClass() {
-        Class clazz = (Class) converter.convertValue(context, "java.util.Date", Class.class);
+        Class<?> clazz = (Class<?>) converter.convertValue(context, "java.util.Date", Class.class);
         assertEquals(Date.class.getName(), clazz.getName());
 
-        Class clazz2 = (Class) converter.convertValue(context, "com.opensymphony.xwork2.util.Bar", Class.class);
+        Class<?> clazz2 = (Class<?>) converter.convertValue(context, "com.opensymphony.xwork2.util.Bar", Class.class);
         assertEquals(Bar.class.getName(), clazz2.getName());
 
         assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, "com.opensymphony.xwork2.util.IDoNotExist", Class.class));
@@ -713,7 +715,7 @@
         assertEquals(Boolean.TRUE, converter.convertValue(context, Boolean.TRUE, Boolean.class));
         assertEquals(Boolean.FALSE, converter.convertValue(context, Boolean.FALSE, Boolean.class));
 
-        assertEquals(null, converter.convertValue(context, null, Boolean.class));
+        assertNull(converter.convertValue(context, null, Boolean.class));
         assertEquals(Boolean.TRUE, converter.convertValue(context, new Bar(), Boolean.class)); // Ognl converter will default to true
     }
 
@@ -729,7 +731,7 @@
         names.add("XWork");
         names.add("Struts");
 
-        Collection col = (Collection) converter.convertValue(context, names, Collection.class);
+        Collection<String> col = (Collection<String>) converter.convertValue(context, names, Collection.class);
         assertSame(names, col);
     }
 
@@ -752,7 +754,7 @@
         assertEquals(321, cat.getFoo().getNumber());
     }
 
-    public void testCollectionConversion() throws Exception {
+    public void testCollectionConversion() {
         // given
         String[] col1 = new String[]{"1", "2", "ble", "3"};
 
@@ -781,8 +783,7 @@
 
         converter = container.getInstance(XWorkConverter.class);
 
-        ActionContext ac = ActionContext.getContext();
-        ac.setLocale(Locale.US);
+        ActionContext ac = ActionContext.getContext().withLocale(Locale.US);
         context = ac.getContextMap();
         stack = (OgnlValueStack) ac.getValueStack();
     }
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..d809e4b 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 @@
         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.of(new HashMap<>()).bind());
         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..22a9c28 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptorTest.java
@@ -90,8 +90,6 @@
 
     /**
      * See WW-3668
-     *
-     * @throws Exception
      */
     public void testWithPreResultListenerAgainstMaliciousCode() throws Exception {
         conversionErrors.put("foo", new ConversionData("\" + #root + \"", int.class));
@@ -128,10 +126,10 @@
     }
 
     private ActionContext createActionContext() {
-        ActionContext ac = new ActionContext(stack.getContext());
-        ac.setConversionErrors(conversionErrors);
-        ac.setValueStack(stack);
-        return ac;
+        return ActionContext.of(stack.getContext())
+            .withConversionErrors(conversionErrors)
+            .withValueStack(stack)
+            .bind();
     }
 
     @Override
@@ -141,9 +139,10 @@
         mockInvocation = new Mock(ActionInvocation.class);
         invocation = (ActionInvocation) mockInvocation.proxy();
         stack = ActionContext.getContext().getValueStack();
-        context = new ActionContext(stack.getContext());
         conversionErrors = new HashMap<>();
-        context.setConversionErrors(conversionErrors);
+        context = ActionContext.of(stack.getContext())
+            .withConversionErrors(conversionErrors)
+            .bind();
         mockInvocation.matchAndReturn("getInvocationContext", context);
         mockInvocation.expect("addPreResultListener", C.isA(PreResultListener.class));
         mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
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..d2a0f83 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.InterceptorConfig;
 import com.opensymphony.xwork2.validator.ValidationInterceptor;
 import org.easymock.EasyMock;
-import org.easymock.IAnswer;
 
 import java.util.HashMap;
 
@@ -37,12 +36,8 @@
 
     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,31 +166,26 @@
     @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.setActionInvocation(invocation);
+        ActionContext.of(new HashMap<>())
+            .withActionInvocation(invocation)
+            .bind();
     }
 
     @Override
@@ -206,7 +196,7 @@
     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..d14b2b8 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 @@
         stack = ActionContext.getContext().getValueStack();
         mockInvocation = new Mock(ActionInvocation.class);
         mockInvocation.expectAndReturn("getStack", stack);
-        mockInvocation.expectAndReturn("getInvocationContext", new ActionContext(new HashMap<>()));
+        mockInvocation.expectAndReturn("getInvocationContext", ActionContext.of(new HashMap<>()).bind());
         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 @@
  */
 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 @@
             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..928d13f 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 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.of(contextMap).bind();
+
+        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/ScopedModelDrivenInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java
index b243237..6938d30 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java
@@ -18,7 +18,12 @@
  */
 package com.opensymphony.xwork2.interceptor;
 
-import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.ProxyObjectFactory;
+import com.opensymphony.xwork2.SimpleAction;
+import com.opensymphony.xwork2.XWorkTestCase;
 import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
 import com.opensymphony.xwork2.mock.MockActionProxy;
@@ -31,7 +36,7 @@
 public class ScopedModelDrivenInterceptorTest extends XWorkTestCase {
 
     protected ScopedModelDrivenInterceptor inter = null;
-    
+
     /**
      * Set up instance variables required by this test case.
      */
@@ -45,24 +50,23 @@
     }
 
     public void testResolveModel() throws Exception {
-        ActionContext ctx = ActionContext.getContext();
-        ctx.setSession(new HashMap<String, Object>());
+        ActionContext ctx = ActionContext.getContext().withSession(new HashMap<>());
 
         ObjectFactory factory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
         Object obj = inter.resolveModel(factory, ctx, "java.lang.String", "request", "foo");
         assertNotNull(obj);
         assertTrue(obj instanceof String);
-        assertTrue(obj == ctx.get("foo"));
+        assertSame(obj, ctx.get("foo"));
 
         obj = inter.resolveModel(factory, ctx, "java.lang.String", "session", "foo");
         assertNotNull(obj);
         assertTrue(obj instanceof String);
-        assertTrue(obj == ctx.getSession().get("foo"));
+        assertSame(obj, ctx.getSession().get("foo"));
 
         obj = inter.resolveModel(factory, ctx, "java.lang.String", "session", "foo");
         assertNotNull(obj);
         assertTrue(obj instanceof String);
-        assertTrue(obj == ctx.getSession().get("foo"));
+        assertSame(obj, ctx.getSession().get("foo"));
     }
 
     public void testScopedModelDrivenAction() throws Exception {
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..79a126b 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.validator.ValidationInterceptor;
 import org.junit.Assert;
 import org.easymock.EasyMock;
-import org.easymock.IAnswer;
 
 import java.util.HashMap;
 
@@ -73,20 +72,12 @@
         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,9 @@
         EasyMock.replay(action);
         EasyMock.replay(proxy);
 
-        ActionContext contex = new ActionContext(new HashMap<String, Object>());
-        ActionContext.setContext(contex);
-        contex.setActionInvocation(invocation);
+        ActionContext.of(new HashMap<>())
+            .withActionInvocation(invocation)
+            .bind();
     }
 
     @Override
@@ -109,7 +100,7 @@
     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..7e39be0 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,91 @@
  */
 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";
+        method = "save";
+        result = Action.INPUT;
 
-		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>());
+        ValidationInterceptor interceptor = create();
+        String result = interceptor.intercept(invocation);
+
+        assertEquals(Action.INPUT, result);
     }
-	
-	private interface ValidateAction extends Action, Validateable, ValidationAware {
-		void validateDoSave();
-		void validateSubmit();
-		String submit();
+
+    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 {
+        @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.of(new HashMap<>())
+            .withActionInvocation(invocation)
+            .bind();
     }
 }
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..ead2045 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 @@
 /**
  * @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));
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
 
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
 
-		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());
-		
-	}
+        Action action = new BlockingByDefaultAction();
+        stack.push(action);
 
-	/**
-	 * "name" should be removed from the map, as it is blocked.
-	 * All other parameters should remain
-	 * @throws Exception
-	 */
-	public void testAllowingByDefault() throws Exception {
+        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);
 
-		Map<String, Object> contextMap = new HashMap<>();
+        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.of(new HashMap<>()).bind();
+        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");
 
-		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));
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
+        parameterMap.put("m1", "s1");
+        parameterMap.put("m2", "s2");
 
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
+        stack.push(new BlockingByDefaultModel());
 
-		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());
-		
-	}
+        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);
 
-	/**
-	 * Only "name" should remain in the parameter map.  All others
-	 * should be removed
-	 * @throws Exception
-	 */
-	public void testBlockingByDefaultWithModel() throws Exception {
+        ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
 
-		Map<String, Object> contextMap = new HashMap<>();
+        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 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));
+        parameterMap.put("job", "Baker");
+        parameterMap.put("name", "Martin");
+        parameterMap.put("m1", "s1");
+        parameterMap.put("m2", "s2");
 
-		ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
-		
-		AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
-		interceptor.intercept(invocation);
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
+        actionContext.setParameters(HttpParameters.create(parameterMap).build());
+        stack.push(new AllowingByDefaultModel());
 
-		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());
-		
-	}
+        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);
 
-	/**
-	 * "name" should be removed from the map, as it is blocked.
-	 * All other parameters should remain
-	 * @throws Exception
-	 */
-	public void testAllowingByDefaultWithModel() throws Exception {
+        ActionInvocation invocation = (ActionInvocation) mockInvocation.proxy();
 
-		Map<String, Object> contextMap = new HashMap<>();
-		Map<String, Object> parameterMap = new HashMap<>();
-		
-		parameterMap.put("job", "Baker");
-		parameterMap.put("name", "Martin");
-		parameterMap.put("m1", "s1");
-		parameterMap.put("m2", "s2");
+        AnnotationParameterFilterInterceptor interceptor = new AnnotationParameterFilterInterceptor();
+        interceptor.intercept(invocation);
 
-		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));
+        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());
 
-		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/ognl/OgnlUtilTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java
index 707b0e5..7641dcc 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java
@@ -480,7 +480,7 @@
         Map<String, Object> props = new HashMap<>();
         props.put("birthday", "02/12/1982");
         // US style test
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         ognlUtil.setProperties(props, foo, context);
 
         Calendar cal = Calendar.getInstance(Locale.US);
@@ -519,7 +519,7 @@
             .format(meetingTime);
         props.put("meeting", formatted);
 
-        context.put(ActionContext.LOCALE, Locale.UK);
+        context = ActionContext.of(context).withLocale(Locale.UK).getContextMap();
 
         ognlUtil.setProperties(props, foo, context);
 
@@ -529,7 +529,7 @@
 
         //test RFC 3339 date format for JSON
         props.put("event", "1996-12-19T16:39:57Z");
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         ognlUtil.setProperties(props, foo, context);
 
         cal = Calendar.getInstance(Locale.US);
@@ -545,7 +545,7 @@
 
         //test setting a calendar property
         props.put("calendar", "1996-12-19T16:39:57Z");
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         ognlUtil.setProperties(props, foo, context);
         assertEquals(cal, foo.getCalendar());
     }
diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java
index e84956b..b5704c3 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java
@@ -20,6 +20,7 @@
 
 import com.opensymphony.xwork2.*;
 import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.conversion.impl.ConversionData;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.ContainerBuilder;
 import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
@@ -849,11 +850,11 @@
             assertTrue(true);
         }
 
-        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertTrue(conversionErrors.containsKey("bar"));
     }
 
-    public void testPrimitiveSettingWithInvalidValueAddsFieldErrorInNonDevMode() throws Exception {
+    public void testPrimitiveSettingWithInvalidValueAddsFieldErrorInNonDevMode() {
         SimpleAction action = new SimpleAction();
         OgnlValueStack stack = createValueStack();
         stack.getContext().put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);
@@ -861,7 +862,7 @@
         stack.push(action);
         stack.setValue("bar", "3x");
 
-        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertTrue(conversionErrors.containsKey("bar"));
     }
 
@@ -879,7 +880,7 @@
             // expected
         }
 
-        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertTrue(conversionErrors.containsKey("bean"));
         assertNotNull(action.getBean());
     }
@@ -978,8 +979,8 @@
         vs.setValue("animalMap[6].name", "Cat Six by interface");
         assertNotNull(foo.getAnimalMap());
         assertEquals(2, foo.getAnimalMap().size());
-        assertEquals("Cat Three by interface", foo.getAnimalMap().get(new Long(3)).getName());
-        assertEquals("Cat Six by interface", foo.getAnimalMap().get(new Long(6)).getName());
+        assertEquals("Cat Three by interface", foo.getAnimalMap().get(3L).getName());
+        assertEquals("Cat Six by interface", foo.getAnimalMap().get(6L).getName());
 
         vs.setValue("annotatedCats[0].name", "Cat One By Annotation");
         vs.setValue("annotatedCats[1].name", "Cat Two By Annotation");
@@ -1057,7 +1058,7 @@
 
         vs.setValue("male", "false");
 
-        assertEquals(false, dog.isMale());
+        assertFalse(dog.isMale());
     }
 
     public void testStatics() {
@@ -1074,7 +1075,7 @@
         assertEquals("Canine", vs.findValue("@vs@SCIENTIFIC_NAME"));
         assertEquals("Canine", vs.findValue("@vs1@SCIENTIFIC_NAME"));
         assertEquals("Feline", vs.findValue("@vs2@SCIENTIFIC_NAME"));
-        assertEquals(new Integer(BigDecimal.ROUND_HALF_DOWN), vs.findValue("@java.math.BigDecimal@ROUND_HALF_DOWN"));
+        assertEquals(BigDecimal.ROUND_HALF_DOWN, vs.findValue("@java.math.BigDecimal@ROUND_HALF_DOWN"));
         assertNull(vs.findValue("@vs3@BLAH"));
         assertNull(vs.findValue("@com.nothing.here.Nothing@BLAH"));
     }
@@ -1177,7 +1178,7 @@
             // expected
         }
 
-        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
+        Map<String, ConversionData> conversionErrors = stack.getActionContext().getConversionErrors();
         assertTrue(conversionErrors.containsKey("count"));
     }
 
@@ -1202,11 +1203,11 @@
         stack.push("Hello World");
 
         assertEquals("Hello World", stack.findValue("top"));
-        assertEquals(null, stack.findValue(null));
+        assertNull(stack.findValue(null));
 
         stack.setDefaultType(Integer.class);
-        stack.push(new Integer(123));
-        assertEquals(new Integer(123), stack.findValue("top"));
+        stack.push(123);
+        assertEquals(123, stack.findValue("top"));
     }
 
     public void testFindString() {
@@ -1215,11 +1216,11 @@
         stack.push("Hello World");
 
         assertEquals("Hello World", stack.findString("top"));
-        assertEquals(null, stack.findString(null));
+        assertNull(stack.findString(null));
     }
 
     public void testExpOverrides() {
-        Map overrides = new HashMap();
+        Map<Object, Object> overrides = new HashMap<>();
         overrides.put("claus", "top");
 
         OgnlValueStack stack = createValueStack();
@@ -1236,7 +1237,7 @@
 
         stack.getContext().put("santa", "Hello Santa");
         assertEquals("Hello Santa", stack.findValue("santa", String.class));
-        assertEquals(null, stack.findValue("unknown", String.class));
+        assertNull(stack.findValue("unknown", String.class));
     }
 
     public void testWarnAboutInvalidProperties() {
@@ -1247,16 +1248,16 @@
 
         // how to test the warning was logged?
         assertEquals("Don", stack.findValue("name", String.class));
-        assertEquals(null, stack.findValue("address", String.class));
+        assertNull(stack.findValue("address", String.class));
         // should log warning
-        assertEquals(null, stack.findValue("address.invalidProperty", String.class));
+        assertNull(stack.findValue("address.invalidProperty", String.class));
 
         // if country is null, OGNL throws an exception
         /*action.setAddress(new Address());
         stack.push(action);*/
         // should log warning
-        assertEquals(null, stack.findValue("address.country.id", String.class));
-        assertEquals(null, stack.findValue("address.country.name", String.class));
+        assertNull(stack.findValue("address.country.id", String.class));
+        assertNull(stack.findValue("address.country.name", String.class));
     }
 
    /**
@@ -1498,7 +1499,7 @@
         ognlUtil = container.getInstance(OgnlUtil.class);
     }
 
-    class BadJavaBean {
+    static class BadJavaBean {
         private int count;
         private int count2;
 
@@ -1519,7 +1520,7 @@
         }
     }
 
-    class MyAction {
+    static class MyAction {
         private Long id;
         private String name;
         private Address address;
@@ -1549,7 +1550,7 @@
         }
     }
 
-    class Address {
+    static class Address {
         private String address;
         private Country country;
         private String city;
@@ -1579,7 +1580,7 @@
         }
     }
 
-    class Country {
+    static class Country {
         private String iso;
         private String name;
         private String displayName;
@@ -1609,7 +1610,7 @@
         }
     }
 
-    class TestAppender extends AbstractAppender {
+    static class TestAppender extends AbstractAppender {
         List<LogEvent> logEvents = new ArrayList<>();
 
         TestAppender() {
@@ -1625,4 +1626,4 @@
 
 enum MyNumbers {
     ONE, TWO, THREE
-}
\ No newline at end of file
+}
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..afcd262 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,13 @@
     }
 
     protected void loadSpringApplicationContextIntoApplication(ApplicationContext appContext) {
-        Map<Object, Object> application = new HashMap<>();
+        Map<String, 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.of(context)
+            .withApplication(application)
+            .bind();
     }
 
     public void testLoadsApplicationContextUsingWebApplicationContextUtils() throws Exception {
@@ -105,10 +105,9 @@
     }
 
     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.of(new HashMap<>())
+            .withApplication(new HashMap<>())
+            .bind();
 
         ActionAutowiringInterceptor interceptor = new ActionAutowiringInterceptor();
         interceptor.init();
diff --git a/core/src/test/java/com/opensymphony/xwork2/util/StrutsLocalizedTextProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/util/StrutsLocalizedTextProviderTest.java
index 7429082..251217a 100644
--- a/core/src/test/java/com/opensymphony/xwork2/util/StrutsLocalizedTextProviderTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/util/StrutsLocalizedTextProviderTest.java
@@ -63,8 +63,9 @@
 		
 		Mock mockActionInvocation = new Mock(ActionInvocation.class);
         mockActionInvocation.expectAndReturn("getAction", action);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
-		ActionContext.getContext().getValueStack().push(action);
+        ActionContext.getContext()
+            .withActionInvocation((ActionInvocation) mockActionInvocation.proxy())
+		    .getValueStack().push(action);
 		
 		String message = action.getText("barObj.title");
 		assertEquals("Title:", message);
@@ -92,7 +93,7 @@
 
         Mock mockActionInvocation = new Mock(ActionInvocation.class);
         mockActionInvocation.expectAndReturn("getAction", action);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
+        ActionContext.getContext().withActionInvocation((ActionInvocation) mockActionInvocation.proxy());
         ActionContext.getContext().getValueStack().push(action);
         ActionContext.getContext().getValueStack().push(action.getModel());
 
@@ -112,8 +113,9 @@
 
         Mock mockActionInvocation = new Mock(ActionInvocation.class);
         mockActionInvocation.expectAndReturn("getAction", action);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
-        ActionContext.getContext().getValueStack().push(action);
+        ActionContext.getContext()
+            .withActionInvocation((ActionInvocation) mockActionInvocation.proxy())
+            .getValueStack().push(action);
 
         String message = action.getText("bean.name");
         String foundBean2 = action.getText("bean2.name");
@@ -163,7 +165,7 @@
         Mock mockActionInvocation = new Mock(ActionInvocation.class);
         mockActionInvocation.expectAndReturn("hashCode", 0);
         mockActionInvocation.expectAndReturn("getAction", action);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
+        ActionContext.getContext().withActionInvocation((ActionInvocation) mockActionInvocation.proxy());
         ActionContext.getContext().getValueStack().push(action);
         ActionContext.getContext().getValueStack().push(action.getModel());
 
@@ -175,7 +177,7 @@
         Action action = new ModelDrivenAction2();
         Mock mockActionInvocation = new Mock(ActionInvocation.class);
         mockActionInvocation.expectAndReturn("getAction", action);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
+        ActionContext.getContext().withActionInvocation((ActionInvocation) mockActionInvocation.proxy());
 
         String message = localizedTextProvider.findText(ModelDrivenAction2.class, "test.foo", Locale.getDefault());
         assertEquals("Foo!", message);
@@ -186,7 +188,7 @@
 
         Mock mockActionInvocation = new Mock(ActionInvocation.class);
         mockActionInvocation.expectAndReturn("getAction", action);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
+        ActionContext.getContext().withActionInvocation((ActionInvocation) mockActionInvocation.proxy());
 
         String message = localizedTextProvider.findText(ModelDrivenAction2.class, "package.properties", Locale.getDefault());
         assertEquals("It works!", message);
@@ -325,7 +327,7 @@
 
         localizedTextProvider = container.getInstance(LocalizedTextProvider.class);
         
-        ActionContext.getContext().setLocale(Locale.US);
+        ActionContext.getContext().withLocale(Locale.US);
     }
 
     @Override
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManagerTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManagerTest.java
index 62a5e1e..e915de0 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManagerTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManagerTest.java
@@ -82,7 +82,7 @@
         EasyMock.replay(invocation);
         EasyMock.replay(proxy);
 
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
 
         tpf = container.getInstance(TextProviderFactory.class);
     }
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilderTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilderTest.java
index 4d6fddd..6ce0225 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilderTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilderTest.java
@@ -336,7 +336,7 @@
         });
 
         // ActionContext is destroyed during rebuilding configuration
-        ActionContext.getContext().setLocale(locale);
+        ActionContext.getContext().withLocale(locale);
 
         ActionInvocation invocation = new DefaultActionInvocation(ActionContext.getContext().getContextMap(), true);
         container.inject(invocation);
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..913fa9b 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/ConversionErrorFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/ConversionErrorFieldValidatorTest.java
@@ -49,11 +49,13 @@
     public void setUp() throws Exception {
         super.setUp();
         ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext context = new ActionContext(stack.getContext());
 
         Map<String, ConversionData> conversionErrors = new HashMap<>();
         conversionErrors.put("foo", new ConversionData("bar", Integer.class));
-        context.setConversionErrors(conversionErrors);
+        ActionContext.of(stack.getContext())
+            .withConversionErrors(conversionErrors)
+            .bind();
+
         validator = new ConversionErrorFieldValidator();
         validationAware = new ValidationAwareSupport();
 
@@ -71,17 +73,17 @@
         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..b320feb 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,9 @@
         actionValidatorManager.setValidatorFactory((ValidatorFactory)mockValidatorFactory.proxy());
 
         stubValueStack = new StubValueStack();
-        ActionContext.setContext(new ActionContext(new HashMap<String, Object>()));
-        ActionContext.getContext().setValueStack(stubValueStack);
+        ActionContext.of(new HashMap<>())
+            .withValueStack(stubValueStack)
+            .bind();
 
         DefaultFileManagerFactory factory = new DefaultFileManagerFactory();
         factory.setContainer(container);
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/DoubleRangeFieldValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/DoubleRangeFieldValidatorTest.java
index c71a013..de9a944 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/DoubleRangeFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/DoubleRangeFieldValidatorTest.java
@@ -99,7 +99,6 @@
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.push(prod);
-        ActionContext.getContext().setValueStack(stack);
 
         val.setMinInclusive(0d);
         val.setMaxInclusive(10d);
@@ -115,7 +114,6 @@
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.push(prod);
-        ActionContext.getContext().setValueStack(stack);
 
         val.setMinInclusive(0d);
         val.setMaxInclusive(30d);
@@ -129,7 +127,6 @@
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.push(prod);
-        ActionContext.getContext().setValueStack(stack);
 
         val.setMinInclusive(0d);
         val.setMaxInclusive(10d);
@@ -151,7 +148,6 @@
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.push(prod);
-        ActionContext.getContext().setValueStack(stack);
 
         val.setFieldName("price");
 
@@ -160,7 +156,7 @@
 
         val.setMaxInclusive(9.95d);
         val.validate(prod); // should pass
-        assertTrue(!context.hasErrors());
+        assertFalse(context.hasErrors());
         assertEquals(9.95d, val.getMaxInclusive());
 
         val.setMaxExclusive(9.95d);
@@ -176,7 +172,6 @@
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.push(prod);
-        ActionContext.getContext().setValueStack(stack);
 
         val.setFieldName("price");
 
@@ -185,7 +180,7 @@
 
         val.setMinInclusive(9.95d);
         val.validate(prod); // should pass
-        assertTrue(!context.hasErrors());
+        assertFalse(context.hasErrors());
 
         val.setMinExclusive(9.95d);
         val.validate(prod); // should not pass
@@ -193,9 +188,6 @@
     }
 
     public void testNoValue() throws Exception {
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         val.setFieldName("price");
 
         DelegatingValidatorContext context = new DelegatingValidatorContext(new ValidationAwareSupport(), tpf);
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/ExpressionValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/ExpressionValidatorTest.java
index ac4ed2a..500824c 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/ExpressionValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/ExpressionValidatorTest.java
@@ -140,7 +140,7 @@
         EasyMock.replay(invocation);
         EasyMock.replay(proxy);
 
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
 
         tpf = container.getInstance(TextProviderFactory.class);
     }
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/RegexFieldValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/RegexFieldValidatorTest.java
index b5495d2..df7f505 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/RegexFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/RegexFieldValidatorTest.java
@@ -53,9 +53,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setUsername("Secret");
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("^Sec.*");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
@@ -73,9 +70,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setUsername("Secret "); // must end with one whitespace
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setTrim(false);
         validator.setRegex("^Sec.*\\s");
@@ -94,9 +88,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setUsername("Superman");
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("^Sec.*");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
@@ -108,7 +99,7 @@
         assertTrue(validator.getValidatorContext().hasFieldErrors());
         List<String> msgs = validator.getValidatorContext().getFieldErrors().get("username");
         assertNotNull(msgs);
-        assertTrue(msgs.size() == 1); // should contain 1 error message
+        assertEquals(1, msgs.size()); // should contain 1 error message
 
         // when failing the validator will not add action errors/msg
         assertFalse(validator.getValidatorContext().hasActionErrors());
@@ -119,9 +110,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setUsername("NoExpression");
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("^Sec.*");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
@@ -135,26 +123,23 @@
         assertFalse(validator.getValidatorContext().hasFieldErrors());
     }
 
-    public void testGetExpression() throws Exception {
+    public void testGetExpression() {
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("^Hello.*");
         assertEquals("^Hello.*", validator.getRegex());
     }
 
-    public void testIsTrimmed() throws Exception {
+    public void testIsTrimmed() {
         RegexFieldValidator validator = new RegexFieldValidator();
-        assertEquals(true, validator.isTrimed());
+        assertTrue(validator.isTrimed());
         validator.setTrim(false);
-        assertEquals(false, validator.isTrimed());
+        assertFalse(validator.isTrimed());
     }
 
     public void testEmptyName() throws Exception {
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setUsername("");
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("^Sec.*");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
@@ -172,9 +157,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setAge(33);
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("[0-9][0-9]");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
@@ -192,9 +174,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setFriends(new String[]{"Alice", "Matt"});
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("A([a-zA-Z]*)");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
@@ -217,9 +196,6 @@
         MyTestPerson testPerson = new MyTestPerson();
         testPerson.setCars(Arrays.asList("Audi", "BMW"));
 
-        ValueStack stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
         RegexFieldValidator validator = new RegexFieldValidator();
         validator.setRegex("A([a-zA-Z]*)");
         validator.setValidatorContext(new DummyValidatorContext(new Object(), tpf));
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/RepopulateConversionErrorFieldValidatorSupportTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/RepopulateConversionErrorFieldValidatorSupportTest.java
index be49dcd..bcb0281 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/RepopulateConversionErrorFieldValidatorSupportTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/RepopulateConversionErrorFieldValidatorSupportTest.java
@@ -88,8 +88,7 @@
 		ValueStack stack = ActionContext.getContext().getValueStack();
 		MockActionInvocation invocation = new MockActionInvocation();
 		invocation.setStack(stack);
-		ActionContext.getContext().setValueStack(stack);
-		ActionContext.getContext().setActionInvocation(invocation);
+		ActionContext.getContext().withActionInvocation(invocation);
 		
 		String[] conversionErrorValue = new String[] { "some value" };
 		Map<String, ConversionData> conversionErrors = ActionContext.getContext().getConversionErrors();
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..c866ee4 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 @@
             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.of(new HashMap<>()).bind();
 
             params.put("bar", "42");
             extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
@@ -124,8 +124,8 @@
         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);
+            stack.getActionContext().withLocale(Locale.US);
+
             proxy.execute();
             assertTrue(((ValidationAware) proxy.getAction()).hasFieldErrors());
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/StringValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/StringValidatorTest.java
index 27f695a..0aa41cd 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/StringValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/StringValidatorTest.java
@@ -218,7 +218,7 @@
         EasyMock.replay(invocation);
         EasyMock.replay(proxy);
 
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
 
         tpf = container.getInstance(TextProviderFactory.class);
     }
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..d88fa27 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorModelTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorModelTest.java
@@ -61,8 +61,7 @@
         EasyMock.replay(invocation);
         EasyMock.replay(proxy);
 
-        ActionContext.getContext().setActionInvocation(invocation);
-
+        ActionContext.getContext().withActionInvocation(invocation);
     }
 
     public void testModelFieldErrorsAddedWithoutFieldPrefix() throws Exception {
@@ -122,6 +121,6 @@
     @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 0b64968..9f4f0cb 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java
@@ -18,19 +18,27 @@
  */
 package com.opensymphony.xwork2.validator;
 
-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.TestBean;
+import com.opensymphony.xwork2.XWorkTestCase;
 import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.conversion.impl.ConversionData;
-
 import org.easymock.EasyMock;
 
-import java.util.*;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * VisitorFieldValidatorTest
  *
  * @author Jason Carreira
- *         Created Aug 4, 2003 1:26:01 AM
+ * Created Aug 4, 2003 1:26:01 AM
  */
 public class VisitorFieldValidatorTest extends XWorkTestCase {
 
@@ -44,7 +52,7 @@
         action = container.inject(VisitorValidatorTestAction.class);
 
         TestBean bean = action.getBean();
-        Calendar cal = new GregorianCalendar(1900, 1, 1);
+        Calendar cal = new GregorianCalendar(1900, Calendar.FEBRUARY, 1);
         bean.setBirth(cal.getTime());
         bean.setCount(-1);
 
@@ -57,12 +65,12 @@
         EasyMock.expect(invocation.invoke()).andReturn(Action.SUCCESS).anyTimes();
         EasyMock.expect(proxy.getMethod()).andReturn("execute").anyTimes();
         EasyMock.expect(proxy.getConfig()).andReturn(config).anyTimes();
-        
+
 
         EasyMock.replay(invocation);
         EasyMock.replay(proxy);
 
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
     }
 
     public void testArrayValidation() throws Exception {
@@ -107,8 +115,8 @@
     }
 
     public void testCollectionValidation() throws Exception {
-        List testBeanList = action.getTestBeanList();
-        TestBean testBean = (TestBean) testBeanList.get(0);
+        List<TestBean> testBeanList = action.getTestBeanList();
+        TestBean testBean = testBeanList.get(0);
         testBean.setName("foo");
         validate("validateList");
 
@@ -141,7 +149,7 @@
         assertEquals(3, fieldErrors.size());
         assertTrue(fieldErrors.containsKey("bean.count"));
         assertTrue(fieldErrors.containsKey("bean.name"));
-        assertTrue(!fieldErrors.containsKey("bean.birth"));
+        assertFalse(fieldErrors.containsKey("bean.birth"));
 
         //the error from the action should be there too
         assertTrue(fieldErrors.containsKey("context"));
@@ -153,7 +161,7 @@
 
         Map<String, List<String>> fieldErrors = action.getFieldErrors();
         assertEquals(3, fieldErrors.size());
-        assertTrue(!fieldErrors.containsKey("bean.count"));
+        assertFalse(fieldErrors.containsKey("bean.count"));
         assertTrue(fieldErrors.containsKey("bean.name"));
         assertTrue(fieldErrors.containsKey("bean.birth"));
 
@@ -167,13 +175,13 @@
 
         Map<String, List<String>> fieldErrors = action.getFieldErrors();
         assertEquals(5, fieldErrors.size());
-        assertTrue(!fieldErrors.containsKey("bean.count"));
+        assertFalse(fieldErrors.containsKey("bean.count"));
         assertTrue(fieldErrors.containsKey("bean.name"));
         assertTrue(fieldErrors.containsKey("bean.birth"));
 
         assertTrue(fieldErrors.containsKey("bean.child.name"));
         assertTrue(fieldErrors.containsKey("bean.child.birth"));
-        
+
         //the error from the action should be there too
         assertTrue(fieldErrors.containsKey("context"));
     }
@@ -182,14 +190,14 @@
         //add conversion error
         Map<String, ConversionData> conversionErrors = new HashMap<>();
         conversionErrors.put("bean.child.count", new ConversionData("bar", Integer.class));
-        ActionContext.getContext().setConversionErrors(conversionErrors);
+        ActionContext.getContext().withConversionErrors(conversionErrors);
 
         validate("visitorChildValidation");
         assertTrue(action.hasFieldErrors());
 
         Map<String, List<String>> fieldErrors = action.getFieldErrors();
         assertEquals(6, fieldErrors.size());
-        assertTrue(!fieldErrors.containsKey("bean.count"));
+        assertFalse(fieldErrors.containsKey("bean.count"));
         assertTrue(fieldErrors.containsKey("bean.name"));
         assertTrue(fieldErrors.containsKey("bean.birth"));
 
@@ -206,12 +214,11 @@
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
-        ActionContext.setContext(null);
+        ActionContext.clear();
     }
 
     private void validate(String context) throws ValidationException {
-        ActionContext actionContext = ActionContext.getContext();
-        actionContext.setName(context);
+        ActionContext.getContext().withActionName(context);
         container.getInstance(ActionValidatorManager.class).validate(action, context);
     }
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidatorTest.java
index 57e5475..de564c6 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidatorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidatorTest.java
@@ -44,7 +44,7 @@
         validator.validate(action);
 
         // then
-        assertTrue(context.getFieldErrors().size() == 0);
+        assertEquals(0, context.getFieldErrors().size());
     }
 
     public void testMinValidation() throws Exception {
@@ -53,11 +53,13 @@
         ValidatorContext context = new DummyValidatorContext(action, tpf);
         DateRangeFieldValidator validator = prepareValidator(action, context);
 
+        System.out.println(ActionContext.getContext().getLocale());
+
         // when
         validator.validate(action);
 
         // then
-        assertTrue(context.getFieldErrors().size() == 1);
+        assertEquals(1, context.getFieldErrors().size());
         assertEquals("Max is 12.12.13, min is 01.01.13 but value is 03.03.12", context.getFieldErrors().get("dateRange").get(0));
     }
 
@@ -71,7 +73,7 @@
         validator.validate(action);
 
         // then
-        assertTrue(context.getFieldErrors().size() == 1);
+        assertEquals(1, context.getFieldErrors().size());
         assertEquals("Max is 12.12.13, min is 01.01.13 but value is 04.04.14", context.getFieldErrors().get("dateRange").get(0));
     }
 
@@ -91,6 +93,7 @@
 
     private DateRangeFieldValidator prepareValidator(ValidationAction action, ValidatorContext context) {
         ValueStack valueStack = container.getInstance(ValueStackFactory.class).createValueStack();
+        valueStack.getActionContext().withLocale(new Locale("de"));
         valueStack.push(action);
 
         DateRangeFieldValidator validator = new DateRangeFieldValidator();
@@ -108,7 +111,6 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        ActionContext.getContext().setLocale(new Locale("DE"));
         tpf = container.getInstance(TextProviderFactory.class);
     }
 
diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/validators/ValidatorSupportTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/validators/ValidatorSupportTest.java
index 42a6b2f..c1b7f18 100644
--- a/core/src/test/java/com/opensymphony/xwork2/validator/validators/ValidatorSupportTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/validator/validators/ValidatorSupportTest.java
@@ -21,36 +21,26 @@
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.XWorkTestCase;
 import com.opensymphony.xwork2.ognl.OgnlValueStack;
-import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.ValueStackFactory;
 import com.opensymphony.xwork2.validator.ValidationException;
 
-/**
- * @author tmjee
- * @version $Date$ $Id$
- */
 public class ValidatorSupportTest extends XWorkTestCase {
 
-	public void testConditionalParseExpression()  throws Exception {
-		ValueStack oldStack = ActionContext.getContext().getValueStack();
-		try {
-			OgnlValueStack stack = (OgnlValueStack) container.getInstance(ValueStackFactory.class).createValueStack();
-			stack.getContext().put(ActionContext.CONTAINER, container);
-			stack.getContext().put("something", "somevalue");
-			ActionContext.getContext().setValueStack(stack);
-			ValidatorSupport validator = new ValidatorSupport() {
-				public void validate(Object object) throws ValidationException {
-				}
-			};
-            validator.setValueStack(ActionContext.getContext().getValueStack());
+    public void testConditionalParseExpression() {
+        OgnlValueStack stack = (OgnlValueStack) container.getInstance(ValueStackFactory.class).createValueStack();
+        stack.getContext().put("something", "somevalue");
 
-			String result1 = validator.parse("${#something}", String.class).toString();
+        ActionContext.of(stack.getContext()).withContainer(container).bind();
 
-			assertEquals(result1, "somevalue");
-		}
-		finally {
-			ActionContext.getContext().setValueStack(oldStack);
-		}
-	}
+        ValidatorSupport validator = new ValidatorSupport() {
+            public void validate(Object object) throws ValidationException {
+            }
+        };
+        validator.setValueStack(ActionContext.getContext().getValueStack());
+
+        String result1 = validator.parse("${#something}", String.class).toString();
+
+        assertEquals(result1, "somevalue");
+    }
 
 }
diff --git a/core/src/test/java/org/apache/struts2/ServletActionContextTest.java b/core/src/test/java/org/apache/struts2/ServletActionContextTest.java
index bd748c6..2148b1c 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 @@
  */
 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 @@
         extraContext.put(HTTP_RESPONSE, response);
         extraContext.put(SERVLET_CONTEXT, servletContext);
 
-        actionContext = new ActionContext(extraContext);
-        ServletActionContext.setContext(actionContext);
+        actionContext = ActionContext.of(extraContext).bind();
     }
 
     public void testContextParams() {
@@ -66,7 +64,7 @@
     }
 
     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/components/ComponentTest.java b/core/src/test/java/org/apache/struts2/components/ComponentTest.java
index 70f9114..722f6be 100644
--- a/core/src/test/java/org/apache/struts2/components/ComponentTest.java
+++ b/core/src/test/java/org/apache/struts2/components/ComponentTest.java
@@ -430,7 +430,7 @@
 
 
     public void testI18nComponentDisposeItselfFromComponentStack() throws Exception {
-        stack.getContext().put(ActionContext.LOCALE, Locale.getDefault());
+        stack.getActionContext().withLocale(Locale.getDefault());
 
         TextFieldTag t = new TextFieldTag();
         t.setPageContext(pageContext);
diff --git a/core/src/test/java/org/apache/struts2/components/FormTest.java b/core/src/test/java/org/apache/struts2/components/FormTest.java
index 1442f96..649747a 100644
--- a/core/src/test/java/org/apache/struts2/components/FormTest.java
+++ b/core/src/test/java/org/apache/struts2/components/FormTest.java
@@ -110,6 +110,6 @@
 
         ((DefaultActionMapper) container.getInstance(ActionMapper.class)).setAllowDynamicMethodCalls("true");
         
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/components/UIComponentTest.java b/core/src/test/java/org/apache/struts2/components/UIComponentTest.java
index addd7e2..65df34e 100644
--- a/core/src/test/java/org/apache/struts2/components/UIComponentTest.java
+++ b/core/src/test/java/org/apache/struts2/components/UIComponentTest.java
@@ -245,7 +245,6 @@
     public void testFormComponentDisposeItselfFromComponentStack() throws Exception {
         configurationManager.clearContainerProviders();
         configurationManager.addContainerProvider(new TestConfigurationProvider());
-        ActionContext.getContext().setValueStack(stack);
 
         request.setupGetServletPath("/testAction");
 
diff --git a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
index 1669fc6..c336fc6 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
@@ -368,7 +368,7 @@
         Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
 
         MockActionInvocation mai = new MockActionInvocation();
-        ActionContext.getContext().setActionInvocation(mai);
+        ActionContext.getContext().withActionInvocation(mai);
 
         MockActionProxy actionProxy = new MockActionProxy();
         actionProxy.setInvocation(mai);
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 @@
 
         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 @@
     }
 
     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 @@
                 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..2928648 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 @@
 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 @@
 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.of(new HashMap<>()).bind();
+        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 @@
  */
 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 @@
         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..08c034b 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java
@@ -18,7 +18,12 @@
  */
 package org.apache.struts2.interceptor;
 
-import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.DefaultActionProxyFactory;
+import com.opensymphony.xwork2.ObjectFactory;
 import com.opensymphony.xwork2.config.Configuration;
 import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.config.ConfigurationProvider;
@@ -52,9 +57,9 @@
 
     private StrutsMockHttpServletRequest request;
     private HttpSession httpSession;
-    private Map context;
-    private Map params;
-    private Map session;
+    private Map<String, Object> context;
+    private Map<String, Object> params;
+    private Map<String, Object> session;
     private ExecuteAndWaitInterceptor waitInterceptor;
     private ParametersInterceptor parametersInterceptor;
 
@@ -175,13 +180,13 @@
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(session);//WW-4900 action1 and invocation are 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();
-        context.put(ActionContext.SESSION, session);
+        session = (Map<String, Object>) ois.readObject();
+        context = ActionContext.of(context).withSession(session).getContextMap();
         ois.close();
         bais.close();
 
@@ -203,31 +208,35 @@
     }
 
     protected void setUp() throws Exception {
+        super.setUp();
         loadConfigurationProviders(new WaitConfigurationProvider());
 
-        session = new HashMap();
-        params = new HashMap();
-        context = new HashMap();
-        context.put(ActionContext.SESSION, session);
-        context.put(ActionContext.PARAMETERS, HttpParameters.create().build());
+        session = new HashMap<>();
+        params = new HashMap<>();
+        context = new HashMap<>();
 
         request = new StrutsMockHttpServletRequest();
         httpSession = new StrutsMockHttpSession();
         request.setSession(httpSession);
         request.setParameterMap(params);
-        context.put(ServletActionContext.HTTP_REQUEST, request);
+
+        context = ActionContext.of(context)
+            .withSession(session)
+            .withParameters(HttpParameters.create().build())
+            .withServletRequest(request)
+            .getContextMap();
+
         container.inject(parametersInterceptor);
     }
 
     protected void tearDown() throws Exception {
-        configurationManager.clearContainerProviders();
-        configurationManager.destroyConfiguration();
-        ActionContext.setContext(null);
+        super.tearDown();
     }
 
     private class WaitConfigurationProvider implements ConfigurationProvider {
 
         Configuration configuration;
+
         public void destroy() {
             waitInterceptor.destroy();
         }
@@ -252,8 +261,8 @@
                     .addResultConfig(new ResultConfig.Builder(ExecuteAndWaitInterceptor.WAIT, MockResult.class.getName()).build())
                     .addInterceptor(new InterceptorMapping("params", parametersInterceptor))
                     .addInterceptor(new InterceptorMapping("execAndWait", waitInterceptor))
-                .build())
-            .build();
+                    .build())
+                .build();
             configuration.addPackageConfig("", wait);
         }
 
diff --git a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
index 3e621b1..b6a4101 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
@@ -393,10 +393,10 @@
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
         Map<String, Object> param = new HashMap<>();
-        ActionContext.getContext().setParameters(HttpParameters.create(param).build());
-        // set German locale
-        ActionContext.getContext().setLocale(Locale.GERMAN);
-        ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 10));
+        ActionContext.getContext()
+            .withParameters(HttpParameters.create(param).build())
+            .withLocale(Locale.GERMAN)
+            .withServletRequest(createMultipartRequest(req, 10));
 
         interceptor.intercept(mai);
 
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..99b9ffb 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 @@
     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 @@
         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 {
@@ -214,7 +214,7 @@
 
     public void testActionContextLocaleIsPreservedWhenNotOverridden() throws Exception {
         final Locale locale1 = Locale.TRADITIONAL_CHINESE;
-        mai.getInvocationContext().setLocale(locale1);
+        mai.getInvocationContext().withLocale(locale1);
         interceptor.intercept(mai);
 
         Locale locale = (Locale) session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE);
@@ -254,24 +254,17 @@
         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.of(new HashMap<>()).bind();
+        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..4413099 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 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 com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.ActionSupport;
-
 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 @@
         action.addActionMessage("some action message 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
         actionContext.setParameters(HttpParameters.create().build());
 
         HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
@@ -90,7 +87,7 @@
         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 @@
         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 @@
 
         EasyMock.replay(mockedRequest);
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
         actionContext.setParameters(HttpParameters.create().build());
-        actionContext.put(ActionContext.SESSION, sessionMap);
+        actionContext.setSession(sessionMap);
 
         mockActionInvocation.getInvocationContext();
         EasyMock.expectLastCall().andReturn(actionContext);
@@ -155,8 +152,8 @@
         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 @@
         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 @@
         action.addFieldError("field1", "some field error 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
         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 @@
         EasyMock.expectLastCall().andReturn(actionContext);
         EasyMock.expectLastCall().anyTimes();
 
-        mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
+        mockActionInvocation.addPreResultListener(EasyMock.anyObject());
         EasyMock.expectLastCall();
 
         mockActionInvocation.invoke();
@@ -234,12 +231,12 @@
         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.of(new HashMap<>()).bind();
         actionContext.setParameters(HttpParameters.create(paramMap).build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
@@ -257,12 +254,12 @@
         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.of(new HashMap<>()).bind();
         actionContext.setParameters(HttpParameters.create(paramMap).build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
@@ -280,9 +277,9 @@
         EasyMock.verify(mockActionInvocation);
     }
 
-    public void testRequestOperationMode3() throws Exception {
+    public void testRequestOperationMode3() {
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
         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..6b85930 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 @@
 
 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.of(new HashMap<>()).bind();
+        actionContext.setParameters(HttpParameters.create().build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
 
@@ -82,10 +82,10 @@
         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.of(new HashMap<>()).bind();
+        actionContext.setParameters(HttpParameters.create().build());
 
         ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
 
@@ -114,14 +114,14 @@
         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 @@
         action.addFieldError("field1", "some field error 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
         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 @@
         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 @@
         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 @@
         action.addFieldError("field1", "some field error 1");
         action.addFieldError("field2", "some field error 2");
 
-        ActionContext actionContext = new ActionContext(new HashMap());
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
         actionContext.setParameters(HttpParameters.create().build());
         actionContext.setSession(sessionMap);
 
diff --git a/core/src/test/java/org/apache/struts2/interceptor/ServletConfigInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/ServletConfigInterceptorTest.java
index 20057e2..7829f26 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/ServletConfigInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/ServletConfigInterceptorTest.java
@@ -195,12 +195,12 @@
     }
 
     public void testApplicationAware() throws Exception {
-        ApplicationAware mock = (ApplicationAware) createMock(ApplicationAware.class);
+        ApplicationAware mock = createMock(ApplicationAware.class);
 
         MockActionInvocation mai = createActionInvocation(mock);
 
         Map<String, Object> app = new HashMap<String, Object>();
-        mai.getInvocationContext().setApplication(app);
+        mai.getInvocationContext().withApplication(app);
 
         mock.setApplication(app);
         expectLastCall().times(1);
@@ -216,7 +216,7 @@
         MockActionInvocation mai = createActionInvocation(mock);
 
         Map<String, Object> app = new HashMap<>();
-        mai.getInvocationContext().setApplication(app);
+        mai.getInvocationContext().withApplication(app);
 
         mock.withApplication(app);
         expectLastCall().times(1);
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..17d5fb4 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptorTest.java
@@ -83,9 +83,10 @@
         mockInvocation = new Mock(ActionInvocation.class);
         invocation = (ActionInvocation) mockInvocation.proxy();
         stack = ActionContext.getContext().getValueStack();
-        context = new ActionContext(stack.getContext());
         conversionErrors = new HashMap<>();
-        context.setConversionErrors(conversionErrors);
+        context = ActionContext.of(stack.getContext())
+            .withConversionErrors(conversionErrors)
+            .bind();
         mockInvocation.matchAndReturn("getInvocationContext", context);
         mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
         mockInvocation.expectAndReturn("getStack", stack);
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..2208047 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/TokenInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/TokenInterceptorTest.java
@@ -18,12 +18,10 @@
  */
 package org.apache.struts2.interceptor;
 
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.util.ValueStack;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
 import org.apache.struts2.TestConfigurationProvider;
@@ -32,10 +30,10 @@
 import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest;
 import org.apache.struts2.views.jsp.StrutsMockHttpSession;
 
-import com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionProxy;
-import com.opensymphony.xwork2.util.ValueStack;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.util.Map;
+import java.util.TreeMap;
 
 
 /**
@@ -95,22 +93,26 @@
 
     protected void setToken(String token) {
         request.getParameterMap().put(TokenHelper.TOKEN_NAME_FIELD, new String[]{
-                TokenHelper.DEFAULT_TOKEN_NAME
+            TokenHelper.DEFAULT_TOKEN_NAME
         });
         request.getParameterMap().put(TokenHelper.DEFAULT_TOKEN_NAME, new String[]{
-                token
+            token
         });
         extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
     }
 
     protected void setUp() throws Exception {
+        super.setUp();
+
         loadConfigurationProviders(new TestConfigurationProvider());
 
         session = new TreeMap<>();
         params = new TreeMap<>();
         extraContext = new TreeMap<>();
-        extraContext.put(ActionContext.SESSION, session);
-        extraContext.put(ActionContext.PARAMETERS, HttpParameters.create().build());
+        extraContext = ActionContext.of(extraContext)
+            .withSession(session)
+            .withParameters(HttpParameters.create().build())
+            .getContextMap();
 
         request = new StrutsMockHttpServletRequest();
         httpSession = new StrutsMockHttpSession();
@@ -120,8 +122,7 @@
 
         ValueStack stack = ActionContext.getContext().getValueStack();
         stack.getContext().putAll(extraContext);
-        oldContext = new ActionContext(stack.getContext());
-        ActionContext.setContext(oldContext);
+        oldContext = ActionContext.of(stack.getContext()).bind();
     }
 
     protected ActionProxy buildProxy(String actionName) throws Exception {
diff --git a/core/src/test/java/org/apache/struts2/interceptor/validation/AnnotationValidationInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/validation/AnnotationValidationInterceptorTest.java
index fc82253..ba70d88 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/validation/AnnotationValidationInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/validation/AnnotationValidationInterceptorTest.java
@@ -45,7 +45,7 @@
         mockActionInvocation.matchAndReturn("getAction", test);
         mockActionInvocation.expect("invoke");
 
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInvocation.proxy());
+        ActionContext.getContext().withActionInvocation((ActionInvocation) mockActionInvocation.proxy());
     }
 
     public void testShouldNotSkip() 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 @@
 
     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..2a055d3 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 @@
         response.setWriter(writer);
         servletContext = new StrutsMockServletContext();
         stack = ActionContext.getContext().getValueStack();
-        context = new ActionContext(stack.getContext());
+        context = ActionContext.of(stack.getContext()).bind();
         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..c6be2ab 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.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 @@
             fail();
         }
     }
+
     public void testPrependServletContextFalse() {
         view.setLocation("/bar/foo.jsp");
         view.setPrependServletContext(false);
@@ -197,7 +189,7 @@
         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 @@
         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 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 @@
         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,11 +293,10 @@
         ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
 
         ValueStack mockValueStack = control.createMock(ValueStack.class);
-        Map<String, Object> mockContext = new HashMap<String, Object>();
-        mockContext.put(ActionContext.CONTAINER, container);
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).withContainer(container);
 
         expect(mockInvocation.getStack()).andReturn(mockValueStack);
-        expect(mockValueStack.getContext()).andReturn(mockContext);
+        expect(mockValueStack.getActionContext()).andReturn(actionContext);
 
         expect(mockInvocation.getStack()).andReturn(mockValueStack);
 
@@ -316,7 +307,7 @@
         expect(mockActionProxy.getConfig()).andReturn(actionConfig);
         expect(mockInvocation.getInvocationContext()).andReturn(context);
 
-        expect(mockValueStack.getContext()).andReturn(mockContext);
+        expect(mockValueStack.getActionContext()).andReturn(actionContext);
 
         control.replay();
         result.setActionMapper(container.getInstance(ActionMapper.class));
@@ -326,11 +317,11 @@
     }
 
     /**
-     * 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 +343,11 @@
     }
 
     /**
-     * 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 +372,7 @@
     }
 
     /**
-     * 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 +401,11 @@
     }
 
     /**
-     * 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 +445,28 @@
         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 = new ActionContext(Ognl.createDefaultContext(null, new SecurityMemberAccess(false, true)));
-        ac.put(ServletActionContext.HTTP_REQUEST, requestMock.proxy());
-        ac.put(ServletActionContext.HTTP_RESPONSE, responseMock.proxy());
+        ActionContext ac = ActionContext.getContext();
+        ac.withServletRequest((HttpServletRequest) requestMock.proxy());
+        ac.withServletResponse((HttpServletResponse) responseMock.proxy());
+
         MockActionInvocation ai = new MockActionInvocation();
         ai.setInvocationContext(ac);
         ai.setResultCode("myResult");
+
         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..b581812 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.ObjectOutputStream;
 import java.util.HashMap;
 import java.util.Map;
-import org.apache.struts2.ServletActionContext;
 
 
 /**
@@ -61,9 +60,7 @@
     public void testValueStackReset() {
         ActionContext actionContext = ActionContext.getContext();
         assertEquals(stack, actionContext.getValueStack());
-        InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
-        actionContext.setValueStack(null);
-        assertNull(actionContext.getValueStack());
+
         InvocationSessionStore.loadInvocation(INVOCATION_KEY, TOKEN_VALUE);
         assertEquals(stack, actionContext.getValueStack());
     }
@@ -72,9 +69,9 @@
         ActionContext actionContext = ActionContext.getContext();
         InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
 
-        ActionContext actionContext2 = new ActionContext(new HashMap<String, Object>());
+        ActionContext actionContext2 = ActionContext.of(new HashMap<>()).bind();
         actionContext2.setSession(session);
-        ActionContext.setContext(actionContext2);
+
         assertEquals(actionContext2, ActionContext.getContext());
 
         InvocationSessionStore.loadInvocation(INVOCATION_KEY, TOKEN_VALUE);
@@ -88,12 +85,12 @@
         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();
@@ -103,49 +100,51 @@
     }
 
     public void testStoreAndLoadPreservesPageContext() {
-        ActionContext actionContext = ActionContext.getContext();
-
-        // 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));
-
-        InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
-
-        ActionContext actionContext2 = new ActionContext(new HashMap<String, Object>());
-        actionContext2.setSession(session);
-        ActionContext.setContext(actionContext2);
-        assertEquals(actionContext2, ActionContext.getContext());
-
-        // Create mock PageContext to put with the current context (simulating a PageContext 
+        // Create mock PageContext to put with the current context (simulating a PageContext
         // associated with the current (active) process flow).  In real-world processing it
         // 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));
+
+        // 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 actionContext = ActionContext.getContext()
+            .withPageContext(mockSavedPageContext);
+
+        assertEquals(mockSavedPageContext, ActionContext.getContext().getPageContext());
+
+        InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
+
+        ActionContext actionContext2 = ActionContext.of(new HashMap<>())
+            .withSession(session)
+            .withPageContext(mockPreviousPageContext)
+            .bind();
+
+        assertEquals(actionContext2, ActionContext.getContext());
+
+        actionContext2.withPageContext(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);
-
         session = new HashMap<>();
-        actionContext.setSession(session);
+
+        ActionContext actionContext = ActionContext.of(stack.getContext())
+            .withSession(session)
+            .withValueStack(stack)
+            .bind();
 
         invocationMock = new Mock(ActionInvocation.class);
         invocation = (ActionInvocation) invocationMock.proxy();
         invocationMock.matchAndReturn("getInvocationContext", actionContext);
-
-        actionContext.setValueStack(stack);
         invocationMock.matchAndReturn("getStack", stack);
 
         Mock proxyMock = new Mock(ActionProxy.class);
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..e1bd5d7 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 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.of(ctxMap).bind();
+        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..935d2ae 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.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.PrintWriter;
 import java.io.StringWriter;
 
-import static org.apache.struts2.views.jsp.AbstractUITagTest.normalize;
-
 /**
  * Test case for FreeMarkerResult.
  */
@@ -51,7 +47,7 @@
     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 @@
 
         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 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 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 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,12 @@
         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.of(stack.getContext())
+            .withServletResponse(response)
+            .withServletRequest(request)
+            .withServletContext(servletContext)
+            .bind();
 
-        ServletActionContext.setServletContext(servletContext);
-        ServletActionContext.setRequest(request);
-        ServletActionContext.setResponse(response);
         servletContext.setAttribute(FreemarkerManager.CONFIG_SERVLET_CONTEXT_KEY, null);
 
         invocation = new MockActionInvocation();
@@ -153,7 +147,7 @@
         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..cd3ce51 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.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 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 @@
         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 @@
         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 @@
         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,12 @@
         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.of(stack.getContext())
+            .withServletResponse(response)
+            .withServletRequest(request)
+            .withServletContext(servletContext)
+            .bind();
 
-        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 @@
 
     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..f31ccc4 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
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.io.StringWriter;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
@@ -49,8 +50,8 @@
  */
 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 +76,10 @@
 
     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 +107,10 @@
         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,
@@ -121,14 +119,14 @@
                 response);
         // let's not set the locale -- there is a test that checks if Dispatcher actually picks this up...
         // ... but generally we want to just use no locale (let it stay system default)
-        extraContext.remove(ActionContext.LOCALE);
+        extraContext = ActionContext.of(extraContext).withLocale(null).getContextMap();
         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.of(context)
+            .withServletRequest(request)
+            .withServletResponse(response)
+            .withServletContext(servletContext)
+            .bind();
     }
 
     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 @@
 
     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..8956390 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
@@ -39,7 +39,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-
 /**
  * Unit test for {@link ActionTag}.
  */
@@ -107,7 +106,7 @@
         this.testSimple();
     }
 
-    public void testSimpleWithctionMethodInOriginalURI() {
+    public void testSimpleWithActionMethodInOriginalURI() {
         request.setupGetServletPath("/foo!foo.action");
 
         ActionConfig config = configuration.getRuntimeConfiguration().getActionConfig("", "testAction");
@@ -156,7 +155,7 @@
         TestActionTagResult result = (TestActionTagResult) component.getProxy().getInvocation().getResult();
 
         assertTrue(stack.getContext().containsKey(ServletActionContext.PAGE_CONTEXT));
-        assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT)instanceof PageContext);
+        assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT) instanceof PageContext);
         assertTrue(result.isExecuted());
     }
 
@@ -177,18 +176,18 @@
         TestActionTagResult result = (TestActionTagResult) component.getProxy().getInvocation().getResult();
 
         assertTrue(stack.getContext().containsKey(ServletActionContext.PAGE_CONTEXT));
-        assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT)instanceof PageContext);
+        assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT) instanceof PageContext);
         assertNull(result); // result is never executed, hence never set into invocation
     }
 
-     public void testExecuteButResetReturnSameInvocation() throws Exception {
+    public void testExecuteButResetReturnSameInvocation() throws Exception {
         Mock mockActionInv = new Mock(ActionInvocation.class);
         ActionTag tag = new ActionTag();
         tag.setPageContext(pageContext);
         tag.setNamespace("");
         tag.setName("testActionTagAction");
         tag.setExecuteResult(true);
-        ActionContext.getContext().setActionInvocation((ActionInvocation) mockActionInv.proxy());
+        ActionContext.getContext().withActionInvocation((ActionInvocation) mockActionInv.proxy());
 
         ActionInvocation oldInvocation = ActionContext.getContext().getActionInvocation();
         assertNotNull(oldInvocation);
@@ -199,7 +198,7 @@
         ActionComponent component = (ActionComponent) tag.getComponent();
 
         tag.doEndTag();
-        assertTrue(oldInvocation == ActionContext.getContext().getActionInvocation());
+        assertSame(oldInvocation, ActionContext.getContext().getActionInvocation());
     }
 
     public void testIngoreContextParamsFalse() throws Exception {
@@ -236,7 +235,7 @@
         tag.setIgnoreContextParams(true);
 
         Map<String, String[]> params = new HashMap<>();
-        params.put("user", new String[] { "Santa Claus" });
+        params.put("user", new String[]{"Santa Claus"});
         ActionContext.getContext().setParameters(HttpParameters.create(params).build());
 
         tag.doStartTag();
@@ -264,7 +263,7 @@
             tag.doEndTag();
             fail("Should have thrown RuntimeException");
         } catch (StrutsException e) {
-             assertEquals("tag 'actioncomponent', field 'name': Action name is required. Example: updatePerson", e.getMessage());
+            assertEquals("tag 'actioncomponent', field 'name': Action name is required. Example: updatePerson", e.getMessage());
         }
     }
 
@@ -287,7 +286,7 @@
         tag.setNamespace("");
         tag.setName("testActionTagAction!input");
         tag.setExecuteResult(true);
-        ((DefaultActionMapper)container.getInstance(ActionMapper.class)).setAllowDynamicMethodCalls("true");
+        ((DefaultActionMapper) container.getInstance(ActionMapper.class)).setAllowDynamicMethodCalls("true");
 
         tag.doStartTag();
 
@@ -299,18 +298,19 @@
         TestActionTagResult result = (TestActionTagResult) component.getProxy().getInvocation().getResult();
 
         assertTrue(stack.getContext().containsKey(ServletActionContext.PAGE_CONTEXT));
-        assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT)instanceof PageContext);
+        assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT) instanceof PageContext);
         assertTrue(result.isExecuted());
     }
 
     protected void setUp() throws Exception {
         super.setUp();
-        initDispatcher(new HashMap<String, String>() {{ put("configProviders", TestConfigurationProvider.class.getName()); }});
+        initDispatcher(new HashMap<String, String>() {{
+            put("configProviders", TestConfigurationProvider.class.getName());
+        }});
         createMocks();
     }
 
     protected void tearDown() throws Exception {
-
         super.tearDown();
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/I18nTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/I18nTagTest.java
index aaccb47..6b5ac86 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/I18nTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/I18nTagTest.java
@@ -43,7 +43,6 @@
 
         // create the mock http servlet request
         StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest();
-        ActionContext.getContext().setValueStack(stack);
         request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
 
         // create the mock page context
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/IfTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/IfTagTest.java
index cb4ccdd..4537e9a 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/IfTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/IfTagTest.java
@@ -323,7 +323,6 @@
 
         // create the mock http servlet request
         StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest();
-        ActionContext.getContext().setValueStack(stack);
         request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
 
         // create the mock page context
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/NumberTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/NumberTagTest.java
index 475e66f..6056a94 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/NumberTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/NumberTagTest.java
@@ -29,7 +29,7 @@
 
     public void testSimpleFloatFormat() throws Exception {
         // given
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
 
         TestAction testAction = (TestAction) action;
         testAction.setFloatNumber(120.0f);
@@ -48,7 +48,7 @@
     
     public void testSimpleCurrencyUSFormat() throws Exception {
         // given
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
         
         TestAction testAction = (TestAction) action;
         testAction.setFloatNumber(120.0f);
@@ -68,7 +68,7 @@
     
     public void testSimpleCurrencyPLFormat() throws Exception {
         // given
-        context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
+        context = ActionContext.of(context).withLocale(new Locale("pl", "PL")).getContextMap();
         
         TestAction testAction = (TestAction) action;
         testAction.setFloatNumber(120.0f);
@@ -83,7 +83,8 @@
         tag.doEndTag();
 
         // then
-        NumberFormat format = NumberFormat.getCurrencyInstance((Locale) context.get(ActionContext.LOCALE));
+        Locale locale = ActionContext.of(context).getLocale();
+        NumberFormat format = NumberFormat.getCurrencyInstance(locale);
         format.setRoundingMode(RoundingMode.CEILING);
         String expected = format.format(120.0f);
 
@@ -92,7 +93,7 @@
 
     public void testSimpleRoundingCeiling() throws Exception {
         // given
-        context.put(ActionContext.LOCALE, Locale.US);
+        context = ActionContext.of(context).withLocale(Locale.US).getContextMap();
 
         TestAction testAction = (TestAction) action;
         testAction.setFloatNumber(120.45f);
@@ -107,7 +108,8 @@
         tag.doEndTag();
 
         // then
-        NumberFormat format = NumberFormat.getInstance((Locale) context.get(ActionContext.LOCALE));
+        Locale locale = ActionContext.of(context).getLocale();
+        NumberFormat format = NumberFormat.getInstance(locale);
         format.setRoundingMode(RoundingMode.DOWN);
         String expected = format.format(120.45f);
 
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..29b5627 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
@@ -18,15 +18,11 @@
  */
 package org.apache.struts2.views.jsp;
 
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.BodyTag;
-
+import com.mockobjects.servlet.MockJspWriter;
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsException;
 import org.apache.struts2.TestAction;
@@ -34,16 +30,19 @@
 import org.apache.struts2.views.jsp.ui.StrutsBodyContent;
 import org.apache.struts2.views.jsp.ui.TestAction1;
 
-import com.mockobjects.servlet.MockJspWriter;
-import com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.ValueStackFactory;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTag;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.Assert.assertNotEquals;
 
 
 /**
  * TextTagTest
- *
  */
 public class TextTagTest extends AbstractTagTest {
 
@@ -104,10 +103,10 @@
     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);
@@ -173,8 +172,7 @@
         final StringBuffer buffer = writer.getBuffer();
         buffer.delete(0, buffer.length());
         ValueStack newStack = container.getInstance(ValueStackFactory.class).createValueStack();
-        newStack.getContext().put(ActionContext.LOCALE, foreignLocale);
-        newStack.getContext().put(ActionContext.CONTAINER, container);
+        newStack.getActionContext().withLocale(foreignLocale).withContainer(container);
         newStack.push(container.inject(TestAction1.class));
         request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, newStack);
         assertNotSame(ActionContext.getContext().getValueStack().peek(), newStack.peek());
@@ -192,7 +190,7 @@
         Locale foreignLocale = getForeignLocale();
         assertNotSame(defaultLocale, foreignLocale);
 
-        ActionContext.getContext().setLocale(defaultLocale);
+        ActionContext.getContext().withLocale(defaultLocale);
         String key = "simpleKey";
         String value_default = getLocalizedMessage(defaultLocale);
         tag.setName(key);
@@ -203,11 +201,10 @@
         final StringBuffer buffer = writer.getBuffer();
         buffer.delete(0, buffer.length());
         String value_int = getLocalizedMessage(foreignLocale);
-        assertFalse(value_default.equals(value_int));
+        assertNotEquals(value_default, value_int);
         ValueStack newStack = container.getInstance(ValueStackFactory.class).createValueStack(stack);
-        newStack.getContext().put(ActionContext.LOCALE, foreignLocale);
-        newStack.getContext().put(ActionContext.CONTAINER, container);
-        assertNotSame(newStack.getContext().get(ActionContext.LOCALE), ActionContext.getContext().getLocale());
+        newStack.getActionContext().withLocale(foreignLocale).withContainer(container);
+        assertNotSame(newStack.getActionContext().getLocale(), ActionContext.getContext().getLocale());
         request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, newStack);
         assertEquals(ActionContext.getContext().getValueStack().peek(), newStack.peek());
         tag.doStartTag();
@@ -255,7 +252,7 @@
     }
 
     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 +283,7 @@
 
     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 +301,11 @@
         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.of(stack.getContext()).bind();
     }
 
     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..7b6117d 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
@@ -24,6 +24,7 @@
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 import javax.servlet.http.HttpSession;
@@ -147,14 +148,13 @@
 
     /**
      * 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 +180,6 @@
      *  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 +227,7 @@
 
         // 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 +351,7 @@
 
     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 +514,13 @@
     }
     
     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 +545,7 @@
 
         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,
@@ -559,17 +554,17 @@
                 response);
         // let's not set the locale -- there is a test that checks if Dispatcher actually picks this up...
         // ... but generally we want to just use no locale (let it stay system default)
-        extraContext.remove(ActionContext.LOCALE);
+        extraContext = ActionContext.of(extraContext).withLocale(null).getContextMap();
         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.of(context)
+            .withServletRequest(request)
+            .withServletResponse(response)
+            .withServletContext(servletContext)
+            .bind();
 
-        ActionContext.setContext(new ActionContext(context));
-        
         // Make sure we have an action invocation available
-        ActionContext.getContext().setActionInvocation(new DefaultActionInvocation(null, true));
+        ActionContext.getContext().withActionInvocation(new DefaultActionInvocation(null, true));
         DefaultActionProxyFactory apFactory = new DefaultActionProxyFactory();
         apFactory.setContainer(container);
         ActionProxy ap = apFactory.createActionProxy("/", "hello", null, null);
@@ -780,7 +775,8 @@
         
         
     }
-    
+
+    @SuppressWarnings("unused")
     public static class RedBlueActionMapper extends DefaultActionMapper {
         
         @Override
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java
index 0a4e50d..bbee23b 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java
@@ -110,6 +110,6 @@
 
         configurationManager.reload();
         container = configurationManager.getConfiguration().getContainer();
-        stack.getContext().put(ActionContext.CONTAINER, container);
+        stack.getActionContext().withContainer(container);
     }
 }
\ No newline at end of file
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/FormTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/FormTagTest.java
index 27d79ce..535e42f 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/FormTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/FormTagTest.java
@@ -341,8 +341,6 @@
     }
 
     private void prepareMockInvocation() throws Exception {
-        ActionContext.getContext().setValueStack(stack);
-
         ActionConfig config = new ActionConfig.Builder("", "name", "").build();
         ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
         ActionProxy proxy = EasyMock.createNiceMock(ActionProxy.class);
@@ -356,7 +354,7 @@
         EasyMock.replay(invocation);
         EasyMock.replay(proxy);
 
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
     }
 
     /**
diff --git a/core/src/test/java/org/apache/struts2/views/util/ContextUtilTest.java b/core/src/test/java/org/apache/struts2/views/util/ContextUtilTest.java
index 3da2447..9a79cb8 100755
--- a/core/src/test/java/org/apache/struts2/views/util/ContextUtilTest.java
+++ b/core/src/test/java/org/apache/struts2/views/util/ContextUtilTest.java
@@ -36,7 +36,7 @@
     private void setAltSyntax(ValueStack stack, String val) {
         Mock container = new Mock(Container.class);
         container.expectAndReturn("getInstance", C.args(C.eq(String.class), C.eq(StrutsConstants.STRUTS_TAG_ALTSYNTAX)), val);
-        stack.getContext().put(ActionContext.CONTAINER, container.proxy());
+        stack.getActionContext().withContainer((Container) container.proxy());
     }
     
     public void testAltSyntaxMethod1() throws Exception {
diff --git a/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java
index 1532e9b..9266e49 100644
--- a/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java
+++ b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java
@@ -427,7 +427,7 @@
     public void setUp() throws Exception {
         super.setUp();
         stubContainer = new StubContainer(container);
-        ActionContext.getContext().put(ActionContext.CONTAINER, stubContainer);
+        ActionContext.getContext().withContainer(stubContainer);
         urlHelper = new DefaultUrlHelper();
     }
     
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..6d0abd4 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,9 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        ActionContext context = new ActionContext(new HashMap<String, Object>());
-        context.setContainer(new DummyContainer());
-        ActionContext.setContext(context);
+        ActionContext.of(new HashMap<>())
+            .withContainer(new DummyContainer())
+            .bind();
     }
 
     public void testActionPackages() throws MalformedURLException {
@@ -692,7 +692,7 @@
 
         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.of(Collections.emptyMap()).bind();
 
         Result result = uh.handleUnknownResult(actionContext, "foo", pkgConfig.getActionConfigs().get("foo"), "bar");
         assertNotNull(result);
diff --git a/plugins/dwr/src/main/java/org/apache/struts2/validators/DWRValidator.java b/plugins/dwr/src/main/java/org/apache/struts2/validators/DWRValidator.java
index 60edf4b..c8cc669 100644
--- a/plugins/dwr/src/main/java/org/apache/struts2/validators/DWRValidator.java
+++ b/plugins/dwr/src/main/java/org/apache/struts2/validators/DWRValidator.java
@@ -75,11 +75,11 @@
         if (params != null) {
             requestParams = requestParams.withExtraParams(params);
         }
-        Map requestMap = new RequestMap(req);
-        Map session = new SessionMap(req);
-        Map application = new ApplicationMap(servletContext);
+        Map<String, Object> requestMap = new RequestMap(req);
+        Map<String, Object> session = new SessionMap<>(req);
+        Map<String, Object> application = new ApplicationMap(servletContext);
         Dispatcher du = Dispatcher.getInstance();
-        HashMap<String, Object> ctx = du.createContextMap(requestMap,
+        Map<String, Object> ctx = du.createContextMap(requestMap,
                 requestParams.build(),
                 session,
                 application,
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 @@
         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/main/java/org/apache/struts2/JSPRuntime.java b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java
index 1a47a4d..81bbed4 100644
--- a/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java
+++ b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java
@@ -19,8 +19,8 @@
 package org.apache.struts2;

 

 import com.opensymphony.xwork2.ActionContext;

-import org.apache.struts2.dispatcher.HttpParameters;

 import org.apache.struts2.dispatcher.Parameter;

+import org.apache.struts2.views.util.DefaultUrlHelper;

 import org.apache.struts2.views.util.UrlHelper;

 

 import javax.servlet.Servlet;

@@ -29,7 +29,6 @@
 import javax.servlet.jsp.HttpJspPage;

 import java.util.HashMap;

 import java.util.Map;

-import org.apache.struts2.views.util.DefaultUrlHelper;

 

 /**

  * Maintains a cache of jsp locations -&gt; servlet instances for those jsps. When a jsp is requested

@@ -55,7 +54,7 @@
         if (i > 0) {

             //extract params from the url and add them to the request

             final UrlHelper urlHelperGetInstance = ServletActionContext.getContext().getInstance(UrlHelper.class);

-            final UrlHelper contextUrlHelper = (urlHelperGetInstance != null ? urlHelperGetInstance : (UrlHelper) ServletActionContext.getContext().get(StrutsConstants.STRUTS_URL_HELPER));

+            final UrlHelper contextUrlHelper = (urlHelperGetInstance != null ? urlHelperGetInstance : (UrlHelper) ActionContext.getContext().get(StrutsConstants.STRUTS_URL_HELPER));

             final UrlHelper urlHelper = (contextUrlHelper != null ? contextUrlHelper : new DefaultUrlHelper());

             String query = location.substring(i + 1);

             Map<String, Object> queryParams = urlHelper.parseQueryString(query, true);

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..a5b3bb2 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.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.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.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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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,26 +324,14 @@
 

         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.setParameters(HttpParameters.create(params).build());

-        ServletActionContext.setRequest(request);

-        ServletActionContext.setResponse(response);

-        ServletActionContext.setServletContext(context);

-

         //mock value stack

-        Map stackContext = new HashMap();

         ValueStack valueStack = EasyMock.createNiceMock(ValueStack.class);

-        EasyMock.expect(valueStack.getContext()).andReturn(stackContext).anyTimes();

+        EasyMock.expect(valueStack.getActionContext()).andReturn(ActionContext.getContext()).anyTimes();

         EasyMock.replay(valueStack);

 

         //mock converter

@@ -354,7 +345,7 @@
         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();

@@ -363,10 +354,15 @@
         EasyMock.expect(container.getInstance(FileManagerFactory.class)).andReturn(fileManagerFactory).anyTimes();

 

         EasyMock.replay(container);

-        stackContext.put(ActionContext.CONTAINER, container);

-        actionContext.setContainer(container);

 

-        actionContext.setValueStack(valueStack);

+        ActionContext.of(new HashMap<>())

+            .withParameters(HttpParameters.create(params).build())

+            .withServletRequest(request)

+            .withServletResponse(response)

+            .withServletContext(context)

+            .withContainer(container)

+            .withValueStack(valueStack)

+            .bind();

     }

 

 }

@@ -408,7 +404,7 @@
     }

 

     public void run() {

-        ActionContext.setContext(actionContext);

+        actionContext = ActionContext.bind(actionContext);

         //wait to start all threads at once..or try at least

         try {

             startBarrier.await();

@@ -432,7 +428,7 @@
 }

 

 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/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java b/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java
index 3fbdf98..b639570 100644
--- a/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java
+++ b/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java
@@ -39,7 +39,6 @@
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.result.StrutsResultSupport;
 
 import javax.servlet.ServletContext;
@@ -252,10 +251,8 @@
 
         LOG.debug("Creating JasperReport for dataSource = {}, format = {}", dataSource, format);
 
-        HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext()
-                .get(ServletActionContext.HTTP_REQUEST);
-        HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext()
-                .get(ServletActionContext.HTTP_RESPONSE);
+        HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
+        HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
 
         // Handle IE special case: it sends a "contype" request first.
         // TODO Set content type to config settings?
@@ -290,8 +287,7 @@
         // Determine the directory that the report file is in and set the reportDirectory parameter
         // For WW 2.1.7:
         //  ServletContext servletContext = ((ServletConfig) invocation.getInvocationContext().get(ServletActionContext.SERVLET_CONFIG)).getServletContext();
-        ServletContext servletContext = (ServletContext) invocation.getInvocationContext()
-                .get(ServletActionContext.SERVLET_CONTEXT);
+        ServletContext servletContext = invocation.getInvocationContext().getServletContext();
         String systemId = servletContext.getRealPath(finalLocation);
         Map<String, Object> parameters = new ValueStackShadowMap(stack);
         File directory = new File(systemId.substring(0, systemId.lastIndexOf(File.separator)));
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..00f17fc 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
@@ -21,20 +21,19 @@
 
 package org.apache.struts2.views.java.simple;
 
+import com.opensymphony.xwork2.Action;
 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 +42,15 @@
 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 +58,7 @@
     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 +70,7 @@
 
     @Override
     protected void setUp() throws Exception {
-        super.setUp();    
+        super.setUp();
         scriptingAttrs.put("onclick", "onclick_");
         scriptingAttrs.put("ondblclick", "ondblclick_");
         scriptingAttrs.put("onmousedown", "onmousedown_");
@@ -89,20 +93,22 @@
 
         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());
+        ActionContext actionContext = ActionContext.of(stackContext).bind();
 
         request = createNiceMock(HttpServletRequest.class);
         expect(request.getContextPath()).andReturn("/some/path").anyTimes();
         response = createNiceMock(HttpServletResponse.class);
 
+        expect(stack.getActionContext()).andReturn(actionContext).anyTimes();
         expect(stack.getContext()).andReturn(stackContext).anyTimes();
 
         Container container = createNiceMock(Container.class);
@@ -111,22 +117,19 @@
         expect(container.getInstance(XWorkConverter.class)).andReturn(converter).anyTimes();
         TextParser parser = new OgnlTextParser();
         expect(container.getInstance(TextParser.class)).andReturn(parser).anyTimes();
-        stackContext.put(ActionContext.CONTAINER, container);
-
 
         replay(request);
         replay(stack);
         replay(container);
 
-        ActionContext.setContext(new ActionContext(stackContext));
-        ServletActionContext.setRequest(request);
+        actionContext.withContainer(container).withServletRequest(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 @@
     }
 
     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..d0f0896 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.UIBean;
 
 import java.util.HashMap;
-import java.util.Map;
 import java.util.regex.Pattern;
 
 public class TokenTest extends AbstractTest {
@@ -51,9 +50,8 @@
         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.of(new HashMap<>()).bind();
+        actionContext.setSession(new HashMap<>());
     }
 
     @Override
diff --git a/plugins/jfreechart/src/test/java/org/apache/struts2/dispatcher/ChartResultTest.java b/plugins/jfreechart/src/test/java/org/apache/struts2/dispatcher/ChartResultTest.java
index b05496d..0dda173 100644
--- a/plugins/jfreechart/src/test/java/org/apache/struts2/dispatcher/ChartResultTest.java
+++ b/plugins/jfreechart/src/test/java/org/apache/struts2/dispatcher/ChartResultTest.java
@@ -153,8 +153,6 @@
 
 
         stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
-
 
         mockActionProxy = EasyMock.createNiceMock(ActionProxy.class);
         EasyMock.expect(mockActionProxy.getNamespace()).andReturn("/html");
diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
index 0547e92..2327244 100644
--- a/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
+++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
@@ -18,29 +18,26 @@
  */
 package org.apache.struts2.json;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.StrutsStatics;
-import org.apache.struts2.json.smd.SMDGenerator;
-
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.Result;
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.WildcardUtil;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.json.smd.SMDGenerator;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * <!-- START SNIPPET: description -->
@@ -101,15 +98,15 @@
     private String wrapSuffix;
     private boolean devMode = false;
     private JSONUtil jsonUtil;
-    
+
     @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
     public void setDefaultEncoding(String val) {
         this.defaultEncoding = val;
     }
-    
-    @Inject(StrutsConstants.STRUTS_DEVMODE) 
+
+    @Inject(StrutsConstants.STRUTS_DEVMODE)
     public void setDevMode(String val) {
-    	this.devMode = BooleanUtils.toBoolean(val);
+        this.devMode = BooleanUtils.toBoolean(val);
     }
 
     @Inject
@@ -188,12 +185,12 @@
 
     public void execute(ActionInvocation invocation) throws Exception {
         ActionContext actionContext = invocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
-        HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
-        
+        HttpServletRequest request = actionContext.getServletRequest();
+        HttpServletResponse response = actionContext.getServletResponse();
+
         // only permit caching bean information when struts devMode = false
         cacheBeanInfo = !devMode;
-        
+
         try {
             Object rootObject;
             rootObject = readRootObject(invocation);
@@ -224,7 +221,7 @@
 
     protected String createJSONString(HttpServletRequest request, Object rootObject) throws JSONException {
         String json = jsonUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
-                                         enumAsBean, excludeNullProperties, defaultDateFormat, cacheBeanInfo);
+            enumAsBean, excludeNullProperties, defaultDateFormat, cacheBeanInfo);
         json = addCallbackIfApplicable(request, json);
         return json;
     }
@@ -235,8 +232,8 @@
 
     protected void writeToResponse(HttpServletResponse response, String json, boolean gzip) throws IOException {
         JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), isWrapWithComments(),
-                json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,
-                wrapSuffix));
+            json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,
+            wrapSuffix));
     }
 
     @SuppressWarnings("unchecked")
@@ -248,7 +245,7 @@
      * Retrieve the encoding
      *
      * @return The encoding associated with this template (defaults to the value
-     *         of param 'encoding', if empty default to 'struts.i18n.encoding' property)
+     * of param 'encoding', if empty default to 'struts.i18n.encoding' property)
      */
     protected String getEncoding() {
         String encoding = this.encoding;
@@ -327,9 +324,9 @@
     }
 
     /**
-     * @param ignoreInterfaces  Controls whether interfaces should be inspected for method annotations
-     * You may need to set to this true if your action is a proxy as annotations
-     * on methods are not inherited
+     * @param ignoreInterfaces Controls whether interfaces should be inspected for method annotations
+     *                         You may need to set to this true if your action is a proxy as annotations
+     *                         on methods are not inherited
      */
     public void setIgnoreInterfaces(boolean ignoreInterfaces) {
         this.ignoreInterfaces = ignoreInterfaces;
@@ -337,8 +334,8 @@
 
     /**
      * @param enumAsBean Controls how Enum's are serialized : If true, an Enum is serialized as a
-     * name=value pair (name=name()) (default) If false, an Enum is serialized
-     * as a bean with a special property _name=name()
+     *                   name=value pair (name=name()) (default) If false, an Enum is serialized
+     *                   as a bean with a special property _name=name()
      */
     public void setEnumAsBean(boolean enumAsBean) {
         this.enumAsBean = enumAsBean;
@@ -423,7 +420,7 @@
     }
 
     /**
-     * @param wrapPrefix  Text to be inserted at the begining of the response
+     * @param wrapPrefix Text to be inserted at the begining of the response
      */
     public void setWrapPrefix(String wrapPrefix) {
         this.wrapPrefix = wrapPrefix;
@@ -434,7 +431,7 @@
     }
 
     /**
-     * @param wrapSuffix  Text to be inserted at the end of the response
+     * @param wrapSuffix Text to be inserted at the end of the response
      */
     public void setWrapSuffix(String wrapSuffix) {
         this.wrapSuffix = wrapSuffix;
@@ -443,7 +440,7 @@
     /**
      * If defined will be used instead of {@link #defaultEncoding}, you can define it with result
      * &lt;result name=&quot;success&quot; type=&quot;json&quot;&gt;
-     *     &lt;param name=&quot;encoding&quot;&gt;UTF-8&lt;/param&gt;
+     * &lt;param name=&quot;encoding&quot;&gt;UTF-8&lt;/param&gt;
      * &lt;/result&gt;
      *
      * @param encoding valid encoding string
diff --git a/plugins/json/src/main/java/org/apache/struts2/json/smd/SMDGenerator.java b/plugins/json/src/main/java/org/apache/struts2/json/smd/SMDGenerator.java
index 9746aa5..e1c3ce5 100644
--- a/plugins/json/src/main/java/org/apache/struts2/json/smd/SMDGenerator.java
+++ b/plugins/json/src/main/java/org/apache/struts2/json/smd/SMDGenerator.java
@@ -20,9 +20,8 @@
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
-import org.apache.struts2.StrutsStatics;
+import org.apache.logging.log4j.Logger;
 import org.apache.struts2.json.JSONUtil;
 import org.apache.struts2.json.annotations.SMD;
 import org.apache.struts2.json.annotations.SMDMethod;
@@ -51,15 +50,15 @@
 
     public org.apache.struts2.json.smd.SMD generate(ActionInvocation actionInvocation) {
         ActionContext actionContext = actionInvocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
+        HttpServletRequest request = actionContext.getServletRequest();
 
-        Class clazz = rootObject.getClass();
+        Class<?> clazz = rootObject.getClass();
         org.apache.struts2.json.smd.SMD smd = new org.apache.struts2.json.smd.SMD();
         // URL
         smd.setServiceUrl(request.getRequestURI());
 
         // customize SMD
-        org.apache.struts2.json.annotations.SMD smdAnnotation = (SMD) clazz.getAnnotation(SMD.class);
+        org.apache.struts2.json.annotations.SMD smdAnnotation = clazz.getAnnotation(SMD.class);
         if (smdAnnotation != null) {
             smd.setObjectName(smdAnnotation.objectName());
             smd.setServiceType(smdAnnotation.serviceType());
@@ -87,7 +86,7 @@
             // find params for this method
             processMethodsParameters(method, smdMethod);
 
-        } else if(LOG.isDebugEnabled()) {
+        } else if (LOG.isDebugEnabled()) {
             LOG.debug("Ignoring property " + method.getName());
         }
     }
@@ -137,7 +136,7 @@
      * Find an SMDethodParameter annotation on this array
      */
     private org.apache.struts2.json.annotations.SMDMethodParameter getSMDMethodParameterAnnotation(
-            Annotation[] annotations) {
+        Annotation[] annotations) {
         for (Annotation annotation : annotations) {
             if (annotation instanceof org.apache.struts2.json.annotations.SMDMethodParameter)
                 return (org.apache.struts2.json.annotations.SMDMethodParameter) annotation;
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..48cfcb5 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
@@ -22,7 +22,6 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.StrutsTestCase;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
@@ -517,16 +516,15 @@
         this.request = new MockHttpServletRequest();
         this.response = new MockHttpServletResponse();
 
-        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);
-
         MockServletContext servletContext = new MockServletContext();
 
-        context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+        ActionContext context = ActionContext.getContext()
+            .withServletRequest(request)
+            .withServletResponse(response)
+            .withServletContext(servletContext);
+
+        ValueStack stack = context.getValueStack();
+
         this.invocation = new MockActionInvocationEx();
         this.invocation.setInvocationContext(context);
         this.invocation.setStack(stack);
diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONValidationInterceptorTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONValidationInterceptorTest.java
index a4fd58d..753ff11 100644
--- a/plugins/json/src/test/java/org/apache/struts2/json/JSONValidationInterceptorTest.java
+++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONValidationInterceptorTest.java
@@ -197,7 +197,7 @@
 
         context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
         invocation = new MockActionInvocation();
-        ActionContext.getContext().setActionInvocation(invocation);
+        ActionContext.getContext().withActionInvocation(invocation);
         invocation.setAction(action);
         invocation.setInvocationContext(context);
         MockActionProxy proxy = new MockActionProxy();
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..634cc0a 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.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,12 +138,12 @@
         actionContext.setParameters(HttpParameters.create(request.getParameterMap()).build());

         initSession(actionContext);

         // set the action context to the one used by the proxy

-        ActionContext.setContext(actionContext);

+        ActionContext.bind(actionContext);

     }

 

     protected void initSession(ActionContext actionContext) {

         if (actionContext.getSession() == null) {

-            actionContext.setSession(new HashMap<String, Object>());

+            actionContext.withSession(new HashMap<>());

             request.setSession(new MockHttpSession(servletContext));

         }

     }

diff --git a/plugins/junit/src/main/java/org/apache/struts2/StrutsPortletTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/StrutsPortletTestCase.java
index 624a186..b5b4309 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/StrutsPortletTestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/StrutsPortletTestCase.java
@@ -71,7 +71,7 @@
         portletResponse = new MockStateAwareResponse();
         portletSession = new MockPortletSession();
         portletRequest.setSession(portletSession);
-        actionContext.setSession(createSession());
+        actionContext.withSession(createSession());
         actionContext.put(PortletConstants.REQUEST, portletRequest);
         actionContext.put(PortletConstants.RESPONSE, portletResponse);
         actionContext.put(PortletConstants.MODE_NAMESPACE_MAP, new HashMap<PortletMode, String>());
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..b4f84a6 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.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 @@
      *
      * @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 @@
      * 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 @@
 
         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 @@
      * 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.bind(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..8451515 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.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,12 +122,12 @@
         initSession(actionContext);
         applyAdditionalParams(actionContext);
         // set the action context to the one used by the proxy
-        ActionContext.setContext(actionContext);
+        ActionContext.bind(actionContext);
     }
 
     protected void initSession(ActionContext actionContext) {
         if (actionContext.getSession() == null) {
-            actionContext.setSession(new HashMap<String, Object>());
+            actionContext.withSession(new HashMap<>());
             request.setSession(new MockHttpSession(servletContext));
         }
     }
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..0608ad7 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 @@
             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 @@
     }
 
     protected ActionContext createActionContext() {
-        return new ActionContext(new HashMap<String, Object>());
+        return ActionContext.of(new HashMap<>()).bind();
     }
 
     /**
@@ -120,7 +118,6 @@
         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..4b51f7e 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 @@
 	}

 

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

@@ -165,7 +165,7 @@
         if (formComponent.action != null) {

             action = formComponent.findString(formComponent.action);

         } else {

-            ActionInvocation ai = (ActionInvocation) formComponent.getStack().getContext().get(ActionContext.ACTION_INVOCATION);

+            ActionInvocation ai = formComponent.getStack().getActionContext().getActionInvocation();

             action = ai.getProxy().getActionName();

         }

 

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..cc7b00c 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
@@ -24,9 +24,9 @@
 import com.opensymphony.xwork2.config.ConfigurationException;

 import com.opensymphony.xwork2.inject.Container;

 import org.apache.commons.lang3.LocaleUtils;

-import org.apache.logging.log4j.Logger;

-import org.apache.logging.log4j.LogManager;

 import org.apache.commons.lang3.StringUtils;

+import org.apache.logging.log4j.LogManager;

+import org.apache.logging.log4j.Logger;

 import org.apache.logging.log4j.message.ParameterizedMessage;

 import org.apache.struts2.StrutsConstants;

 import org.apache.struts2.StrutsException;

@@ -214,21 +214,21 @@
         LOG.debug("PortletNamespace: {}", portletNamespace);

 

         parseModeConfig(actionMap, cfg, PortletMode.VIEW, "viewNamespace",

-                "defaultViewAction");

+            "defaultViewAction");

         parseModeConfig(actionMap, cfg, PortletMode.EDIT, "editNamespace",

-                "defaultEditAction");

+            "defaultEditAction");

         parseModeConfig(actionMap, cfg, PortletMode.HELP, "helpNamespace",

-                "defaultHelpAction");

+            "defaultHelpAction");

         parseModeConfig(actionMap, cfg, new PortletMode("config"), "configNamespace",

-                "defaultConfigAction");

+            "defaultConfigAction");

         parseModeConfig(actionMap, cfg, new PortletMode("about"), "aboutNamespace",

-                "defaultAboutAction");

+            "defaultAboutAction");

         parseModeConfig(actionMap, cfg, new PortletMode("print"), "printNamespace",

-                "defaultPrintAction");

+            "defaultPrintAction");

         parseModeConfig(actionMap, cfg, new PortletMode("preview"), "previewNamespace",

-                "defaultPreviewAction");

+            "defaultPreviewAction");

         parseModeConfig(actionMap, cfg, new PortletMode("edit_defaults"),

-                "editDefaultsNamespace", "defaultEditDefaultsAction");

+            "editDefaultsNamespace", "defaultEditDefaultsAction");

         if (StringUtils.isEmpty(portletNamespace)) {

             portletNamespace = "";

         }

@@ -239,13 +239,14 @@
 

     /**

      * Parse the mode to namespace mappings configured in portlet.xml

-     * @param actionMap The map with mode <-> default action mapping.

-     * @param portletConfig The PortletConfig.

-     * @param portletMode The PortletMode.

-     * @param nameSpaceParam Name of the init parameter where the namespace for the mode

-     * is configured.

+     *

+     * @param actionMap          The map with mode <-> default action mapping.

+     * @param portletConfig      The PortletConfig.

+     * @param portletMode        The PortletMode.

+     * @param nameSpaceParam     Name of the init parameter where the namespace for the mode

+     *                           is configured.

      * @param defaultActionParam Name of the init parameter where the default action to

-     * execute for the mode is configured.

+     *                           execute for the mode is configured.

      */

     void parseModeConfig(Map<PortletMode, ActionMapping> actionMap, PortletConfig portletConfig,

                          PortletMode portletMode, String nameSpaceParam,

@@ -286,45 +287,41 @@
     /**

      * Service an action from the <tt>event</tt> phase.

      *

-     * @param request action request

-     * @param response  action response

-     *

+     * @param request  action request

+     * @param response action response

      * @throws PortletException in case of errors

-     * @throws IOException in case of IO errors

-     *

+     * @throws IOException      in case of IO errors

      * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,

-     *      javax.portlet.ActionResponse)

+     * javax.portlet.ActionResponse)

      */

     public void processAction(ActionRequest request, ActionResponse response)

-            throws PortletException, IOException {

+        throws PortletException, IOException {

         if (LOG.isDebugEnabled()) {

             LOG.debug("Entering processAction in mode ", request.getPortletMode().toString());

         }

         resetActionContext();

         try {

             serviceAction(request, response, getRequestMap(request), getParameterMap(request),

-                    getSessionMap(request), getApplicationMap(),

-                    portletNamespace, PortletPhase.ACTION_PHASE);

+                getSessionMap(request), getApplicationMap(),

+                portletNamespace, PortletPhase.ACTION_PHASE);

             if (LOG.isDebugEnabled()) LOG.debug("Leaving processAction");

         } finally {

-            ActionContext.setContext(null);

+            ActionContext.clear();

         }

     }

 

     /**

      * Service an action from the <tt>render</tt> phase.

      *

-     * @param request render request

-     * @param response  render response

-     *

+     * @param request  render request

+     * @param response render response

      * @throws PortletException in case of errors

-     * @throws IOException in case of IO errors

-     *

+     * @throws IOException      in case of IO errors

      * @see javax.portlet.Portlet#render(javax.portlet.RenderRequest,

-     *      javax.portlet.RenderResponse)

+     * javax.portlet.RenderResponse)

      */

     public void render(RenderRequest request, RenderResponse response)

-            throws PortletException, IOException {

+        throws PortletException, IOException {

 

         if (LOG.isDebugEnabled()) {

             LOG.debug("Entering render in mode ", request.getPortletMode().toString());

@@ -335,8 +332,8 @@
             try {

                 // Check to see if an event set the render to be included directly

                 serviceAction(request, response, getRequestMap(request), getParameterMap(request),

-                        getSessionMap(request), getApplicationMap(),

-                        portletNamespace, PortletPhase.RENDER_PHASE);

+                    getSessionMap(request), getApplicationMap(),

+                    portletNamespace, PortletPhase.RENDER_PHASE);

                 if (LOG.isDebugEnabled()) LOG.debug("Leaving render");

             } finally {

                 resetActionContext();

@@ -345,69 +342,64 @@
     }

 

     /**

-     *  Reset the action context.

+     * Reset the action context.

      */

     void resetActionContext() {

-        ActionContext.setContext(null);

+        ActionContext.clear();

     }

 

     /**

      * Merges all application and portlet attributes into a single

      * <tt>HashMap</tt> to represent the entire <tt>Action</tt> context.

      *

-     * @param requestMap a Map of all request attributes.

-     * @param parameterMap a Map of all request parameters.

-     * @param sessionMap a Map of all session attributes.

-     * @param applicationMap a Map of all servlet context attributes.

-     * @param request the PortletRequest object.

-     * @param response the PortletResponse object.

-     * @param servletRequest the HttpServletRequest object.

+     * @param requestMap      a Map of all request attributes.

+     * @param parameterMap    a Map of all request parameters.

+     * @param sessionMap      a Map of all session attributes.

+     * @param applicationMap  a Map of all servlet context attributes.

+     * @param request         the PortletRequest object.

+     * @param response        the PortletResponse object.

+     * @param servletRequest  the HttpServletRequest object.

      * @param servletResponse the HttpServletResponse object.

-     * @param servletContext the ServletContext object.

-     * @param portletConfig the PortletConfig object.

-     * @param phase The portlet phase (render or action, see

-     *        {@link PortletConstants})

+     * @param servletContext  the ServletContext object.

+     * @param portletConfig   the PortletConfig object.

+     * @param phase           The portlet phase (render or action, see

+     *                        {@link PortletConstants})

      * @return a HashMap representing the <tt>Action</tt> context.

-     *

      * @throws IOException in case of IO errors

      */

-    public HashMap<String, Object> createContextMap(Map<String, Object> requestMap, Map<String, String[]> parameterMap,

-                                                    Map<String, Object> sessionMap, Map<String, Object> applicationMap,

-                                                    PortletRequest request, PortletResponse response, HttpServletRequest servletRequest,

-                                                    HttpServletResponse servletResponse, ServletContext servletContext,

-                                                    PortletConfig portletConfig, PortletPhase phase) throws IOException {

+    public Map<String, Object> createContextMap(Map<String, Object> requestMap, Map<String, String[]> parameterMap,

+                                                Map<String, Object> sessionMap, Map<String, Object> applicationMap,

+                                                PortletRequest request, PortletResponse response, HttpServletRequest servletRequest,

+                                                HttpServletResponse servletResponse, ServletContext servletContext,

+                                                PortletConfig portletConfig, PortletPhase phase) throws IOException {

 

         // TODO Must put http request/response objects into map for use with

         container.inject(servletRequest);

 

         // ServletActionContext

-        HashMap<String, Object> extraContext = new HashMap<String, Object>();

-        // The dummy servlet objects. Eases reuse of existing interceptors that uses the servlet objects.

-        extraContext.put(StrutsStatics.HTTP_REQUEST, servletRequest);

-        extraContext.put(StrutsStatics.HTTP_RESPONSE, servletResponse);

-        extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);

-        // End dummy servlet objects

-        extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(parameterMap).build());

-        extraContext.put(ActionContext.SESSION, sessionMap);

-        extraContext.put(ActionContext.APPLICATION, applicationMap);

-

-        extraContext.put(ActionContext.LOCALE, getLocale(request));

-

-        extraContext.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, getPortletContext());

-        extraContext.put(REQUEST, request);

-        extraContext.put(RESPONSE, response);

-        extraContext.put(PORTLET_CONFIG, portletConfig);

-        extraContext.put(PORTLET_NAMESPACE, portletNamespace);

-        extraContext.put(DEFAULT_ACTION_FOR_MODE, actionMap.get(request.getPortletMode()));

-        // helpers to get access to request/session/application scope

-        extraContext.put("request", requestMap);

-        extraContext.put("session", sessionMap);

-        extraContext.put("application", applicationMap);

-        extraContext.put("parameters", parameterMap);

-        extraContext.put(MODE_NAMESPACE_MAP, modeMap);

-        extraContext.put(PortletConstants.DEFAULT_ACTION_MAP, actionMap);

-

-        extraContext.put(PortletConstants.PHASE, phase);

+        Map<String, Object> extraContext = ActionContext.of(new HashMap<String, Object>())

+            .withServletRequest(servletRequest)

+            .withServletResponse(servletResponse)

+            .withServletContext(servletContext)

+            .withParameters(HttpParameters.create(parameterMap).build())

+            .withSession(sessionMap)

+            .withApplication(applicationMap)

+            .withLocale(getLocale(request))

+            .with(StrutsStatics.STRUTS_PORTLET_CONTEXT, getPortletContext())

+            .with(REQUEST, request)

+            .with(RESPONSE, response)

+            .with(PORTLET_CONFIG, portletConfig)

+            .with(PORTLET_NAMESPACE, portletNamespace)

+            .with(DEFAULT_ACTION_FOR_MODE, actionMap.get(request.getPortletMode()))

+            // helpers to get access to request/session/application scope

+            .with("request", requestMap)

+            .with("session", sessionMap)

+            .with("application", applicationMap)

+            .with("parameters", parameterMap)

+            .with(MODE_NAMESPACE_MAP, modeMap)

+            .with(PortletConstants.DEFAULT_ACTION_MAP, actionMap)

+            .with(PortletConstants.PHASE, phase)

+            .getContextMap();

 

         AttributeMap attrMap = new AttributeMap(extraContext);

         extraContext.put("attr", attrMap);

@@ -423,7 +415,7 @@
                 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 {

@@ -438,15 +430,14 @@
      * from the given action name and namespace. After that, the action is

      * executed and output channels throught the response object.

      *

-     * @param request the HttpServletRequest object.

-     * @param response the HttpServletResponse object.

-     * @param requestMap a Map of request attributes.

-     * @param parameterMap a Map of request parameters.

-     * @param sessionMap a Map of all session attributes.

-     * @param applicationMap a Map of all application attributes.

+     * @param request          the HttpServletRequest object.

+     * @param response         the HttpServletResponse object.

+     * @param requestMap       a Map of request attributes.

+     * @param parameterMap     a Map of request parameters.

+     * @param sessionMap       a Map of all session attributes.

+     * @param applicationMap   a Map of all application attributes.

      * @param portletNamespace the namespace or context of the action.

-     * @param phase The portlet phase (render or action, see {@link PortletConstants})

-     *

+     * @param phase            The portlet phase (render or action, see {@link PortletConstants})

      * @throws PortletException in case of errors

      */

     public void serviceAction(PortletRequest request, PortletResponse response, Map<String, Object> requestMap, Map<String, String[]> parameterMap,

@@ -475,9 +466,9 @@
             } else {

                 namespace = mapping.getNamespace();

             }

-            HashMap<String, Object> extraContext = createContextMap(requestMap, parameterMap,

-                    sessionMap, applicationMap, request, response, servletRequest, servletResponse,

-                    servletContext, getPortletConfig(), phase);

+            Map<String, Object> extraContext = createContextMap(requestMap, parameterMap,

+                sessionMap, applicationMap, request, response, servletRequest, servletResponse,

+                servletContext, getPortletConfig(), phase);

             extraContext.put(PortletConstants.ACTION_MAPPING, mapping);

             if (LOG.isDebugEnabled()) {

                 LOG.debug("Creating action proxy for name = " + actionName + ", namespace = " + namespace);

@@ -518,7 +509,6 @@
      *

      * @param portletRequest the PortletRequest object.

      * @param servletRequest the ServletRequest to use

-     *

      * @return the namespace of the action.

      */

     protected ActionMapping getActionMapping(final PortletRequest portletRequest, final HttpServletRequest servletRequest) {

@@ -551,6 +541,7 @@
 

     /**

      * Get the namespace part of the action path.

+     *

      * @param actionPath Full path to action

      * @return The namespace part.

      */

@@ -565,6 +556,7 @@
 

     /**

      * Get the action name part of the action path.

+     *

      * @param actionPath Full path to action

      * @return The action name.

      */

@@ -584,7 +576,7 @@
      * @param request the PortletRequest object.

      * @return a Map of all request parameters.

      * @throws IOException if an exception occurs while retrieving the parameter

-     *         map.

+     *                     map.

      */

     protected Map<String, String[]> getParameterMap(PortletRequest request) throws IOException {

         return new HashMap<String, String[]>(request.getParameterMap());

@@ -616,6 +608,7 @@
 

     /**

      * Convenience method to ease testing.

+     *

      * @param factory action proxy factory

      */

     protected void setActionProxyFactory(ActionProxyFactory factory) {

@@ -627,6 +620,7 @@
      * mode has been changed with the portal widgets, the action name is invalid, since the

      * action name belongs to the previous executing portlet mode. If this method evaluates to

      * <code>true</code> the <code>default&lt;Mode&gt;Action</code> is used instead.

+     *

      * @param request The portlet request.

      * @return <code>true</code> if the action should be reset.

      */

@@ -670,10 +664,9 @@
      * Method to create a PortletServletResponse matching the used Portlet API, to be overridden for JSR286 Dispatcher.

      *

      * @param response The Response used for building the wrapper.

-     *

      * @return The wrapper response for Servlet bound usage.

      */

-    protected PortletServletResponse createPortletServletResponse( PortletResponse response ) {

+    protected PortletServletResponse createPortletServletResponse(PortletResponse response) {

         return new PortletServletResponse(response);

     }

 

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 @@
                     portletNamespace, PortletPhase.EVENT_PHASE);

             if (LOG.isDebugEnabled()) LOG.debug("Leaving processEvent");

         } finally {

-            ActionContext.setContext(null);

+            ActionContext.clear();

         }

     }

 

@@ -70,7 +70,7 @@
                     getSessionMap(request), getApplicationMap(),

                     portletNamespace, PortletPhase.SERVE_RESOURCE_PHASE);

         } finally {

-            ActionContext.setContext(null);

+            ActionContext.clear();

         }

     }

 

diff --git a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
index 9016145..fcae67c 100644
--- a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
+++ b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
@@ -159,11 +159,11 @@
         velocityManager.init(servletContext);

 

         boolean usedJspFactory = false;

-        PageContext pageContext = (PageContext) ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT);

+        PageContext pageContext = ActionContext.getContext().getPageContext();

 

         if (pageContext == null && servlet != null) {

             pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true);

-            ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext);

+            ActionContext.getContext().withPageContext(pageContext);

             usedJspFactory = true;

         }

 

diff --git a/plugins/portlet/src/test/java/org/apache/struts2/StrutsTestCasePortletTests.java b/plugins/portlet/src/test/java/org/apache/struts2/StrutsTestCasePortletTests.java
index 1e7ea61..56d7cb0 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/StrutsTestCasePortletTests.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/StrutsTestCasePortletTests.java
@@ -134,12 +134,12 @@
         initSession(actionContext);
         applyAdditionalParams(actionContext);
         // set the action context to the one used by the proxy
-        ActionContext.setContext(actionContext);
+        ActionContext.bind(actionContext);
     }
 
     protected void initSession(ActionContext actionContext) {
         if (actionContext.getSession() == null) {
-            actionContext.setSession(new HashMap<String, Object>());
+            actionContext.setSession(new HashMap<>());
             request.setSession(new MockHttpSession(servletContext));
         }
     }
diff --git a/plugins/portlet/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java b/plugins/portlet/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java
index fbc87db..afc2c5c 100644
--- a/plugins/portlet/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java
+++ b/plugins/portlet/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java
@@ -43,7 +43,7 @@
         super.setUp();
 
         ActionProxy actionProxy = getActionProxy("/portlettest/test"); // creates new empty ActionContext
-        ActionContext.getContext().put(ActionContext.ACTION_INVOCATION, actionProxy.getInvocation());
+        ActionContext.getContext().withActionInvocation(actionProxy.getInvocation());
 
         PortletContext portletCtx = new MockPortletContext();
         ActionContext.getContext().put(StrutsStatics.STRUTS_PORTLET_CONTEXT, portletCtx);
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..4088ba7 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 @@
 

     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 @@
         portletConfig = (PortletConfig)mockPortletConfig.proxy();

 

 

-        ActionContext.setContext(new ActionContext(context));

+        ActionContext.of(context).bind();

     }

 

     public void testGetPhase() {

@@ -200,7 +200,7 @@
     }

 

     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..8c400a1 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 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.of(ctx).bind();

+

         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 void testActionPortletRequestAware() throws Exception {

         PortletRequest request = EasyMock.createMock(PortletRequest.class);

         Map<String, Object> ctx = new HashMap<>();

+        ActionContext actionContext = ActionContext.of(ctx).bind();

         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 @@
         PortletResponse response = EasyMock.createMock(PortletResponse.class);

         Map<String, Object> ctx = new HashMap<>();

         ctx.put(PortletConstants.RESPONSE, response);

+        ActionContext actionContext = ActionContext.of(ctx).bind();

         org.apache.struts2.portlet.action.PortletResponseAware action = EasyMock.createMock(org.apache.struts2.portlet.action.PortletResponseAware.class);

         action.withPortletResponse(response);

 

         ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);

-        EasyMock.expect(invocation.getInvocationContext()).andReturn(new ActionContext(ctx));

+        EasyMock.expect(invocation.getInvocationContext()).andReturn(actionContext);

         EasyMock.expect(invocation.getAction()).andReturn(action);

 

         EasyMock.replay(action);

diff --git a/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java b/plugins/portlet/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java
index 3cf0675..f324cf2 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 @@
 

 public class PortletStateInterceptorTest extends StrutsTestCasePortletTests {

 

-	private PortletStateInterceptor interceptor;

-	

-	public void setUp() throws Exception {

-	    super.setUp();

-		interceptor = new PortletStateInterceptor();

-	}

-	

-	public void testCopyValueStackFromEventToRenderPhase() throws Exception {

-		ActionResponse actionResponse = EasyMock.createNiceMock(ActionResponse.class);

-		ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);

-		

-		Map<String, Object> ctxMap = new HashMap<String, Object>();

-		ctxMap.put(PHASE, PortletPhase.ACTION_PHASE);

-		ctxMap.put(RESPONSE, actionResponse);

-		Map<String, Object> session = new HashMap<String, Object>();

-		

-		ActionContext ctx = new ActionContext(ctxMap);

-		ctx.setSession(session);

-		EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);

-		actionResponse.setRenderParameter(EVENT_ACTION, "true");

-		

-		ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();

-		EasyMock.expect(invocation.getStack()).andStubReturn(stack);

-		

-		EasyMock.replay(actionResponse);

-		EasyMock.replay(invocation);

-		

-		interceptor.intercept(invocation);

-		

-		EasyMock.verify(actionResponse);

-		EasyMock.verify(invocation);

-		

-		assertSame(stack, session.get(STACK_FROM_EVENT_PHASE));

-		

-	}

-	

-	public void testDoNotRestoreValueStackInRenderPhaseWhenProperPrg() throws Exception {

-		RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);

-		ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);

-		

-		

-		ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();

-		eventPhaseStack.set("testKey", "testValue");

-		

-		ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();

-		currentStack.set("anotherTestKey", "anotherTestValue");

-		

-		Map<String, Object> ctxMap = new HashMap<String, Object>();

-		Map<String, Object> session = new HashMap<String, Object>();

-		

-		session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);

-		

-		ctxMap.put(PHASE, PortletPhase.RENDER_PHASE);

-		ctxMap.put(REQUEST, renderRequest);

-		

-		ActionContext ctx = new ActionContext(ctxMap);

-		ctx.setSession(session);

-		

-		EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);

-		EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);

-		EasyMock.expect(invocation.getAction()).andStubReturn(new DefaultActionSupport());

-		EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");

-		

-		EasyMock.replay(renderRequest);

-		EasyMock.replay(invocation);

-		

-		interceptor.intercept(invocation);

-		

-		ValueStack resultingStack = invocation.getStack();

-		

-		assertNull(resultingStack.findValue("testKey"));

-		assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));

-		

-		

-	}

-	

-	public void testRestoreValueStackInRenderPhaseWhenNotProperPrg() throws Exception {

-		RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);

-		ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);

-		

-		ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();

-		eventPhaseStack.set("testKey", "testValue");

-		

-		ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();

-		currentStack.set("anotherTestKey", "anotherTestValue");

-		

-		EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);

-		

-		Map<String, Object> ctxMap = new HashMap<String, Object>();

-		Map<String, Object> session = new HashMap<String, Object>();

-		

-		session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);

-		

-		ctxMap.put(PHASE, PortletPhase.RENDER_PHASE);

-		ctxMap.put(REQUEST, renderRequest);

-		

-		ActionContext ctx = new ActionContext(ctxMap);

-		ctx.setSession(session);

-		

-		EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);

-		EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);

-		EasyMock.expect(invocation.getAction()).andStubReturn(new DirectRenderFromEventAction());

-		EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");

-		

-		EasyMock.replay(renderRequest);

-		EasyMock.replay(invocation);

-		

-		interceptor.intercept(invocation);

-		

-		ValueStack resultingStack = invocation.getStack();

-		assertEquals("testValue", resultingStack.findValue("testKey"));

-		assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));

-		

-		

-	}

+    private PortletStateInterceptor interceptor;

+

+    public void setUp() throws Exception {

+        super.setUp();

+        interceptor = new PortletStateInterceptor();

+    }

+

+    public void testCopyValueStackFromEventToRenderPhase() throws Exception {

+        ActionResponse actionResponse = EasyMock.createNiceMock(ActionResponse.class);

+        ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);

+

+        Map<String, Object> ctxMap = new HashMap<>();

+        ctxMap.put(PHASE, PortletPhase.ACTION_PHASE);

+        ctxMap.put(RESPONSE, actionResponse);

+        Map<String, Object> session = new HashMap<>();

+

+        ActionContext ctx = ActionContext.of(ctxMap).bind();

+        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.of(ctxMap).bind();

+        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.of(ctxMap).bind();

+        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..4994a13 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 @@
 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 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 @@
 

 /**

  * 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 @@
         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.of(new HashMap<>()).bind();

+        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 @@
         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 @@
         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 @@
         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 @@
         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 @@
 

         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 @@
         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 @@
 

         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 @@
         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 @@
         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..f33f122 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.RESPONSE;

 

 /**

+ *

  */

 public class PortletUrlHelperTest extends TestCase {

 

@@ -59,146 +60,143 @@
         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.of(new HashMap<>()).bind();

+        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 {

 

-		public WindowState getWindowState() {

-			return windowState;

-		}

+        private PortletMode portletMode;

+        private WindowState windowState;

+        private Map<String, String[]> parameters;

 

-		public void removePublicRenderParameter(String name) {

-		}

+        public PortletMode getPortletMode() {

+            return portletMode;

+        }

 

-		public void setPortletMode(PortletMode portletMode) throws PortletModeException {

-			this.portletMode = portletMode;

-		}

+        public WindowState getWindowState() {

+            return windowState;

+        }

 

-		public void setWindowState(WindowState windowState) throws WindowStateException {

-			this.windowState = windowState;

-		}

+        public void removePublicRenderParameter(String name) {

+        }

 

-		public void addProperty(String arg0, String arg1) {

-		}

+        public void setPortletMode(PortletMode portletMode) throws PortletModeException {

+            this.portletMode = portletMode;

+        }

 

-		public Map<String, String[]> getParameterMap() {

-			return parameters;

-		}

+        public void setWindowState(WindowState windowState) throws WindowStateException {

+            this.windowState = windowState;

+        }

 

-		public void setParameter(String name, String value) {

-			parameters.put(name, new String[]{value});

-		}

+        public void addProperty(String arg0, String arg1) {

+        }

 

-		public void setParameter(String name, String[] values) {

-			parameters.put(name, values);

-		}

+        public Map<String, String[]> getParameterMap() {

+            return parameters;

+        }

 

-		public void setParameters(Map<String, String[]> parameters) {

-			this.parameters = parameters;

-		}

+        public void setParameter(String name, String value) {

+            parameters.put(name, new String[]{value});

+        }

 

-		public void setProperty(String arg0, String arg1) {

-		}

+        public void setParameter(String name, String[] values) {

+            parameters.put(name, values);

+        }

 

-		public void setSecure(boolean arg0) throws PortletSecurityException {

-		}

+        public void setParameters(Map<String, String[]> parameters) {

+            this.parameters = parameters;

+        }

 

-		public void write(Writer arg0) throws IOException {

-		}

+        public void setProperty(String arg0, String arg1) {

+        }

 

-		public void write(Writer arg0, boolean arg1) throws IOException {

-		}

-    	

+        public void setSecure(boolean arg0) throws PortletSecurityException {

+        }

+

+        public void write(Writer arg0) 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..9beef5d 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.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.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 @@
     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 @@
 

         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,32 @@
 

         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);

-        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);

-        ctx.setValueStack(stack);

-        ActionInvocation ai = (ActionInvocation)mockActionInvocation.proxy();

-    	stack.getContext().put(ActionContext.ACTION_INVOCATION, ai);

-        ActionContext.setContext(ctx);

-        

+        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.getActionContext()

+            .withSession(new HashMap<>())

+            .with(PortletConstants.REQUEST, mockPortletReq.proxy())

+            .with(PortletConstants.RESPONSE, mockPortletRes.proxy())

+            .with(PortletConstants.PHASE, PortletPhase.RENDER_PHASE)

+            .with(PortletConstants.MODE_NAMESPACE_MAP, modeMap)

+            .with(PortletConstants.DEFAULT_ACTION_MAP, actionMap)

+            .with(STRUTS_PORTLET_CONTEXT, mockCtx.proxy())

+            .getContextMap();

+

+        ActionInvocation ai = (ActionInvocation) mockActionInvocation.proxy();

+

+        ActionContext.of(contextMap)

+            .withValueStack(stack)

+            .withContainer(dispatcher.getContainer())

+            .withActionInvocation(ai)

+            .bind();

     }

 

     public void tearDown() throws Exception {

@@ -169,13 +172,13 @@
     }

 

     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 +188,10 @@
         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 +203,19 @@
         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 +229,18 @@
         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 +260,10 @@
         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 +284,10 @@
         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 +320,7 @@
         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 +339,12 @@
         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 +359,49 @@
     }

 

     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 +411,21 @@
          * @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,28 +441,27 @@
          * @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);

-        	

-            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(", ");

-        		}

-        	}

+            sb.append("\n Expected: ");

+            describeTo(myExpectedMap, sb);

+            sb.append("\n Actual: ");

+            describeTo(myActualMap, sb);

+

             return sb;

         }

 

+        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/main/java/org/apache/struts2/rest/RestWorkflowInterceptor.java b/plugins/rest/src/main/java/org/apache/struts2/rest/RestWorkflowInterceptor.java
index f685fa3..6a981c8 100644
--- a/plugins/rest/src/main/java/org/apache/struts2/rest/RestWorkflowInterceptor.java
+++ b/plugins/rest/src/main/java/org/apache/struts2/rest/RestWorkflowInterceptor.java
@@ -21,12 +21,11 @@
 import com.opensymphony.xwork2.Action;
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.interceptor.ValidationAware;
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
+import com.opensymphony.xwork2.interceptor.ValidationAware;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
 
 import java.util.HashMap;
@@ -35,17 +34,17 @@
 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
 
 /**
- * <!-- START SNIPPET: description -->
- * <p>
  * An interceptor that makes sure there are not validation errors before allowing the interceptor chain to continue.
  * <b>This interceptor does not perform any validation</b>.
+ *
+ * <p>
+ * Copied from the {@link com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor}, this interceptor adds support
+ * for error handling of Restful operations.  For example, if an validation error is discovered, a map of errors
+ * is created and processed to be returned, using the appropriate content handler for rendering the body.
  * </p>
  *
- * <p>Copied from the {@link com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor}, this interceptor adds support for error handling of Restful
- * operations.  For example, if an validation error is discovered, a map of errors is created and processed to be
- * returned, using the appropriate content handler for rendering the body.</p>
- *
- * <p>This interceptor does nothing if the name of the method being invoked is specified in the <b>excludeMethods</b>
+ * <p>
+ * This interceptor does nothing if the name of the method being invoked is specified in the <b>excludeMethods</b>
  * parameter. <b>excludeMethods</b> accepts a comma-delimited list of method names. For example, requests to
  * <b>foo!input.action</b> and <b>foo!back.action</b> will be skipped by this interceptor if you set the
  * <b>excludeMethods</b> parameter to "input, back".
@@ -59,41 +58,27 @@
  * all methods for both parameters.
  * See {@link MethodFilterInterceptor} for more info.
  * </p>
- * <!-- END SNIPPET: description -->
  *
- * <p><u>Interceptor parameters:</u></p>
- *
- * <!-- START SNIPPET: parameters -->
+ * <b>Interceptor parameters:</b>
  *
  * <ul>
- *
  * <li>inputResultName - Default to "input". Determine the result name to be returned when
  * an action / field error is found.</li>
- *
  * </ul>
  *
- * <!-- END SNIPPET: parameters -->
- *
- * <p><u>Extending the interceptor:</u></p>
- *
- * <!-- START SNIPPET: extending -->
- * <p>
+ * <b>Extending the interceptor:</b>
  * There are no known extension points for this interceptor.
- * </p>
- * <!-- END SNIPPET: extending -->
  *
- * <p><u>Example code:</u></p>
+ * <b>Example code:</b>
  *
  * <pre>
- * <!-- START SNIPPET: example -->
- * 
  * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
  *     &lt;interceptor-ref name="params"/&gt;
  *     &lt;interceptor-ref name="validation"/&gt;
  *     &lt;interceptor-ref name="workflow"/&gt;
  *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
  * &lt;/action&gt;
- * 
+ *
  * &lt;-- In this case myMethod as well as mySecondMethod of the action class
  *        will not pass through the workflow process --&gt;
  * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
@@ -114,13 +99,11 @@
  *     &lt;interceptor-ref name="validation"/&gt;
  *     &lt;interceptor-ref name="workflow"&gt;
  *        &lt;param name="inputResultName"&gt;error&lt;/param&gt;
-*         &lt;param name="excludeMethods"&gt;*&lt;/param&gt;
-*         &lt;param name="includeMethods"&gt;myWorkflowMethod&lt;/param&gt;
+ *         &lt;param name="excludeMethods"&gt;*&lt;/param&gt;
+ *         &lt;param name="includeMethods"&gt;myWorkflowMethod&lt;/param&gt;
  *     &lt;/interceptor-ref&gt;
  *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
  * &lt;/action&gt;
- *
- * <!-- END SNIPPET: example -->
  * </pre>
  *
  * @author Jason Carreira
@@ -130,14 +113,14 @@
  * @author tm_jee
  */
 public class RestWorkflowInterceptor extends MethodFilterInterceptor {
-	
-	private static final long serialVersionUID = 7563014655616490865L;
 
-	private static final Logger LOG = LogManager.getLogger(RestWorkflowInterceptor.class);
-	
-	private String inputResultName = Action.INPUT;
-	
-	private ContentTypeHandlerManager manager;
+    private static final long serialVersionUID = 7563014655616490865L;
+
+    private static final Logger LOG = LogManager.getLogger(RestWorkflowInterceptor.class);
+
+    private String inputResultName = Action.INPUT;
+
+    private ContentTypeHandlerManager manager;
 
     private String postMethodName = "create";
     private String editMethodName = "edit";
@@ -172,26 +155,26 @@
     }
 
     @Inject
-	public void setContentTypeHandlerManager(ContentTypeHandlerManager mgr) {
-	    this.manager = mgr;
-	}
-	
-	/**
-	 * Set the <code>inputResultName</code> (result name to be returned when 
-	 * a action / field error is found registered). Default to {@link Action#INPUT}
-	 * 
-	 * @param inputResultName what result name to use when there was validation error(s).
-	 */
-	public void setInputResultName(String inputResultName) {
-		this.inputResultName = inputResultName;
-	}
-	
-	/**
-	 * Intercept {@link ActionInvocation} and processes the errors using the {@link org.apache.struts2.rest.handler.ContentTypeHandler}
-	 * appropriate for the request.  
-	 * 
-	 * @return String result name
-	 */
+    public void setContentTypeHandlerManager(ContentTypeHandlerManager mgr) {
+        this.manager = mgr;
+    }
+
+    /**
+     * Set the <code>inputResultName</code> (result name to be returned when
+     * a action / field error is found registered). Default to {@link Action#INPUT}
+     *
+     * @param inputResultName what result name to use when there was validation error(s).
+     */
+    public void setInputResultName(String inputResultName) {
+        this.inputResultName = inputResultName;
+    }
+
+    /**
+     * Intercept {@link ActionInvocation} and processes the errors using the {@link org.apache.struts2.rest.handler.ContentTypeHandler}
+     * appropriate for the request.
+     *
+     * @return String result name
+     */
     protected String doIntercept(ActionInvocation invocation) throws Exception {
         Object action = invocation.getAction();
 
@@ -199,26 +182,26 @@
             ValidationAware validationAwareAction = (ValidationAware) action;
 
             if (validationAwareAction.hasErrors()) {
-          		LOG.debug("Errors on action {}, returning result name 'input'", validationAwareAction);
-            	ActionMapping mapping = (ActionMapping) ActionContext.getContext().get(ServletActionContext.ACTION_MAPPING);
-            	String method = inputResultName;
+                LOG.debug("Errors on action {}, returning result name 'input'", validationAwareAction);
+                ActionMapping mapping = ActionContext.getContext().getActionMapping();
+                String method = inputResultName;
                 if (postMethodName.equals(mapping.getMethod())) {
-                   method = newMethodName;
+                    method = newMethodName;
                 } else if (putMethodName.equals(mapping.getMethod())) {
-                   method = editMethodName;
+                    method = editMethodName;
                 }
-                
-                
-            	HttpHeaders info = new DefaultHttpHeaders()
-            	    .disableCaching()
-            	    .renderResult(method)
-            	    .withStatus(validationFailureStatusCode);
-            	
-            	Map<String, Object> errors = new HashMap<>();
-            	
-            	errors.put("actionErrors", validationAwareAction.getActionErrors());
-            	errors.put("fieldErrors", validationAwareAction.getFieldErrors());
-            	return manager.handleResult(invocation, info, errors);
+
+
+                HttpHeaders info = new DefaultHttpHeaders()
+                    .disableCaching()
+                    .renderResult(method)
+                    .withStatus(validationFailureStatusCode);
+
+                Map<String, Object> errors = new HashMap<>();
+
+                errors.put("actionErrors", validationAwareAction.getActionErrors());
+                errors.put("fieldErrors", validationAwareAction.getFieldErrors());
+                return manager.handleResult(invocation, info, errors);
             }
         }
 
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..3e2a41e 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.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 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 @@
         mockResponse = new MockHttpServletResponse();
         mockRequest = new MockHttpServletRequest();
         mockRequest.setMethod("GET");
-        ActionContext.setContext(new ActionContext(new HashMap()));
-        ServletActionContext.setRequest(mockRequest);
-        ServletActionContext.setResponse(mockResponse);
+        ActionContext actionContext = ActionContext.of(new HashMap<>()).bind();
+        actionContext.withServletRequest(mockRequest);
+        actionContext.withServletResponse(mockResponse);
 
         invocation = new MockActionInvocation();
         invocation.setProxy(new MockActionProxy());
@@ -69,7 +69,7 @@
     @Override
     public void tearDown() {
         mockRequest = null;
-        mockRequest = null;
+        mockResponse = null;
         mgr = null;
     }
 
@@ -106,7 +106,7 @@
         assertEquals(0, mockResponse.getContentLength());
     }
 
-    public void testHandleValidationError() throws Exception {
+    public void testHandleValidationError() {
         mockRequest.setMethod("PUT");
 
     }
@@ -129,7 +129,7 @@
         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 @@
     }
 
     /** 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 @@
         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..45d82e4 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.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,10 @@
         }, null);
         wf.setContentTypeHandlerManager((ContentTypeHandlerManager) mockContentTypeHandlerManager.proxy());
 
-        ActionContext.setContext(new ActionContext(new HashMap<String, Object>() {{
-            put(ServletActionContext.ACTION_MAPPING, new ActionMapping());
-        }}));
+        ActionContext.of(new HashMap<>())
+            .withActionMapping(new ActionMapping())
+            .bind();
+
         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..0398ebf 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,8 +51,7 @@
             "</object>";
         handler = new JuneauXmlHandler();
         ai = new MockActionInvocation();
-        ActionContext context = new ActionContext(new HashMap<String, Object>());
-        context.setLocale(Locale.US);
+        ActionContext context = ActionContext.of(new HashMap<>()).withLocale(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..b6bd1ac 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
@@ -22,13 +22,17 @@
 import com.opensymphony.module.sitemesh.RequestConstants;

 import com.opensymphony.xwork2.ActionContext;

 import freemarker.core.InvalidReferenceException;

-import freemarker.template.*;

+import freemarker.template.Configuration;

+import freemarker.template.ObjectWrapper;

+import freemarker.template.SimpleHash;

+import freemarker.template.Template;

+import freemarker.template.TemplateException;

+import freemarker.template.TemplateModel;

 import org.apache.logging.log4j.LogManager;

 import org.apache.logging.log4j.Logger;

 import org.apache.struts2.ServletActionContext;

-import org.apache.struts2.StrutsStatics;

+import org.apache.struts2.StrutsException;

 import org.apache.struts2.dispatcher.Dispatcher;

-import org.apache.struts2.dispatcher.StrutsRequestWrapper;

 import org.apache.struts2.dispatcher.listener.StrutsListener;

 import org.apache.struts2.views.freemarker.FreemarkerManager;

 import org.apache.struts2.views.freemarker.ScopesHashModel;

@@ -66,7 +70,7 @@
 

     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?");

             }

@@ -146,10 +150,13 @@
         setBrowserCachingPolicy(response);

 

         ServletContext servletContext = getServletContext();

-        ScopesHashModel model = (ScopesHashModel) request.getAttribute(freemarkerManager.ATTR_TEMPLATE_MODEL);

+        ScopesHashModel model = (ScopesHashModel) request.getAttribute(FreemarkerManager.ATTR_TEMPLATE_MODEL);

         try {

             if (model == null) {

                 ActionContext ctx = ServletActionContext.getActionContext(request);

+                if (ctx == null) {

+                    throw new StrutsException("ActionContext is null! Freemarker accessed out of action?");

+                }

                 model = freemarkerManager.buildTemplateModel(ctx.getValueStack(), ctx.getActionInvocation().getAction(), servletContext, request, response, wrapper);

             }

 

@@ -165,15 +172,15 @@
             }

         } catch (InvalidReferenceException x) {

             // this exception is thrown if there is an error processing a reference.  We want to report these!

-            HttpServletRequest req = ((StrutsRequestWrapper) ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"));

+            HttpServletRequest req = ActionContext.getContext().getServletRequest();

             String resultCode = ActionContext.getContext().getActionInvocation().getResultCode();

-            if (req == null){

+            if (req == null) {

                 req = request;

             }

 

             StringBuilder msgBuf = new StringBuilder("Error applying freemarker template to\n       request: ");

             msgBuf.append(req.getRequestURL());

-            if (req.getQueryString() != null){

+            if (req.getQueryString() != null) {

                 msgBuf.append("?").append(req.getQueryString());

             }

             msgBuf.append(" with resultCode: ").append(resultCode).append(".\n\n").append(x.getMessage());

@@ -186,14 +193,14 @@
             // constructor-passed throwable won't show up automatically in

             // stack traces.

             try {

-                e.getClass().getMethod("initCause", new Class[]{Throwable.class}).invoke(e, new Object[]{x});

+                e.getClass().getMethod("initCause", new Class[]{Throwable.class}).invoke(e, x);

             } catch (Exception ex) {

                 // Can't set init cause, we're probably running on a pre-1.4

                 // JDK, oh well...

             }

             throw e;

         } catch (TemplateException te) {

-            if (config.getTemplateExceptionHandler().getClass().getName().indexOf("Debug") != -1) {

+            if (config.getTemplateExceptionHandler().getClass().getName().contains("Debug")) {

                 this.log("Error executing FreeMarker template", te);

             } else {

                 ServletException e = new ServletException("Error executing FreeMarker template", te);

@@ -202,7 +209,7 @@
                 // constructor-passed throwable won't show up automatically in

                 // stack traces.

                 try {

-                    e.getClass().getMethod("initCause", new Class[]{Throwable.class}).invoke(e, new Object[]{te});

+                    e.getClass().getMethod("initCause", new Class[]{Throwable.class}).invoke(e, te);

                 } catch (Exception ex) {

                     // Can't set init cause, we're probably running on a pre-1.4

                     // JDK, oh well...

@@ -320,7 +327,7 @@
             // HTTP/1.0

             res.setHeader("Pragma", "no-cache");

             // Last resort for those that ignore all of the above

-            res.setHeader("Expires", freemarkerManager.EXPIRATION_DATE);

+            res.setHeader("Expires", FreemarkerManager.EXPIRATION_DATE);

         }

     }

 }

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..f5a0cd1 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,12 +95,12 @@
             // 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();

                 vs.push(action);

-                ctx.setActionInvocation(new DummyActionInvocation(action));

+                ctx.withActionInvocation(new DummyActionInvocation(action));

             }

         }

 

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 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?");

         }

diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java
index 99f0059..e955b32 100644
--- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java
+++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java
@@ -62,7 +62,7 @@
         HttpServletRequest req = (HttpServletRequest) stack.getContext().get(ServletActionContext.HTTP_REQUEST);
         HttpServletResponse res = (HttpServletResponse) stack.getContext().get(ServletActionContext.HTTP_RESPONSE);
         Component bean = getBean(stack, req, res);
-        Container container = (Container) stack.getContext().get(ActionContext.CONTAINER);
+        Container container = stack.getActionContext().getContainer();
         container.inject(bean);
         // get the parameters
         Map params = createPropertyMap(ctx, node);
diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/result/VelocityResult.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/result/VelocityResult.java
index c62f687..68b5645 100644
--- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/result/VelocityResult.java
+++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/result/VelocityResult.java
@@ -128,11 +128,11 @@
         velocityManager.init(servletContext);
 
         boolean usedJspFactory = false;
-        PageContext pageContext = (PageContext) ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT);
+        PageContext pageContext = (PageContext) ActionContext.getContext().getPageContext();
 
         if (pageContext == null && servlet != null) {
             pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true);
-            ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext);
+            ActionContext.getContext().withPageContext(pageContext);
             usedJspFactory = true;
         }
 
diff --git a/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/result/VelocityResultTest.java b/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/result/VelocityResultTest.java
index 7fa4bb9..b027e92 100644
--- a/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/result/VelocityResultTest.java
+++ b/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/result/VelocityResultTest.java
@@ -93,7 +93,6 @@
         namespace = "/html";
         result = new VelocityResult();
         stack = ActionContext.getContext().getValueStack();
-        ActionContext.getContext().setValueStack(stack);
         velocity = new TestVelocityEngine();
         mockActionProxy = new Mock(ActionProxy.class);
         mockActionProxy.expectAndReturn("getNamespace", "/html");