Add parsing method for ElasticsearchException.generateFailureXContent() (#22815)
This commit adds a ElasticsearchException.failureFromXContent() that can be used to parse the result of ElasticsearchException.generateFailureXContent().
This commit is contained in:
parent
1baa884ab7
commit
ea7077fb1b
|
@ -51,6 +51,7 @@ import static java.util.Collections.singletonMap;
|
|||
import static java.util.Collections.unmodifiableMap;
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_UUID_NA_VALUE;
|
||||
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
|
||||
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureFieldName;
|
||||
|
||||
/**
|
||||
* A base class for all elasticsearch exceptions.
|
||||
|
@ -478,16 +479,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
}
|
||||
}
|
||||
|
||||
StringBuilder message = new StringBuilder("Elasticsearch exception [");
|
||||
message.append(TYPE).append('=').append(type).append(", ");
|
||||
message.append(REASON).append('=').append(reason);
|
||||
if (stack != null) {
|
||||
message.append(", ").append(STACK_TRACE).append('=').append(stack);
|
||||
}
|
||||
message.append(']');
|
||||
|
||||
ElasticsearchException e = new ElasticsearchException(message.toString(), cause);
|
||||
|
||||
ElasticsearchException e = new ElasticsearchException(buildMessage(type, reason, stack), cause);
|
||||
for (Map.Entry<String, List<String>> entry : metadata.entrySet()) {
|
||||
//subclasses can print out additional metadata through the metadataToXContent method. Simple key-value pairs will be
|
||||
//parsed back and become part of this metadata set, while objects and arrays are not supported when parsing back.
|
||||
|
@ -527,7 +519,8 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
* exception is rendered. When it's true all detail are provided including guesses root causes, cause and potentially stack
|
||||
* trace.
|
||||
*
|
||||
* This method is usually used when the {@link Exception} is rendered as a full XContent object.
|
||||
* This method is usually used when the {@link Exception} is rendered as a full XContent object, and its output can be parsed
|
||||
* by the {@link #failureFromXContent(XContentParser)} method.
|
||||
*/
|
||||
public static void generateFailureXContent(XContentBuilder builder, Params params, @Nullable Exception e, boolean detailed)
|
||||
throws IOException {
|
||||
|
@ -568,6 +561,27 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
builder.endObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the output of {@link #generateFailureXContent(XContentBuilder, Params, Exception, boolean)}
|
||||
*/
|
||||
public static ElasticsearchException failureFromXContent(XContentParser parser) throws IOException {
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
ensureFieldName(parser, token, ERROR);
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token.isValue()) {
|
||||
return new ElasticsearchException(buildMessage("exception", parser.text(), null));
|
||||
}
|
||||
|
||||
ensureExpectedToken(token, XContentParser.Token.START_OBJECT, parser::getTokenLocation);
|
||||
token = parser.nextToken();
|
||||
|
||||
// TODO Root causes are ignored for now. They will be skipped by innerFromXContent() because
|
||||
// it ignores metadata arrays of objects. If we decide to parse root causes, we'll have to
|
||||
// change innerFromXContent() so that it does not parse root causes on its own.
|
||||
return innerFromXContent(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root cause of this exception or multiple if different shards caused different exceptions
|
||||
*/
|
||||
|
@ -613,6 +627,17 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
return toUnderscoreCase(simpleName);
|
||||
}
|
||||
|
||||
static String buildMessage(String type, String reason, String stack) {
|
||||
StringBuilder message = new StringBuilder("Elasticsearch exception [");
|
||||
message.append(TYPE).append('=').append(type).append(", ");
|
||||
message.append(REASON).append('=').append(reason);
|
||||
if (stack != null) {
|
||||
message.append(", ").append(STACK_TRACE).append('=').append(stack);
|
||||
}
|
||||
message.append(']');
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -589,22 +589,67 @@ public class ElasticsearchExceptionTests extends ESTestCase {
|
|||
assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());
|
||||
assertNull(parser.nextToken());
|
||||
}
|
||||
assertNotNull(parsedException);
|
||||
assertDeepEquals(exceptions.v2(), parsedException);
|
||||
}
|
||||
|
||||
public void testUnknownFailureToAndFromXContent() throws IOException {
|
||||
final XContent xContent = randomFrom(XContentType.values()).xContent();
|
||||
|
||||
BytesReference failureBytes = XContentHelper.toXContent((builder, params) -> {
|
||||
// Prints a null failure using generateFailureXContent()
|
||||
ElasticsearchException.generateFailureXContent(builder, params, null, randomBoolean());
|
||||
return builder;
|
||||
}, xContent.type(), randomBoolean());
|
||||
|
||||
ElasticsearchException parsedFailure;
|
||||
try (XContentParser parser = createParser(xContent, failureBytes)) {
|
||||
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
|
||||
parsedFailure = ElasticsearchException.failureFromXContent(parser);
|
||||
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
|
||||
assertNull(parser.nextToken());
|
||||
}
|
||||
|
||||
// Failure was null, expecting a "unknown" reason
|
||||
assertEquals("Elasticsearch exception [type=exception, reason=unknown]", parsedFailure.getMessage());
|
||||
assertEquals(0, parsedFailure.getHeaders().size());
|
||||
assertEquals(0, parsedFailure.getMetadata().size());
|
||||
}
|
||||
|
||||
public void testFailureToAndFromXContent() throws IOException {
|
||||
final XContent xContent = randomFrom(XContentType.values()).xContent();
|
||||
final Tuple<Throwable, ElasticsearchException> exceptions = randomExceptions();
|
||||
final boolean detailed = randomBoolean();
|
||||
|
||||
Exception failure = (Exception) exceptions.v1();
|
||||
BytesReference failureBytes = XContentHelper.toXContent((builder, params) -> {
|
||||
ElasticsearchException.generateFailureXContent(builder, params, failure, detailed);
|
||||
return builder;
|
||||
}, xContent.type(), randomBoolean());
|
||||
|
||||
ElasticsearchException parsedFailure;
|
||||
try (XContentParser parser = createParser(xContent, failureBytes)) {
|
||||
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
|
||||
parsedFailure = ElasticsearchException.failureFromXContent(parser);
|
||||
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
|
||||
assertNull(parser.nextToken());
|
||||
}
|
||||
assertNotNull(parsedFailure);
|
||||
|
||||
ElasticsearchException expected = exceptions.v2();
|
||||
do {
|
||||
assertEquals(expected.getMessage(), parsedException.getMessage());
|
||||
assertEquals(expected.getHeaders(), parsedException.getHeaders());
|
||||
assertEquals(expected.getMetadata(), parsedException.getMetadata());
|
||||
assertEquals(expected.getResourceType(), parsedException.getResourceType());
|
||||
assertEquals(expected.getResourceId(), parsedException.getResourceId());
|
||||
|
||||
expected = (ElasticsearchException) expected.getCause();
|
||||
parsedException = (ElasticsearchException) parsedException.getCause();
|
||||
if (expected == null) {
|
||||
assertNull(parsedException);
|
||||
if (detailed) {
|
||||
assertDeepEquals(expected, parsedFailure);
|
||||
} else {
|
||||
String reason;
|
||||
if (failure instanceof ElasticsearchException) {
|
||||
reason = failure.getClass().getSimpleName() + "[" + failure.getMessage() + "]";
|
||||
} else {
|
||||
reason = "No ElasticsearchException found";
|
||||
}
|
||||
} while (expected != null);
|
||||
assertEquals(ElasticsearchException.buildMessage("exception", reason, null), parsedFailure.getMessage());
|
||||
assertEquals(0, parsedFailure.getHeaders().size());
|
||||
assertEquals(0, parsedFailure.getMetadata().size());
|
||||
assertNull(parsedFailure.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -625,6 +670,28 @@ public class ElasticsearchExceptionTests extends ESTestCase {
|
|||
}, expectedJson);
|
||||
}
|
||||
|
||||
private static void assertDeepEquals(ElasticsearchException expected, ElasticsearchException actual) {
|
||||
if (expected == null) {
|
||||
assertNull(actual);
|
||||
} else {
|
||||
assertNotNull(actual);
|
||||
}
|
||||
|
||||
do {
|
||||
assertEquals(expected.getMessage(), actual.getMessage());
|
||||
assertEquals(expected.getHeaders(), actual.getHeaders());
|
||||
assertEquals(expected.getMetadata(), actual.getMetadata());
|
||||
assertEquals(expected.getResourceType(), actual.getResourceType());
|
||||
assertEquals(expected.getResourceId(), actual.getResourceId());
|
||||
|
||||
expected = (ElasticsearchException) expected.getCause();
|
||||
actual = (ElasticsearchException) actual.getCause();
|
||||
if (expected == null) {
|
||||
assertNull(actual);
|
||||
}
|
||||
} while (expected != null);
|
||||
}
|
||||
|
||||
private static Tuple<Throwable, ElasticsearchException> randomExceptions() {
|
||||
Throwable actual;
|
||||
ElasticsearchException expected;
|
||||
|
|
Loading…
Reference in New Issue