Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 6df7e43

Browse files
committed
J-605: Fixed entity filtering when repeating classes in object graph were incorrectly filtered out - both MOXy and Jackson (JERSEY-2892) cases are covered now.
Change-Id: Ic2457aaf8a2fb93edd64a22060bec9a9818e3297
1 parent 71eb8b8 commit 6df7e43

File tree

9 files changed

+244
-34
lines changed

9 files changed

+244
-34
lines changed

ext/entity-filtering/src/main/java/org/glassfish/jersey/message/filtering/spi/AbstractObjectProvider.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import java.lang.reflect.Proxy;
4545
import java.lang.reflect.Type;
4646
import java.util.ArrayList;
47+
import java.util.Collections;
48+
import java.util.HashSet;
4749
import java.util.Set;
4850

4951
import javax.inject.Inject;
@@ -128,16 +130,41 @@ private Annotation[] getEntityAnnotations(final Annotation[] annotations) {
128130
/**
129131
* Create entity-filtering object after this object has not been found in the cache.
130132
*
131-
* @param entityClass entity class the entity-filtering object should be created for.
133+
* @param entityClass entity class the entity-filtering object should be created for.
132134
* @param filteringScopes entity-filtering scopes to create the entity-filtering object for.
133-
* @param forWriter flag determining whether the class should be examined for reader or writer.
135+
* @param forWriter flag determining whether the class should be examined for reader or writer.
134136
* @return entity-filtering object.
135137
*/
136138
private T createFilteringObject(final Class<?> entityClass, final Set<String> filteringScopes, final boolean forWriter) {
137139
// Obtain the filtering object.
138140
return transform(graphProvider.createObjectGraph(entityClass, filteringScopes, forWriter));
139141
}
140142

143+
/**
144+
* A helper method for a creation of an immutable set based on a provided set together with a given item.
145+
*
146+
* @param set The set to create the immutable set from.
147+
* @param item The item to add to the set before it's made immutable.
148+
* @return The immutable set from given set and item.
149+
*/
150+
protected Set<String> immutableSetOf(final Set<String> set, final String item) {
151+
final Set<String> duplicate = new HashSet<>(set);
152+
duplicate.add(item);
153+
return Collections.unmodifiableSet(duplicate);
154+
}
155+
156+
/**
157+
* Creates a string identifier of a sub-graph.
158+
*
159+
* @param parent The parent class.
160+
* @param field The field name.
161+
* @param fieldClass The class of the field.
162+
* @return The string identifier of the sub-graph.
163+
*/
164+
protected String subgraphIdentifier(final Class<?> parent, final String field, final Class<?> fieldClass) {
165+
return parent.getName() + "_" + field + "_" + fieldClass.getName();
166+
}
167+
141168
/**
142169
* Class to be used as a key in cache ({@code EntityContext} -&gt; filtering object) when processing similar requests.
143170
*/
@@ -150,7 +177,7 @@ private static final class EntityContext {
150177
/**
151178
* Create entity context class for given entity class and set of entity-filtering scopes.
152179
*
153-
* @param entityClass entity class.
180+
* @param entityClass entity class.
154181
* @param filteringScopes entity-filtering scopes.
155182
*/
156183
private EntityContext(final Class<?> entityClass, final Set<String> filteringScopes) {

media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242

4343
import java.util.Collections;
4444
import java.util.HashMap;
45-
import java.util.HashSet;
4645
import java.util.Map;
4746
import java.util.Set;
4847
import java.util.Stack;
@@ -90,7 +89,7 @@ private Map<String, FilteringPropertyFilter> createSubfilters(final Class<?> ent
9089
Map<String, FilteringPropertyFilter> subSubfilters = new HashMap<>();
9190
if (!subgraphs.isEmpty()) {
9291
final Class<?> subEntityClass = graph.getEntityClass();
93-
final Set<String> processed = Collections.singleton(getProcessedSubgraph(entityClass, fieldName, subEntityClass));
92+
final Set<String> processed = Collections.singleton(subgraphIdentifier(entityClass, fieldName, subEntityClass));
9493

9594
subSubfilters = createSubfilters(fieldName, subEntityClass, subgraphs, processed);
9695
}
@@ -119,7 +118,7 @@ private Map<String, FilteringPropertyFilter> createSubfilters(final String paren
119118
final Map<String, ObjectGraph> subgraphs = graph.getSubgraphs(path);
120119

121120
final Class<?> subEntityClass = graph.getEntityClass();
122-
final String processedSubgraph = getProcessedSubgraph(entityClass, fieldName, subEntityClass);
121+
final String processedSubgraph = subgraphIdentifier(entityClass, fieldName, subEntityClass);
123122

124123
Map<String, FilteringPropertyFilter> subSubfilters = new HashMap<>();
125124
if (!subgraphs.isEmpty() && !processed.contains(processedSubgraph)) {
@@ -135,16 +134,6 @@ private Map<String, FilteringPropertyFilter> createSubfilters(final String paren
135134
return subfilters;
136135
}
137136

138-
private Set<String> immutableSetOf(final Set<String> set, final String item) {
139-
final Set<String> duplicate = new HashSet<>(set);
140-
duplicate.add(item);
141-
return Collections.unmodifiableSet(duplicate);
142-
}
143-
144-
private String getProcessedSubgraph(final Class<?> parent, final String field, final Class<?> fieldClass) {
145-
return parent.getName() + "_" + field + "_" + fieldClass.getName();
146-
}
147-
148137
private static class FilteringFilterProvider extends FilterProvider {
149138

150139
private final FilteringPropertyFilter root;

media/moxy/src/main/java/org/glassfish/jersey/moxy/internal/MoxyObjectProvider.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@
5555
import org.eclipse.persistence.jaxb.Subgraph;
5656
import org.eclipse.persistence.jaxb.TypeMappingInfo;
5757

58-
import jersey.repackaged.com.google.common.collect.Sets;
59-
6058
/**
6159
* @author Michal Gajdos
6260
*/
@@ -97,8 +95,6 @@ private org.eclipse.persistence.jaxb.ObjectGraph createObjectGraph(final Class<?
9795

9896
private void createSubgraphs(final org.eclipse.persistence.jaxb.ObjectGraph graph,
9997
final Class<?> entityClass, final Map<String, ObjectGraph> entitySubgraphs) {
100-
final Set<String> processed = Sets.newHashSet();
101-
10298
for (final Map.Entry<String, ObjectGraph> entry : entitySubgraphs.entrySet()) {
10399
final String fieldName = entry.getKey();
104100

@@ -114,7 +110,7 @@ private void createSubgraphs(final org.eclipse.persistence.jaxb.ObjectGraph grap
114110
if (!subgraphs.isEmpty()) {
115111
final Class<?> subEntityClass = entityGraph.getEntityClass();
116112

117-
processed.add(getProcessedSubgraph(entityClass, fieldName, subEntityClass));
113+
final Set<String> processed = Collections.singleton(subgraphIdentifier(entityClass, fieldName, subEntityClass));
118114
createSubgraphs(fieldName, subgraph, subEntityClass, subgraphs, processed);
119115
}
120116
}
@@ -137,16 +133,13 @@ private void createSubgraphs(final String parent, final Subgraph graph, final Cl
137133

138134
final Map<String, ObjectGraph> subgraphs = entityGraph.getSubgraphs(path);
139135
final Class<?> subEntityClass = entityGraph.getEntityClass();
140-
final String processedSubgraph = getProcessedSubgraph(entityClass, fieldName, subEntityClass);
136+
final String processedSubgraph = subgraphIdentifier(entityClass, fieldName, subEntityClass);
141137

142138
if (!subgraphs.isEmpty() && !processed.contains(processedSubgraph)) {
143-
processed.add(processedSubgraph);
144-
createSubgraphs(path, subgraph, subEntityClass, subgraphs, processed);
139+
// duplicate processed set so that elements in different subtrees aren't skipped (J-605)
140+
final Set<String> subProcessed = immutableSetOf(processed, processedSubgraph);
141+
createSubgraphs(path, subgraph, subEntityClass, subgraphs, subProcessed);
145142
}
146143
}
147144
}
148-
149-
private String getProcessedSubgraph(final Class<?> parent, final String field, final Class<?> fieldClass) {
150-
return parent.getName() + "_" + field + "_" + fieldClass.getName();
151-
}
152145
}

tests/integration/jersey-2892/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
<scope>test</scope>
7272
</dependency>
7373

74+
<!-- which jersey media provider to use is determined in web.xml by using 'jersey.config.jsonFeature' init param -->
75+
<dependency>
76+
<groupId>org.glassfish.jersey.media</groupId>
77+
<artifactId>jersey-media-moxy</artifactId>
78+
</dependency>
7479
<dependency>
7580
<groupId>org.glassfish.jersey.media</groupId>
7681
<artifactId>jersey-media-json-jackson</artifactId>

tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestApplication.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import org.glassfish.jersey.server.ResourceConfig;
4747

4848
/**
49-
* Jersey application for JERSEY-2878.
49+
* Jersey application for JERSEY-2878 and also J-605 (which is derived from the former one).
5050
*
5151
* @author Stepan Vavra (stepan.vavra at oracle.com)
5252
*/
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5+
6+
Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
7+
8+
The contents of this file are subject to the terms of either the GNU
9+
General Public License Version 2 only ("GPL") or the Common Development
10+
and Distribution License("CDDL") (collectively, the "License"). You
11+
may not use this file except in compliance with the License. You can
12+
obtain a copy of the License at
13+
http://glassfish.java.net/public/CDDL+GPL_1_1.html
14+
or packager/legal/LICENSE.txt. See the License for the specific
15+
language governing permissions and limitations under the License.
16+
17+
When distributing the software, include this License Header Notice in each
18+
file and include the License file at packager/legal/LICENSE.txt.
19+
20+
GPL Classpath Exception:
21+
Oracle designates this particular file as subject to the "Classpath"
22+
exception as provided by Oracle in the GPL Version 2 section of the License
23+
file that accompanied this code.
24+
25+
Modifications:
26+
If applicable, add the following below the License Header, with the fields
27+
enclosed by brackets [] replaced by your own identifying information:
28+
"Portions Copyright [year] [name of copyright owner]"
29+
30+
Contributor(s):
31+
If you wish your version of this file to be governed by only the CDDL or
32+
only the GPL Version 2, indicate your decision by adding "[Contributor]
33+
elects to include this software in this distribution under the [CDDL or GPL
34+
Version 2] license." If you don't indicate a single choice of license, a
35+
recipient has the option to distribute your version of this file under
36+
either the CDDL, the GPL Version 2 or to extend the choice of license to
37+
its licensees as provided above. However, if you add GPL Version 2 code
38+
and therefore, elected the GPL Version 2 license, then the option applies
39+
only if the new code is made subject to such option by the copyright
40+
holder.
41+
42+
-->
43+
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44+
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
45+
46+
<servlet>
47+
<servlet-name>jerseyMoxyFilteringFeature</servlet-name>
48+
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
49+
<init-param>
50+
<param-name>javax.ws.rs.Application</param-name>
51+
<param-value>org.glassfish.jersey.tests.integration.jersey2892.TestApplication</param-value>
52+
</init-param>
53+
<init-param>
54+
<param-name>jersey.config.jsonFeature</param-name>
55+
<param-value>MoxyJsonFeature</param-value>
56+
</init-param>
57+
<load-on-startup>1</load-on-startup>
58+
</servlet>
59+
<servlet-mapping>
60+
<servlet-name>jerseyMoxyFilteringFeature</servlet-name>
61+
<url-pattern>/moxy/*</url-pattern>
62+
</servlet-mapping>
63+
64+
<servlet>
65+
<servlet-name>jerseyJacksonFilteringFeature</servlet-name>
66+
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
67+
<init-param>
68+
<param-name>javax.ws.rs.Application</param-name>
69+
<param-value>org.glassfish.jersey.tests.integration.jersey2892.TestApplication</param-value>
70+
</init-param>
71+
<init-param>
72+
<param-name>jersey.config.jsonFeature</param-name>
73+
<param-value>JacksonFeature</param-value>
74+
</init-param>
75+
<load-on-startup>1</load-on-startup>
76+
</servlet>
77+
<servlet-mapping>
78+
<servlet-name>jerseyJacksonFilteringFeature</servlet-name>
79+
<url-pattern>/jackson/*</url-pattern>
80+
</servlet-mapping>
81+
82+
</web-app>

tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/Jersey2892ITCase.java renamed to tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/AbstractJerseyEntityFilteringITCase.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
*
5757
* @author Stepan Vavra (stepan.vavra at oracle.com)
5858
*/
59-
public class Jersey2892ITCase extends JerseyTest {
59+
public abstract class AbstractJerseyEntityFilteringITCase extends JerseyTest {
6060

6161
@Override
6262
protected Application configure() {
@@ -69,14 +69,14 @@ protected TestContainerFactory getTestContainerFactory() throws TestContainerExc
6969
}
7070

7171
/**
72-
* Tests whether sub-sub-field, {@link org.glassfish.jersey.tests.integration.jersey2892.TestResource.Street} in particular,
72+
* Tests whether sub-sub-field, {@link TestResource.Street} in particular,
7373
* is not filtered out.
7474
* <p/>
7575
* This corresponds with the JERSEY-2892 reported case.
7676
*/
7777
@Test
7878
public void testWhetherSubSubFiledIsNotFilteredOut() {
79-
Response response = target("test").request(MediaType.APPLICATION_JSON_TYPE).get();
79+
Response response = target(provider() + "/test").request(MediaType.APPLICATION_JSON_TYPE).get();
8080

8181
final TestResource.Persons persons = response.readEntity(TestResource.Persons.class);
8282

@@ -90,7 +90,7 @@ public void testWhetherSubSubFiledIsNotFilteredOut() {
9090
*/
9191
@Test
9292
public void testWhetherSubSubSubFieldIsNotFilteredOut() {
93-
Response response = target("pointer").request(MediaType.APPLICATION_JSON_TYPE).get();
93+
Response response = target(provider() + "/pointer").request(MediaType.APPLICATION_JSON_TYPE).get();
9494

9595
final TestResource.Pointer pointer = response.readEntity(TestResource.Pointer.class);
9696

@@ -103,10 +103,18 @@ public void testWhetherSubSubSubFieldIsNotFilteredOut() {
103103
*/
104104
@Test
105105
public void testWhetherReferenceCycleIsDetected() {
106-
Response response = target("recursive").request(MediaType.APPLICATION_JSON_TYPE).get();
106+
Response response = target(provider() + "/recursive").request(MediaType.APPLICATION_JSON_TYPE).get();
107107

108108
final TestResource.Recursive recursive = response.readEntity(TestResource.Recursive.class);
109109

110110
Assert.assertEquals("c", recursive.subField.subSubField.idSubSubField);
111111
}
112+
113+
/**
114+
* Jersey Entity filtering feature provider.
115+
*
116+
* @return The provider string to match with appropriate Jersey app configured in web.xml.
117+
*/
118+
protected abstract String provider();
119+
112120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
package org.glassfish.jersey.tests.integration.jersey2892;
41+
42+
/**
43+
* Tests whether classes repeating in the object graph are filtered out correctly when using MOXY json provider.
44+
*
45+
* @author Stepan Vavra (stepan.vavra at oracle.com)
46+
*/
47+
public class J605MoxyITCase extends AbstractJerseyEntityFilteringITCase {
48+
49+
@Override
50+
protected String provider() {
51+
return "moxy";
52+
}
53+
}

0 commit comments

Comments
 (0)