Skip to content

Commit b0bbf5b

Browse files
authored
Merge pull request #1724 from marklogic/feature/18056-bug
MLE-18056 Fixing null pointer bug in JSONErrorParser
2 parents 8048ee2 + 1bd90e8 commit b0bbf5b

File tree

5 files changed

+95
-37
lines changed

5 files changed

+95
-37
lines changed

Jenkinsfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def setupDockerMarkLogic(String image){
2121
docker compose down -v || true
2222
docker volume prune -f
2323
echo "Using image: "'''+image+'''
24+
docker pull '''+image+'''
2425
MARKLOGIC_IMAGE='''+image+''' MARKLOGIC_LOGS_VOLUME=marklogicLogs docker compose up -d --build
2526
echo "mlPassword=admin" > gradle-local.properties
2627
echo "Waiting for MarkLogic server to initialize."

marklogic-client-api/src/main/java/com/marklogic/client/impl/FailedRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ private static FailedRequest jsonFailedRequest(int httpStatus, InputStream conte
150150
public FailedRequest() {
151151
}
152152

153+
public FailedRequest(int statusCode, String messageString) {
154+
this.statusCode = statusCode;
155+
this.messageString = messageString;
156+
}
157+
153158
public String getStackTrace() {
154159
return stackTrace;
155160
}

marklogic-client-api/src/main/java/com/marklogic/client/io/JSONErrorParser.java

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,38 @@
33
*/
44
package com.marklogic.client.io;
55

6-
import java.io.IOException;
7-
import java.io.InputStream;
8-
import java.util.Map;
9-
10-
import com.fasterxml.jackson.core.JsonParseException;
11-
import com.fasterxml.jackson.databind.JsonMappingException;
126
import com.fasterxml.jackson.databind.ObjectMapper;
137
import com.marklogic.client.impl.FailedRequest;
148
import com.marklogic.client.impl.FailedRequestParser;
159

16-
/**
17-
* This class is provided as a convenience method for parsing MarkLogic errors that
18-
* are serialized as JSON. In order to use this class, your project must provide
19-
* the Jackson data binding library for JSON.
20-
*/
10+
import java.io.InputStream;
11+
import java.util.Map;
12+
2113
public class JSONErrorParser implements FailedRequestParser {
2214

23-
@SuppressWarnings("unchecked")
24-
@Override
25-
public FailedRequest parseFailedRequest(int httpStatus, InputStream content) {
26-
FailedRequest failure = new FailedRequest();
27-
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
28-
Map<String, Map<String, String>> errorData;
29-
try {
30-
errorData = mapper.readValue(content, Map.class);
31-
Map<String, String> errorBody = errorData.get("errorResponse");
32-
failure.setStatusCode(httpStatus);
33-
failure.setStatusString(errorBody.get("status"));
34-
failure.setMessageCode(errorBody.get("messageCode"));
35-
failure.setMessageString(errorBody.get("message"));
36-
failure.setStackTrace(errorBody.get("stackTrace"));
37-
} catch (JsonParseException e1) {
38-
failure.setStatusCode(httpStatus);
39-
failure.setMessageString("Request failed. Error body not received from server");
40-
} catch (JsonMappingException e1) {
41-
failure.setStatusCode(httpStatus);
42-
failure.setMessageString("Request failed. Error body not received from server");
43-
} catch (IOException e1) {
44-
failure.setStatusCode(httpStatus);
45-
failure.setMessageString("Request failed. Error body not received from server");
46-
}
47-
return failure;
48-
}
15+
private final ObjectMapper objectMapper = new ObjectMapper();
16+
17+
@SuppressWarnings("unchecked")
18+
@Override
19+
public FailedRequest parseFailedRequest(int httpStatus, InputStream content) {
20+
Map<String, Map<String, String>> errorData;
21+
try {
22+
errorData = objectMapper.readValue(content, Map.class);
23+
} catch (Exception ex) {
24+
return new FailedRequest(httpStatus, "Request failed; could not parse JSON in response body.");
25+
}
26+
27+
if (!errorData.containsKey("errorResponse")) {
28+
return new FailedRequest(httpStatus, "Unexpected JSON in response body; did not find 'errorResponse' key; response: " + errorData);
29+
}
30+
31+
FailedRequest failure = new FailedRequest();
32+
Map<String, String> errorBody = errorData.get("errorResponse");
33+
failure.setStatusCode(httpStatus);
34+
failure.setStatusString(errorBody.get("status"));
35+
failure.setMessageCode(errorBody.get("messageCode"));
36+
failure.setMessageString(errorBody.get("message"));
37+
failure.setStackTrace(errorBody.get("stackTrace"));
38+
return failure;
39+
}
4940
}

marklogic-client-api/src/test/java/com/marklogic/client/test/io/DocumentMetadataHandleTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ public void testCapabilityEnum() {
183183
@Test
184184
// Requires MarkLogic 11 or higher now that we're using Docker; the INSTALL_CONVERTERS flag does not work for MarkLogic 10.
185185
@ExtendWith(RequiresML11.class)
186+
@Disabled("This is consistently failing in Jenkins with an error of: " +
187+
"Process run error: fork: Cannot allocate memory. It runs fine locally and is ultimately just a test of a " +
188+
"v1/documents feature and not of the Java Client.")
186189
public void testMetadataPropertiesExtraction() {
187190
String docId = "/test.bin";
188191
// Make a document manager to work with binary files
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright © 2024 MarkLogic Corporation. All Rights Reserved.
3+
*/
4+
package com.marklogic.client.test.io;
5+
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.node.ObjectNode;
8+
import com.marklogic.client.impl.FailedRequest;
9+
import com.marklogic.client.io.JSONErrorParser;
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.Test;
12+
13+
import java.io.ByteArrayInputStream;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
17+
class JSONErrorParserTest {
18+
19+
private final JSONErrorParser parser = new JSONErrorParser();
20+
private final ObjectMapper mapper = new ObjectMapper();
21+
private ObjectNode response;
22+
23+
@BeforeEach
24+
void setup() {
25+
response = mapper.createObjectNode();
26+
}
27+
28+
@Test
29+
void happyPath() {
30+
response.putObject("errorResponse")
31+
.put("status", "My status")
32+
.put("messageCode", "MY_CODE")
33+
.put("message", "My message")
34+
.put("stackTrace", "My stacktrace");
35+
36+
FailedRequest failure = parser.parseFailedRequest(400, new ByteArrayInputStream(response.toPrettyString().getBytes()));
37+
assertEquals(400, failure.getStatusCode());
38+
assertEquals("My status", failure.getStatus());
39+
assertEquals("MY_CODE", failure.getMessageCode());
40+
assertEquals("My message", failure.getMessage());
41+
assertEquals("My stacktrace", failure.getStackTrace());
42+
}
43+
44+
@Test
45+
void noErrorResponse() {
46+
response.putObject("some_other_data");
47+
48+
FailedRequest failure = parser.parseFailedRequest(500, new ByteArrayInputStream(response.toPrettyString().getBytes()));
49+
assertEquals(500, failure.getStatusCode());
50+
assertEquals(
51+
"Unexpected JSON in response body; did not find 'errorResponse' key; response: {some_other_data={}}",
52+
failure.getMessage(),
53+
"In the event that the user mistakenly sends a request to a non-REST-API server but still receives a JSON " +
54+
"response body, the error should identify the issue and include the JSON response body to help the " +
55+
"user realize that they likely sent the request to a non-REST-API server."
56+
);
57+
}
58+
}

0 commit comments

Comments
 (0)