Add Refresh API for RestHighLevelClient (#27799)

Relates to #27205
This commit is contained in:
Yu 2018-02-28 11:49:14 +01:00 committed by Luca Cavanna
parent 303ce30f77
commit 95dea2408d
14 changed files with 600 additions and 12 deletions

View File

@ -37,6 +37,8 @@ import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
@ -215,6 +217,26 @@ public final class IndicesClient {
listener, emptySet(), headers);
}
/**
* Refresh one or more indices using the Refresh API
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html"> Refresh API on elastic.co</a>
*/
public RefreshResponse refresh(RefreshRequest refreshRequest, Header... headers) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(refreshRequest, Request::refresh, RefreshResponse::fromXContent,
emptySet(), headers);
}
/**
* Asynchronously refresh one or more indices using the Refresh API
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html"> Refresh API on elastic.co</a>
*/
public void refreshAsync(RefreshRequest refreshRequest, ActionListener<RefreshResponse> listener, Header... headers) {
restHighLevelClient.performRequestAsyncAndParseEntity(refreshRequest, Request::refresh, RefreshResponse::fromXContent,
listener, emptySet(), headers);
}
/**
* Checks if the index (indices) exists or not.
* <p>

View File

@ -38,6 +38,7 @@ import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
@ -216,6 +217,15 @@ public final class Request {
return new Request(HttpPut.METHOD_NAME, endpoint, parameters.getParams(), entity);
}
static Request refresh(RefreshRequest refreshRequest) {
String endpoint = endpoint(refreshRequest.indices(), "_refresh");
Params parameters = Params.builder();
parameters.withIndicesOptions(refreshRequest.indicesOptions());
return new Request(HttpPost.METHOD_NAME, endpoint, parameters.getParams(), null);
}
static Request info() {
return new Request(HttpGet.METHOD_NAME, "/", Collections.emptyMap(), null);
}

View File

@ -39,6 +39,8 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
@ -47,6 +49,7 @@ import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
@ -381,6 +384,32 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertEquals(RestStatus.NOT_FOUND, exception.status());
}
public void testRefresh() throws IOException {
{
String index = "index";
Settings settings = Settings.builder()
.put("number_of_shards", 1)
.put("number_of_replicas", 0)
.build();
createIndex(index, settings);
RefreshRequest refreshRequest = new RefreshRequest(index);
RefreshResponse refreshResponse =
execute(refreshRequest, highLevelClient().indices()::refresh, highLevelClient().indices()::refreshAsync);
assertThat(refreshResponse.getTotalShards(), equalTo(1));
assertThat(refreshResponse.getSuccessfulShards(), equalTo(1));
assertThat(refreshResponse.getFailedShards(), equalTo(0));
assertThat(refreshResponse.getShardFailures(), equalTo(BroadcastResponse.EMPTY));
}
{
String nonExistentIndex = "non_existent_index";
assertFalse(indexExists(nonExistentIndex));
RefreshRequest refreshRequest = new RefreshRequest(nonExistentIndex);
ElasticsearchException exception = expectThrows(ElasticsearchException.class,
() -> execute(refreshRequest, highLevelClient().indices()::refresh, highLevelClient().indices()::refreshAsync));
assertEquals(RestStatus.NOT_FOUND, exception.status());
}
}
public void testExistsAlias() throws IOException {
GetAliasesRequest getAliasesRequest = new GetAliasesRequest("alias");
assertFalse(execute(getAliasesRequest, highLevelClient().indices()::existsAlias, highLevelClient().indices()::existsAliasAsync));
@ -495,4 +524,4 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertEquals("test_new", rolloverResponse.getNewIndex());
}
}
}
}

View File

@ -40,6 +40,7 @@ import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
@ -535,6 +536,21 @@ public class RequestTests extends ESTestCase {
}
}
public void testRefresh() {
String[] indices = randomIndicesNames(1, 5);
RefreshRequest refreshRequest = new RefreshRequest(indices);
Map<String, String> expectedParams = new HashMap<>();
setRandomIndicesOptions(refreshRequest::indicesOptions, refreshRequest::indicesOptions, expectedParams);
Request request = Request.refresh(refreshRequest);
StringJoiner endpoint = new StringJoiner("/", "/", "").add(String.join(",", indices)).add("_refresh");
assertThat(endpoint.toString(), equalTo(request.getEndpoint()));
assertThat(request.getParameters(), equalTo(expectedParams));
assertThat(request.getEntity(), nullValue());
assertThat(request.getMethod(), equalTo(HttpPost.METHOD_NAME));
}
public void testUpdate() throws IOException {
XContentType xContentType = randomFrom(XContentType.values());
@ -1058,7 +1074,7 @@ public class RequestTests extends ESTestCase {
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> Request.existsAlias(getAliasesRequest));
assertEquals("existsAlias requires at least an alias or an index", iae.getMessage());
}
public void testRankEval() throws Exception {
RankEvalSpec spec = new RankEvalSpec(
Collections.singletonList(new RatedRequest("queryId", Collections.emptyList(), new SearchSourceBuilder())),
@ -1130,7 +1146,7 @@ public class RequestTests extends ESTestCase {
assertEquals(expectedParams, request.getParameters());
assertToXContentBody(resizeRequest, request.getEntity());
}
public void testClusterPutSettings() throws IOException {
ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest();
Map<String, String> expectedParams = new HashMap<>();

View File

@ -38,12 +38,15 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RestHighLevelClient;
@ -620,6 +623,74 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
}
}
public void testRefreshIndex() throws Exception {
RestHighLevelClient client = highLevelClient();
{
createIndex("index1", Settings.EMPTY);
}
{
// tag::refresh-request
RefreshRequest request = new RefreshRequest("index1"); // <1>
RefreshRequest requestMultiple = new RefreshRequest("index1", "index2"); // <2>
RefreshRequest requestAll = new RefreshRequest(); // <3>
// end::refresh-request
// tag::refresh-request-indicesOptions
request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1>
// end::refresh-request-indicesOptions
// tag::refresh-execute
RefreshResponse refreshResponse = client.indices().refresh(request);
// end::refresh-execute
// tag::refresh-response
int totalShards = refreshResponse.getTotalShards(); // <1>
int successfulShards = refreshResponse.getSuccessfulShards(); // <2>
int failedShards = refreshResponse.getFailedShards(); // <3>
DefaultShardOperationFailedException[] failures = refreshResponse.getShardFailures(); // <4>
// end::refresh-response
// tag::refresh-execute-listener
ActionListener<RefreshResponse> listener = new ActionListener<RefreshResponse>() {
@Override
public void onResponse(RefreshResponse refreshResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::refresh-execute-listener
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::refresh-execute-async
client.indices().refreshAsync(request, listener); // <1>
// end::refresh-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
{
// tag::refresh-notfound
try {
RefreshRequest request = new RefreshRequest("does_not_exist");
client.indices().refresh(request);
} catch (ElasticsearchException exception) {
if (exception.status() == RestStatus.NOT_FOUND) {
// <1>
}
}
// end::refresh-notfound
}
}
public void testCloseIndex() throws Exception {
RestHighLevelClient client = highLevelClient();

View File

@ -0,0 +1,84 @@
[[java-rest-high-refresh]]
=== Refresh API
[[java-rest-high-refresh-request]]
==== Refresh Request
A `RefreshRequest` can be applied to one or more indices, or even on `_all` the indices:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-request]
--------------------------------------------------
<1> Refresh one index
<2> Refresh multiple indices
<3> Refresh all the indices
==== Optional arguments
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-request-indicesOptions]
--------------------------------------------------
<1> Setting `IndicesOptions` controls how unavailable indices are resolved and
how wildcard expressions are expanded
[[java-rest-high-refresh-sync]]
==== Synchronous Execution
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-execute]
--------------------------------------------------
[[java-rest-high-refresh-async]]
==== Asynchronous Execution
The asynchronous execution of a refresh request requires both the `RefreshRequest`
instance and an `ActionListener` instance to be passed to the asynchronous
method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-execute-async]
--------------------------------------------------
<1> The `RefreshRequest` to execute and the `ActionListener` to use when
the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `RefreshResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of failure. The raised exception is provided as an argument
[[java-rest-high-refresh-response]]
==== Refresh Response
The returned `RefreshResponse` allows to retrieve information about the
executed operation as follows:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-response]
--------------------------------------------------
<1> Total number of shards hit by the refresh request
<2> Number of shards where the refresh has succeeded
<3> Number of shards where the refresh has failed
<4> A list of failures if the operation failed on one or more shards
By default, if the indices were not found, an `ElasticsearchException` will be thrown:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[refresh-notfound]
--------------------------------------------------
<1> Do something if the indices to be refreshed were not found

View File

@ -52,6 +52,7 @@ Index Management::
* <<java-rest-high-close-index>>
* <<java-rest-high-shrink-index>>
* <<java-rest-high-split-index>>
* <<java-rest-high-refresh>>
* <<java-rest-high-rollover-index>>
Mapping Management::
@ -68,6 +69,7 @@ include::indices/open_index.asciidoc[]
include::indices/close_index.asciidoc[]
include::indices/shrink_index.asciidoc[]
include::indices/split_index.asciidoc[]
include::indices/refresh.asciidoc[]
include::indices/rollover.asciidoc[]
include::indices/put_mapping.asciidoc[]
include::indices/update_aliases.asciidoc[]

View File

@ -19,7 +19,6 @@
package org.elasticsearch.action.admin.indices.refresh;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.support.broadcast.BroadcastRequest;
/**

View File

@ -21,7 +21,10 @@ package org.elasticsearch.action.admin.indices.refresh;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.util.Arrays;
import java.util.List;
/**
@ -29,10 +32,25 @@ import java.util.List;
*/
public class RefreshResponse extends BroadcastResponse {
private static final ConstructingObjectParser<RefreshResponse, Void> PARSER = new ConstructingObjectParser<>("refresh", true,
arg -> {
BroadcastResponse response = (BroadcastResponse) arg[0];
return new RefreshResponse(response.getTotalShards(), response.getSuccessfulShards(), response.getFailedShards(),
Arrays.asList(response.getShardFailures()));
});
static {
declareBroadcastFields(PARSER);
}
RefreshResponse() {
}
RefreshResponse(int totalShards, int successfulShards, int failedShards, List<DefaultShardOperationFailedException> shardFailures) {
super(totalShards, successfulShards, failedShards, shardFailures);
}
public static RefreshResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
}

