This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch feature/WW-5333-attribute-map
in repository https://gitbox.apache.org/repos/asf/struts.git
commit 91143fe4a300c78f02a6ec803feae993cf64dce8
Author: Lukasz Lenart <lukaszlen...@apache.org>
AuthorDate: Sat Nov 4 15:50:59 2023 +0100
WW-5333 Refactors AttributeMap
---
.../java/org/apache/struts2/components/Set.java | 16 +-
.../struts2/{util => dispatcher}/AttributeMap.java | 71 +++---
.../org/apache/struts2/dispatcher/Dispatcher.java | 11 +-
.../struts2/dispatcher/DispatcherConstants.java | 33 +++
.../org/apache/struts2/dispatcher/RequestMap.java | 4 +-
.../debugging/DebuggingInterceptor.java | 16 +-
.../org/apache/struts2/views/jsp/TagUtils.java | 2 +-
.../org/apache/struts2/views/util/ContextUtil.java | 7 +-
.../struts2/dispatcher/AttributeMapTest.java | 279 +++++++++++++++++++++
.../portlet/dispatcher/Jsr168Dispatcher.java | 13 +-
10 files changed, 390 insertions(+), 62 deletions(-)
diff --git a/core/src/main/java/org/apache/struts2/components/Set.java
b/core/src/main/java/org/apache/struts2/components/Set.java
index 8cb1ca461..cca990ea8 100644
--- a/core/src/main/java/org/apache/struts2/components/Set.java
+++ b/core/src/main/java/org/apache/struts2/components/Set.java
@@ -20,6 +20,7 @@ package org.apache.struts2.components;
import java.io.Writer;
+import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -53,16 +54,11 @@ import com.opensymphony.xwork2.util.ValueStack;
* <!-- START SNIPPET: params -->
*
* <ul>
- *
* <li>var* (String): The name of the new variable that is assigned the value
of <i>value</i></li>
- *
* <li>value (Object): The value that is assigned to the variable named
<i>name</i></li>
- *
* <li>scope (String): The scope in which to assign the variable. Can be
<b>application</b>, <b>session</b>,
* <b>request</b>, <b>page</b>, or <b>action</b>. By default it is
<b>action</b>.</li>
- *
* <li>Note: With the <b>action</b> scope, the variable is <em>also</em>
assigned to the <b>page</b> scope.
- *
* </ul>
*
* <!-- END SNIPPET: params -->
@@ -107,16 +103,16 @@ public class Set extends ContextBean {
body="";
- if ("application".equalsIgnoreCase(scope)) {
+ if (DispatcherConstants.APPLICATION.equalsIgnoreCase(scope)) {
stack.setValue("#application['" + getVar() + "']", o);
- } else if ("session".equalsIgnoreCase(scope)) {
+ } else if (DispatcherConstants.SESSION.equalsIgnoreCase(scope)) {
stack.setValue("#session['" + getVar() + "']", o);
- } else if ("request".equalsIgnoreCase(scope)) {
+ } else if (DispatcherConstants.REQUEST.equalsIgnoreCase(scope)) {
stack.setValue("#request['" + getVar() + "']", o);
- } else if ("page".equalsIgnoreCase(scope)) {
+ } else if (DispatcherConstants.PAGE.equalsIgnoreCase(scope)) {
stack.setValue("#attr['" + getVar() + "']", o, false);
} else {
- // Default scope is action. Note: The action acope handling also
adds the var to the page scope.
+ // Default scope is action. Note: The action scope handling also
adds the var to the page scope.
stack.getContext().put(getVar(), o);
stack.setValue("#attr['" + getVar() + "']", o, false);
}
diff --git a/core/src/main/java/org/apache/struts2/util/AttributeMap.java
b/core/src/main/java/org/apache/struts2/dispatcher/AttributeMap.java
similarity index 64%
rename from core/src/main/java/org/apache/struts2/util/AttributeMap.java
rename to core/src/main/java/org/apache/struts2/dispatcher/AttributeMap.java
index ea088b4af..01855ab4b 100644
--- a/core/src/main/java/org/apache/struts2/util/AttributeMap.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/AttributeMap.java
@@ -16,14 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.struts2.util;
+package org.apache.struts2.dispatcher;
-import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsStatics;
import javax.servlet.jsp.PageContext;
+import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -38,17 +40,16 @@ import java.util.Set;
* <li>Session scope</li>
* <li>Application scope</li>
* </ul>
- *
+ * <p>
* A object is searched in the order above, starting from page and ending at
application scope.
- *
*/
-public class AttributeMap implements Map {
+public class AttributeMap extends AbstractMap<String, Object> {
protected static final String UNSUPPORTED = "method makes no sense for a
simplified map";
- Map context;
+ private final Map<String, Object> context;
- public AttributeMap(Map context) {
+ public AttributeMap(Map<String, Object> context) {
this.context = context;
}
@@ -73,18 +74,22 @@ public class AttributeMap implements Map {
}
@Override
- public Set entrySet() {
- return Collections.EMPTY_SET;
+ public Set<Map.Entry<String, Object>> entrySet() {
+ return Collections.unmodifiableSet(this.context.entrySet());
}
@Override
public Object get(Object key) {
+ if (key == null) {
+ return null;
+ }
+
PageContext pc = getPageContext();
if (pc == null) {
- Map request = (Map) context.get("request");
- Map session = (Map) context.get("session");
- Map application = (Map) context.get("application");
+ RequestMap request = (RequestMap)
context.get(DispatcherConstants.REQUEST);
+ SessionMap session = (SessionMap)
context.get(DispatcherConstants.SESSION);
+ ApplicationMap application = (ApplicationMap)
context.get(DispatcherConstants.APPLICATION);
if ((request != null) && (request.get(key) != null)) {
return request.get(key);
@@ -94,26 +99,22 @@ public class AttributeMap implements Map {
return application.get(key);
}
} else {
- try {
- return pc.findAttribute(key.toString());
- } catch (NullPointerException npe) {
- return null;
- }
+ return pc.findAttribute(key.toString());
}
return null;
}
@Override
- public Set keySet() {
- return Collections.EMPTY_SET;
+ public Set<String> keySet() {
+ return Collections.unmodifiableSet(this.context.keySet());
}
@Override
- public Object put(Object key, Object value) {
+ public Object put(String key, Object value) {
PageContext pc = getPageContext();
if (pc != null) {
- pc.setAttribute(key.toString(), value);
+ pc.setAttribute(key, value);
}
return null;
@@ -135,21 +136,21 @@ public class AttributeMap implements Map {
}
@Override
- public Collection values() {
- return Collections.EMPTY_SET;
+ public Collection<Object> values() {
+ return Collections.unmodifiableCollection(this.context.values());
}
private PageContext getPageContext() {
- return (PageContext) context.get(ServletActionContext.PAGE_CONTEXT);
+ return (PageContext) context.get(StrutsStatics.PAGE_CONTEXT);
}
@Override
public String toString() {
return "AttributeMap {" +
- "request=" + toStringSafe(context.get("request")) +
- ", session=" + toStringSafe(context.get("session")) +
- ", application=" + toStringSafe(context.get("application")) +
- '}';
+ "request=" +
toStringSafe(context.get(DispatcherConstants.REQUEST)) +
+ ", session=" +
toStringSafe(context.get(DispatcherConstants.SESSION)) +
+ ", application=" +
toStringSafe(context.get(DispatcherConstants.APPLICATION)) +
+ '}';
}
private String toStringSafe(Object obj) {
@@ -163,4 +164,18 @@ public class AttributeMap implements Map {
}
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof AttributeMap)) return false;
+ if (!super.equals(o)) return false;
+ AttributeMap that = (AttributeMap) o;
+ return Objects.equals(context, that.context);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), context);
+ }
+
}
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 51ae95d27..e378633e2 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
@@ -67,7 +67,6 @@ import
org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
-import org.apache.struts2.util.AttributeMap;
import org.apache.struts2.util.ObjectFactoryDestroyable;
import org.apache.struts2.util.fs.JBossFileManager;
@@ -779,14 +778,14 @@ public class Dispatcher {
.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)
+ .with(DispatcherConstants.REQUEST, requestMap)
+ .with(DispatcherConstants.SESSION, sessionMap)
+ .with(DispatcherConstants.APPLICATION, applicationMap)
+ .with(DispatcherConstants.PARAMETERS, parameters)
.getContextMap();
AttributeMap attrMap = new AttributeMap(extraContext);
- extraContext.put("attr", attrMap);
+ extraContext.put(DispatcherConstants.ATTRIBUTES, attrMap);
return extraContext;
}
diff --git
a/core/src/main/java/org/apache/struts2/dispatcher/DispatcherConstants.java
b/core/src/main/java/org/apache/struts2/dispatcher/DispatcherConstants.java
new file mode 100644
index 000000000..7ccd220cb
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/dispatcher/DispatcherConstants.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.dispatcher;
+
+public final class DispatcherConstants {
+
+ public static final String REQUEST = "request";
+ public static final String RESPONSE = "response";
+ public static final String SESSION = "session";
+ public static final String APPLICATION = "application";
+ public static final String PARAMETERS = "parameters";
+ public static final String ATTRIBUTES = "attr";
+ public static final String PAGE = "page";
+
+ private DispatcherConstants() {
+ }
+}
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
b/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
index a75dffb75..b8e5b8e67 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
@@ -32,8 +32,9 @@ public class RequestMap extends AbstractMap<String, Object>
implements Serializa
private static final long serialVersionUID = -7675640869293787926L;
+ private final HttpServletRequest request;
+
private Set<Entry<String, Object>> entries;
- private HttpServletRequest request;
/**
* Saves the request to use as the backing for getting and setting values
@@ -44,7 +45,6 @@ public class RequestMap extends AbstractMap<String, Object>
implements Serializa
this.request = request;
}
-
/**
* Removes all attributes from the request as well as clears entries in
this map.
*/
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 62f871690..7f116829e 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
@@ -29,8 +29,10 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.PrepareOperations;
+import org.apache.struts2.dispatcher.RequestMap;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.freemarker.FreemarkerResult;
@@ -97,11 +99,13 @@ public class DebuggingInterceptor extends
AbstractInterceptor {
private final static Logger LOG =
LogManager.getLogger(DebuggingInterceptor.class);
- private String[] ignorePrefixes = new String[]{"org.apache.struts.",
- "com.opensymphony.xwork2.", "xwork."};
- private String[] _ignoreKeys = new String[]{"application", "session",
- "parameters", "request"};
- private HashSet<String> ignoreKeys = new
HashSet<>(Arrays.asList(_ignoreKeys));
+ private final String[] ignorePrefixes = new String[]{"org.apache.struts.",
"com.opensymphony.xwork2.", "xwork."};
+ private final HashSet<String> ignoreKeys = new HashSet<>(Arrays.asList(
+ DispatcherConstants.APPLICATION,
+ DispatcherConstants.SESSION,
+ DispatcherConstants.PARAMETERS,
+ DispatcherConstants.REQUEST
+ ));
private final static String XML_MODE = "xml";
private final static String CONSOLE_MODE = "console";
@@ -319,7 +323,7 @@ public class DebuggingInterceptor extends
AbstractInterceptor {
}
}
writer.endNode();
- Map<String, Object> requestMap = (Map<String, Object>)
ctx.get("request");
+ RequestMap requestMap = (RequestMap)
ctx.get(DispatcherConstants.REQUEST);
serializeIt(requestMap, "request", writer,
filterValueStack(requestMap));
serializeIt(ctx.getSession(), "session", writer, new ArrayList<>());
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 f813e00d1..044f5d1af 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
@@ -24,7 +24,7 @@ import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.util.AttributeMap;
+import org.apache.struts2.dispatcher.AttributeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.PageContext;
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 4ab3c2786..0f1c85aae 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
@@ -20,6 +20,7 @@ package org.apache.struts2.views.util;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.util.StrutsUtil;
import javax.servlet.http.HttpServletRequest;
@@ -31,9 +32,9 @@ import java.util.Map;
* Value Stack's Context related Utilities.
*/
public class ContextUtil {
- public static final String REQUEST = "request";
- public static final String RESPONSE = "response";
- public static final String SESSION = "session";
+ public static final String REQUEST = DispatcherConstants.REQUEST;
+ public static final String RESPONSE = DispatcherConstants.RESPONSE;
+ public static final String SESSION = DispatcherConstants.SESSION;
public static final String BASE = "base";
public static final String STACK = "stack";
public static final String STRUTS = "struts";
diff --git
a/core/src/test/java/org/apache/struts2/dispatcher/AttributeMapTest.java
b/core/src/test/java/org/apache/struts2/dispatcher/AttributeMapTest.java
new file mode 100644
index 000000000..7de0be46e
--- /dev/null
+++ b/core/src/test/java/org/apache/struts2/dispatcher/AttributeMapTest.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.dispatcher;
+
+import org.apache.struts2.StrutsStatics;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpSession;
+import org.springframework.mock.web.MockPageContext;
+import org.springframework.mock.web.MockServletContext;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.PageContext;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertThrows;
+
+public class AttributeMapTest {
+
+ @Test
+ public void shouldRetrievePageContextAttribute() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+ }
+
+ @Test
+ public void shouldRetrieveRequestAttribute() {
+ // given
+ HttpServletRequest request = new MockHttpServletRequest();
+ request.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(DispatcherConstants.REQUEST, new RequestMap(request));
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+ }
+
+ @Test
+ public void shouldRetrieveSessionAttribute() {
+ // given
+ HttpSession session = new MockHttpSession();
+ session.setAttribute("attr", "value");
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setSession(session);
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(DispatcherConstants.SESSION, new SessionMap(request));
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+ }
+
+ @Test
+ public void shouldRetrieveApplicationAttribute() {
+ // given
+ ServletContext sc = new MockServletContext();
+ sc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(DispatcherConstants.APPLICATION, new ApplicationMap(sc));
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+ }
+
+ @Test
+ public void shouldReturnNullIfKeyIsNull() {
+ // given
+ // when
+ AttributeMap am = new AttributeMap(new HashMap<>());
+ Object value = am.get(null);
+
+ // then
+ assertNull(value);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void shouldThrowExceptionOnRemove() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+
+ // then
+ Object value = am.get("attr");
+ assertEquals("value", value);
+
+ assertThrows(UnsupportedOperationException.class, () ->
am.remove("attr"));
+ }
+
+ @Test
+ public void shouldThrowExceptionOnClear() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+
+ // when
+ assertThrows(UnsupportedOperationException.class, am::clear);
+ }
+
+ @Test
+ public void shouldThrowExceptionOnIsEmpty() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+
+ // when
+ assertThrows(UnsupportedOperationException.class, am::isEmpty);
+ }
+
+ @Test
+ public void shouldThrowExceptionOnContainsValue() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+
+ // when
+ assertThrows(UnsupportedOperationException.class, () ->
am.containsValue("attr"));
+ }
+
+ @Test
+ public void shouldThrowExceptionOnPutAll() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+
+ // when
+ assertThrows(UnsupportedOperationException.class, () -> am.putAll(new
HashMap<>()));
+ }
+
+ @Test
+ public void shouldThrowExceptionOnSize() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+
+ // when
+ assertThrows(UnsupportedOperationException.class, am::size);
+ }
+
+ @Test
+ public void shouldGetAllValues() {
+ // given
+ PageContext pc = new MockPageContext();
+ pc.setAttribute("attr", "value");
+
+ Map<String, Object> context = new HashMap<String, Object>() {{
+ put(StrutsStatics.PAGE_CONTEXT, pc);
+ }};
+
+ // when
+ AttributeMap am = new AttributeMap(context);
+ Object value = am.get("attr");
+
+ // then
+ assertEquals("value", value);
+
+ // when
+ Collection<Object> values = am.values();
+
+ // then
+ assertThat(values, hasItem(pc));
+ }
+
+}
\ No newline at end of file
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 cc7b00c5b..394ef6042 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
@@ -33,6 +33,7 @@ import org.apache.struts2.StrutsException;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.ApplicationMap;
import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.RequestMap;
import org.apache.struts2.dispatcher.SessionMap;
@@ -48,7 +49,7 @@ import
org.apache.struts2.portlet.context.PortletActionContext;
import org.apache.struts2.portlet.servlet.PortletServletContext;
import org.apache.struts2.portlet.servlet.PortletServletRequest;
import org.apache.struts2.portlet.servlet.PortletServletResponse;
-import org.apache.struts2.util.AttributeMap;
+import org.apache.struts2.dispatcher.AttributeMap;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
@@ -377,7 +378,7 @@ public class Jsr168Dispatcher extends GenericPortlet
implements StrutsStatics {
container.inject(servletRequest);
// ServletActionContext
- Map<String, Object> extraContext = ActionContext.of(new
HashMap<String, Object>())
+ Map<String, Object> extraContext = ActionContext.of(new HashMap<>())
.withServletRequest(servletRequest)
.withServletResponse(servletResponse)
.withServletContext(servletContext)
@@ -392,10 +393,10 @@ public class Jsr168Dispatcher extends GenericPortlet
implements StrutsStatics {
.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(DispatcherConstants.REQUEST, requestMap)
+ .with(DispatcherConstants.SESSION, sessionMap)
+ .with(DispatcherConstants.APPLICATION, applicationMap)
+ .with(DispatcherConstants.PARAMETERS, parameterMap)
.with(MODE_NAMESPACE_MAP, modeMap)
.with(PortletConstants.DEFAULT_ACTION_MAP, actionMap)
.with(PortletConstants.PHASE, phase)