From 3092176b49f073bde7563fda9f77050b3f6fc204 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 15 Dec 2016 17:00:23 +0100 Subject: [PATCH 1/2] Add fromXContent() methods for ReplicationResponse This commit adds the parsing fromXContent() methods to the ReplicationResponse.ShardInfo and ReplicationResponse.ShardInfo.Failure classes. --- .../replication/ReplicationResponse.java | 88 +++++++ .../replication/ReplicationResponseTests.java | 223 ++++++++++++------ 2 files changed, 238 insertions(+), 73 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationResponse.java b/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationResponse.java index 6e44c4f158cf1..afb92e27205f1 100644 --- a/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationResponse.java +++ b/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationResponse.java @@ -23,19 +23,26 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ShardOperationFailedException; +import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownField; + /** * Base class for write action responses. */ @@ -186,6 +193,48 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + public static ShardInfo fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + + String currentFieldName = parser.currentName(); + if (_SHARDS.equals(currentFieldName) == false) { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation); + + int total = 0, successful = 0; + List failuresList = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (TOTAL.equals(currentFieldName)) { + total = parser.intValue(); + } else if (SUCCESSFUL.equals(currentFieldName)) { + successful = parser.intValue(); + } else if (FAILED.equals(currentFieldName) == false) { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (FAILURES.equals(currentFieldName)) { + failuresList = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + failuresList.add(Failure.fromXContent(parser)); + } + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } + } + Failure[] failures = EMPTY; + if (failuresList != null) { + failures = failuresList.toArray(new Failure[failuresList.size()]); + } + return new ShardInfo(total, successful, failures); + } + @Override public String toString() { return "ShardInfo{" + @@ -338,6 +387,45 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); return builder; } + + public static Failure fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token = parser.currentToken(); + ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation); + + String shardIndex = null, nodeId = null; + int shardId = -1; + boolean primary = false; + RestStatus status = null; + ElasticsearchException reason = null; + + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (_INDEX.equals(currentFieldName)) { + shardIndex = parser.text(); + } else if (_SHARD.equals(currentFieldName)) { + shardId = parser.intValue(); + } else if (_NODE.equals(currentFieldName)) { + nodeId = parser.text(); + } else if (STATUS.equals(currentFieldName)) { + status = RestStatus.valueOf(parser.text()); + } else if (PRIMARY.equals(currentFieldName)) { + primary = parser.booleanValue(); + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (REASON.equals(currentFieldName)) { + reason = ElasticsearchException.fromXContent(parser); + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } + } + return new Failure(new ShardId(shardIndex, IndexMetaData.INDEX_UUID_NA_VALUE, shardId), nodeId, reason, status, primary); + } } } } diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java index 7d82b7441332b..c19e1de70ff6f 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java @@ -21,9 +21,8 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.RoutingMissingException; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContent; -import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.Index; @@ -117,95 +116,173 @@ public void testFailureEqualsAndHashcode() { } public void testShardInfoToXContent() throws IOException { - ReplicationResponse.ShardInfo shardInfo = new ReplicationResponse.ShardInfo(5, 3); + final XContentType xContentType = randomFrom(XContentType.values()); - final XContent xContent = randomFrom(XContentType.values()).xContent(); - try (XContentBuilder builder = XContentBuilder.builder(xContent)) { - builder.startObject(); - shardInfo.toXContent(builder, ToXContent.EMPTY_PARAMS); - builder.endObject(); + final ReplicationResponse.ShardInfo shardInfo = new ReplicationResponse.ShardInfo(5, 3); + final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType, true); - // Expected JSON is {"_shards":{"total":5,"successful":3,"failed":0}} - try (XContentParser parser = xContent.createParser(builder.bytes())) { - assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("_shards", parser.currentName()); - assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("total", parser.currentName()); - assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); - assertEquals(shardInfo.getTotal(), parser.intValue()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("successful", parser.currentName()); - assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); - assertEquals(shardInfo.getSuccessful(), parser.intValue()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("failed", parser.currentName()); - assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); - assertEquals(shardInfo.getFailed(), parser.intValue()); - assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); - assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); - assertNull(parser.nextToken()); - } + // Expected JSON is {"_shards":{"total":5,"successful":3,"failed":0}} + try (XContentParser parser = xContentType.xContent().createParser(shardInfoBytes)) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("_shards", parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("total", parser.currentName()); + assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); + assertEquals(shardInfo.getTotal(), parser.intValue()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("successful", parser.currentName()); + assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); + assertEquals(shardInfo.getSuccessful(), parser.intValue()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("failed", parser.currentName()); + assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); + assertEquals(shardInfo.getFailed(), parser.intValue()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); } } + public void testShardInfoFromXContent() throws IOException { + final XContentType xContentType = randomFrom(XContentType.values()); + + final ReplicationResponse.ShardInfo shardInfo = new ReplicationResponse.ShardInfo(randomIntBetween(1, 5), randomIntBetween(1, 5)); + final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType, true); + + ReplicationResponse.ShardInfo parsedShardInfo; + try (XContentParser parser = xContentType.xContent().createParser(shardInfoBytes)) { + // Move to the start object that was manually added when building the object + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + parsedShardInfo = ReplicationResponse.ShardInfo.fromXContent(parser); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + // We can use assertEquals because the shardInfo doesn't have a failure (and exceptions) + assertEquals(shardInfo, parsedShardInfo); + + BytesReference parsedShardInfoBytes = XContentHelper.toXContent(parsedShardInfo, xContentType, true); + assertEquals(shardInfoBytes, parsedShardInfoBytes); + } + public void testRandomShardInfoToXContent() throws IOException { + final XContentType xContentType = randomFrom(XContentType.values()); + final ReplicationResponse.ShardInfo shardInfo = randomShardInfo(); + final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType, true); - final XContent xContent = randomFrom(XContentType.values()).xContent(); - try (XContentBuilder builder = XContentBuilder.builder(xContent)) { - builder.startObject(); - shardInfo.toXContent(builder, ToXContent.EMPTY_PARAMS); - builder.endObject(); + try (XContentParser parser = xContentType.xContent().createParser(shardInfoBytes)) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("_shards", parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("total", parser.currentName()); + assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); + assertEquals(shardInfo.getTotal(), parser.intValue()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("successful", parser.currentName()); + assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); + assertEquals(shardInfo.getSuccessful(), parser.intValue()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("failed", parser.currentName()); + assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); + assertEquals(shardInfo.getFailed(), parser.intValue()); - try (XContentParser parser = xContent.createParser(builder.bytes())) { - assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("_shards", parser.currentName()); - assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + if (shardInfo.getFailures() != null && shardInfo.getFailures().length > 0) { assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("total", parser.currentName()); - assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); - assertEquals(shardInfo.getTotal(), parser.intValue()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("successful", parser.currentName()); - assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); - assertEquals(shardInfo.getSuccessful(), parser.intValue()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("failed", parser.currentName()); - assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken()); - assertEquals(shardInfo.getFailed(), parser.intValue()); - - if (shardInfo.getFailures() != null && shardInfo.getFailures().length > 0) { - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - assertEquals("failures", parser.currentName()); - assertEquals(XContentParser.Token.START_ARRAY, parser.nextToken()); - - for (int i = 0; i < shardInfo.getFailures().length; i++) { - assertFailure(parser, shardInfo.getFailures()[i]); - } - assertEquals(XContentParser.Token.END_ARRAY, parser.nextToken()); - } + assertEquals("failures", parser.currentName()); + assertEquals(XContentParser.Token.START_ARRAY, parser.nextToken()); - assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); - assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); - assertNull(parser.nextToken()); + for (int i = 0; i < shardInfo.getFailures().length; i++) { + assertFailure(parser, shardInfo.getFailures()[i]); + } + assertEquals(XContentParser.Token.END_ARRAY, parser.nextToken()); } + + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + } + + public void testRandomShardInfoFromXContent() throws IOException { + final XContentType xContentType = randomFrom(XContentType.values()); + + final ReplicationResponse.ShardInfo shardInfo = randomShardInfo(); + final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType, true); + + ReplicationResponse.ShardInfo parsedShardInfo; + try (XContentParser parser = xContentType.xContent().createParser(shardInfoBytes)) { + // Move to the start object that was manually added when building the object + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + parsedShardInfo = ReplicationResponse.ShardInfo.fromXContent(parser); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + + // We can't use assertEquals to compare the original ShardInfo with the parsed ShardInfo + // because it may include random failures with exceptions, and exception types are not + // preserved during ToXContent->FromXContent process. + assertNotNull(parsedShardInfo); + assertEquals(shardInfo.getTotal(), parsedShardInfo.getTotal()); + assertEquals(shardInfo.getSuccessful(), parsedShardInfo.getSuccessful()); + assertEquals(shardInfo.getFailed(), parsedShardInfo.getFailed()); + assertEquals(shardInfo.getFailures().length, parsedShardInfo.getFailures().length); + + for (int i = 0; i < shardInfo.getFailures().length; i++) { + ReplicationResponse.ShardInfo.Failure parsedFailure = parsedShardInfo.getFailures()[i]; + ReplicationResponse.ShardInfo.Failure failure = shardInfo.getFailures()[i]; + + assertEquals(failure.index(), parsedFailure.index()); + assertEquals(failure.shardId(), parsedFailure.shardId()); + assertEquals(failure.nodeId(), parsedFailure.nodeId()); + assertEquals(failure.status(), parsedFailure.status()); + assertEquals(failure.primary(), parsedFailure.primary()); + + Throwable cause = failure.getCause(); + String expectedMessage = "Elasticsearch exception [type=" + ElasticsearchException.getExceptionName(cause) + + ", reason=" + cause.getMessage() + "]"; + assertEquals(expectedMessage, parsedFailure.getCause().getMessage()); } } public void testRandomFailureToXContent() throws IOException { - ReplicationResponse.ShardInfo.Failure shardInfoFailure = randomFailure(); + final XContentType xContentType = randomFrom(XContentType.values()); - final XContent xContent = randomFrom(XContentType.values()).xContent(); - try (XContentBuilder builder = XContentBuilder.builder(xContent)) { - shardInfoFailure.toXContent(builder, ToXContent.EMPTY_PARAMS); + final ReplicationResponse.ShardInfo.Failure shardInfoFailure = randomFailure(); + final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfoFailure, xContentType, false); - try (XContentParser parser = xContent.createParser(builder.bytes())) { - assertFailure(parser, shardInfoFailure); - } + try (XContentParser parser = xContentType.xContent().createParser(shardInfoBytes)) { + assertFailure(parser, shardInfoFailure); + } + } + + public void testRandomFailureFromXContent() throws IOException { + final XContentType xContentType = randomFrom(XContentType.values()); + + final ReplicationResponse.ShardInfo.Failure shardInfoFailure = randomFailure(); + final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfoFailure, xContentType, false); + + ReplicationResponse.ShardInfo.Failure parsedFailure; + try (XContentParser parser = xContentType.xContent().createParser(shardInfoBytes)) { + // Move to the first start object + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + parsedFailure = ReplicationResponse.ShardInfo.Failure.fromXContent(parser); + assertNull(parser.nextToken()); } + + assertEquals(shardInfoFailure.index(), parsedFailure.index()); + assertEquals(shardInfoFailure.shardId(), parsedFailure.shardId()); + assertEquals(shardInfoFailure.nodeId(), parsedFailure.nodeId()); + assertEquals(shardInfoFailure.status(), parsedFailure.status()); + assertEquals(shardInfoFailure.primary(), parsedFailure.primary()); + + Throwable cause = shardInfoFailure.getCause(); + String expectedMessage = "Elasticsearch exception [type=" + ElasticsearchException.getExceptionName(cause) + + ", reason=" + cause.getMessage() + "]"; + assertEquals(expectedMessage, parsedFailure.getCause().getMessage()); } private static void assertFailure(XContentParser parser, ReplicationResponse.ShardInfo.Failure failure) throws IOException { From a24107bc440e5f0bcd5780d4be8c400583cc0a78 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 19 Dec 2016 21:36:12 +0100 Subject: [PATCH 2/2] Rebase and update after Luca comments --- .../support/replication/ReplicationResponseTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java index c19e1de70ff6f..966bee8f85c38 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationResponseTests.java @@ -145,7 +145,7 @@ public void testShardInfoToXContent() throws IOException { } } - public void testShardInfoFromXContent() throws IOException { + public void testShardInfoToAndFromXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final ReplicationResponse.ShardInfo shardInfo = new ReplicationResponse.ShardInfo(randomIntBetween(1, 5), randomIntBetween(1, 5)); @@ -166,7 +166,7 @@ public void testShardInfoFromXContent() throws IOException { assertEquals(shardInfoBytes, parsedShardInfoBytes); } - public void testRandomShardInfoToXContent() throws IOException { + public void testShardInfoWithFailureToXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final ReplicationResponse.ShardInfo shardInfo = randomShardInfo(); @@ -259,7 +259,7 @@ public void testRandomFailureToXContent() throws IOException { } } - public void testRandomFailureFromXContent() throws IOException { + public void testRandomFailureToAndFromXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final ReplicationResponse.ShardInfo.Failure shardInfoFailure = randomFailure();