View File

@ -22,17 +22,36 @@ package org.elasticsearch.action.support;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import static org.elasticsearch.ExceptionsHelper.detailedMessage;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
public class DefaultShardOperationFailedException implements ShardOperationFailedException {
private static final String INDEX = "index";
private static final String SHARD_ID = "shard";
private static final String REASON = "reason";
private static final ConstructingObjectParser<DefaultShardOperationFailedException, Void> PARSER = new ConstructingObjectParser<>(
"failures", true, arg -> new DefaultShardOperationFailedException((String) arg[0], (int) arg[1] ,(Throwable) arg[2]));
static {
PARSER.declareString(constructorArg(), new ParseField(INDEX));
PARSER.declareInt(constructorArg(), new ParseField(SHARD_ID));
PARSER.declareObject(constructorArg(), (p, c) -> ElasticsearchException.fromXContent(p), new ParseField(REASON));
}
private String index;
private int shardId;
@ -45,8 +64,10 @@ public class DefaultShardOperationFailedException implements ShardOperationFaile
}
public DefaultShardOperationFailedException(ElasticsearchException e) {
this.index = e.getIndex() == null ? null : e.getIndex().getName();
this.shardId = e.getShardId().id();
Index index = e.getIndex();
this.index = index == null ? null : index.getName();
ShardId shardId = e.getShardId();
this.shardId = shardId == null ? -1 : shardId.id();
this.reason = e;
this.status = e.status();
}
@ -123,12 +144,14 @@ public class DefaultShardOperationFailedException implements ShardOperationFaile
builder.field("index", index());
builder.field("status", status.name());
if (reason != null) {
builder.field("reason");
builder.startObject();
builder.startObject("reason");
ElasticsearchException.generateThrowableXContent(builder, params, reason);
builder.endObject();
}
return builder;
}
public static DefaultShardOperationFailedException fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
}

