Skip to content

Commit 110f813

Browse files
authored
Merge pull request #1295 from hmcts/DTSPB-4818-ObjectMapper-AUTO_CLOSE_JSON_CONTENT
Dtspb 4818 object mapper auto close json content
2 parents 91d05dd + c5bc661 commit 110f813

File tree

10 files changed

+106
-18
lines changed

10 files changed

+106
-18
lines changed

charts/probate-orchestrator-service/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
apiVersion: v2
22
name: probate-orchestrator-service
33
home: https://github.com/hmcts/probate-orchestrator-service
4-
version: 1.0.50
4+
version: 1.0.51
55
description: HMCTS Probate Orchestrator Service
66
maintainers:
77
- name: HMCTS Probate Team

src/contractTest/java/uk/gov/hmcts/probate/contract/tests/BusinessServiceConsumerTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import au.com.dius.pact.core.model.RequestResponsePact;
1010
import com.fasterxml.jackson.databind.DeserializationFeature;
1111
import com.fasterxml.jackson.databind.ObjectMapper;
12+
import com.fasterxml.jackson.databind.SerializationFeature;
1213
import io.pactfoundation.consumer.dsl.LambdaDslJsonArray;
1314
import org.json.JSONException;
1415
import org.json.JSONObject;
@@ -258,6 +259,8 @@ public void verifyExecuteValidationErrorsLegalDeclarationPact() throws JSONExcep
258259
@Test
259260
@PactTestFor(pactMethod = "executeSuccessGetLegalDeclarationPact")
260261
public void verifyExecuteSuccessGetLegalDeclarationPact() throws JSONException, IOException {
262+
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
263+
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
261264
businessServiceApi.generateLegalDeclarationPDF(SOME_AUTHORIZATION_TOKEN, SOME_SERVICE_AUTHORIZATION_TOKEN,
262265
getLegalDeclaration("businessDocuments/validLegalDeclaration.json"));
263266

@@ -266,6 +269,8 @@ public void verifyExecuteSuccessGetLegalDeclarationPact() throws JSONException,
266269
@Test
267270
@PactTestFor(pactMethod = "executeSuccessGetBulkScanCoversheetPact")
268271
public void verifyExecuteSuccessGetBulkScanCoversheetPact() throws JSONException, IOException {
272+
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
273+
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
269274
businessServiceApi.generateBulkScanCoverSheetPDF(SOME_AUTHORIZATION_TOKEN, SOME_SERVICE_AUTHORIZATION_TOKEN,
270275
getBulkScanCoverSheet("businessDocuments/bulkScanCoverSheet.json"));
271276

src/main/java/uk/gov/hmcts/probate/client/ResponseDecorator.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,26 @@
88
import uk.gov.hmcts.reform.probate.model.client.ErrorResponse;
99

1010
import java.io.IOException;
11+
import java.nio.charset.StandardCharsets;
1112

1213
@Slf4j
1314
public class ResponseDecorator {
1415

1516
private Response response;
1617

17-
public ResponseDecorator(Response response) {
18+
private ObjectMapper objectMapper;
19+
20+
public ResponseDecorator(Response response, ObjectMapper objectMapper) {
21+
1822
this.response = response;
23+
this.objectMapper = objectMapper;
1924
}
2025

2126
public String bodyToString() {
2227
String apiError = "";
2328
try {
2429
if (this.response.body() != null) {
25-
apiError = Util.toString(this.response.body().asReader());
30+
apiError = Util.toString(this.response.body().asReader(StandardCharsets.UTF_8));
2631
}
2732
} catch (IOException ignored) {
2833
log.debug("Unable to read response body");
@@ -31,10 +36,9 @@ public String bodyToString() {
3136
}
3237

3338
public ErrorResponse mapToErrorResponse() {
34-
ObjectMapper mapper = new ObjectMapper();
3539
ErrorResponse errorResponse = null;
3640
try {
37-
errorResponse = mapper.readValue(this.bodyToString(), ErrorResponse.class);
41+
errorResponse = this.objectMapper.readValue(this.bodyToString(), ErrorResponse.class);
3842
} catch (IOException e) {
3943
log.debug("Response contained empty body");
4044
}

src/main/java/uk/gov/hmcts/probate/client/business/BusinessServiceConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package uk.gov.hmcts.probate.client.business;
22

3+
import com.fasterxml.jackson.core.JsonGenerator;
34
import com.fasterxml.jackson.databind.DeserializationFeature;
45
import com.fasterxml.jackson.databind.ObjectMapper;
56
import com.fasterxml.jackson.databind.SerializationFeature;
@@ -20,6 +21,7 @@ Encoder feignEncoder() {
2021
ObjectMapper objectMapper = new ObjectMapper();
2122
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
2223
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
24+
objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
2325
return new JacksonEncoder(objectMapper);
2426
}
2527

src/main/java/uk/gov/hmcts/probate/client/submit/SubmitServiceApiErrorDecoder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package uk.gov.hmcts.probate.client.submit;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import feign.Response;
45
import feign.codec.ErrorDecoder;
56
import lombok.extern.slf4j.Slf4j;
@@ -12,11 +13,17 @@
1213
@Slf4j
1314
public class SubmitServiceApiErrorDecoder implements ErrorDecoder {
1415

16+
private ObjectMapper objectMapper;
17+
18+
public SubmitServiceApiErrorDecoder(ObjectMapper objectMapper) {
19+
this.objectMapper = objectMapper;
20+
}
21+
1522
@Override
1623
public Exception decode(String methodKey, Response response) {
1724
log.error("Response status: {}", response.status());
1825

19-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
26+
ResponseDecorator responseDecorator = new ResponseDecorator(response,this.objectMapper);
2027
ErrorResponse errorResponse = responseDecorator.mapToErrorResponse();
2128

2229
return new ApiClientException(response.status(), errorResponse);

src/main/java/uk/gov/hmcts/probate/client/submit/SubmitServiceConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ public class SubmitServiceConfiguration {
1717
static final String INVITATION_ID = "invitationId";
1818
static final String CASE_ID = "caseId";
1919

20+
2021
@Bean
2122
@Primary
2223
public Decoder feignDecoder(ObjectMapper objectMapper) {
2324
return new JacksonDecoder(objectMapper);
2425
}
2526

2627
@Bean
27-
public SubmitServiceApiErrorDecoder submitServiceApiErrorDecoder() {
28-
return new SubmitServiceApiErrorDecoder();
28+
public SubmitServiceApiErrorDecoder submitServiceApiErrorDecoder(ObjectMapper objectMapper) {
29+
return new SubmitServiceApiErrorDecoder(objectMapper);
2930
}
3031

3132
@Bean
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package uk.gov.hmcts.probate.configuration;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.springframework.stereotype.Component;
6+
7+
@Component
8+
public class JacksonConfigurationVerifier {
9+
10+
public JacksonConfigurationVerifier(ObjectMapper objectMapper) {
11+
if (objectMapper.getFactory().isEnabled(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)) {
12+
throw new IllegalStateException("Jackson ObjectMapper is configured with AUTO_CLOSE_JSON_CONTENT enabled. "
13+
+ "This can cause issues with streaming JSON responses and must be disabled.");
14+
}
15+
}
16+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package uk.gov.hmcts.probate.configuration;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.core.JsonGenerator;
5+
import com.fasterxml.jackson.databind.DeserializationFeature;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.SerializationFeature;
8+
import com.fasterxml.jackson.databind.json.JsonMapper;
9+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
10+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
11+
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.context.annotation.Bean;
14+
import org.springframework.context.annotation.Configuration;
15+
import org.springframework.context.annotation.Primary;
16+
17+
@Configuration
18+
@RequiredArgsConstructor
19+
public class ObjectMapperConfiguration {
20+
21+
22+
@Bean
23+
@Primary
24+
public ObjectMapper objectMapper() {
25+
return JsonMapper
26+
.builder()
27+
.addModule(new JavaTimeModule())
28+
.addModule(new Jdk8Module())
29+
.addModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))
30+
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
31+
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
32+
.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)
33+
.build();
34+
35+
}
36+
37+
}

src/test/java/uk/gov/hmcts/probate/client/ResponseDecoratorTest.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package uk.gov.hmcts.probate.client;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import feign.Request;
45
import feign.Response;
56
import feign.Util;
67
import org.junit.jupiter.api.Test;
78
import org.junit.jupiter.api.extension.ExtendWith;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.boot.test.context.SpringBootTest;
811
import org.springframework.http.HttpMethod;
912
import org.springframework.test.context.junit.jupiter.SpringExtension;
1013
import uk.gov.hmcts.probate.TestUtils;
@@ -23,12 +26,16 @@
2326
import static org.junit.jupiter.api.Assertions.assertEquals;
2427

2528
@ExtendWith(SpringExtension.class)
29+
@SpringBootTest
2630
public class ResponseDecoratorTest {
2731

2832
private Map<String, Collection<String>> headers = new LinkedHashMap<>();
2933

34+
@Autowired
35+
private ObjectMapper objectMapper;
36+
3037
@Test
31-
public void bodyToStringShouldReturnString() {
38+
public void bodyToStringShouldReturnString() throws ReflectiveOperationException {
3239
Response response = Response.builder()
3340
.status(400)
3441
.reason("Bad Request")
@@ -37,7 +44,7 @@ public void bodyToStringShouldReturnString() {
3744
.body("hello world", UTF_8)
3845
.build();
3946

40-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
47+
ResponseDecorator responseDecorator = new ResponseDecorator(response,objectMapper);
4148
String body = responseDecorator.bodyToString();
4249

4350
assertThat(body).isEqualTo("hello world");
@@ -52,7 +59,7 @@ public void bodyToStringShouldReturnEmptyStringIfResponseBodyIsNull() {
5259
.headers(headers)
5360
.build();
5461

55-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
62+
ResponseDecorator responseDecorator = new ResponseDecorator(response,objectMapper);
5663
String body = responseDecorator.bodyToString();
5764

5865
assertThat(response.body()).isNull();
@@ -73,7 +80,7 @@ public int read() throws IOException {
7380
}
7481
}, 1)
7582
.build();
76-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
83+
ResponseDecorator responseDecorator = new ResponseDecorator(response,objectMapper);
7784

7885
String body = responseDecorator.bodyToString();
7986

@@ -92,7 +99,7 @@ public void mapToErrorResponseShouldReturnValidationErrorResponse() throws IOExc
9299
.body(validationErrorResponse, UTF_8)
93100
.build();
94101

95-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
102+
ResponseDecorator responseDecorator = new ResponseDecorator(response,objectMapper);
96103
ErrorResponse errorResponse = responseDecorator.mapToErrorResponse();
97104

98105
assertThat(errorResponse.getType()).isEqualTo(ErrorType.VALIDATION);
@@ -110,7 +117,7 @@ public void mapToErrorResponseShouldReturnApiClientErrorResponse() throws IOExce
110117
.body(apiClientErrorResponse, UTF_8)
111118
.build();
112119

113-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
120+
ResponseDecorator responseDecorator = new ResponseDecorator(response,objectMapper);
114121
ErrorResponse errorResponse = responseDecorator.mapToErrorResponse();
115122

116123
assertThat(errorResponse.getType()).isEqualTo(ErrorType.API_CLIENT);
@@ -126,7 +133,7 @@ public void mapToErrorResponseResponseBodyIsNull() {
126133
.headers(headers)
127134
.build();
128135

129-
ResponseDecorator responseDecorator = new ResponseDecorator(response);
136+
ResponseDecorator responseDecorator = new ResponseDecorator(response,objectMapper);
130137
ErrorResponse errorResponse = responseDecorator.mapToErrorResponse();
131138

132139
assertThat(errorResponse).isNull();

src/test/java/uk/gov/hmcts/probate/client/SubmitServiceApiErrorDecoderTest.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package uk.gov.hmcts.probate.client;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import feign.Request;
45
import feign.Response;
56
import feign.Util;
67
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.ExtendWith;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.boot.test.context.SpringBootTest;
711
import org.springframework.http.HttpMethod;
12+
import org.springframework.test.context.junit.jupiter.SpringExtension;
813
import uk.gov.hmcts.probate.client.submit.SubmitServiceApiErrorDecoder;
914
import uk.gov.hmcts.reform.probate.model.client.ApiClientException;
1015

@@ -15,15 +20,20 @@
1520

1621
import static org.junit.jupiter.api.Assertions.assertThrows;
1722

18-
23+
@ExtendWith(SpringExtension.class)
24+
@SpringBootTest
1925
public class SubmitServiceApiErrorDecoderTest {
2026

27+
@Autowired
28+
private ObjectMapper objectMapper;
29+
2130
private Map<String, Collection<String>> headers = new LinkedHashMap<>();
2231

23-
private SubmitServiceApiErrorDecoder errorDecoder = new SubmitServiceApiErrorDecoder();
32+
private SubmitServiceApiErrorDecoder errorDecoder;
2433

2534
@Test
2635
public void throwsApiClientException() throws Throwable {
36+
errorDecoder = new SubmitServiceApiErrorDecoder(objectMapper);
2737
assertThrows(ApiClientException.class, () -> {
2838
Response response = Response.builder()
2939
.status(500)
@@ -32,7 +42,6 @@ public void throwsApiClientException() throws Throwable {
3242
"/api", Collections.emptyMap(), null, Util.UTF_8))
3343
.headers(headers)
3444
.build();
35-
3645
throw errorDecoder.decode("Service#foo()", response);
3746
});
3847

0 commit comments

Comments
 (0)