Skip to content

Commit 7bec154

Browse files
Add misc improvements to the framework (#760) (#761)
Adds a number of improvements and bugfixes to the JSON and utility classes: JSON framework: * A new `BufferingJsonpMapper` allows the creation of `JsonGenerator` that store JSON events in a buffer that can be replayed. This is useful to efficiently create synthetic JSON documents. * An additional `JsonpMapper.deserialize` method variant accepts the current JSON event. Fixes #741 * The Jackson implementation of `JsonpMapper` now enables the `ACCEPT_SINGLE_VALUE_AS_ARRAY` deserialization feature. This allows single values in a JSON stream to be considered as a single-value collection. API & transport framework: * `ElasticsearchException` now holds the low level http response, so that the application can inspect the details of the error. * Endpoints now have a `call` methods, to make calling endpoints programmatically easier. This is for advanced use, as an application will normally use the `ElasticsearchClient` that hides endpoint objects. * A `BinaryDataResponse` can now easily be created from a byte array. Test framework: * The `ElasticsearchTestServer` now tries to contact an Elasticsearch server running on `http://elastic:changeme@localhost:9200` before spawning a container. * A `MockHttpClient` has been added that allows writing integration-like tests without requiring a running server. This is an alternative to using `com.sun.net.httpserver.HttpServer` when we want to test http response decoding but don't need to test the network layer. Co-authored-by: Sylvain Wallez <sylvain@elastic.co>
1 parent 985e72a commit 7bec154

File tree

19 files changed

+478
-31
lines changed

19 files changed

+478
-31
lines changed

java-client-serverless/src/main/java/co/elastic/clients/elasticsearch/_types/ElasticsearchException.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919

2020
package co.elastic.clients.elasticsearch._types;
2121

22+
import co.elastic.clients.transport.http.TransportHttpClient;
23+
24+
import javax.annotation.Nullable;
25+
2226
/**
2327
* Exception thrown by API client methods when Elasticsearch could not accept or
2428
* process a request.
@@ -31,11 +35,18 @@ public class ElasticsearchException extends RuntimeException {
3135

3236
private final ErrorResponse response;
3337
private final String endpointId;
38+
@Nullable
39+
private final TransportHttpClient.Response httpResponse;
3440

35-
public ElasticsearchException(String endpointId, ErrorResponse response) {
41+
public ElasticsearchException(String endpointId, ErrorResponse response, @Nullable TransportHttpClient.Response httpResponse) {
3642
super("[" + endpointId + "] failed: [" + response.error().type() + "] " + response.error().reason());
3743
this.response = response;
3844
this.endpointId = endpointId;
45+
this.httpResponse = httpResponse;
46+
}
47+
48+
public ElasticsearchException(String endpointId, ErrorResponse response) {
49+
this(endpointId, response, null);
3950
}
4051

4152
/**
@@ -66,4 +77,12 @@ public ErrorCause error() {
6677
public int status() {
6778
return this.response.status();
6879
}
80+
81+
/**
82+
* The underlying http response, if available.
83+
*/
84+
@Nullable
85+
public TransportHttpClient.Response httpResponse() {
86+
return this.httpResponse;
87+
}
6988
}

java-client/src/main/java/co/elastic/clients/elasticsearch/_types/ElasticsearchException.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919

2020
package co.elastic.clients.elasticsearch._types;
2121

22+
import co.elastic.clients.transport.http.TransportHttpClient;
23+
24+
import javax.annotation.Nullable;
25+
2226
/**
2327
* Exception thrown by API client methods when Elasticsearch could not accept or
2428
* process a request.
@@ -31,11 +35,18 @@ public class ElasticsearchException extends RuntimeException {
3135

3236
private final ErrorResponse response;
3337
private final String endpointId;
38+
@Nullable
39+
private final TransportHttpClient.Response httpResponse;
3440

35-
public ElasticsearchException(String endpointId, ErrorResponse response) {
41+
public ElasticsearchException(String endpointId, ErrorResponse response, @Nullable TransportHttpClient.Response httpResponse) {
3642
super("[" + endpointId + "] failed: [" + response.error().type() + "] " + response.error().reason());
3743
this.response = response;
3844
this.endpointId = endpointId;
45+
this.httpResponse = httpResponse;
46+
}
47+
48+
public ElasticsearchException(String endpointId, ErrorResponse response) {
49+
this(endpointId, response, null);
3950
}
4051

4152
/**
@@ -66,4 +77,12 @@ public ErrorCause error() {
6677
public int status() {
6778
return this.response.status();
6879
}
80+
81+
/**
82+
* The underlying http response, if available.
83+
*/
84+
@Nullable
85+
public TransportHttpClient.Response httpResponse() {
86+
return this.httpResponse;
87+
}
6988
}

