Skip to content

Commit 1069cf8

Browse files
authored
Merge pull request #615 from apache/WW-2815-xstream
[WW-2815] Refactors XStreamHandler to allow to provide a custom configuration
2 parents 738d279 + 802afb0 commit 1069cf8

File tree

6 files changed

+213
-29
lines changed

6 files changed

+213
-29
lines changed

plugins/rest/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,23 @@
2121
import com.opensymphony.xwork2.ActionInvocation;
2222
import com.opensymphony.xwork2.ModelDriven;
2323
import com.thoughtworks.xstream.XStream;
24+
import com.thoughtworks.xstream.io.xml.StaxDriver;
2425
import com.thoughtworks.xstream.security.ArrayTypePermission;
2526
import com.thoughtworks.xstream.security.ExplicitTypePermission;
26-
import com.thoughtworks.xstream.security.NoTypePermission;
2727
import com.thoughtworks.xstream.security.NullPermission;
2828
import com.thoughtworks.xstream.security.PrimitiveTypePermission;
2929
import com.thoughtworks.xstream.security.TypePermission;
3030
import org.apache.logging.log4j.LogManager;
3131
import org.apache.logging.log4j.Logger;
32+
import org.apache.struts2.rest.handler.xstream.XStreamAllowedClassNames;
33+
import org.apache.struts2.rest.handler.xstream.XStreamAllowedClasses;
34+
import org.apache.struts2.rest.handler.xstream.XStreamPermissionProvider;
35+
import org.apache.struts2.rest.handler.xstream.XStreamProvider;
3236

3337
import java.io.IOException;
3438
import java.io.Reader;
3539
import java.io.Writer;
3640
import java.util.Collection;
37-
import java.util.Date;
3841
import java.util.Map;
3942
import java.util.Set;
4043

@@ -58,19 +61,15 @@ public void toObject(ActionInvocation invocation, Reader in, Object target) {
5861
xstream.fromXML(in, target);
5962
}
6063

