diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
index 6968839d1ee42..6f67557d5fea7 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
@@ -37,6 +37,8 @@
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 void existsAliasAsync(GetAliasesRequest getAliasesRequest, ActionListener
listener, emptySet(), headers);
}
+ /**
+ * Refresh one or more indices using the Refresh API
+ *
+ * See Refresh API on elastic.co
+ */
+ 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
+ *
+ * See Refresh API on elastic.co
+ */
+ public void refreshAsync(RefreshRequest refreshRequest, ActionListener listener, Header... headers) {
+ restHighLevelClient.performRequestAsyncAndParseEntity(refreshRequest, Request::refresh, RefreshResponse::fromXContent,
+ listener, emptySet(), headers);
+ }
+
/**
* Checks if the index (indices) exists or not.
*
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java
index fd849f5e47883..d05c4e6ffe70b 100755
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java
@@ -38,6 +38,7 @@
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 @@ static Request putMapping(PutMappingRequest putMappingRequest) throws IOExceptio
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);
}
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java
index fbc15b10a7488..7799171b45b56 100755
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java
@@ -39,6 +39,8 @@
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.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 void testCloseNonExistentIndex() throws IOException {
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 void testRollover() throws IOException {
assertEquals("test_new", rolloverResponse.getNewIndex());
}
}
-}
\ No newline at end of file
+}
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java
index 71724cab82ec7..2c46249ea217c 100755
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java
@@ -40,6 +40,7 @@
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 void testIndex() throws IOException {
}
}
+ public void testRefresh() {
+ String[] indices = randomIndicesNames(1, 5);
+ RefreshRequest refreshRequest = new RefreshRequest(indices);
+
+ Map 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 void testExistsAliasNoAliasNoIndex() {
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 @@ private static void resizeTest(ResizeType resizeType, CheckedFunction expectedParams = new HashMap<>();
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java
index 843c5c42fffa1..a16d5e2f5fc74 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java
@@ -38,12 +38,15 @@
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;
@@ -612,6 +615,74 @@ public void onFailure(Exception e) {
}
}
+ 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 listener = new ActionListener() {
+ @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();
diff --git a/docs/java-rest/high-level/indices/refresh.asciidoc b/docs/java-rest/high-level/indices/refresh.asciidoc
new file mode 100644
index 0000000000000..f61c1c37d4ee1
--- /dev/null
+++ b/docs/java-rest/high-level/indices/refresh.asciidoc
@@ -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
\ No newline at end of file
diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc
index 9269da0923a56..634409b235669 100644
--- a/docs/java-rest/high-level/supported-apis.asciidoc
+++ b/docs/java-rest/high-level/supported-apis.asciidoc
@@ -52,6 +52,7 @@ Index Management::
* <>
* <>
* <>
+* <>
* <>
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[]
diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshRequest.java
index b5bce3c85ccd5..20687b8e53418 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshRequest.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshRequest.java
@@ -19,7 +19,6 @@
package org.elasticsearch.action.admin.indices.refresh;
-import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.support.broadcast.BroadcastRequest;
/**
diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponse.java
index b629ac22b89a9..20165d078c5c9 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponse.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponse.java
@@ -21,7 +21,10 @@
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 @@
*/
public class RefreshResponse extends BroadcastResponse {
+ private static final ConstructingObjectParser 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 shardFailures) {
super(totalShards, successfulShards, failedShards, shardFailures);
}
+
+ public static RefreshResponse fromXContent(XContentParser parser) {
+ return PARSER.apply(parser, null);
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/support/DefaultShardOperationFailedException.java b/server/src/main/java/org/elasticsearch/action/support/DefaultShardOperationFailedException.java
index 2ced9145674a2..8a4a787fbe5f2 100644
--- a/server/src/main/java/org/elasticsearch/action/support/DefaultShardOperationFailedException.java
+++ b/server/src/main/java/org/elasticsearch/action/support/DefaultShardOperationFailedException.java
@@ -22,17 +22,36 @@
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 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 @@ protected DefaultShardOperationFailedException() {
}
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 XContentBuilder toXContent(XContentBuilder builder, Params params) throws
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);
}
}
diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastResponse.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastResponse.java
index 2baf5a1d50ec1..ce812644faea6 100644
--- a/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastResponse.java
+++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastResponse.java
@@ -21,25 +21,51 @@
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 void declareBroadcastFields(ConstructingObjectParser PARSER) {
+ ConstructingObjectParser shardsParser = new ConstructingObjectParser<>("_shards", true,
+ arg -> new BroadcastResponse((int) arg[0], (int) arg[1], (int) arg[2], (List) 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 void writeTo(StreamOutput out) throws IOException {
exp.writeTo(out);
}
}
+
+ @Override
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+ RestActions.buildBroadcastShardsHeader(builder, params, this);
+ return builder;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRefreshAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRefreshAction.java
index 46dadf9040cd5..486d8664a49d2 100644
--- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRefreshAction.java
+++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRefreshAction.java
@@ -37,7 +37,6 @@
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 RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
@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);
}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponseTests.java
new file mode 100644
index 0000000000000..dbdb807fb5723
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshResponseTests.java
@@ -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 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 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);
+ }
+}
diff --git a/server/src/test/java/org/elasticsearch/action/support/DefaultShardOperationFailedExceptionTests.java b/server/src/test/java/org/elasticsearch/action/support/DefaultShardOperationFailedExceptionTests.java
new file mode 100644
index 0000000000000..28099506e08e6
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/support/DefaultShardOperationFailedExceptionTests.java
@@ -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]");
+ }
+}