Skip to content

Commit 9de733a

Browse files
hatstandsenivam
authored andcommitted
Fix WORKER_BY_TYPE_COMPARATOR to follow contract (#3915)
Fixes transitivity in providers ordering (#3387) Signed-off-by: John Maguire <[email protected]>
1 parent b99bc9c commit 9de733a

File tree

2 files changed

+138
-2
lines changed

2 files changed

+138
-2
lines changed

core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public int hash(final MediaType mt) {
138138
* and then by the declared supported media types, if the provided classes
139139
* are the same.
140140
*/
141-
private static final Comparator<AbstractEntityProviderModel<?>> WORKER_BY_TYPE_COMPARATOR =
141+
static final Comparator<AbstractEntityProviderModel<?>> WORKER_BY_TYPE_COMPARATOR =
142142
new Comparator<AbstractEntityProviderModel<?>>() {
143143

144144
@Override
@@ -154,7 +154,8 @@ public int compare(final AbstractEntityProviderModel<?> o1, final AbstractEntity
154154
} else if (o2ProviderClassParam.isAssignableFrom(o1ProviderClassParam)) {
155155
return -1;
156156
}
157-
return 0;
157+
// Fallback to comparing provided class name.
158+
return CLASS_BY_NAME_COMPARATOR.compare(o1ProviderClassParam, o2ProviderClassParam);
158159
}
159160

160161
private int compare(List<MediaType> mediaTypeList1, List<MediaType> mediaTypeList2) {
@@ -165,6 +166,8 @@ private int compare(List<MediaType> mediaTypeList1, List<MediaType> mediaTypeLis
165166
}
166167
};
167168

169+
private static final Comparator<Class<?>> CLASS_BY_NAME_COMPARATOR = Comparator.comparing(Class::getName);
170+
168171
private InjectionManager injectionManager;
169172

170173
private final Boolean legacyProviderOrdering;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package org.glassfish.jersey.message.internal;
2+
3+
import java.lang.reflect.Type;
4+
import java.util.Collections;
5+
import java.io.OutputStream;
6+
import java.lang.annotation.Annotation;
7+
import java.util.ArrayList;
8+
9+
import javax.ws.rs.core.MediaType;
10+
import javax.ws.rs.core.MultivaluedMap;
11+
import javax.ws.rs.ext.MessageBodyWriter;
12+
13+
import org.glassfish.jersey.message.AbstractEntityProviderModel;
14+
import org.glassfish.jersey.message.WriterModel;
15+
16+
import org.junit.Test;
17+
18+
import static org.junit.Assert.assertEquals;
19+
import static org.junit.Assert.fail;
20+
21+
import static org.glassfish.jersey.message.internal.MessageBodyFactory.WORKER_BY_TYPE_COMPARATOR;
22+
23+
public class MessageBodyFactoryTest {
24+
25+
@Test
26+
public void testWorkerByTypeComparatorContract() {
27+
ArrayList<WriterModel> list = new ArrayList<>();
28+
list.add(new WriterModel(new BarMessageBodyWriter(), new ArrayList<>(), true));
29+
list.add(new WriterModel(new ObjectMessageBodyWriter(), new ArrayList<>(), true));
30+
list.add(new WriterModel(new BazMessageBodyWriter(), new ArrayList<>(), true));
31+
32+
for (WriterModel a : list) {
33+
for (WriterModel b : list) {
34+
assertEquals(
35+
"Comparator breaks contract: compare(a, b) != -compare(b, a)",
36+
-Integer.signum(WORKER_BY_TYPE_COMPARATOR.compare(a, b)),
37+
Integer.signum(WORKER_BY_TYPE_COMPARATOR.compare(b, a)));
38+
39+
for (WriterModel c : list) {
40+
if (WORKER_BY_TYPE_COMPARATOR.compare(a, b) > 0
41+
&& WORKER_BY_TYPE_COMPARATOR.compare(b, c) > 0
42+
&& WORKER_BY_TYPE_COMPARATOR.compare(a, c) <= 0) {
43+
fail("Comparator breaks contract: a > b and b > c but a <= c");
44+
}
45+
46+
if (WORKER_BY_TYPE_COMPARATOR.compare(a, b) == 0) {
47+
assertEquals(
48+
"Comparator breaks contract: a == b but a < c and b > c or vice versa",
49+
Integer.signum(WORKER_BY_TYPE_COMPARATOR.compare(a, c)),
50+
Integer.signum(WORKER_BY_TYPE_COMPARATOR.compare(b, c)));
51+
}
52+
}
53+
}
54+
}
55+
}
56+
57+
static class Bar {
58+
59+
}
60+
61+
static class Baz extends Bar {
62+
63+
}
64+
65+
/** Implements writer for arbitrary class. */
66+
static class BarMessageBodyWriter implements MessageBodyWriter<Bar> {
67+
68+
@Override
69+
public long getSize(Bar bar, Class<?> type, Type genericType, Annotation[] annotation, MediaType mediaType) {
70+
return -1;
71+
}
72+
73+
@Override
74+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotation, MediaType mediaType) {
75+
return true;
76+
}
77+
78+
@Override
79+
public void writeTo(
80+
Bar bar,
81+
Class<?> type,
82+
Type genericType,
83+
Annotation[] annotation,
84+
MediaType mediaType,
85+
MultivaluedMap<String, Object> headers, OutputStream entityStream) {}
86+
}
87+
88+
/** Implements writer for class that extends from something already provided. */
89+
static class BazMessageBodyWriter implements MessageBodyWriter<Baz> {
90+
91+
@Override
92+
public long getSize(Baz baz, Class<?> type, Type genericType, Annotation[] annotation, MediaType mediaType) {
93+
return -1;
94+
}
95+
96+
@Override
97+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotation, MediaType mediaType) {
98+
return true;
99+
}
100+
101+
@Override
102+
public void writeTo(
103+
Baz baz,
104+
Class<?> type,
105+
Type genericType,
106+
Annotation[] annotation,
107+
MediaType mediaType,
108+
MultivaluedMap<String, Object> headers, OutputStream entityStream) {}
109+
}
110+
111+
/** Implements writer for arbitrary class that cannot be assigned to from other providers. */
112+
static class ObjectMessageBodyWriter implements MessageBodyWriter<Integer> {
113+
114+
@Override
115+
public long getSize(Integer i, Class<?> type, Type genericType, Annotation[] annotation, MediaType mediaType) {
116+
return -1;
117+
}
118+
119+
@Override
120+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotation, MediaType mediaType) {
121+
return true;
122+
}
123+
124+
@Override
125+
public void writeTo(
126+
Integer i,
127+
Class<?> type,
128+
Type genericType,
129+
Annotation[] annotation,
130+
MediaType mediaType,
131+
MultivaluedMap<String, Object> headers, OutputStream entityStream) {}
132+
}
133+
}

0 commit comments

Comments
 (0)