java-client/src/main/java/co/elastic/clients/elasticsearch/_types/FieldValue.java

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import co.elastic.clients.util.ObjectBuilderBase;
3434
import co.elastic.clients.util.TaggedUnion;
3535
import co.elastic.clients.util.TaggedUnionUtils;
36+
import jakarta.json.Json;
3637
import jakarta.json.stream.JsonGenerator;
3738
import jakarta.json.stream.JsonParser;
3839

@@ -69,6 +70,10 @@ public static FieldValue of(JsonData value) {
6970
return new FieldValue(Kind.Any, value);
7071
}
7172

73+
public static FieldValue of(Object value) {
74+
return of(JsonData.of(value));
75+
}
76+
7277
public static final FieldValue NULL = new FieldValue(Kind.Null, null);
7378
public static final FieldValue TRUE = new FieldValue(Kind.Boolean, Boolean.TRUE);
7479
public static final FieldValue FALSE = new FieldValue(Kind.Boolean, Boolean.FALSE);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.json;
21+
22+
import jakarta.json.stream.JsonGenerator;
23+
import jakarta.json.stream.JsonParser;
24+
25+
public interface BufferingJsonGenerator extends JsonGenerator {
26+
27+
/**
28+
* Close this generator and return the buffered content.
29+
*/
30+
JsonData getJsonData();
31+
32+
/**
33+
* Close this generator and return the buffered content as a parser.
34+
*/
35+
JsonParser getParser();
36+
37+
void copyValue(JsonParser parser);
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.json;
21+
22+
/**
23+
* A Jsonp mapper that has the additional capability of being able to buffer events.
24+
*/
25+
public interface BufferingJsonpMapper extends JsonpMapper {
26+
27+
BufferingJsonGenerator createBufferingGenerator();
28+
}

java-client/src/main/java/co/elastic/clients/json/DelegatingJsonpMapper.java

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public <T> T deserialize(JsonParser parser, Type type) {
4444
return mapper.deserialize(parser, type);
4545
}
4646

47+
@Override
48+
public <T> T deserialize(JsonParser parser, Type type, JsonParser.Event event) {
49+
return mapper.deserialize(parser, type, event);
50+
}
51+
4752
@Override
4853
public <T> void serialize(T value, JsonGenerator generator) {
4954
mapper.serialize(value, generator);

java-client/src/main/java/co/elastic/clients/json/JsonpDeserializer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public T deserialize(JsonParser parser, JsonpMapper mapper) {
110110

111111
@Override
112112
public T deserialize(JsonParser parser, JsonpMapper mapper, JsonParser.Event event) {
113-
throw new UnsupportedOperationException();
113+
return mapper.deserialize(parser, type, event);
114114
}
115115
};
116116
}

java-client/src/main/java/co/elastic/clients/json/JsonpMapper.java

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ default <T> T deserialize(JsonParser parser, Class<T> clazz) {
5454
*/
5555
<T> T deserialize(JsonParser parser, Type type);
5656

57+
/**
58+
* Deserialize an object, given its class and the current event the parser is at.
59+
*/
60+
default <T> T deserialize(JsonParser parser, Class<T> clazz, JsonParser.Event event) {
61+
return deserialize(parser, (Type)clazz, event);
62+
}
63+
64+
/**
65+
* Deserialize an object, given its type and the current event the parser is at.
66+
*/
67+
<T> T deserialize(JsonParser parser, Type type, JsonParser.Event event);
68+
5769
/**
5870
* Serialize an object.
5971
*/

java-client/src/main/java/co/elastic/clients/json/JsonpMapperBase.java

+25-2
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,44 @@ protected JsonpMapperBase addAttribute(String name, Object value) {
6161
return this;
6262
}
6363

64+
//----- Deserialization
65+
6466
@Override
6567
public <T> T deserialize(JsonParser parser, Type type) {
68+
@SuppressWarnings("unchecked")
69+
T result = (T)getDeserializer(type).deserialize(parser, this);
70+
return result;
71+
}
72+
73+
@Override
74+
public <T> T deserialize(JsonParser parser, Type type, JsonParser.Event event) {
75+
@SuppressWarnings("unchecked")
76+
T result = (T)getDeserializer(type).deserialize(parser, this, event);
77+
return result;
78+
}
79+
80+
private <T> JsonpDeserializer<T> getDeserializer(Type type) {
6681
JsonpDeserializer<T> deserializer = findDeserializer(type);
6782
if (deserializer != null) {
68-
return deserializer.deserialize(parser, this);
83+
return deserializer;
6984
}
7085

7186
@SuppressWarnings("unchecked")
72-
T result = (T)getDefaultDeserializer(type).deserialize(parser, this);
87+
JsonpDeserializer<T> result = getDefaultDeserializer(type);
7388
return result;
7489
}
7590

91+
/**
92+
* Find a built-in deserializer for a given class, if any.
93+
*/
7694
@Nullable
7795
public static <T> JsonpDeserializer<T> findDeserializer(Class<T> clazz) {
7896
return findDeserializer((Type)clazz);
7997
}
8098

99+
/**
100+
* Find a built-in deserializer for a given type, if any.
101+
*/
81102
@Nullable
82103
@SuppressWarnings("unchecked")
83104
public static <T> JsonpDeserializer<T> findDeserializer(Type type) {
@@ -101,6 +122,8 @@ public static <T> JsonpDeserializer<T> findDeserializer(Type type) {
101122
return null;
102123
}
103124

125+
//----- Serialization
126+
104127
@Nullable
105128
@SuppressWarnings("unchecked")
106129
public static <T> JsonpSerializer<T> findSerializer(T value) {

java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -412,10 +412,10 @@ public void close() {
412412
return dest;
413413
}
414414

415-
public static String toJsonString(JsonpSerializable value, JsonpMapper mapper) {
415+
public static String toJsonString(Object value, JsonpMapper mapper) {
416416
StringWriter writer = new StringWriter();
417417
JsonGenerator generator = mapper.jsonProvider().createGenerator(writer);
418-
value.serialize(generator, mapper);
418+
mapper.serialize(value, generator);
419419
generator.close();
420420
return writer.toString();
421421
}

java-client/src/main/java/co/elastic/clients/json/jackson/JacksonJsonpGenerator.java

+41
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919

2020
package co.elastic.clients.json.jackson;
2121

22+
import co.elastic.clients.json.BufferingJsonGenerator;
23+
import co.elastic.clients.json.JsonData;
2224
import com.fasterxml.jackson.core.JsonStreamContext;
25+
import com.fasterxml.jackson.databind.util.TokenBuffer;
2326
import jakarta.json.JsonNumber;
2427
import jakarta.json.JsonString;
2528
import jakarta.json.JsonValue;
2629
import jakarta.json.stream.JsonGenerationException;
2730
import jakarta.json.stream.JsonGenerator;
31+
import jakarta.json.stream.JsonParser;
2832

2933
import java.io.IOException;
3034
import java.math.BigDecimal;
@@ -42,6 +46,43 @@ public JacksonJsonpGenerator(com.fasterxml.jackson.core.JsonGenerator generator)
4246
this.generator = generator;
4347
}
4448

49+
public static class Buffering extends JacksonJsonpGenerator implements BufferingJsonGenerator {
50+
51+
private final JacksonJsonpMapper mapper;
52+
53+
public Buffering(JacksonJsonpMapper mapper) {
54+
super(new TokenBuffer(mapper.objectMapper(), false));
55+
this.mapper = mapper;
56+
}
57+
58+
@Override
59+
public JsonData getJsonData() {
60+
this.close();
61+
return new JacksonJsonBuffer((TokenBuffer)jacksonGenerator(), mapper);
62+
}
63+
64+
@Override
65+
public JsonParser getParser() {
66+
this.close();
67+
TokenBuffer tokenBuffer = (TokenBuffer) jacksonGenerator();
68+
return new JacksonJsonpParser(tokenBuffer.asParser(), mapper);
69+
}
70+
71+
@Override
72+
public void copyValue(JsonParser parser) {
73+
if (!(parser instanceof JacksonJsonpGenerator)) {
74+
throw new IllegalArgumentException("Can only be used with a JacksonJsonpGenerator");
75+
}
76+
77+
com.fasterxml.jackson.core.JsonParser jkParser = ((JacksonJsonpParser) parser).jacksonParser();
78+
try {
79+
jacksonGenerator().copyCurrentStructure(jkParser);
80+
} catch (IOException e) {
81+
throw JacksonUtils.convertException(e);
82+
}
83+
}
84+
}
85+
4586
/**
4687
* Returns the underlying Jackson generator.
4788
*/

0 commit comments

Comments
 (0)