61-
/**
62-
* @deprecated use version with {@link ActionInvocation}
63-
*/
64-
@Deprecated
65-
protected XStream createXStream() {
66-
LOG.warn("You are using a deprecated API!");
67-
return new XStream();
68-
}
69-
7064
protected XStream createXStream(ActionInvocation invocation) {
71-
XStream stream = new XStream();
72-
LOG.debug("Clears existing permissions");
73-
stream.addPermission(NoTypePermission.NONE);
65+
XStream stream;
66+
if (invocation.getAction() instanceof XStreamProvider) {
67+
LOG.debug("Using provider {} to create instance of XStream", invocation.getAction().getClass().getSimpleName());
68+
stream = ((XStreamProvider) invocation.getAction()).createXStream();
69+
} else {
70+
LOG.debug("Creating default XStream instance using Stax driver: {}", StaxDriver.class.getSimpleName());
71+
stream = new XStream(new StaxDriver());
72+
}
7473

7574
LOG.debug("Adds per action permissions");
7675
addPerActionPermission(invocation, stream);
@@ -82,13 +81,13 @@ protected XStream createXStream(ActionInvocation invocation) {
8281

8382
private void addPerActionPermission(ActionInvocation invocation, XStream stream) {
8483
Object action = invocation.getAction();
85-
if (action instanceof AllowedClasses) {
86-
Set<Class<?>> allowedClasses = ((AllowedClasses) action).allowedClasses();
87-
stream.addPermission(new ExplicitTypePermission(allowedClasses.toArray(new Class[allowedClasses.size()])));
84+
if (action instanceof XStreamAllowedClasses) {
85+
Set<Class<?>> allowedClasses = ((XStreamAllowedClasses) action).allowedClasses();
86+
stream.addPermission(new ExplicitTypePermission(allowedClasses.toArray(new Class[0])));
8887
}
89-
if (action instanceof AllowedClassNames) {
90-
Set<String> allowedClassNames = ((AllowedClassNames) action).allowedClassNames();
91-
stream.addPermission(new ExplicitTypePermission(allowedClassNames.toArray(new String[allowedClassNames.size()])));
88+
if (action instanceof XStreamAllowedClassNames) {
89+
Set<String> allowedClassNames = ((XStreamAllowedClassNames) action).allowedClassNames();
90+
stream.addPermission(new ExplicitTypePermission(allowedClassNames.toArray(new String[0])));
9291
}
9392
if (action instanceof XStreamPermissionProvider) {
9493
Collection<TypePermission> permissions = ((XStreamPermissionProvider) action).getTypePermissions();
@@ -101,13 +100,13 @@ private void addPerActionPermission(ActionInvocation invocation, XStream stream)
101100
protected void addDefaultPermissions(ActionInvocation invocation, XStream stream) {
102101
stream.addPermission(new ExplicitTypePermission(new Class[]{invocation.getAction().getClass()}));
103102
if (invocation.getAction() instanceof ModelDriven) {
104-
stream.addPermission(new ExplicitTypePermission(new Class[]{((ModelDriven) invocation.getAction()).getModel().getClass()}));
103+
stream.addPermission(new ExplicitTypePermission(new Class[]{((ModelDriven<?>) invocation.getAction()).getModel().getClass()}));
105104
}
106105
stream.addPermission(NullPermission.NULL);
106+
stream.addPermission(new ExplicitTypePermission(new Class[]{String.class}));
107107
stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
108108
stream.addPermission(ArrayTypePermission.ARRAYS);
109109
stream.addPermission(CollectionTypePermission.COLLECTIONS);
110-
stream.addPermission(new ExplicitTypePermission(new Class[]{Date.class}));
111110
}
112111

113112
public String getContentType() {
@@ -124,8 +123,7 @@ private static class CollectionTypePermission implements TypePermission {
124123

125124
@Override
126125
public boolean allows(Class type) {
127-
return type != null && type.isInterface() &&
128-
(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type));
126+
return type != null && (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type));
129127
}
130128

131129
}

plugins/rest/src/main/java/org/apache/struts2/rest/handler/AllowedClassNames.java renamed to plugins/rest/src/main/java/org/apache/struts2/rest/handler/xstream/XStreamAllowedClassNames.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
package org.apache.struts2.rest.handler;
19+
package org.apache.struts2.rest.handler.xstream;
2020

2121
import java.util.Set;
2222

23-
public interface AllowedClassNames {
23+
public interface XStreamAllowedClassNames {
2424
Set<String> allowedClassNames();
2525
}

plugins/rest/src/main/java/org/apache/struts2/rest/handler/AllowedClasses.java renamed to plugins/rest/src/main/java/org/apache/struts2/rest/handler/xstream/XStreamAllowedClasses.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
package org.apache.struts2.rest.handler;
19+
package org.apache.struts2.rest.handler.xstream;
2020

2121
import java.util.Set;
2222

23-
public interface AllowedClasses {
23+
public interface XStreamAllowedClasses {
2424
Set<Class<?>> allowedClasses();
2525
}

plugins/rest/src/main/java/org/apache/struts2/rest/handler/XStreamPermissionProvider.java renamed to plugins/rest/src/main/java/org/apache/struts2/rest/handler/xstream/XStreamPermissionProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
package org.apache.struts2.rest.handler;
19+
package org.apache.struts2.rest.handler.xstream;
2020

2121
import com.thoughtworks.xstream.security.TypePermission;
2222

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.struts2.rest.handler.xstream;
20+
21+
import com.thoughtworks.xstream.XStream;
22+
23+
/**
24+
* An interface to be implemented by an action to create/provide an instance
25+
* of XStream - it allows customisation per action
26+
*/
27+
public interface XStreamProvider {
28+
XStream createXStream();
29+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.struts2.rest.handler;
20+
21+
import com.opensymphony.xwork2.ActionContext;
22+
import com.opensymphony.xwork2.ActionSupport;
23+
import com.opensymphony.xwork2.XWorkTestCase;
24+
import com.opensymphony.xwork2.mock.MockActionInvocation;
25+
import com.thoughtworks.xstream.XStream;
26+
import com.thoughtworks.xstream.io.xml.StaxDriver;
27+
import com.thoughtworks.xstream.security.ExplicitTypePermission;
28+
import com.thoughtworks.xstream.security.TypePermission;
29+
import org.apache.struts2.rest.handler.xstream.XStreamAllowedClassNames;
30+
import org.apache.struts2.rest.handler.xstream.XStreamAllowedClasses;
31+
import org.apache.struts2.rest.handler.xstream.XStreamPermissionProvider;
32+
import org.apache.struts2.rest.handler.xstream.XStreamProvider;
33+
34+
import java.io.Reader;
35+
import java.io.StringReader;
36+
import java.io.StringWriter;
37+
import java.io.Writer;
38+
import java.util.ArrayList;
39+
import java.util.Arrays;
40+
import java.util.Collection;
41+
import java.util.Collections;
42+
import java.util.HashMap;
43+
import java.util.HashSet;
44+
import java.util.Locale;
45+
import java.util.Set;
46+
47+
import static org.assertj.core.api.Assertions.assertThat;
48+
49+
public class XStreamHandlerTest extends XWorkTestCase {
50+
51+
private XStreamHandler handler;
52+
private MockActionInvocation ai;
53+
54+
public void setUp() throws Exception {
55+
super.setUp();
56+
handler = new XStreamHandler();
57+
ai = new MockActionInvocation();
58+
ActionSupport action = new ActionSupport();
59+
ActionContext context = ActionContext.of(new HashMap<>()).withLocale(Locale.US);
60+
ai.setInvocationContext(context);
61+
ai.setAction(action);
62+
}
63+
64+
public void testObjectToXml() throws Exception {
65+
// given
66+
SimpleBean obj = new SimpleBean();
67+
obj.setName("Jan");
68+
obj.setAge(12L);
69+
obj.setParents(Arrays.asList("Adam", "Ewa"));
70+
71+
// when
72+
Writer stream = new StringWriter();
73+
handler.fromObject(ai, obj, null, stream);
74+
75+
// then
76+
stream.flush();
77+
assertThat(stream.toString())
78+
.contains("<org.apache.struts2.rest.handler.SimpleBean>")
79+
.contains("<name>Jan</name>")
80+
.contains("<age>12</age>")
81+
.contains("<parents class=\"java.util.Arrays$ArrayList\">")
82+
.contains("<string>Adam</string>")
83+
.contains("<string>Ewa</string>")
84+
.contains("</org.apache.struts2.rest.handler.SimpleBean>");
85+
}
86+
87+
public void testXmlToObject() {
88+
// given
89+
String xml = "<?xml version='1.0' encoding='UTF-8'?><org.apache.struts2.rest.handler.SimpleBean><name>Jan</name><age>12</age><parents class=\"java.util.ArrayList\"><string>Adam</string><string>Ewa</string></parents></org.apache.struts2.rest.handler.SimpleBean>";
90+
91+
SimpleBean obj = new SimpleBean();
92+
ai.setAction(new SimpleAction());
93+
94+
// when
95+
Reader in = new StringReader(xml);
96+
handler.toObject(ai, in, obj);
97+
98+
// then
99+
assertNotNull(obj);
100+
assertEquals("Jan", obj.getName());
101+
assertEquals(12L, obj.getAge().longValue());
102+
assertNotNull(obj.getParents());
103+
assertThat(obj.getParents())
104+
.hasSize(2)
105+
.containsExactly("Adam", "Ewa");
106+
}
107+
108+
public void testXmlToObjectWithAliases() {
109+
// given
110+
String xml = "<?xml version='1.0' encoding='UTF-8'?><data><name>Jan</name><age>12</age><parents><string>Adam</string><string>Ewa</string></parents></data>";
111+
112+
SimpleBean obj = new SimpleBean();
113+
ai.setAction(new SimpleAliasAction());
114+
115+
// when
116+
Reader in = new StringReader(xml);
117+
handler.toObject(ai, in, obj);
118+
119+
// then
120+
assertNotNull(obj);
121+
assertEquals("Jan", obj.getName());
122+
assertEquals(12L, obj.getAge().longValue());
123+
assertNotNull(obj.getParents());
124+
assertThat(obj.getParents())
125+
.hasSize(2)
126+
.containsExactly("Adam", "Ewa");
127+
}
128+
129+
private static class SimpleAction implements XStreamAllowedClasses, XStreamAllowedClassNames, XStreamPermissionProvider {
130+
@Override
131+
public Set<Class<?>> allowedClasses() {
132+
Set<Class<?>> classes = new HashSet<>();
133+
classes.add(SimpleBean.class);
134+
return classes;
135+
}
136+
137+
@Override
138+
public Set<String> allowedClassNames() {
139+
return Collections.emptySet();
140+
}
141+
142+
@Override
143+
public Collection<TypePermission> getTypePermissions() {
144+
return Collections.emptyList();
145+
}
146+
}
147+
148+
private static class SimpleAliasAction extends SimpleAction implements XStreamProvider {
149+
@Override
150+
public XStream createXStream() {
151+
XStream stream = new XStream(new StaxDriver());
152+
stream.alias("parents", ArrayList.class);
153+
stream.alias("data", SimpleBean.class);
154+
return stream;
155+
}
156+
}
157+
}

0 commit comments

Comments
 (0)