View File

@ -21,25 +21,51 @@ package org.elasticsearch.action.support.broadcast;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestActions;
import java.io.IOException;
import java.util.List;
import static org.elasticsearch.action.support.DefaultShardOperationFailedException.readShardOperationFailed;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
/**
* Base class for all broadcast operation based responses.
*/
public class BroadcastResponse extends ActionResponse {
private static final DefaultShardOperationFailedException[] EMPTY = new DefaultShardOperationFailedException[0];
public class BroadcastResponse extends ActionResponse implements ToXContentFragment {
public static final DefaultShardOperationFailedException[] EMPTY = new DefaultShardOperationFailedException[0];
private static final ParseField _SHARDS_FIELD = new ParseField("_shards");
private static final ParseField TOTAL_FIELD = new ParseField("total");
private static final ParseField SUCCESSFUL_FIELD = new ParseField("successful");
private static final ParseField FAILED_FIELD = new ParseField("failed");
private static final ParseField FAILURES_FIELD = new ParseField("failures");
private int totalShards;
private int successfulShards;
private int failedShards;
private DefaultShardOperationFailedException[] shardFailures = EMPTY;
protected static <T extends BroadcastResponse> void declareBroadcastFields(ConstructingObjectParser<T, Void> PARSER) {
ConstructingObjectParser<BroadcastResponse, Void> shardsParser = new ConstructingObjectParser<>("_shards", true,
arg -> new BroadcastResponse((int) arg[0], (int) arg[1], (int) arg[2], (List<DefaultShardOperationFailedException>) arg[3]));
shardsParser.declareInt(constructorArg(), TOTAL_FIELD);
shardsParser.declareInt(constructorArg(), SUCCESSFUL_FIELD);
shardsParser.declareInt(constructorArg(), FAILED_FIELD);
shardsParser.declareObjectArray(optionalConstructorArg(),
(p, c) -> DefaultShardOperationFailedException.fromXContent(p), FAILURES_FIELD);
PARSER.declareObject(constructorArg(), shardsParser, _SHARDS_FIELD);
}
public BroadcastResponse() {
}
@ -120,4 +146,10 @@ public class BroadcastResponse extends ActionResponse {
exp.writeTo(out);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
RestActions.buildBroadcastShardsHeader(builder, params, this);
return builder;
}
}

View File

@ -37,7 +37,6 @@ import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.action.RestActions.buildBroadcastShardsHeader;
public class RestRefreshAction extends BaseRestHandler {
public RestRefreshAction(Settings settings, RestController controller) {
@ -62,7 +61,7 @@ public class RestRefreshAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(RefreshResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, request, response);
response.toXContent(builder, request);
builder.endObject();
return new BytesRestResponse(response.getStatus(), builder);
}

View File

@ -0,0 +1,171 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.refresh;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
public class RefreshResponseTests extends ESTestCase {
public void testToXContent() {
RefreshResponse response = new RefreshResponse(10, 10, 0, null);
String output = Strings.toString(response);
assertEquals("{\"_shards\":{\"total\":10,\"successful\":10,\"failed\":0}}", output);
}
public void testToAndFromXContent() throws IOException {
doFromXContentTestWithRandomFields(false);
}
public void testFromXContentWithRandomFields() throws IOException {
doFromXContentTestWithRandomFields(true);
}
public void testFailuresDeduplication() throws IOException {
List<DefaultShardOperationFailedException> failures = new ArrayList<>();
Index index = new Index("test", "_na_");
ElasticsearchException exception1 = new ElasticsearchException("foo", new IllegalArgumentException("bar"));
exception1.setIndex(index);
exception1.setShard(new ShardId(index, 0));
ElasticsearchException exception2 = new ElasticsearchException("foo", new IllegalArgumentException("bar"));
exception2.setIndex(index);
exception2.setShard(new ShardId(index, 1));
ElasticsearchException exception3 = new ElasticsearchException("fizz", new IllegalStateException("buzz"));
exception3.setIndex(index);
exception3.setShard(new ShardId(index, 2));
failures.add(new DefaultShardOperationFailedException(exception1));
failures.add(new DefaultShardOperationFailedException(exception2));
failures.add(new DefaultShardOperationFailedException(exception3));
RefreshResponse response = new RefreshResponse(10, 7, 3, failures);
boolean humanReadable = randomBoolean();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference bytesReference = toShuffledXContent(response, xContentType, ToXContent.EMPTY_PARAMS, humanReadable);
RefreshResponse parsedResponse;
try(XContentParser parser = createParser(xContentType.xContent(), bytesReference)) {
parsedResponse = RefreshResponse.fromXContent(parser);
assertNull(parser.nextToken());
}
assertThat(parsedResponse.getShardFailures().length, equalTo(2));
DefaultShardOperationFailedException[] parsedFailures = parsedResponse.getShardFailures();
assertThat(parsedFailures[0].index(), equalTo("test"));
assertThat(parsedFailures[0].shardId(), anyOf(equalTo(0), equalTo(1)));
assertThat(parsedFailures[0].status(), equalTo(RestStatus.INTERNAL_SERVER_ERROR));
assertThat(parsedFailures[0].getCause().getMessage(), containsString("foo"));
assertThat(parsedFailures[1].index(), equalTo("test"));
assertThat(parsedFailures[1].shardId(), equalTo(2));
assertThat(parsedFailures[1].status(), equalTo(RestStatus.INTERNAL_SERVER_ERROR));
assertThat(parsedFailures[1].getCause().getMessage(), containsString("fizz"));
ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap("group_shard_failures", "false"));
BytesReference bytesReferenceWithoutDedup = toShuffledXContent(response, xContentType, params, humanReadable);
try(XContentParser parser = createParser(xContentType.xContent(), bytesReferenceWithoutDedup)) {
parsedResponse = RefreshResponse.fromXContent(parser);
assertNull(parser.nextToken());
}
assertThat(parsedResponse.getShardFailures().length, equalTo(3));
parsedFailures = parsedResponse.getShardFailures();
for (int i = 0; i < 3; i++) {
if (i < 2) {
assertThat(parsedFailures[i].index(), equalTo("test"));
assertThat(parsedFailures[i].shardId(), equalTo(i));
assertThat(parsedFailures[i].status(), equalTo(RestStatus.INTERNAL_SERVER_ERROR));
assertThat(parsedFailures[i].getCause().getMessage(), containsString("foo"));
} else {
assertThat(parsedFailures[i].index(), equalTo("test"));
assertThat(parsedFailures[i].shardId(), equalTo(i));
assertThat(parsedFailures[i].status(), equalTo(RestStatus.INTERNAL_SERVER_ERROR));
assertThat(parsedFailures[i].getCause().getMessage(), containsString("fizz"));
}
}
}
private void doFromXContentTestWithRandomFields(boolean addRandomFields) throws IOException {
RefreshResponse response = createTestItem(10);
boolean humanReadable = randomBoolean();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference bytesReference = toShuffledXContent(response, xContentType, ToXContent.EMPTY_PARAMS, humanReadable);
if (addRandomFields) {
bytesReference = insertRandomFields(xContentType, bytesReference, null, random());
}
RefreshResponse parsedResponse;
try(XContentParser parser = createParser(xContentType.xContent(), bytesReference)) {
parsedResponse = RefreshResponse.fromXContent(parser);
assertNull(parser.nextToken());
}
assertThat(response.getTotalShards(), equalTo(parsedResponse.getTotalShards()));
assertThat(response.getSuccessfulShards(), equalTo(parsedResponse.getSuccessfulShards()));
assertThat(response.getFailedShards(), equalTo(parsedResponse.getFailedShards()));
assertFailureEquals(response.getShardFailures(), parsedResponse.getShardFailures());
}
private static void assertFailureEquals(DefaultShardOperationFailedException[] original,
DefaultShardOperationFailedException[] parsedback) {
assertThat(original.length, equalTo(parsedback.length));
for (int i = 0; i < original.length; i++) {
assertThat(original[i].index(), equalTo(parsedback[i].index()));
assertThat(original[i].shardId(), equalTo(parsedback[i].shardId()));
assertThat(original[i].status(), equalTo(parsedback[i].status()));
assertThat(parsedback[i].getCause().getMessage(), containsString(original[i].getCause().getMessage()));
}
}
private static RefreshResponse createTestItem(int totalShards) {
List<DefaultShardOperationFailedException> failures = null;
int successfulShards = randomInt(totalShards);
int failedShards = totalShards - successfulShards;
if (failedShards > 0) {
failures = new ArrayList<>();
for (int i = 0; i < failedShards; i++) {
ElasticsearchException exception = new ElasticsearchException("exception message " + i);
exception.setIndex(new Index("index" + i, "_na_"));
exception.setShard(new ShardId("index" + i, "_na_", i));
if (randomBoolean()) {
failures.add(new DefaultShardOperationFailedException(exception));
} else {
failures.add(new DefaultShardOperationFailedException("index" + i, i, new Exception("exception message " + i)));
}
}
}
return new RefreshResponse(totalShards, successfulShards, failedShards, failures);
}
}

View File

@ -0,0 +1,112 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.support;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationFailedException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
public class DefaultShardOperationFailedExceptionTests extends ESTestCase {
public void testToString() {
{
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException(
new ElasticsearchException("foo", new IllegalArgumentException("bar", new RuntimeException("baz"))));
assertEquals("[null][-1] failed, reason [ElasticsearchException[foo]; nested: " +
"IllegalArgumentException[bar]; nested: RuntimeException[baz]; ]", exception.toString());
}
{
ElasticsearchException elasticsearchException = new ElasticsearchException("foo");
elasticsearchException.setIndex(new Index("index1", "_na_"));
elasticsearchException.setShard(new ShardId("index1", "_na_", 1));
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException(elasticsearchException);
assertEquals("[index1][1] failed, reason [ElasticsearchException[foo]]", exception.toString());
}
{
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException("index2", 2, new Exception("foo"));
assertEquals("[index2][2] failed, reason [Exception[foo]]", exception.toString());
}
}
public void testToXContent() throws IOException {
{
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException(new ElasticsearchException("foo"));
assertEquals("{\"shard\":-1,\"index\":null,\"status\":\"INTERNAL_SERVER_ERROR\"," +
"\"reason\":{\"type\":\"exception\",\"reason\":\"foo\"}}", Strings.toString(exception));
}
{
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException(
new ElasticsearchException("foo", new IllegalArgumentException("bar")));
assertEquals("{\"shard\":-1,\"index\":null,\"status\":\"INTERNAL_SERVER_ERROR\",\"reason\":{\"type\":\"exception\"," +
"\"reason\":\"foo\",\"caused_by\":{\"type\":\"illegal_argument_exception\",\"reason\":\"bar\"}}}",
Strings.toString(exception));
}
{
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException(
new BroadcastShardOperationFailedException(new ShardId("test", "_uuid", 2), "foo", new IllegalStateException("bar")));
assertEquals("{\"shard\":2,\"index\":\"test\",\"status\":\"INTERNAL_SERVER_ERROR\"," +
"\"reason\":{\"type\":\"illegal_state_exception\",\"reason\":\"bar\"}}", Strings.toString(exception));
}
{
DefaultShardOperationFailedException exception = new DefaultShardOperationFailedException("test", 1,
new IllegalArgumentException("foo"));
assertEquals("{\"shard\":1,\"index\":\"test\",\"status\":\"BAD_REQUEST\"," +
"\"reason\":{\"type\":\"illegal_argument_exception\",\"reason\":\"foo\"}}", Strings.toString(exception));
}
}
public void testFromXContent() throws IOException {
XContent xContent = randomFrom(XContentType.values()).xContent();
XContentBuilder builder = XContentBuilder.builder(xContent)
.startObject()
.field("shard", 1)
.field("index", "test")
.field("status", "INTERNAL_SERVER_ERROR")
.startObject("reason")
.field("type", "exception")
.field("reason", "foo")
.endObject()
.endObject();
builder = shuffleXContent(builder);
DefaultShardOperationFailedException parsed;
try(XContentParser parser = createParser(xContent, builder.bytes())) {
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
parsed = DefaultShardOperationFailedException.fromXContent(parser);
assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());
assertNull(parser.nextToken());
}
assertNotNull(parsed);
assertEquals(parsed.shardId(), 1);
assertEquals(parsed.index(), "test");
assertEquals(parsed.status(), RestStatus.INTERNAL_SERVER_ERROR);
assertEquals(parsed.getCause().getMessage(), "Elasticsearch exception [type=exception, reason=foo]");
}
}