Skip to content

Commit b6761a7

Browse files
committed
idel review comments round 2
1 parent 7b55b24 commit b6761a7

File tree

38 files changed

+785
-411
lines changed

38 files changed

+785
-411
lines changed

servicetalk-data-jackson-jersey/src/main/java/io/servicetalk/data/jackson/jersey/SerializationExceptionMapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
import javax.ws.rs.core.Response;
2424
import javax.ws.rs.ext.ExceptionMapper;
2525

26-
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
2726
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
27+
import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE;
2828
import static javax.ws.rs.core.Response.status;
2929

3030
final class SerializationExceptionMapper implements ExceptionMapper<SerializationException> {
3131
@Override
3232
public Response toResponse(final SerializationException e) {
33-
return status(isDueToBadUserData(e) ? BAD_REQUEST : INTERNAL_SERVER_ERROR).build();
33+
return status(isDueToBadUserData(e) ? UNSUPPORTED_MEDIA_TYPE : INTERNAL_SERVER_ERROR).build();
3434
}
3535

3636
private static boolean isDueToBadUserData(final SerializationException e) {

servicetalk-data-jackson-jersey/src/main/java/io/servicetalk/data/jackson/jersey/SerializationExceptionMapperDeprecated.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

servicetalk-data-jackson-jersey/src/main/java/io/servicetalk/data/jackson/jersey/ServiceTalkJacksonSerializerFeature.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ public boolean configure(final FeatureContext context) {
6161
ST_JSON_FEATURE);
6262

6363
if (!config.isRegistered(JacksonSerializerMessageBodyReaderWriter.class)) {
64-
context.register(SerializationExceptionMapperDeprecated.class);
6564
context.register(SerializationExceptionMapper.class);
6665
context.register(JacksonSerializerMessageBodyReaderWriter.class);
6766
}

servicetalk-data-jackson/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies {
3434
testImplementation project(":servicetalk-test-resources")
3535
testImplementation project(":servicetalk-buffer-netty")
3636
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit5Version"
37+
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit5Version"
3738
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
3839
testImplementation "org.mockito:mockito-core:$mockitoCoreVersion"
3940

servicetalk-data-jackson/src/main/java/io/servicetalk/data/jackson/ByteArrayJacksonDeserializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ final class ByteArrayJacksonDeserializer<T> extends AbstractJacksonDeserializer<
4040
@Nonnull
4141
Iterable<T> doDeserialize(final Buffer buffer, @Nullable List<T> resultHolder) throws IOException {
4242
if (buffer.hasArray()) {
43-
feeder.feedInput(buffer.array(), buffer.arrayOffset() + buffer.readerIndex(),
44-
buffer.arrayOffset() + buffer.readableBytes());
43+
final int start = buffer.arrayOffset() + buffer.readerIndex();
44+
feeder.feedInput(buffer.array(), start, start + buffer.readableBytes());
4545
} else {
4646
int readableBytes = buffer.readableBytes();
4747
if (readableBytes != 0) {

servicetalk-data-jackson/src/main/java/io/servicetalk/data/jackson/JacksonSerializerCache.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
import java.util.concurrent.ConcurrentHashMap;
2828

2929
/**
30-
* Caches instances of {@link SerializerDeserializer} and {@link StreamingSerializerDeserializer} for jackson.
30+
* Caches instances of {@link SerializerDeserializer} and {@link StreamingSerializerDeserializer} for
31+
* <a href="https://github.com/FasterXML/jackson">jackson</a>.
3132
*/
3233
public final class JacksonSerializerCache {
3334
public static final JacksonSerializerCache INSTANCE = new JacksonSerializerCache();

servicetalk-data-jackson/src/main/java/io/servicetalk/data/jackson/JacksonStreamingSerializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ private ByteArrayDeserializeSubscriber(final Subscriber<? super Iterable<T>> sub
133133
@Override
134134
boolean consumeOnNext(final Buffer buffer) throws IOException {
135135
if (buffer.hasArray()) {
136-
feeder.feedInput(buffer.array(), buffer.arrayOffset() + buffer.readerIndex(),
137-
buffer.arrayOffset() + buffer.readableBytes());
136+
final int start = buffer.arrayOffset() + buffer.readerIndex();
137+
feeder.feedInput(buffer.array(), start, start + buffer.readableBytes());
138138
} else {
139139
int readableBytes = buffer.readableBytes();
140140
if (readableBytes != 0) {
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*
2+
* Copyright © 2021 Apple Inc. and the ServiceTalk project authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.servicetalk.data.jackson;
17+
18+
import io.servicetalk.buffer.api.Buffer;
19+
import io.servicetalk.buffer.api.BufferAllocator;
20+
import io.servicetalk.serializer.api.SerializerDeserializer;
21+
import io.servicetalk.serializer.api.StreamingSerializerDeserializer;
22+
23+
import com.fasterxml.jackson.core.type.TypeReference;
24+
import org.junit.jupiter.api.Disabled;
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.Arguments;
27+
import org.junit.jupiter.params.provider.MethodSource;
28+
29+
import java.util.concurrent.ThreadLocalRandom;
30+
import java.util.stream.Stream;
31+
32+
import static io.servicetalk.buffer.api.EmptyBuffer.EMPTY_BUFFER;
33+
import static io.servicetalk.buffer.netty.BufferAllocators.PREFER_DIRECT_ALLOCATOR;
34+
import static io.servicetalk.buffer.netty.BufferAllocators.PREFER_HEAP_ALLOCATOR;
35+
import static io.servicetalk.concurrent.api.Publisher.from;
36+
import static io.servicetalk.data.jackson.JacksonSerializerCache.INSTANCE;
37+
import static java.lang.System.lineSeparator;
38+
import static java.nio.charset.StandardCharsets.UTF_8;
39+
import static java.util.Collections.singletonList;
40+
import static org.hamcrest.MatcherAssert.assertThat;
41+
import static org.hamcrest.Matchers.contains;
42+
import static org.hamcrest.Matchers.emptyIterable;
43+
import static org.hamcrest.Matchers.nullValue;
44+
import static org.hamcrest.core.Is.is;
45+
import static org.junit.jupiter.api.Assertions.assertEquals;
46+
import static org.junit.jupiter.api.Assertions.assertThrows;
47+
48+
class JacksonSerializerCacheTest {
49+
private static final TypeReference<TestPojo> TEST_POJO_TYPE_REFERENCE = new TypeReference<TestPojo>() { };
50+
private static final TypeReference<String> STRING_TYPE_REFERENCE = new TypeReference<String>() { };
51+
private static final TypeReference<Boolean> BOOLEAN_TYPE_REFERENCE = new TypeReference<Boolean>() { };
52+
private static final TypeReference<Integer> INTEGER_TYPE_REFERENCE = new TypeReference<Integer>() { };
53+
54+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
55+
@MethodSource("params")
56+
void aggregatedSerializeDeserialize(boolean typeRef, BufferAllocator alloc) {
57+
TestPojo expected = new TestPojo(true, (byte) -2, (short) -3, 'a', 2, 5, 3.2f, -8.5, null, new String[] {"bar"},
58+
null);
59+
60+
assertEquals(expected, pojoSerializer(typeRef).deserialize(
61+
pojoSerializer(typeRef).serialize(expected, alloc), alloc));
62+
}
63+
64+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
65+
@MethodSource("params")
66+
void deserializeInvalidData(boolean typeRef, BufferAllocator alloc) {
67+
TestPojo expected = new TestPojo(true, (byte) -2, (short) -3, 'a', 2, 5, 3.2f, -8.5, null, new String[] {"bar"},
68+
null);
69+
final Buffer serialized = pojoSerializer(typeRef).serialize(expected, alloc);
70+
serialized.setByte(serialized.writerIndex() - 1, serialized.getByte(serialized.writerIndex() - 1) + 1);
71+
72+
assertThrows(io.servicetalk.serializer.api.SerializationException.class, () ->
73+
pojoSerializer(typeRef).deserialize(serialized, alloc));
74+
}
75+
76+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
77+
@MethodSource("params")
78+
void deserializeEmpty(boolean typeRef, BufferAllocator alloc) {
79+
assertThrows(io.servicetalk.serializer.api.SerializationException.class,
80+
() -> pojoSerializer(typeRef).deserialize(EMPTY_BUFFER, alloc));
81+
}
82+
83+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
84+
@MethodSource("params")
85+
void deserializeIncomplete(boolean typeRef, BufferAllocator alloc) {
86+
TestPojo expected = new TestPojo(true, (byte) -2, (short) -3, 'a', 2, 5, 3.2f, -8.5, null, new String[] {"bar"},
87+
null);
88+
Buffer buffer = pojoSerializer(typeRef).serialize(expected, alloc);
89+
assertThrows(io.servicetalk.serializer.api.SerializationException.class, () ->
90+
pojoSerializer(typeRef).deserialize(buffer.readBytes(buffer.readableBytes() - 1), alloc));
91+
}
92+
93+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
94+
@MethodSource("params")
95+
void deserializeEmptyStreaming(boolean typeRef, BufferAllocator alloc) {
96+
assertThat(pojoStreamingSerializer(typeRef).deserialize(singletonList(EMPTY_BUFFER), alloc),
97+
emptyIterable());
98+
}
99+
100+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
101+
@MethodSource("params")
102+
void streamingSerializeDeserialize(boolean typeRef, BufferAllocator alloc) {
103+
TestPojo expected = new TestPojo(true, Byte.MAX_VALUE, Short.MAX_VALUE, Character.MAX_VALUE, Integer.MIN_VALUE,
104+
Long.MAX_VALUE, Float.MAX_VALUE, Double.MAX_VALUE, "foo", new String[] {"bar", "baz"}, null);
105+
106+
assertThat(pojoStreamingSerializer(typeRef).deserialize(
107+
pojoStreamingSerializer(typeRef).serialize(singletonList(expected), alloc),
108+
alloc), contains(expected));
109+
}
110+
111+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
112+
@MethodSource("params")
113+
void streamingSerializeDeserialize2(boolean typeRef, BufferAllocator alloc) {
114+
TestPojo expected1 = new TestPojo(true, (byte) -2, (short) -3, 'a', 2, 5, 3.2f, -8.5, null,
115+
new String[] {"bar", "baz"}, null);
116+
TestPojo expected2 = new TestPojo(false, (byte) 500, (short) 353, 'r', 100, 534, 33.25f, 888.5, null,
117+
new String[] {"foo"}, expected1);
118+
assertThat(pojoStreamingSerializer(typeRef).deserialize(
119+
pojoStreamingSerializer(typeRef).serialize(from(expected1, expected2), alloc), alloc)
120+
.toIterable(), contains(expected1, expected2));
121+
}
122+
123+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
124+
@MethodSource("params")
125+
void streamingSerializeDeserialize2SingleBuffer(boolean typeRef, BufferAllocator alloc) {
126+
TestPojo expected1 = new TestPojo(true, (byte) -2, (short) -3, 'a', 2, 5, 3.2f, -8.5, null,
127+
new String[] {"bar", "baz"}, null);
128+
TestPojo expected2 = new TestPojo(false, (byte) 500, (short) 353, 'r', 100, 534, 33.25f, 888.5, null,
129+
new String[] {"foo"}, expected1);
130+
131+
final Buffer buffer1 = pojoSerializer(typeRef).serialize(expected1, alloc);
132+
final Buffer buffer2 = pojoSerializer(typeRef).serialize(expected2, alloc);
133+
134+
Buffer composite = alloc.newBuffer(buffer1.readableBytes() + buffer2.readableBytes());
135+
composite.writeBytes(buffer1).writeBytes(buffer2);
136+
137+
assertThat(pojoStreamingSerializer(typeRef).deserialize(from(composite), alloc)
138+
.toIterable(), contains(expected1, expected2));
139+
}
140+
141+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
142+
@MethodSource("params")
143+
void streamingSerializeDeserializeMultipleSplitBuffers(boolean typeRef, BufferAllocator alloc) {
144+
TestPojo expected1 = new TestPojo(true, (byte) -2, (short) -3, 'a', 2, 5, 3.2f, -8.5, null,
145+
new String[] {"bar", "baz"}, null);
146+
TestPojo expected2 = new TestPojo(false, (byte) 500, (short) 353, 'r', 100, 534, 33.25f, 888.5, null,
147+
new String[] {"foo"}, expected1);
148+
149+
final Buffer buffer1 = pojoSerializer(typeRef).serialize(expected1, alloc);
150+
final Buffer buffer2 = pojoSerializer(typeRef).serialize(expected2, alloc);
151+
152+
int buffer1Split = ThreadLocalRandom.current().nextInt(buffer1.readableBytes());
153+
int buffer2Split = ThreadLocalRandom.current().nextInt(buffer2.readableBytes());
154+
String debugString = splitDebugString(buffer1, buffer1Split) + lineSeparator() +
155+
splitDebugString(buffer2, buffer2Split);
156+
try {
157+
assertThat(pojoStreamingSerializer(typeRef).deserialize(
158+
from(buffer1.readBytes(buffer1Split), buffer1, buffer2.readBytes(buffer2Split), buffer2),
159+
alloc).toIterable(), contains(expected1, expected2));
160+
} catch (Throwable cause) {
161+
throw new AssertionError("failed to parse split buffers: " + debugString, cause);
162+
}
163+
}
164+
165+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
166+
@MethodSource("params")
167+
void deserializeString(boolean typeRef, BufferAllocator alloc) {
168+
String json = "\"x\"";
169+
final Buffer buffer = alloc.fromAscii(json);
170+
assertThat(stringSerializer(typeRef).deserialize(buffer, alloc), is("x"));
171+
}
172+
173+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
174+
@MethodSource("params")
175+
void streamingDeserializeString(boolean typeRef, BufferAllocator alloc) {
176+
String json = "\"x\"";
177+
final Buffer buffer = alloc.fromAscii(json);
178+
assertThat(stringStreamingSerializer(typeRef).deserialize(from(buffer), alloc).toIterable(),
179+
contains("x"));
180+
}
181+
182+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
183+
@MethodSource("params")
184+
void deserializeStringNull(boolean typeRef, BufferAllocator alloc) {
185+
String json = "null";
186+
final Buffer buffer = alloc.fromAscii(json);
187+
assertThat(stringSerializer(typeRef).deserialize(buffer, alloc), nullValue());
188+
}
189+
190+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
191+
@MethodSource("params")
192+
void streamingDeserializeStringNull(boolean typeRef, BufferAllocator alloc) {
193+
String json = "null";
194+
final Buffer buffer = alloc.fromAscii(json);
195+
assertThat(stringStreamingSerializer(typeRef).deserialize(from(buffer), alloc).toIterable(),
196+
emptyIterable());
197+
}
198+
199+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
200+
@MethodSource("params")
201+
void deserializeBoolean(boolean typeRef, BufferAllocator alloc) {
202+
String json = "true";
203+
final Buffer buffer = alloc.fromAscii(json);
204+
assertThat(boolSerializer(typeRef).deserialize(buffer, alloc), is(true));
205+
}
206+
207+
@Disabled("Jackson streaming currently does not support parsing only Boolean value.")
208+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
209+
@MethodSource("params")
210+
void streamingDeserializeBoolean(boolean typeRef, BufferAllocator alloc) {
211+
String json = "true";
212+
final Buffer buffer = alloc.fromAscii(json);
213+
assertThat(boolStreamingSerializer(typeRef).deserialize(from(buffer), alloc).toIterable(),
214+
contains(true));
215+
}
216+
217+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
218+
@MethodSource("params")
219+
void deserializeInteger(boolean typeRef, BufferAllocator alloc) {
220+
String json = "1";
221+
final Buffer buffer = alloc.fromAscii(json);
222+
assertThat(intSerializer(typeRef).deserialize(buffer, alloc), is(1));
223+
}
224+
225+
@Disabled("Jackson streaming currently does not support parsing only Integer value.")
226+
@ParameterizedTest(name = "{index}, typeRef={0}, alloc={1}")
227+
@MethodSource("params")
228+
void streamingDeserializeInteger(boolean typeRef, BufferAllocator alloc) {
229+
String json = "1";
230+
final Buffer buffer = alloc.fromAscii(json);
231+
assertThat(intStreamingSerializer(typeRef).deserialize(from(buffer), alloc).toIterable(), contains(1));
232+
}
233+
234+
private static Stream<Arguments> params() {
235+
return Stream.of(
236+
Arguments.of(true, PREFER_DIRECT_ALLOCATOR),
237+
Arguments.of(true, PREFER_HEAP_ALLOCATOR),
238+
Arguments.of(false, PREFER_DIRECT_ALLOCATOR),
239+
Arguments.of(false, PREFER_HEAP_ALLOCATOR));
240+
}
241+
242+
private static String splitDebugString(Buffer buffer, int split) {
243+
return buffer.toString(buffer.readerIndex(), split, UTF_8) + lineSeparator() +
244+
buffer.toString(buffer.readerIndex() + split, buffer.readableBytes() - split, UTF_8);
245+
}
246+
247+
private static SerializerDeserializer<TestPojo> pojoSerializer(boolean typeRef) {
248+
return typeRef ? INSTANCE.serializerDeserializer(TEST_POJO_TYPE_REFERENCE) :
249+
INSTANCE.serializerDeserializer(TestPojo.class);
250+
}
251+
252+
private static StreamingSerializerDeserializer<TestPojo> pojoStreamingSerializer(boolean typeRef) {
253+
return typeRef ? INSTANCE.streamingSerializerDeserializer(TEST_POJO_TYPE_REFERENCE) :
254+
INSTANCE.streamingSerializerDeserializer(TestPojo.class);
255+
}
256+
257+
private static SerializerDeserializer<String> stringSerializer(boolean typeRef) {
258+
return typeRef ? INSTANCE.serializerDeserializer(STRING_TYPE_REFERENCE) :
259+
INSTANCE.serializerDeserializer(String.class);
260+
}
261+
262+
private static StreamingSerializerDeserializer<String> stringStreamingSerializer(boolean typeRef) {
263+
return typeRef ? INSTANCE.streamingSerializerDeserializer(STRING_TYPE_REFERENCE) :
264+
INSTANCE.streamingSerializerDeserializer(String.class);
265+
}
266+
267+
private static SerializerDeserializer<Boolean> boolSerializer(boolean typeRef) {
268+
return typeRef ? INSTANCE.serializerDeserializer(BOOLEAN_TYPE_REFERENCE) :
269+
INSTANCE.serializerDeserializer(Boolean.class);
270+
}
271+
272+
private static StreamingSerializerDeserializer<Boolean> boolStreamingSerializer(boolean typeRef) {
273+
return typeRef ? INSTANCE.streamingSerializerDeserializer(BOOLEAN_TYPE_REFERENCE) :
274+
INSTANCE.streamingSerializerDeserializer(Boolean.class);
275+
}
276+
277+
private static SerializerDeserializer<Integer> intSerializer(boolean typeRef) {
278+
return typeRef ? INSTANCE.serializerDeserializer(INTEGER_TYPE_REFERENCE) :
279+
INSTANCE.serializerDeserializer(Integer.class);
280+
}
281+
282+
private static StreamingSerializerDeserializer<Integer> intStreamingSerializer(boolean typeRef) {
283+
return typeRef ? INSTANCE.streamingSerializerDeserializer(INTEGER_TYPE_REFERENCE) :
284+
INSTANCE.streamingSerializerDeserializer(Integer.class);
285+
}
286+
}

0 commit comments

Comments
 (0)