Skip to content

Commit 6eb97a9

Browse files
committed
Prevent cause from being null in ShardOperationFailedException (#32640)
`ShardOperationFailedException` and corresponding implementors seem to suggest that the cause may be null, case that is also handled in a few places. Yet, it does not seem to be possible in practice for the cause to be null, hence we can clean that up and enforce the cause to be a non null value. This is best done by making `ShardOperationFailedException` an abstract class rather than an interface, which holds the basic member instance that all the subclasses have in common and can also enforce that cause, status and reason are non null.
1 parent 71855b3 commit 6eb97a9

11 files changed

+151
-305
lines changed

server/src/main/java/org/elasticsearch/ExceptionsHelper.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,8 @@ private static class GroupBy {
279279
}
280280
}
281281
this.index = indexName;
282-
if (cause == null) {
283-
this.reason = failure.reason();
284-
this.causeType = null;
285-
} else {
286-
this.reason = cause.getMessage();
287-
this.causeType = cause.getClass();
288-
}
282+
this.reason = cause.getMessage();
283+
this.causeType = cause.getClass();
289284
}
290285

291286
@Override

server/src/main/java/org/elasticsearch/action/ShardOperationFailedException.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,70 @@
1919

2020
package org.elasticsearch.action;
2121

22+
import org.elasticsearch.common.Nullable;
2223
import org.elasticsearch.common.io.stream.Streamable;
2324
import org.elasticsearch.common.xcontent.ToXContent;
2425
import org.elasticsearch.rest.RestStatus;
2526

