Skip to content

Commit 6280831

Browse files
imotovaleksmaus
authored andcommitted
EQL: Make EqlSearchResponse immutable (elastic#50810)
Refactors EqlSearchResponse to make it immutable Relates to elastic#49581
1 parent 31d2d01 commit 6280831

File tree

3 files changed

+76
-118
lines changed

3 files changed

+76
-118
lines changed

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/action/EqlSearchResponse.java

Lines changed: 66 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import org.elasticsearch.common.io.stream.StreamOutput;
1515
import org.elasticsearch.common.io.stream.Writeable;
1616
import org.elasticsearch.common.lucene.Lucene;
17-
import org.elasticsearch.common.xcontent.ObjectParser;
17+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
1818
import org.elasticsearch.common.xcontent.ToXContentFragment;
1919
import org.elasticsearch.common.xcontent.ToXContentObject;
2020
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -25,11 +25,10 @@
2525
import java.io.IOException;
2626
import java.util.ArrayList;
2727
import java.util.Arrays;
28+
import java.util.Collections;
2829
import java.util.List;
2930
import java.util.Objects;
30-
import java.util.function.Supplier;
3131

32-
import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
3332
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
3433

3534

@@ -79,9 +78,9 @@
7978
*/
8079
public class EqlSearchResponse extends ActionResponse implements ToXContentObject {
8180

82-
private Hits hits;
83-
private long tookInMillis;
84-
private boolean isTimeout;
81+
private final Hits hits;
82+
private final long tookInMillis;
83+
private final boolean isTimeout;
8584

8685
private static final class Fields {
8786
static final String TOOK = "took";
@@ -93,25 +92,25 @@ private static final class Fields {
9392
private static final ParseField TIMED_OUT = new ParseField(Fields.TIMED_OUT);
9493
private static final ParseField HITS = new ParseField(Fields.HITS);
9594

96-
private static final ObjectParser<EqlSearchResponse, Void> PARSER = objectParser(EqlSearchResponse::new);
97-
98-
private static <R extends EqlSearchResponse> ObjectParser<R, Void> objectParser(Supplier<R> supplier) {
99-
ObjectParser<R, Void> parser = new ObjectParser<>("eql/search_response", false, supplier);
100-
parser.declareLong(EqlSearchResponse::took, TOOK);
101-
parser.declareBoolean(EqlSearchResponse::isTimeout, TIMED_OUT);
102-
parser.declareObject(EqlSearchResponse::hits,
103-
(p, c) -> Hits.fromXContent(p), HITS);
104-
return parser;
105-
}
106-
107-
// Constructor for parser from json
108-
protected EqlSearchResponse() {
109-
super();
95+
private static final ConstructingObjectParser<EqlSearchResponse, Void> PARSER =
96+
new ConstructingObjectParser<>("eql/search_response", true,
97+
args -> {
98+
int i = 0;
99+
Hits hits = (Hits) args[i++];
100+
Long took = (Long) args[i++];
101+
Boolean timeout = (Boolean) args[i];
102+
return new EqlSearchResponse(hits, took, timeout);
103+
});
104+
105+
static {
106+
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> Hits.fromXContent(p), HITS);
107+
PARSER.declareLong(ConstructingObjectParser.constructorArg(), TOOK);
108+
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), TIMED_OUT);
110109
}
111110

112111
public EqlSearchResponse(Hits hits, long tookInMillis, boolean isTimeout) {
113112
super();
114-
this.hits(hits);
113+
this.hits = hits == null ? Hits.EMPTY : hits;
115114
this.tookInMillis = tookInMillis;
116115
this.isTimeout = isTimeout;
117116
}
@@ -152,30 +151,14 @@ public long took() {
152151
return tookInMillis;
153152
}
154153

155-
public void took(long tookInMillis) {
156-
this.tookInMillis = tookInMillis;
157-
}
158-
159154
public boolean isTimeout() {
160155
return isTimeout;
161156
}
162157

163-
public void isTimeout(boolean isTimeout) {
164-
this.isTimeout = isTimeout;
165-
}
166-
167158
public Hits hits() {
168159
return hits;
169160
}
170161

171-
public void hits(Hits hits) {
172-
if (hits == null) {
173-
this.hits = new Hits((Events)null, null);
174-
} else {
175-
this.hits = hits;
176-
}
177-
}
178-
179162
@Override
180163
public boolean equals(Object o) {
181164
if (this == o) {
@@ -210,60 +193,40 @@ private static final class Fields {
210193
private static final ParseField JOIN_KEYS = new ParseField(Fields.JOIN_KEYS);
211194
private static final ParseField EVENTS = new ParseField(Events.NAME);
212195

213-
private static final ObjectParser<EqlSearchResponse.Sequence, Void> PARSER = objectParser(EqlSearchResponse.Sequence::new);
196+
private static final ConstructingObjectParser<EqlSearchResponse.Sequence, Void> PARSER =
197+
new ConstructingObjectParser<>("eql/search_response_sequence", true,
198+
args -> {
199+
int i = 0;
200+
@SuppressWarnings("unchecked") List<String> joinKeys = (List<String>) args[i++];
201+
@SuppressWarnings("unchecked") Events events = new Events(((List<SearchHit>) args[i]).toArray(new SearchHit[0]));
202+
return new EqlSearchResponse.Sequence(joinKeys, events);
203+
});
214204

215-
private static <R extends EqlSearchResponse.Sequence> ObjectParser<R, Void> objectParser(Supplier<R> supplier) {
216-
ObjectParser<R, Void> parser = new ObjectParser<>("eql/search_response_sequence", false, supplier);
217-
parser.declareStringArray(fromList(String.class, EqlSearchResponse.Sequence::joinKeys), JOIN_KEYS);
218-
parser.declareObjectArray(Sequence::setEvents,
219-
(p, c) -> SearchHit.fromXContent(p), EVENTS);
220-
return parser;
205+
static {
206+
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), JOIN_KEYS);
207+
PARSER.declareObjectArray(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> SearchHit.fromXContent(p), EVENTS);
221208
}
222209

223-
private String[] joinKeys = null;
224-
private Events events = null;
210+
private final List<String> joinKeys;
211+
private final Events events;
225212

226-
private Sequence(){
227-
this(null, null);
228-
}
229-
230-
public Sequence(String[] joinKeys, Events events) {
231-
this.joinKeys(joinKeys);
232-
if (events == null) {
233-
this.events = new Events((SearchHit[])(null));
234-
} else {
235-
this.events = events;
236-
}
213+
public Sequence(List<String> joinKeys, Events events) {
214+
this.joinKeys = joinKeys == null ? Collections.emptyList() : joinKeys;
215+
this.events = events == null ? Events.EMPTY : events;
237216
}
238217

239218
public Sequence(StreamInput in) throws IOException {
240-
this.joinKeys = in.readStringArray();
219+
this.joinKeys = in.readStringList();
241220
this.events = new Events(in);
242221
}
243222

244-
public void joinKeys(String[] joinKeys) {
245-
if (joinKeys == null) {
246-
this.joinKeys = new String[0];
247-
} else {
248-
this.joinKeys = joinKeys;
249-
}
250-
}
251-
252-
private void setEvents(List<SearchHit> hits) {
253-
if (hits == null) {
254-
this.events = new Events((SearchHit[])(null));
255-
} else {
256-
this.events = new Events(hits.toArray(new SearchHit[hits.size()]));
257-
}
258-
}
259-
260223
public static Sequence fromXContent(XContentParser parser) {
261224
return PARSER.apply(parser, null);
262225
}
263226

264227
@Override
265228
public void writeTo(StreamOutput out) throws IOException {
266-
out.writeStringArray(joinKeys);
229+
out.writeStringCollection(joinKeys);
267230
out.writeVInt(events.entries().length);
268231
if (events.entries().length > 0) {
269232
for (SearchHit hit : events.entries()) {
@@ -298,13 +261,13 @@ public boolean equals(Object o) {
298261
return false;
299262
}
300263
Sequence that = (Sequence) o;
301-
return Arrays.equals(joinKeys, that.joinKeys)
264+
return Objects.equals(joinKeys, that.joinKeys)
302265
&& Objects.equals(events, that.events);
303266
}
304267

305268
@Override
306269
public int hashCode() {
307-
return Objects.hash(Arrays.hashCode(joinKeys), events);
270+
return Objects.hash(joinKeys, events);
308271
}
309272
}
310273

@@ -316,70 +279,58 @@ private static final class Fields {
316279
static final String PERCENT = "_percent";
317280
}
318281

319-
private int count;
320-
private String[] keys;
321-
private float percent;
282+
private final int count;
283+
private final List<String> keys;
284+
private final float percent;
322285

323286
private static final ParseField COUNT = new ParseField(Fields.COUNT);
324287
private static final ParseField KEYS = new ParseField(Fields.KEYS);
325288
private static final ParseField PERCENT = new ParseField(Fields.PERCENT);
326289

327-
private static final ObjectParser<EqlSearchResponse.Count, Void> PARSER = objectParser(EqlSearchResponse.Count::new);
290+
private static final ConstructingObjectParser<EqlSearchResponse.Count, Void> PARSER =
291+
new ConstructingObjectParser<>("eql/search_response_count", true,
292+
args -> {
293+
int i = 0;
294+
int count = (int) args[i++];
295+
@SuppressWarnings("unchecked") List<String> joinKeys = (List<String>) args[i++];
296+
float percent = (float) args[i];
297+
return new EqlSearchResponse.Count(count, joinKeys, percent);
298+
});
328299

329-
protected static <R extends EqlSearchResponse.Count> ObjectParser<R, Void> objectParser(Supplier<R> supplier) {
330-
ObjectParser<R, Void> parser = new ObjectParser<>("eql/search_response_count", false, supplier);
331-
parser.declareInt(EqlSearchResponse.Count::count, COUNT);
332-
parser.declareStringArray(fromList(String.class, EqlSearchResponse.Count::keys), KEYS);
333-
parser.declareFloat(EqlSearchResponse.Count::percent, PERCENT);
334-
return parser;
300+
static {
301+
PARSER.declareInt(ConstructingObjectParser.constructorArg(), COUNT);
302+
PARSER.declareStringArray(ConstructingObjectParser.constructorArg(), KEYS);
303+
PARSER.declareFloat(ConstructingObjectParser.constructorArg(), PERCENT);
335304
}
336305

337-
private Count() {}
338-
339-
public Count(int count, String[] keys, float percent) {
306+
public Count(int count, List<String> keys, float percent) {
340307
this.count = count;
341-
this.keys(keys);
308+
this.keys = keys == null ? Collections.emptyList() : keys;
342309
this.percent = percent;
343310
}
344311

345312
public Count(StreamInput in) throws IOException {
346313
count = in.readVInt();
347-
keys = in.readStringArray();
314+
keys = in.readStringList();
348315
percent = in.readFloat();
349316
}
350317

351-
public void count(int count) {
352-
this.count = count;
353-
}
354-
355-
public void keys(String[] keys) {
356-
if (keys == null) {
357-
this.keys = new String[0];
358-
} else {
359-
this.keys = keys;
360-
}
361-
}
362-
363-
public void percent(float percent) {
364-
this.percent = percent;
365-
}
366-
367318
public static Count fromXContent(XContentParser parser) {
368319
return PARSER.apply(parser, null);
369320
}
370321

371322
@Override
372323
public void writeTo(StreamOutput out) throws IOException {
373324
out.writeVInt(count);
374-
out.writeStringArray(keys);
325+
out.writeStringCollection(keys);
375326
out.writeFloat(percent);
376327
}
377328

378329
@Override
379330
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
380331
builder.startObject();
381332
builder.field(Fields.COUNT, count);
382-
builder.array(Fields.KEYS, keys);
333+
builder.field(Fields.KEYS, keys);
383334
builder.field(Fields.PERCENT, percent);
384335
builder.endObject();
385336
return builder;
@@ -395,13 +346,13 @@ public boolean equals(Object o) {
395346
}
396347
Count that = (Count) o;
397348
return Objects.equals(count, that.count)
398-
&& Arrays.equals(keys, that.keys)
349+
&& Objects.equals(keys, that.keys)
399350
&& Objects.equals(percent, that.percent);
400351
}
401352

402353
@Override
403354
public int hashCode() {
404-
return Objects.hash(count, Arrays.hashCode(keys), percent);
355+
return Objects.hash(count, keys, percent);
405356
}
406357
}
407358

@@ -477,6 +428,7 @@ public int hashCode() {
477428

478429
// Events
479430
public static class Events extends EqlSearchResponse.Entries<SearchHit> {
431+
private static final Events EMPTY = new Events((SearchHit[]) null);
480432
private static final String NAME = "events";
481433

482434
public Events(SearchHit[] entries) {
@@ -545,6 +497,8 @@ protected final Count createEntry(StreamInput in) throws IOException {
545497

546498
// Hits
547499
public static class Hits implements Writeable, ToXContentFragment {
500+
public static final Hits EMPTY = new Hits((Events)null, null);
501+
548502
private Events events = null;
549503
private Sequences sequences = null;
550504
private Counts counts = null;

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.elasticsearch.xpack.eql.action.EqlSearchRequest;
2323
import org.elasticsearch.xpack.eql.action.EqlSearchResponse;
2424

25+
import java.util.Collections;
26+
2527
public class TransportEqlSearchAction extends HandledTransportAction<EqlSearchRequest, EqlSearchResponse> {
2628
private final SecurityContext securityContext;
2729
private final ClusterService clusterService;
@@ -54,8 +56,8 @@ static EqlSearchResponse createResponse(EqlSearchRequest request) {
5456
new SearchHit(2, "222", null),
5557
});
5658
EqlSearchResponse.Hits hits = new EqlSearchResponse.Hits(new EqlSearchResponse.Sequences(new EqlSearchResponse.Sequence[]{
57-
new EqlSearchResponse.Sequence(new String[]{"4021"}, events),
58-
new EqlSearchResponse.Sequence(new String[]{"2343"}, events)
59+
new EqlSearchResponse.Sequence(Collections.singletonList("4021"), events),
60+
new EqlSearchResponse.Sequence(Collections.singletonList("2343"), events)
5961
}), new TotalHits(0, TotalHits.Relation.EQUAL_TO));
6062
return new EqlSearchResponse(hits, 0, false);
6163
}

x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/action/EqlSearchResponseTests.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import org.elasticsearch.search.SearchHit;
1212
import org.elasticsearch.test.AbstractSerializingTestCase;
1313

14+
import java.util.Arrays;
1415
import java.util.HashMap;
16+
import java.util.List;
1517

1618
public class EqlSearchResponseTests extends AbstractSerializingTestCase<EqlSearchResponse> {
1719

@@ -58,9 +60,9 @@ public static EqlSearchResponse createRandomSequencesResponse(TotalHits totalHit
5860
if (randomBoolean()) {
5961
seq = new EqlSearchResponse.Sequence[size];
6062
for (int i = 0; i < size; i++) {
61-
String[] joins = null;
63+
List<String> joins = null;
6264
if (randomBoolean()) {
63-
joins = generateRandomStringArray(6, 11, false);
65+
joins = Arrays.asList(generateRandomStringArray(6, 11, false));
6466
}
6567
seq[i] = new EqlSearchResponse.Sequence(joins, randomEvents());
6668
}
@@ -82,9 +84,9 @@ public static EqlSearchResponse createRandomCountResponse(TotalHits totalHits) {
8284
if (randomBoolean()) {
8385
cn = new EqlSearchResponse.Count[size];
8486
for (int i = 0; i < size; i++) {
85-
String[] keys = null;
87+
List<String> keys = null;
8688
if (randomBoolean()) {
87-
keys = generateRandomStringArray(6, 11, false);
89+
keys = Arrays.asList(generateRandomStringArray(6, 11, false));
8890
}
8991
cn[i] = new EqlSearchResponse.Count(randomIntBetween(0, 41), keys, randomFloat());
9092
}

0 commit comments

Comments
 (0)