Skip to content

Commit 9a0506a

Browse files
authored
[DE-821] fix deserialize empty response (#560)
* fixed deserialization of empty response body * test async execution and later result retrieval * fixed check entity class * fix concurrent modification exceptions in resilience tests logs
1 parent b65c92e commit 9a0506a

File tree

7 files changed

+118
-39
lines changed

7 files changed

+118
-39
lines changed

Diff for: core/src/main/java/com/arangodb/internal/config/ArangoConfig.java

+1-34
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.arangodb.internal.config;
22

3-
import com.arangodb.ArangoDBException;
43
import com.arangodb.Compression;
5-
import com.arangodb.ContentType;
64
import com.arangodb.Protocol;
75
import com.arangodb.arch.UsedInApi;
86
import com.arangodb.config.ArangoConfigProperties;
@@ -16,8 +14,6 @@
1614
import com.arangodb.serde.ArangoSerde;
1715
import com.arangodb.serde.ArangoSerdeProvider;
1816
import com.fasterxml.jackson.databind.Module;
19-
import org.slf4j.Logger;
20-
import org.slf4j.LoggerFactory;
2117

2218
import javax.net.ssl.SSLContext;
2319
import java.util.*;
@@ -52,35 +48,6 @@ public class ArangoConfig {
5248
private Integer compressionLevel;
5349
private ProtocolConfig protocolConfig;
5450

55-
private static final Logger LOG = LoggerFactory.getLogger(ArangoConfig.class);
56-
57-
private static ArangoSerdeProvider serdeProvider(ContentType contentType) {
58-
ServiceLoader<ArangoSerdeProvider> loader = ServiceLoader.load(ArangoSerdeProvider.class);
59-
ArangoSerdeProvider serdeProvider = null;
60-
Iterator<ArangoSerdeProvider> iterator = loader.iterator();
61-
while (iterator.hasNext()) {
62-
ArangoSerdeProvider p;
63-
try {
64-
p = iterator.next();
65-
} catch (ServiceConfigurationError e) {
66-
LOG.warn("ServiceLoader failed to load ArangoSerdeProvider", e);
67-
continue;
68-
}
69-
if (contentType.equals(p.getContentType())) {
70-
if (serdeProvider != null) {
71-
throw new ArangoDBException("Found multiple serde providers! Please set explicitly the one to use.");
72-
}
73-
serdeProvider = p;
74-
}
75-
}
76-
if (serdeProvider == null) {
77-
LOG.warn("No ArangoSerdeProvider found, using InternalSerdeProvider. Please consider registering a custom " +
78-
"ArangoSerdeProvider to avoid depending on internal classes which are not part of the public API.");
79-
serdeProvider = new InternalSerdeProvider(contentType);
80-
}
81-
return serdeProvider;
82-
}
83-
8451
public ArangoConfig() {
8552
// load default properties
8653
loadProperties(new ArangoConfigProperties() {
@@ -272,7 +239,7 @@ public void setLoadBalancingStrategy(LoadBalancingStrategy loadBalancingStrategy
272239

273240
public ArangoSerde getUserDataSerde() {
274241
if (userDataSerde == null) {
275-
userDataSerde = serdeProvider(ContentTypeFactory.of(getProtocol())).create();
242+
userDataSerde = ArangoSerdeProvider.of(ContentTypeFactory.of(getProtocol())).create();
276243
}
277244
return userDataSerde;
278245
}

Diff for: core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public <T> T deserialize(final JsonNode node, final Type type) {
160160

161161
@Override
162162
public <T> T deserialize(final byte[] content, final Type type) {
163-
if (content == null) {
163+
if (content == null || content.length == 0) {
164164
return null;
165165
}
166166
try {
@@ -175,6 +175,15 @@ private boolean isManagedClass(Class<?> clazz) {
175175
RawJson.class.equals(clazz) ||
176176
RawBytes.class.equals(clazz) ||
177177
BaseDocument.class.equals(clazz) ||
178-
BaseEdgeDocument.class.equals(clazz);
178+
BaseEdgeDocument.class.equals(clazz) ||
179+
isEntityClass(clazz);
180+
}
181+
182+
private boolean isEntityClass(Class<?> clazz) {
183+
Package pkg = clazz.getPackage();
184+
if (pkg == null) {
185+
return false;
186+
}
187+
return pkg.getName().startsWith("com.arangodb.entity");
179188
}
180189
}

Diff for: core/src/main/java/com/arangodb/serde/ArangoSerdeProvider.java

+37
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,46 @@
11
package com.arangodb.serde;
22

3+
import com.arangodb.ArangoDBException;
34
import com.arangodb.ContentType;
5+
import com.arangodb.internal.serde.InternalSerdeProvider;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import java.util.Iterator;
10+
import java.util.ServiceConfigurationError;
11+
import java.util.ServiceLoader;
412

513
public interface ArangoSerdeProvider {
614

15+
static ArangoSerdeProvider of(ContentType contentType) {
16+
Logger LOG = LoggerFactory.getLogger(ArangoSerdeProvider.class);
17+
18+
ServiceLoader<ArangoSerdeProvider> loader = ServiceLoader.load(ArangoSerdeProvider.class);
19+
ArangoSerdeProvider serdeProvider = null;
20+
Iterator<ArangoSerdeProvider> iterator = loader.iterator();
21+
while (iterator.hasNext()) {
22+
ArangoSerdeProvider p;
23+
try {
24+
p = iterator.next();
25+
} catch (ServiceConfigurationError e) {
26+
LOG.warn("ServiceLoader failed to load ArangoSerdeProvider", e);
27+
continue;
28+
}
29+
if (contentType.equals(p.getContentType())) {
30+
if (serdeProvider != null) {
31+
throw new ArangoDBException("Found multiple serde providers! Please set explicitly the one to use.");
32+
}
33+
serdeProvider = p;
34+
}
35+
}
36+
if (serdeProvider == null) {
37+
LOG.warn("No ArangoSerdeProvider found, using InternalSerdeProvider. Please consider registering a custom " +
38+
"ArangoSerdeProvider to avoid depending on internal classes which are not part of the public API.");
39+
serdeProvider = new InternalSerdeProvider(contentType);
40+
}
41+
return serdeProvider;
42+
}
43+
744
/**
845
* @return a new serde instance
946
*/

Diff for: driver/src/test/java/com/arangodb/ArangoDBTest.java

+31
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.arangodb.util.UnicodeUtils;
3232
import com.fasterxml.jackson.databind.JsonNode;
3333
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
34+
import com.fasterxml.jackson.databind.node.ObjectNode;
3435
import org.junit.jupiter.api.AfterAll;
3536
import org.junit.jupiter.api.BeforeAll;
3637
import org.junit.jupiter.api.Disabled;
@@ -691,6 +692,36 @@ void queueTime(ArangoDB arangoDB) throws InterruptedException, ExecutionExceptio
691692
assertThat(avg).isEqualTo(0.0);
692693
assertThat(values).isEmpty();
693694
}
695+
}
696+
697+
@ParameterizedTest
698+
@MethodSource("arangos")
699+
void asyncAndLaterResultRetrieval(ArangoDB arangoDB) throws InterruptedException {
700+
Request<RawJson> request = Request.<RawJson>builder()
701+
.db(ArangoRequestParam.SYSTEM)
702+
.method(Request.Method.POST)
703+
.path("/_api/cursor")
704+
.header("x-arango-async", "store")
705+
.body(RawJson.of("{\"query\":\"RETURN SLEEP(0.1) || 5\"}"))
706+
.build();
707+
708+
Response<?> response = arangoDB.execute(request, Void.class);
709+
String jobId = response.getHeaders().get("x-arango-async-id");
710+
System.out.println(jobId);
711+
712+
Request<?> request2 = Request.builder()
713+
.db(ArangoRequestParam.SYSTEM)
714+
.method(Request.Method.PUT)
715+
.path("/_api/job/" + jobId)
716+
.build();
717+
718+
Response<ObjectNode> response2 = arangoDB.execute(request2, ObjectNode.class);
719+
while (response2.getResponseCode() == 204) {
720+
Thread.sleep(50);
721+
response2 = arangoDB.execute(request2, ObjectNode.class);
722+
}
694723

724+
assertThat(response2.getResponseCode()).isEqualTo(201);
725+
assertThat(response2.getBody().get("result").get(0).numberValue()).isEqualTo(5);
695726
}
696727
}

Diff for: driver/src/test/java/com/arangodb/serde/SerdeTest.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
import org.junit.jupiter.params.ParameterizedTest;
1414
import org.junit.jupiter.params.provider.EnumSource;
1515

16-
import java.util.Collections;
17-
import java.util.Map;
16+
import java.util.*;
1817

1918
import static org.assertj.core.api.Assertions.assertThat;
2019

@@ -69,4 +68,35 @@ void serializeBaseDocumentWithNestedProperties(ContentType type) {
6968
assertThat(on.get("properties").get("foo").textValue()).isEqualTo("bbb");
7069
}
7170

71+
@ParameterizedTest
72+
@EnumSource(ContentType.class)
73+
void deserializeNull(ContentType type) {
74+
InternalSerde s = new InternalSerdeProvider(type).create();
75+
Void deser = s.deserialize((byte[]) null, Void.class);
76+
assertThat(deser).isNull();
77+
}
78+
79+
@ParameterizedTest
80+
@EnumSource(ContentType.class)
81+
void deserializeNullUserSerde(ContentType type) {
82+
ArangoSerde s = ArangoSerdeProvider.of(type).create();
83+
Void deser = s.deserialize(null, Void.class);
84+
assertThat(deser).isNull();
85+
}
86+
87+
@ParameterizedTest
88+
@EnumSource(ContentType.class)
89+
void deserializeEmpty(ContentType type) {
90+
InternalSerde s = new InternalSerdeProvider(type).create();
91+
Void deser = s.deserialize(new byte[0], Void.class);
92+
assertThat(deser).isNull();
93+
}
94+
95+
@ParameterizedTest
96+
@EnumSource(ContentType.class)
97+
void deserializeEmptyUserSerde(ContentType type) {
98+
ArangoSerde s = ArangoSerdeProvider.of(type).create();
99+
Void deser = s.deserialize(new byte[0], Void.class);
100+
assertThat(deser).isNull();
101+
}
72102
}

Diff for: jackson-serde-json/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public <T> T deserialize(final byte[] content, final Class<T> type) {
4343
@Override
4444
public <T> T deserialize(byte[] content, Class<T> type, RequestContext ctx) {
4545
Objects.requireNonNull(ctx);
46+
if (content == null || content.length == 0) {
47+
return null;
48+
}
4649
try {
4750
return mapper.readerFor(mapper.constructType(type))
4851
.with(ContextAttributes.getEmpty().withPerCallAttribute(SERDE_CONTEXT_ATTRIBUTE_NAME, ctx))

Diff for: resilience-tests/src/test/java/resilience/utils/MemoryAppender.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import ch.qos.logback.core.read.ListAppender;
77
import org.slf4j.LoggerFactory;
88

9+
import java.util.ArrayList;
910
import java.util.stream.Stream;
1011

1112
public class MemoryAppender extends ListAppender<ILoggingEvent> {
@@ -22,6 +23,7 @@ public void reset() {
2223
}
2324

2425
public Stream<ILoggingEvent> getLogs() {
25-
return list.stream();
26+
// avoid concurrent modification exceptions
27+
return new ArrayList<>(list).stream();
2628
}
2729
}

0 commit comments

Comments
 (0)