27+
import java.util.Objects;
28+
2629
/**
2730
* An exception indicating that a failure occurred performing an operation on the shard.
2831
*
29-
*
3032
*/
31-
public interface ShardOperationFailedException extends Streamable, ToXContent {
33+
public abstract class ShardOperationFailedException implements Streamable, ToXContent {
34+
35+
protected String index;
36+
protected int shardId;
37+
protected String reason;
38+
protected RestStatus status;
39+
protected Throwable cause;
40+
41+
protected ShardOperationFailedException() {
42+
43+
}
44+
45+
protected ShardOperationFailedException(@Nullable String index, int shardId, String reason, RestStatus status, Throwable cause) {
46+
this.index = index;
47+
this.shardId = shardId;
48+
this.reason = Objects.requireNonNull(reason, "reason cannot be null");
49+
this.status = Objects.requireNonNull(status, "status cannot be null");
50+
this.cause = Objects.requireNonNull(cause, "cause cannot be null");
51+
}
3252

3353
/**
3454
* The index the operation failed on. Might return {@code null} if it can't be derived.
3555
*/
36-
String index();
56+
@Nullable
57+
public final String index() {
58+
return index;
59+
}
3760

3861
/**
3962
* The index the operation failed on. Might return {@code -1} if it can't be derived.
4063
*/
41-
int shardId();
64+
public final int shardId() {
65+
return shardId;
66+
}
4267

4368
/**
4469
* The reason of the failure.
4570
*/
46-
String reason();
71+
public final String reason() {
72+
return reason;
73+
}
4774

4875
/**
4976
* The status of the failure.
5077
*/
51-
RestStatus status();
78+
public final RestStatus status() {
79+
return status;
80+
}
5281

5382
/**
5483
* The cause of this failure
5584
*/
56-
Throwable getCause();
85+
public final Throwable getCause() {
86+
return cause;
87+
}
5788
}

server/src/main/java/org/elasticsearch/action/admin/indices/shards/IndicesShardStoresResponse.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import com.carrotsearch.hppc.cursors.IntObjectCursor;
2323
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
24-
2524
import org.elasticsearch.ElasticsearchException;
2625
import org.elasticsearch.Version;
2726
import org.elasticsearch.action.ActionResponse;
@@ -248,7 +247,7 @@ public String nodeId() {
248247
return nodeId;
249248
}
250249

251-
public static Failure readFailure(StreamInput in) throws IOException {
250+
static Failure readFailure(StreamInput in) throws IOException {
252251
Failure failure = new Failure();
253252
failure.readFrom(in);
254253
return failure;

server/src/main/java/org/elasticsearch/action/search/SearchPhaseExecutionException.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,7 @@ protected void metadataToXContent(XContentBuilder builder, Params params) throws
138138
builder.field("grouped", group); // notify that it's grouped
139139
builder.field("failed_shards");
140140
builder.startArray();
141-
ShardOperationFailedException[] failures = params.paramAsBoolean("group_shard_failures", true) ?
142-
ExceptionsHelper.groupBy(shardFailures) : shardFailures;
141+
ShardOperationFailedException[] failures = group ? ExceptionsHelper.groupBy(shardFailures) : shardFailures;
143142
for (ShardOperationFailedException failure : failures) {
144143
builder.startObject();
145144
failure.toXContent(builder, params);

server/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java

Lines changed: 11 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
/**
4444
* Represents a failure to search on a specific shard.
4545
*/
46-
public class ShardSearchFailure implements ShardOperationFailedException {
46+
public class ShardSearchFailure extends ShardOperationFailedException {
4747

4848
private static final String REASON_FIELD = "reason";
4949
private static final String NODE_FIELD = "node";
@@ -53,9 +53,6 @@ public class ShardSearchFailure implements ShardOperationFailedException {
5353
public static final ShardSearchFailure[] EMPTY_ARRAY = new ShardSearchFailure[0];
5454

5555
private SearchShardTarget shardTarget;
56-
private String reason;
57-
private RestStatus status;
58-
private Throwable cause;
5956

6057
private ShardSearchFailure() {
6158

@@ -66,25 +63,18 @@ public ShardSearchFailure(Exception e) {
6663
}
6764

6865
public ShardSearchFailure(Exception e, @Nullable SearchShardTarget shardTarget) {
66+
super(shardTarget == null ? null : shardTarget.getFullyQualifiedIndexName(),
67+
shardTarget == null ? -1 : shardTarget.getShardId().getId(),
68+
ExceptionsHelper.detailedMessage(e),
69+
ExceptionsHelper.status(ExceptionsHelper.unwrapCause(e)),
70+
ExceptionsHelper.unwrapCause(e));
71+
6972
final Throwable actual = ExceptionsHelper.unwrapCause(e);
7073
if (actual instanceof SearchException) {
7174
this.shardTarget = ((SearchException) actual).shard();
7275
} else if (shardTarget != null) {
7376
this.shardTarget = shardTarget;
7477
}
75-
status = ExceptionsHelper.status(actual);
76-
this.reason = ExceptionsHelper.detailedMessage(e);
77-
this.cause = actual;
78-
}
79-
80-
public ShardSearchFailure(String reason, SearchShardTarget shardTarget) {
81-
this(reason, shardTarget, RestStatus.INTERNAL_SERVER_ERROR);
82-
}
83-
84-
private ShardSearchFailure(String reason, SearchShardTarget shardTarget, RestStatus status) {
85-
this.shardTarget = shardTarget;
86-
this.reason = reason;
87-
this.status = status;
8878
}
8979

9080
/**
@@ -95,41 +85,6 @@ public SearchShardTarget shard() {
9585
return this.shardTarget;
9686
}
9787

98-
@Override
99-
public RestStatus status() {
100-
return this.status;
101-
}
102-
103-
/**
104-
* The index the search failed on.
105-
*/
106-
@Override
107-
public String index() {
108-
if (shardTarget != null) {
109-
return shardTarget.getFullyQualifiedIndexName();
110-
}
111-
return null;
112-
}
113-
114-
/**
115-
* The shard id the search failed on.
116-
*/
117-
@Override
118-
public int shardId() {
119-
if (shardTarget != null) {
120-
return shardTarget.getShardId().id();
121-
}
122-
return -1;
123-
}
124-
125-
/**
126-
* The reason of the failure.
127-
*/
128-
@Override
129-
public String reason() {
130-
return this.reason;
131-
}
132-
13388
@Override
13489
public String toString() {
13590
return "shard [" + (shardTarget == null ? "_na" : shardTarget) + "], reason [" + reason + "], cause [" +
@@ -172,12 +127,10 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
172127
if (shardTarget != null) {
173128
builder.field(NODE_FIELD, shardTarget.getNodeId());
174129
}
175-
if (cause != null) {
176-
builder.field(REASON_FIELD);
177-
builder.startObject();
178-
ElasticsearchException.generateThrowableXContent(builder, params, cause);
179-
builder.endObject();
180-
}
130+
builder.field(REASON_FIELD);
131+
builder.startObject();
132+
ElasticsearchException.generateThrowableXContent(builder, params, cause);
133+
builder.endObject();
181134
return builder;
182135
}
183136

@@ -225,9 +178,4 @@ public static ShardSearchFailure fromXContent(XContentParser parser) throws IOEx
225178
}
226179
return new ShardSearchFailure(exception, searchShardTarget);
227180
}
228-
229-
@Override
230-
public Throwable getCause() {
231-
return cause;
232-
}
233181
}

server/src/main/java/org/elasticsearch/action/support/DefaultShardOperationFailedException.java

Lines changed: 10 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,14 @@
2828
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
2929
import org.elasticsearch.common.xcontent.XContentBuilder;
3030
import org.elasticsearch.common.xcontent.XContentParser;
31-
import org.elasticsearch.index.Index;
32-
import org.elasticsearch.index.shard.ShardId;
3331
import org.elasticsearch.rest.RestStatus;
3432

3533
import java.io.IOException;
3634

3735
import static org.elasticsearch.ExceptionsHelper.detailedMessage;
3836
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
3937

40-
public class DefaultShardOperationFailedException implements ShardOperationFailedException {
38+
public class DefaultShardOperationFailedException extends ShardOperationFailedException {
4139

4240
private static final String INDEX = "index";
4341
private static final String SHARD_ID = "shard";
@@ -52,56 +50,16 @@ public class DefaultShardOperationFailedException implements ShardOperationFaile
5250
PARSER.declareObject(constructorArg(), (p, c) -> ElasticsearchException.fromXContent(p), new ParseField(REASON));
5351
}
5452

55-
private String index;
56-
57-
private int shardId;
58-
59-
private Throwable reason;
60-
61-
private RestStatus status;
62-
6353
protected DefaultShardOperationFailedException() {
6454
}
6555

6656
public DefaultShardOperationFailedException(ElasticsearchException e) {
67-
Index index = e.getIndex();
68-
this.index = index == null ? null : index.getName();
69-
ShardId shardId = e.getShardId();
70-
this.shardId = shardId == null ? -1 : shardId.id();
71-
this.reason = e;
72-
this.status = e.status();
57+
super(e.getIndex() == null ? null : e.getIndex().getName(), e.getShardId() == null ? -1 : e.getShardId().getId(),
58+
detailedMessage(e), e.status(), e);
7359
}
7460

75-
public DefaultShardOperationFailedException(String index, int shardId, Throwable reason) {
76-
this.index = index;
77-
this.shardId = shardId;
78-
this.reason = reason;
79-
this.status = ExceptionsHelper.status(reason);
80-
}
81-
82-
@Override
83-
public String index() {
84-
return this.index;
85-
}
86-
87-
@Override
88-
public int shardId() {
89-
return this.shardId;
90-
}
91-
92-
@Override
93-
public String reason() {
94-
return detailedMessage(reason);
95-
}
96-
97-
@Override
98-
public RestStatus status() {
99-
return status;
100-
}
101-
102-
@Override
103-
public Throwable getCause() {
104-
return reason;
61+
public DefaultShardOperationFailedException(String index, int shardId, Throwable cause) {
62+
super(index, shardId, detailedMessage(cause), ExceptionsHelper.status(cause), cause);
10563
}
10664

10765
public static DefaultShardOperationFailedException readShardOperationFailed(StreamInput in) throws IOException {
@@ -112,24 +70,17 @@ public static DefaultShardOperationFailedException readShardOperationFailed(Stre
11270

11371
@Override
11472
public void readFrom(StreamInput in) throws IOException {
115-
if (in.readBoolean()) {
116-
index = in.readString();
117-
}
73+
index = in.readOptionalString();
11874
shardId = in.readVInt();
119-
reason = in.readException();
75+
cause = in.readException();
12076
status = RestStatus.readFrom(in);
12177
}
12278

12379
@Override
12480
public void writeTo(StreamOutput out) throws IOException {
125-
if (index == null) {
126-
out.writeBoolean(false);
127-
} else {
128-
out.writeBoolean(true);
129-
out.writeString(index);
130-
}
81+
out.writeOptionalString(index);
13182
out.writeVInt(shardId);
132-
out.writeException(reason);
83+
out.writeException(cause);
13384
RestStatus.writeTo(out, status);
13485
}
13586

@@ -145,7 +96,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
14596
builder.field("status", status.name());
14697
if (reason != null) {
14798
builder.startObject("reason");
148-
ElasticsearchException.generateThrowableXContent(builder, params, reason);
99+
ElasticsearchException.generateThrowableXContent(builder, params, cause);
149100
builder.endObject();
150101
}
151102
return builder;

0 commit comments

Comments
 (0)