diff --git a/.github/workflows/ci-actions.yml b/.github/workflows/ci-actions.yml index 18bcd9b96d7d5..e6e919c85fc88 100644 --- a/.github/workflows/ci-actions.yml +++ b/.github/workflows/ci-actions.yml @@ -451,14 +451,20 @@ jobs: hibernate-orm-rest-data-panache - category: Data5 postgres: "true" - timeout: 60 + timeout: 65 test-modules: > jpa-postgresql - hibernate-search-elasticsearch narayana-stm narayana-jta reactive-pg-client hibernate-reactive-postgresql + - category: Data6 + postgres: "true" + timeout: 40 + test-modules: > + elasticsearch-rest-client + elasticsearch-rest-high-level-client + hibernate-search-elasticsearch - category: Amazon amazonServices: "true" timeout: 45 diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 80b0ff2d47d73..b22defaebe009 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -460,6 +460,16 @@ quarkus-datasource-deployment ${project.version} + + io.quarkus + quarkus-elasticsearch-rest-client-common + ${project.version} + + + io.quarkus + quarkus-elasticsearch-rest-client-common-deployment + ${project.version} + io.quarkus quarkus-elasticsearch-rest-client @@ -470,6 +480,16 @@ quarkus-elasticsearch-rest-client-deployment ${project.version} + + io.quarkus + quarkus-elasticsearch-rest-high-level-client + ${project.version} + + + io.quarkus + quarkus-elasticsearch-rest-high-level-client-deployment + ${project.version} + io.quarkus quarkus-security @@ -3408,6 +3428,18 @@ elasticsearch-rest-client ${elasticsearch-rest-client.version} + + org.elasticsearch.client + elasticsearch-rest-high-level-client + ${elasticsearch-rest-client.version} + + + + org.apache.logging.log4j + log4j-api + + + org.elasticsearch.client elasticsearch-rest-client-sniffer diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java index 8aaf6daf1a6a5..56bab5e6a90d5 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java @@ -4,7 +4,7 @@ /** * Represents a feature provided by a core extension. - * + * * @see FeatureBuildItem */ public enum Feature { @@ -23,7 +23,9 @@ public enum Feature { CDI, CONFIG_YAML, CONSUL_CONFIG, + ELASTICSEARCH_REST_CLIENT_COMMON, ELASTICSEARCH_REST_CLIENT, + ELASTICSEARCH_REST_HIGH_LEVEL_CLIENT, FLYWAY, GRPC_CLIENT, GRPC_SERVER, diff --git a/docs/pom.xml b/docs/pom.xml index 2c3c259013cc1..64c7c91d1e3ce 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -117,6 +117,7 @@ ${protoc.version} ${os-maven-plugin.version} ${protobuf-maven-plugin.version} + ${elasticsearch-server.version} ${quarkus-home-url} diff --git a/docs/src/main/asciidoc/elasticsearch.adoc b/docs/src/main/asciidoc/elasticsearch.adoc new file mode 100644 index 0000000000000..f9a8e2e06ea9b --- /dev/null +++ b/docs/src/main/asciidoc/elasticsearch.adoc @@ -0,0 +1,417 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc +//// += Quarkus - Connecting to an Elasticsearch cluster +include::./attributes.adoc[] +:extension-status: preview + +Elasticsearch is a well known full text search engine and NoSQL datastore. + +In this guide, we will see how you can get your REST services to use an Elasticsearch cluster. + +Quarkus provides two ways of accessing Elasticsearch: via the lower level `RestClient` or via the `RestHighLevelClient` we will call them +the low level and the high level clients. + +include::./status-include.adoc[] + +== Prerequisites + +To complete this guide, you need: + +* less than 15 minutes +* an IDE +* JDK 1.8+ installed with `JAVA_HOME` configured appropriately +* Apache Maven {maven-version} +* Elasticsearch installed or Docker installed + +== Architecture + +The application built in this guide is quite simple: the user can add elements in a list using a form and the list is updated. + +All the information between the browser and the server is formatted as JSON. + +The elements are stored in Elasticsearch. + +== Solution + +We recommend that you follow the instructions in the next sections and create the application step by step. +However, you can go right to the completed example. + +Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. + +The solution for the low level client is located in the `elasticsearch-rest-client-quickstart` {quickstarts-tree-url}/elasticsearch-rest-client-quickstart[directory]. + +The solution for the high level client is located in the `elasticsearch-rest-high-level-client-quickstart` {quickstarts-tree-url}/elasticsearch-rest-high-level-client-quickstart[directory]. + +== Creating the Maven project + +First, we need a new project. Create a new project with the following command: + +[source,shell,subs=attributes+] +---- +mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ + -DprojectGroupId=org.acme \ + -DprojectArtifactId=elasticsearch-quickstart \ + -DclassName="org.acme.elasticsearch.FruitResource" \ + -Dpath="/fruits" \ + -Dextensions="resteasy-jackson,elasticsearch-rest-client" +cd elasticsearch-quickstart +---- + +This command generates a Maven structure importing the RESTEasy/JAX-RS, Jackson, and the Elasticsearch low level client extensions. +After this, the `quarkus-elasticsearch-rest-client` extension has been added to your `pom.xml`. + +If you want to use the high level client instead, replace the `elasticsearch-rest-client` extension by the `elasticsearch-rest-high-level-client` extension. + +[NOTE] +==== +We use the `resteasy-jackson` extension here and not the JSON-B variant because we will use the Vert.x `JsonObject` helper +to serialize/deserialize our objects to/from Elasticsearch and it uses Jackson under the hood. +==== + +If you don’t want to generate a new project, add the following dependencies to your pom.xml. + +For the Elasticsearch low level client, add: + +[source,xml] +---- + + io.quarkus + quarkus-elasticsearch-rest-client + +---- + +For the Elasticsearch high level client, add: + +[source,xml] +---- + + io.quarkus + quarkus-elasticsearch-rest-high-level-client + +---- + +== Creating your first JSON REST service + +In this example, we will create an application to manage a list of fruits. + +First, let's create the `Fruit` bean as follows: + +[source,java] +---- +package org.acme.elasticsearch; + +public class Fruit { + public String id; + public String name; + public String color; +} +---- + +Nothing fancy. One important thing to note is that having a default constructor is required by the JSON serialization layer. + +Now create a `org.acme.elasticsearch.FruitService` that will be the business layer of our application and store/load the fruits from the Elasticsearch instance. +Here we use the low level client, if you want to use the high level client instead follow the instructions in the link:#using-the-high-level-rest-client[Using the High Level REST Client] paragraph instead. + +[source,java] +---- +package org.acme.elasticsearch; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +@ApplicationScoped +public class FruitService { + @Inject + RestClient restClient; //<1> + + public void index(Fruit fruit) throws IOException { + Request request = new Request( + "PUT", + "/fruits/_doc/" + fruit.id); //<2> + request.setJsonEntity(JsonObject.mapFrom(fruit).toString()); //<3> + restClient.performRequest(request); //<4> + } + + public Fruit get(String id) throws IOException { + Request request = new Request( + "GET", + "/fruits/_doc/" + id); + Response response = restClient.performRequest(request); + String responseBody = EntityUtils.toString(response.getEntity()); + JsonObject json = new JsonObject(responseBody); //<5> + return json.getJsonObject("_source").mapTo(Fruit.class); + } + + public List searchByColor(String color) throws IOException { + return search("color", color); + } + + public List searchByName(String name) throws IOException { + return search("name", name); + } + + private List search(String term, String match) throws IOException { + Request request = new Request( + "GET", + "/fruits/_search"); + //construct a JSON query like {"query": {"match": {"": " results = new ArrayList<>(hits.size()); + for (int i = 0; i < hits.size(); i++) { + JsonObject hit = hits.getJsonObject(i); + Fruit fruit = hit.getJsonObject("_source").mapTo(Fruit.class); + results.add(fruit); + } + return results; + } +} +---- + +In this example you can note the following: + +1. We inject an Elasticsearch low level `RestClient` into our service. +2. We create an Elasticsearch request. +3. We use Vert.x `JsonObject` to serialize the object before sending it to Elasticsearch, you can use whatever you want to serialize to JSON. +4. We send the request (indexing request here) to Elasticsearch. +5. In order to deserialize the object from Elasticsearch, we again use Vert.x `JsonObject`. + +Now, edit the `org.acme.elasticsearch.FruitResource` class as follows: + +[source,java] +---- +@Path("/fruits") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class FruitResource { + @Inject + FruitService fruitService; + + @POST + public Response index(Fruit fruit) throws IOException { + if (fruit.id == null) { + fruit.id = UUID.randomUUID().toString(); + } + fruitService.index(fruit); + return Response.created(URI.create("/fruits/" + fruit.id)).build(); + } + + @GET + @Path("/{id}") + public Fruit get(@PathParam("id") String id) throws IOException { + return fruitService.get(id); + } + + @GET + @Path("/search") + public List search(@QueryParam("name") String name, @QueryParam("color") String color) throws IOException { + if (name != null) { + return fruitService.searchByName(name); + } else if (color != null) { + return fruitService.searchByColor(color); + } else { + throw new BadRequestException("Should provide name or color query parameter"); + } + } + +} +---- + +The implementation is pretty straightforward and you just need to define your endpoints using the JAX-RS annotations and use the `FruitService` to list/add new fruits. + +== Configuring Elasticsearch +The main property to configure is the URL to connect to the Elasticsearch cluster. + +A sample configuration should look like this: + +[source,properties] +---- +# configure the Elasticsearch client for a cluster of two nodes +quarkus.elasticsearch.hosts = elasticsearch1:9200,elasticsearch2:9200 +---- + +In this example, we are using a single instance running on localhost: + +[source,properties] +---- +# configure the Elasticsearch client for a single instance on localhost +quarkus.elasticsearch.hosts = localhost:9200 +---- + +If you need a more advanced configuration, you can find the comprehensive list of supported configuration properties at the end of this guide. + + +== Running an Elasticsearch cluster + +As by default, the Elasticsearch client is configured to access a local Elasticsearch cluster on port 9200 (the default Elasticsearch port), +if you have a local running instance on this port, there is nothing more to do before being able to test it! + +If you want to use Docker to run an Elasticsearch instance, you can use the following command to launch one: + +[source,shell,subs=attributes+] +---- +docker run --name elasticsearch -e "discovery.type=single-node" -e "ES_JAVA_OPTS=-Xms512m -Xmx512m"\ + --rm -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch-oss:{elasticsearch-version} +---- + +== Creating a frontend + +Now let's add a simple web page to interact with our `FruitResource`. +Quarkus automatically serves static resources located under the `META-INF/resources` directory. +In the `src/main/resources/META-INF/resources` directory, add a `fruits.html` file with the content from this {quickstarts-blob-url}/elasticsearch-low-level-client-quickstart/src/main/resources/META-INF/resources/fruits.html[fruits.html] file in it. + +You can now interact with your REST service: + +* start Quarkus with `./mvnw quarkus:dev` +* open a browser to `http://localhost:8080/fruits.html` +* add new fruits to the list via the 'Add fruit' form +* search for fruits by name or color via the 'Search Fruit' form + +== Using the High Level REST Client + +Quarkus provides support for the Elasticsearch High Level REST Client but keep in mind that it comes with some caveats: + +- It drags a lot of dependencies - especially Lucene -, which doesn't fit well with Quarkus philosophy. The Elasticsearch team is aware of this issue and it might improve sometime in the future. +- It is tied to a certain version of the Elasticsearch server: you cannot use a High Level REST Client version 7 to access a server version 6. + +Here is a version of the `FruitService` using the high level client instead of the low level one: + +[source,java] +---- +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.elasticsearch.action.get.GetRequest; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.builder.SearchSourceBuilder; + +import io.vertx.core.json.JsonObject; + +@ApplicationScoped +public class FruitService { + @Inject + RestHighLevelClient restHighLevelClient; // <1> + + public void index(Fruit fruit) throws IOException { + IndexRequest request = new IndexRequest("fruits"); // <2> + request.id(fruit.id); + request.source(JsonObject.mapFrom(fruit).toString(), XContentType.JSON); // <3> + restHighLevelClient.index(request, RequestOptions.DEFAULT); // <4> + } + + public Fruit get(String id) throws IOException { + GetRequest getRequest = new GetRequest("fruits", id); + GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT); + if (getResponse.isExists()) { + String sourceAsString = getResponse.getSourceAsString(); + JsonObject json = new JsonObject(sourceAsString); // <5> + return json.mapTo(Fruit.class); + } + return null; + } + + public List searchByColor(String color) throws IOException { + return search("color", color); + } + + public List searchByName(String name) throws IOException { + return search("name", name); + } + + private List search(String term, String match) throws IOException { + SearchRequest searchRequest = new SearchRequest("fruits"); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(QueryBuilders.matchQuery(term, match)); + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); + SearchHits hits = searchResponse.getHits(); + List results = new ArrayList<>(hits.getHits().length); + for (SearchHit hit : hits.getHits()) { + String sourceAsString = hit.getSourceAsString(); + JsonObject json = new JsonObject(sourceAsString); + results.add(json.mapTo(Fruit.class)); + } + return results; + } +} +---- + +In this example you can note the following: + +1. We inject an Elasticsearch `RestHighLevelClient` inside the service. +2. We create an Elasticsearch index request. +3. We use Vert.x `JsonObject` to serialize the object before sending it to Elasticsearch, you can use whatever you want to serialize to JSON. +4. We send the request to Elasticsearch. +5. In order to deserialize the object from Elasticsearch, we again use Vert.x `JsonObject`. + +== Hibernate Search Elasticsearch + +Quarkus supports Hibernate Search with Elasticsearch via the `hibernate-search-elasticsearch` extension. + +Hibernate Search Elasticsearch allows to synchronize your JPA entities to an Elasticsearch cluster and offers a way to query your Elasticsearch cluster using the Hibernate Search API. + +If you're interested in it, you can read the link:hibernate-search-elasticsearch[Hibernate Search with Elasticsearch guide]. + +== Cluster Health Check + +If you are using the `quarkus-smallrye-health` extension, both the extension will automatically add a readiness health check +to validate the health of the cluster. + +So when you access the `/health/ready` endpoint of your application you will have information about the cluster status. +It uses the cluster health endpoint, the check will be down if the status of the cluster is **red**, or the cluster is not available. + +This behavior can be disabled by setting the `quarkus.elasticsearch.health.enabled` property to `false` in your `application.properties`. + +== Building a native executable + +You can use both clients in a native executable. + +You can build a native executable with the usual command `./mvnw package -Pnative`. + +Running it is as simple as executing `./target/elasticsearch-low-level-client-quickstart-1.0-SNAPSHOT-runner`. + +You can then point your browser to `http://localhost:8080/fruits.html` and use your application. + +== Conclusion + +Accessing an Elasticsearch cluster from a low level or a high level client is easy with Quarkus as it provides easy configuration, CDI integration and native support for it. + +== Configuration Reference + +include::{generated-dir}/config/quarkus-elasticsearch-low-level-client.adoc[opts=optional, leveloffset=+1] diff --git a/docs/src/main/asciidoc/hibernate-search-elasticsearch.adoc b/docs/src/main/asciidoc/hibernate-search-elasticsearch.adoc index 970263147de92..117b4854b954f 100644 --- a/docs/src/main/asciidoc/hibernate-search-elasticsearch.adoc +++ b/docs/src/main/asciidoc/hibernate-search-elasticsearch.adoc @@ -618,9 +618,9 @@ We need a PostgreSQL instance and an Elasticsearch cluster. Let's use Docker to start one of each: -[source, shell] +[source, shell,subs=attributes+] ---- -docker run -it --rm=true --name elasticsearch_quarkus_test -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.7.0 +docker run -it --rm=true --name elasticsearch_quarkus_test -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch-oss:{elasticsearch-version} ---- [source, shell] diff --git a/extensions/elasticsearch-rest-client-common/deployment/pom.xml b/extensions/elasticsearch-rest-client-common/deployment/pom.xml new file mode 100644 index 0000000000000..3a4a0a046bbcd --- /dev/null +++ b/extensions/elasticsearch-rest-client-common/deployment/pom.xml @@ -0,0 +1,43 @@ + + + + quarkus-elasticsearch-rest-client-common-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-elasticsearch-rest-client-common-deployment + Quarkus - Elasticsearch REST client - Deployment + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-elasticsearch-rest-client-common + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/deployment/ElasticsearchRestClientProcessor.java b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchRestClientProcessor.java similarity index 81% rename from extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/deployment/ElasticsearchRestClientProcessor.java rename to extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchRestClientProcessor.java index a867768b6b965..e59d5977a97cb 100644 --- a/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/deployment/ElasticsearchRestClientProcessor.java +++ b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchRestClientProcessor.java @@ -1,4 +1,4 @@ -package io.quarkus.elasticsearch.restclient.deployment; +package io.quarkus.elasticsearch.restclient.common.deployment; import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; @@ -10,6 +10,6 @@ class ElasticsearchRestClientProcessor { @BuildStep public void build(BuildProducer extensionSslNativeSupport) { // Indicates that this extension would like the SSL support to be enabled - extensionSslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.ELASTICSEARCH_REST_CLIENT.getName())); + extensionSslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.ELASTICSEARCH_REST_CLIENT_COMMON)); } } diff --git a/extensions/elasticsearch-rest-client-common/pom.xml b/extensions/elasticsearch-rest-client-common/pom.xml new file mode 100644 index 0000000000000..0f20c640641ab --- /dev/null +++ b/extensions/elasticsearch-rest-client-common/pom.xml @@ -0,0 +1,20 @@ + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + 4.0.0 + + quarkus-elasticsearch-rest-client-common-parent + Quarkus - Elasticsearch client common + pom + + deployment + runtime + + diff --git a/extensions/elasticsearch-rest-client-common/runtime/pom.xml b/extensions/elasticsearch-rest-client-common/runtime/pom.xml new file mode 100644 index 0000000000000..49bee4f77e45f --- /dev/null +++ b/extensions/elasticsearch-rest-client-common/runtime/pom.xml @@ -0,0 +1,71 @@ + + + + quarkus-elasticsearch-rest-client-common-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-elasticsearch-rest-client-common + Quarkus - Elasticsearch REST client common - Runtime + Elasticsearch REST client common + + + io.quarkus + quarkus-core + + + org.elasticsearch.client + elasticsearch-rest-client + + + commons-logging + commons-logging + + + + + org.jboss.logging + commons-logging-jboss-logging + + + org.elasticsearch.client + elasticsearch-rest-client-sniffer + + + commons-logging + commons-logging + + + + + org.graalvm.nativeimage + svm + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/runtime/graal/Substitute_RestClient.java b/extensions/elasticsearch-rest-client-common/runtime/src/main/java/io/quarkus/elasticsearch/restclient/common/runtime/graal/Substitute_RestClient.java similarity index 98% rename from extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/runtime/graal/Substitute_RestClient.java rename to extensions/elasticsearch-rest-client-common/runtime/src/main/java/io/quarkus/elasticsearch/restclient/common/runtime/graal/Substitute_RestClient.java index 1d730e80f53e6..8807153ae936e 100644 --- a/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/runtime/graal/Substitute_RestClient.java +++ b/extensions/elasticsearch-rest-client-common/runtime/src/main/java/io/quarkus/elasticsearch/restclient/common/runtime/graal/Substitute_RestClient.java @@ -1,4 +1,4 @@ -package io.quarkus.elasticsearch.restclient.runtime.graal; +package io.quarkus.elasticsearch.restclient.common.runtime.graal; import java.util.ArrayList; import java.util.Collection; diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/elasticsearch-rest-client-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml similarity index 71% rename from extensions/elasticsearch-rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml rename to extensions/elasticsearch-rest-client-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 3a584f07e4f60..aab0fe0dbc7ce 100644 --- a/extensions/elasticsearch-rest-client/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/elasticsearch-rest-client-common/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -1,5 +1,5 @@ --- -name: "Elasticsearch REST Client" +name: "Elasticsearch REST Client Common" metadata: keywords: - "search" diff --git a/extensions/elasticsearch-rest-client/deployment/pom.xml b/extensions/elasticsearch-rest-client/deployment/pom.xml index 08a15f8b5f088..379ab306974f6 100644 --- a/extensions/elasticsearch-rest-client/deployment/pom.xml +++ b/extensions/elasticsearch-rest-client/deployment/pom.xml @@ -2,13 +2,13 @@ + 4.0.0 - quarkus-elasticsearch-rest-client-parent io.quarkus + quarkus-elasticsearch-rest-client-parent 999-SNAPSHOT - ../ + ../pom.xml - 4.0.0 quarkus-elasticsearch-rest-client-deployment Quarkus - Elasticsearch REST client - Deployment @@ -18,15 +18,28 @@ io.quarkus quarkus-core-deployment + + io.quarkus + quarkus-elasticsearch-rest-client-common-deployment + io.quarkus quarkus-elasticsearch-rest-client + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-smallrye-health-spi + + org.apache.maven.plugins maven-compiler-plugin @@ -40,4 +53,5 @@ + diff --git a/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchBuildTimeConfig.java b/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchBuildTimeConfig.java new file mode 100644 index 0000000000000..d12b79edc995f --- /dev/null +++ b/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchBuildTimeConfig.java @@ -0,0 +1,14 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.deployment; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(name = "elasticsearch", phase = ConfigPhase.BUILD_TIME) +public class ElasticsearchBuildTimeConfig { + /** + * Whether or not an health check is published in case the smallrye-health extension is present. + */ + @ConfigItem(name = "health.enabled", defaultValue = "true") + public boolean healthEnabled; +} diff --git a/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java b/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java new file mode 100644 index 0000000000000..5aa3aafbf3e38 --- /dev/null +++ b/extensions/elasticsearch-rest-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/deployment/ElasticsearchLowLevelClientProcessor.java @@ -0,0 +1,28 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.deployment; + +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.elasticsearch.restclient.lowlevel.runtime.ElasticsearchRestClientProducer; +import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; + +class ElasticsearchLowLevelClientProcessor { + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.ELASTICSEARCH_REST_CLIENT); + } + + @BuildStep() + AdditionalBeanBuildItem build() { + return AdditionalBeanBuildItem.unremovableOf(ElasticsearchRestClientProducer.class); + } + + @BuildStep + HealthBuildItem addHealthCheck(ElasticsearchBuildTimeConfig buildTimeConfig) { + return new HealthBuildItem("io.quarkus.elasticsearch.restclient.lowlevel.runtime.health.ElasticsearchHealthCheck", + buildTimeConfig.healthEnabled, "elasticsearch"); + } + +} diff --git a/extensions/elasticsearch-rest-client/pom.xml b/extensions/elasticsearch-rest-client/pom.xml index cd0ea6ac1dbc4..41ed2e11676c0 100644 --- a/extensions/elasticsearch-rest-client/pom.xml +++ b/extensions/elasticsearch-rest-client/pom.xml @@ -8,11 +8,13 @@ 999-SNAPSHOT ../../build-parent/pom.xml + 4.0.0 quarkus-elasticsearch-rest-client-parent - Quarkus - Elasticsearch client + Quarkus - Elasticsearch REST client - Parent pom + deployment runtime diff --git a/extensions/elasticsearch-rest-client/runtime/pom.xml b/extensions/elasticsearch-rest-client/runtime/pom.xml index a3e347961dc51..e998f4a228d69 100644 --- a/extensions/elasticsearch-rest-client/runtime/pom.xml +++ b/extensions/elasticsearch-rest-client/runtime/pom.xml @@ -2,49 +2,35 @@ + 4.0.0 - quarkus-elasticsearch-rest-client-parent io.quarkus + quarkus-elasticsearch-rest-client-parent 999-SNAPSHOT - ../ + ../pom.xml - 4.0.0 quarkus-elasticsearch-rest-client Quarkus - Elasticsearch REST client - Runtime - Elasticsearch REST client + Connect to an Elasticsearch cluster using the REST low level client + io.quarkus quarkus-core - org.elasticsearch.client - elasticsearch-rest-client - - - commons-logging - commons-logging - - - - - org.jboss.logging - commons-logging-jboss-logging + io.quarkus + quarkus-elasticsearch-rest-client-common - org.elasticsearch.client - elasticsearch-rest-client-sniffer - - - commons-logging - commons-logging - - + io.quarkus + quarkus-arc - org.graalvm.nativeimage - svm + io.quarkus + quarkus-smallrye-health + true @@ -55,6 +41,7 @@ quarkus-bootstrap-maven-plugin + org.apache.maven.plugins maven-compiler-plugin diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchConfig.java b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchConfig.java new file mode 100644 index 0000000000000..3edd508c89b65 --- /dev/null +++ b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchConfig.java @@ -0,0 +1,99 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.runtime; + +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.List; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public class ElasticsearchConfig { + + /** + * The list of hosts of the Elasticsearch servers. + */ + @ConfigItem(defaultValue = "localhost:9200") + public List hosts; + + /** + * The protocol to use when contacting Elasticsearch servers. + * Set to "https" to enable SSL/TLS. + */ + @ConfigItem(defaultValue = "http") + public String protocol; + + /** + * The username for basic HTTP authentication. + */ + @ConfigItem + public Optional username; + + /** + * The password for basic HTTP authentication. + */ + @ConfigItem + public Optional password; + + /** + * The connection timeout. + */ + @ConfigItem(defaultValue = "1S") + public Duration connectionTimeout; + + /** + * The socket timeout. + */ + @ConfigItem(defaultValue = "30S") + public Duration socketTimeout; + + /** + * The maximum number of connections to all the Elasticsearch servers. + */ + @ConfigItem(defaultValue = "20") + public int maxConnections; + + /** + * The maximum number of connections per Elasticsearch server. + */ + @ConfigItem(defaultValue = "10") + public int maxConnectionsPerRoute; + + /** + * The number of IO thread. + * By default, this is the number of locally detected processors. + *

+ * Thread counts higher than the number of processors should not be necessary because the I/O threads rely on non-blocking + * operations, but you may want to use a thread count lower than the number of processors. + * + * @see number of + * threads + */ + @ConfigItem + public Optional ioThreadCounts; + + /** + * Configuration for the automatic discovery of new Elasticsearch nodes. + */ + @ConfigItem + public DiscoveryConfig discovery; + + @ConfigGroup + public static class DiscoveryConfig { + + /** + * Defines if automatic discovery is enabled. + */ + @ConfigItem(defaultValue = "false") + public boolean enabled; + + /** + * Refresh interval of the node list. + */ + @ConfigItem(defaultValue = "5M") + public Duration refreshInterval; + } +} diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchRestClientProducer.java b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchRestClientProducer.java new file mode 100644 index 0000000000000..2a3260fcbd5d3 --- /dev/null +++ b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchRestClientProducer.java @@ -0,0 +1,52 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.runtime; + +import java.io.IOException; +import java.io.UncheckedIOException; + +import javax.annotation.PreDestroy; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.sniff.Sniffer; + +@ApplicationScoped +public class ElasticsearchRestClientProducer { + + @Inject + ElasticsearchConfig config; + + private RestClient client; + + private Sniffer sniffer; + + @Produces + @Singleton + public RestClient restClient() { + RestClientBuilder builder = RestClientBuilderHelper.createRestClientBuilder(config); + + this.client = builder.build(); + if (config.discovery.enabled) { + this.sniffer = RestClientBuilderHelper.createSniffer(client, config); + } + + return this.client; + } + + @PreDestroy + void destroy() { + try { + if (this.sniffer != null) { + this.sniffer.close(); + } + if (this.client != null) { + this.client.close(); + } + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } +} diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/RestClientBuilderHelper.java b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/RestClientBuilderHelper.java new file mode 100644 index 0000000000000..1262b78f75380 --- /dev/null +++ b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/RestClientBuilderHelper.java @@ -0,0 +1,102 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.runtime; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.reactor.IOReactorConfig; +import org.apache.http.nio.conn.NoopIOSessionStrategy; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.sniff.ElasticsearchNodesSniffer; +import org.elasticsearch.client.sniff.NodesSniffer; +import org.elasticsearch.client.sniff.Sniffer; +import org.elasticsearch.client.sniff.SnifferBuilder; +import org.jboss.logging.Logger; + +public final class RestClientBuilderHelper { + + private static final Logger LOG = Logger.getLogger(RestClientBuilderHelper.class); + + private RestClientBuilderHelper() { + // avoid instantiation + } + + static RestClientBuilder createRestClientBuilder(ElasticsearchConfig config) { + List hosts = new ArrayList<>(config.hosts.size()); + for (InetSocketAddress host : config.hosts) { + hosts.add(new HttpHost(host.getHostString(), host.getPort(), config.protocol)); + } + + RestClientBuilder builder = RestClient.builder(hosts.toArray(new HttpHost[0])); + + builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { + @Override + public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { + return requestConfigBuilder + .setConnectTimeout((int) config.connectionTimeout.toMillis()) + .setSocketTimeout((int) config.socketTimeout.toMillis()) + .setConnectionRequestTimeout(0); // Avoid requests being flagged as timed out even when they didn't time out. + } + }); + + builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { + if (config.username.isPresent()) { + if (!"https".equalsIgnoreCase(config.protocol)) { + LOG.warn("Using Basic authentication in HTTP implies sending plain text passwords over the wire, " + + "use the HTTPS protocol instead."); + } + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(config.username.get(), config.password.orElse(null))); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + + if (config.ioThreadCounts.isPresent()) { + IOReactorConfig ioReactorConfig = IOReactorConfig.custom() + .setIoThreadCount(config.ioThreadCounts.get()) + .build(); + httpClientBuilder.setDefaultIOReactorConfig(ioReactorConfig); + } + + httpClientBuilder.setMaxConnTotal(config.maxConnections); + httpClientBuilder.setMaxConnPerRoute(config.maxConnectionsPerRoute); + + if ("http".equalsIgnoreCase(config.protocol)) { + // In this case disable the SSL capability as it might have an impact on + // bootstrap time, for example consuming entropy for no reason + httpClientBuilder.setSSLStrategy(NoopIOSessionStrategy.INSTANCE); + } + + return httpClientBuilder; + } + }); + + return builder; + } + + static Sniffer createSniffer(RestClient client, ElasticsearchConfig config) { + SnifferBuilder builder = Sniffer.builder(client) + .setSniffIntervalMillis((int) config.discovery.refreshInterval.toMillis()); + + // https discovery support + if ("https".equalsIgnoreCase(config.protocol)) { + NodesSniffer hostsSniffer = new ElasticsearchNodesSniffer( + client, + ElasticsearchNodesSniffer.DEFAULT_SNIFF_REQUEST_TIMEOUT, // 1sec + ElasticsearchNodesSniffer.Scheme.HTTPS); + builder.setNodesSniffer(hostsSniffer); + } + + return builder.build(); + } +} diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/health/ElasticsearchHealthCheck.java b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/health/ElasticsearchHealthCheck.java new file mode 100644 index 0000000000000..46001379905ce --- /dev/null +++ b/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/health/ElasticsearchHealthCheck.java @@ -0,0 +1,43 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.runtime.health; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.apache.http.util.EntityUtils; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; +import org.eclipse.microprofile.health.Readiness; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; + +import io.vertx.core.json.JsonObject; + +@Readiness +@ApplicationScoped +public class ElasticsearchHealthCheck implements HealthCheck { + @Inject + RestClient restClient; + + @Override + public HealthCheckResponse call() { + HealthCheckResponseBuilder builder = HealthCheckResponse.named("Elasticsearch cluster health check").up(); + try { + Request request = new Request("GET", "/_cluster/health"); + Response response = restClient.performRequest(request); + String responseBody = EntityUtils.toString(response.getEntity()); + JsonObject json = new JsonObject(responseBody); + String status = json.getString("status"); + if ("red".equals(status)) { + builder.down().withData("status", status); + } else { + builder.up().withData("status", status); + } + + } catch (Exception e) { + return builder.down().withData("reason", e.getMessage()).build(); + } + return builder.build(); + } +} diff --git a/extensions/elasticsearch-rest-client/runtime/src/main/resources/quarkus-extension.yaml b/extensions/elasticsearch-rest-client/runtime/src/main/resources/quarkus-extension.yaml new file mode 100644 index 0000000000000..3ffbcee954d7a --- /dev/null +++ b/extensions/elasticsearch-rest-client/runtime/src/main/resources/quarkus-extension.yaml @@ -0,0 +1,11 @@ +--- +name: "Elasticsearch REST client" +metadata: + keywords: + - "elasticsearch" + - "full text" + - "search" + guide: "https://quarkus.io/guides/elasticsearch" + categories: + - "data" + status: "preview" diff --git a/extensions/elasticsearch-rest-high-level-client/deployment/pom.xml b/extensions/elasticsearch-rest-high-level-client/deployment/pom.xml new file mode 100644 index 0000000000000..12e5b32daed5d --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/deployment/pom.xml @@ -0,0 +1,58 @@ + + + + io.quarkus + quarkus-elasticsearch-rest-high-level-client-parent + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-elasticsearch-rest-high-level-client-deployment + Quarkus - Elasticsearch REST high level client - Deployment + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-elasticsearch-rest-client-common-deployment + + + io.quarkus + quarkus-elasticsearch-rest-client-deployment + + + io.quarkus + quarkus-elasticsearch-rest-high-level-client + + + io.quarkus + quarkus-arc-deployment + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + + \ No newline at end of file diff --git a/extensions/elasticsearch-rest-high-level-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/deployment/ElasticsearchHighLevelClientProcessor.java b/extensions/elasticsearch-rest-high-level-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/deployment/ElasticsearchHighLevelClientProcessor.java new file mode 100644 index 0000000000000..c78c74997b975 --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/deployment/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/deployment/ElasticsearchHighLevelClientProcessor.java @@ -0,0 +1,21 @@ +package io.quarkus.elasticsearch.restclient.highlevel.deployment; + +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.elasticsearch.restclient.highlevel.runtime.ElasticsearchRestHighLevelClientProducer; + +class ElasticsearchHighLevelClientProcessor { + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.ELASTICSEARCH_REST_HIGH_LEVEL_CLIENT); + } + + @BuildStep() + AdditionalBeanBuildItem build() { + return AdditionalBeanBuildItem.unremovableOf(ElasticsearchRestHighLevelClientProducer.class); + } + +} diff --git a/extensions/elasticsearch-rest-high-level-client/pom.xml b/extensions/elasticsearch-rest-high-level-client/pom.xml new file mode 100644 index 0000000000000..4f356f28a2b78 --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/pom.xml @@ -0,0 +1,23 @@ + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + + 4.0.0 + + quarkus-elasticsearch-rest-high-level-client-parent + Quarkus - Elasticsearch REST high level client - Parent + pom + + + runtime + deployment + + + \ No newline at end of file diff --git a/extensions/elasticsearch-rest-high-level-client/runtime/pom.xml b/extensions/elasticsearch-rest-high-level-client/runtime/pom.xml new file mode 100644 index 0000000000000..946dc9d14c689 --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/runtime/pom.xml @@ -0,0 +1,117 @@ + + + + io.quarkus + quarkus-elasticsearch-rest-high-level-client-parent + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-elasticsearch-rest-high-level-client + Quarkus - Elasticsearch REST high level client - Runtime + Connect to an Elasticsearch cluster using the REST high level client + + + + io.quarkus + quarkus-core + + + io.quarkus + quarkus-elasticsearch-rest-client-common + + + io.quarkus + quarkus-elasticsearch-rest-client + + + io.quarkus + quarkus-arc + + + org.jboss.logmanager + log4j2-jboss-logmanager + + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + + + + + + org.apache.lucene + lucene-analyzers-common + + + org.apache.lucene + lucene-backward-codecs + + + org.apache.lucene + lucene-grouping + + + org.apache.lucene + lucene-memory + + + org.apache.lucene + lucene-misc + + + org.apache.lucene + lucene-queryparser + + + org.apache.lucene + lucene-sandbox + + + org.apache.lucene + lucene-spatial + + + org.apache.lucene + lucene-spatial-extras + + + org.apache.lucene + lucene-spatial3d + + + org.apache.lucene + lucene-suggest + + + + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + diff --git a/extensions/elasticsearch-rest-high-level-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/runtime/ElasticsearchRestHighLevelClientProducer.java b/extensions/elasticsearch-rest-high-level-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/runtime/ElasticsearchRestHighLevelClientProducer.java new file mode 100644 index 0000000000000..c69728bc3e39c --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/runtime/ElasticsearchRestHighLevelClientProducer.java @@ -0,0 +1,43 @@ +package io.quarkus.elasticsearch.restclient.highlevel.runtime; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Collections; + +import javax.annotation.PreDestroy; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestHighLevelClient; + +@ApplicationScoped +public class ElasticsearchRestHighLevelClientProducer { + + @Inject + @Default + RestClient restClient; + + private RestHighLevelClient client; + + @Produces + @Singleton + public RestHighLevelClient restHighLevelClient() { + this.client = new QuarkusRestHighLevelClient(restClient, RestClient::close, Collections.emptyList()); + return this.client; + } + + @PreDestroy + void destroy() { + try { + if (this.client != null) { + this.client.close(); + } + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } +} diff --git a/extensions/elasticsearch-rest-high-level-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/runtime/QuarkusRestHighLevelClient.java b/extensions/elasticsearch-rest-high-level-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/runtime/QuarkusRestHighLevelClient.java new file mode 100644 index 0000000000000..661a422899148 --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/highlevel/runtime/QuarkusRestHighLevelClient.java @@ -0,0 +1,22 @@ +package io.quarkus.elasticsearch.restclient.highlevel.runtime; + +import java.io.IOException; +import java.util.List; + +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.CheckedConsumer; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; + +/** + * The RestHighLevelClient cannot be built with an existing RestClient. + *

+ * The only (and documented - see javadoc) way to do it is to subclass it and use its protected constructor. + */ +class QuarkusRestHighLevelClient extends RestHighLevelClient { + + protected QuarkusRestHighLevelClient(RestClient restClient, CheckedConsumer doClose, + List namedXContentEntries) { + super(restClient, doClose, namedXContentEntries); + } +} diff --git a/extensions/elasticsearch-rest-high-level-client/runtime/src/main/resources/quarkus-extension.yaml b/extensions/elasticsearch-rest-high-level-client/runtime/src/main/resources/quarkus-extension.yaml new file mode 100644 index 0000000000000..444655a28472e --- /dev/null +++ b/extensions/elasticsearch-rest-high-level-client/runtime/src/main/resources/quarkus-extension.yaml @@ -0,0 +1,11 @@ +--- +name: "Elasticsearch REST High Level Client" +metadata: + keywords: + - "elasticsearch" + - "full text" + - "search" + guide: "https://quarkus.io/guides/elasticsearch" + categories: + - "data" + status: "preview" diff --git a/extensions/hibernate-search-elasticsearch/deployment/pom.xml b/extensions/hibernate-search-elasticsearch/deployment/pom.xml index 7a3bab7ddf48d..6b1c4cfc95e09 100644 --- a/extensions/hibernate-search-elasticsearch/deployment/pom.xml +++ b/extensions/hibernate-search-elasticsearch/deployment/pom.xml @@ -24,7 +24,7 @@ io.quarkus - quarkus-elasticsearch-rest-client-deployment + quarkus-elasticsearch-rest-client-common-deployment io.quarkus diff --git a/extensions/hibernate-search-elasticsearch/runtime/pom.xml b/extensions/hibernate-search-elasticsearch/runtime/pom.xml index b37b853de92d8..b7659ba3c09e8 100644 --- a/extensions/hibernate-search-elasticsearch/runtime/pom.xml +++ b/extensions/hibernate-search-elasticsearch/runtime/pom.xml @@ -24,7 +24,7 @@ io.quarkus - quarkus-elasticsearch-rest-client + quarkus-elasticsearch-rest-client-common org.hibernate.search diff --git a/extensions/pom.xml b/extensions/pom.xml index 7fcd1a89c4626..ed53e95a036dc 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -90,7 +90,9 @@ hibernate-validator panache hibernate-search-elasticsearch + elasticsearch-rest-client-common elasticsearch-rest-client + elasticsearch-rest-high-level-client jsch jgit kafka-client diff --git a/integration-tests/elasticsearch-rest-client/.gitignore b/integration-tests/elasticsearch-rest-client/.gitignore new file mode 100644 index 0000000000000..087a18358fe57 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/.gitignore @@ -0,0 +1,35 @@ +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-client/README.md b/integration-tests/elasticsearch-rest-client/README.md new file mode 100644 index 0000000000000..8bba703b91c1e --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/README.md @@ -0,0 +1,30 @@ +# quarkus-integration-test-elasticsearch-rest-client project + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +``` +./mvnw quarkus:dev +``` + +## Packaging and running the application + +The application can be packaged using `./mvnw package`. +It produces the `quarkus-integration-test-elasticsearch-rest-client-1.0-SNAPSHOT-runner.jar` file in the `/target` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory. + +The application is now runnable using `java -jar target/quarkus-integration-test-elasticsearch-rest-client-1.0-SNAPSHOT-runner.jar`. + +## Creating a native executable + +You can create a native executable using: `./mvnw package -Pnative`. + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: `./mvnw package -Pnative -Dquarkus.native.container-build=true`. + +You can then execute your native executable with: `./target/quarkus-integration-test-elasticsearch-rest-client-1.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image-guide. \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-client/pom.xml b/integration-tests/elasticsearch-rest-client/pom.xml new file mode 100644 index 0000000000000..5b38225180a5a --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/pom.xml @@ -0,0 +1,155 @@ + + + 4.0.0 + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + ../ + + + quarkus-integration-test-elasticsearch-rest-client + Quarkus - Integration Tests - Elasticsearch REST client + + + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-elasticsearch-rest-client + + + io.quarkus + quarkus-smallrye-health + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + io.quarkus + quarkus-maven-plugin + ${project.version} + + + + build + + + + + + maven-surefire-plugin + + true + + + + maven-failsafe-plugin + + true + + + + + + + + test-elasticsearch + + + test-elasticsearch + + + + + + maven-surefire-plugin + + false + + + + maven-failsafe-plugin + + false + + + + com.github.alexcojocaru + elasticsearch-maven-plugin + + 90 + ${project.build.directory}/test-classes/elasticsearch-maven-plugin/configuration/ + true + + + + + start-elasticsearch + process-test-classes + + runforked + + + + stop-elasticsearch + post-integration-test + + stop + + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + native + + + + diff --git a/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/Fruit.java b/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/Fruit.java new file mode 100644 index 0000000000000..ff28481a534bf --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/Fruit.java @@ -0,0 +1,7 @@ +package io.quarkus.it.elasticsearch; + +public class Fruit { + public String id; + public String name; + public String color; +} diff --git a/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/FruitResource.java b/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/FruitResource.java new file mode 100644 index 0000000000000..42be689abe124 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/FruitResource.java @@ -0,0 +1,54 @@ +package io.quarkus.it.elasticsearch; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.UUID; + +import javax.inject.Inject; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/fruits") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class FruitResource { + @Inject + FruitService fruitService; + + @POST + public Response index(Fruit fruit) throws IOException { + if (fruit.id == null) { + fruit.id = UUID.randomUUID().toString(); + } + fruitService.index(fruit); + return Response.created(URI.create("/fruits/" + fruit.id)).build(); + } + + @GET + @Path("/{id}") + public Fruit get(@PathParam("id") String id) throws IOException { + return fruitService.get(id); + } + + @GET + @Path("/search") + public List search(@QueryParam("name") String name, @QueryParam("color") String color) throws IOException { + if (name != null) { + return fruitService.searchByName(name); + } else if (color != null) { + return fruitService.searchByColor(color); + } else { + throw new BadRequestException("Should provide name or color query parameter"); + } + } + +} diff --git a/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/FruitService.java b/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/FruitService.java new file mode 100644 index 0000000000000..fe5140c6c6136 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/main/java/io/quarkus/it/elasticsearch/FruitService.java @@ -0,0 +1,71 @@ +package io.quarkus.it.elasticsearch; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +@ApplicationScoped +public class FruitService { + @Inject + RestClient restClient; + + public void index(Fruit fruit) throws IOException { + Request request = new Request( + "PUT", + "/fruits/_doc/" + fruit.id); + request.setJsonEntity(JsonObject.mapFrom(fruit).toString()); + restClient.performRequest(request); + } + + public Fruit get(String id) throws IOException { + Request request = new Request( + "GET", + "/fruits/_doc/" + id); + Response response = restClient.performRequest(request); + String responseBody = EntityUtils.toString(response.getEntity()); + JsonObject json = new JsonObject(responseBody); + return json.getJsonObject("_source").mapTo(Fruit.class); + } + + public List searchByColor(String color) throws IOException { + return search("color", color); + } + + public List searchByName(String name) throws IOException { + return search("name", name); + } + + private List search(String term, String match) throws IOException { + Request request = new Request( + "GET", + "/fruits/_search"); + //construct a JSON query like {"query": {"match": {"": " results = new ArrayList<>(hits.size()); + for (int i = 0; i < hits.size(); i++) { + JsonObject hit = hits.getJsonObject(i); + Fruit fruit = hit.getJsonObject("_source").mapTo(Fruit.class); + results.add(fruit); + } + return results; + } +} diff --git a/integration-tests/elasticsearch-rest-client/src/main/resources/META-INF/resources/fruits.html b/integration-tests/elasticsearch-rest-client/src/main/resources/META-INF/resources/fruits.html new file mode 100644 index 0000000000000..a2144d6a57be8 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/main/resources/META-INF/resources/fruits.html @@ -0,0 +1,120 @@ + + + + + Fruit REST service + + + + + + + +

+

REST Service - Fruit

+ +

Add a fruit

+
+
+
+
+
+
+
+ +
+ +

Search Fruits

+
+
+
+
+
+
+
+ +
+
+
Name
+
Color
+
+
+
{{ fruit.name }}
+
{{ fruit.color }}
+
+
+ + + diff --git a/integration-tests/elasticsearch-rest-client/src/main/resources/META-INF/resources/index.html b/integration-tests/elasticsearch-rest-client/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000000000..f7d857cfe2047 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,152 @@ + + + + + quarkus-integration-test-elasticsearch - 1.0-SNAPSHOT + + + + + + +
+
+

Congratulations, you have created a new Quarkus application.

+ +

Why do you see this?

+ +

This page is served by Quarkus. The source is in + src/main/resources/META-INF/resources/index.html.

+ +

What can I do from here?

+ +

If not already done, run the application in dev mode using: mvn compile quarkus:dev. +

+
    +
  • Add REST resources, Servlets, functions and other services in src/main/java.
  • +
  • Your static assets are located in src/main/resources/META-INF/resources.
  • +
  • Configure your application in src/main/resources/application.properties. +
  • +
+ +

How do I get rid of this page?

+

Just delete the src/main/resources/META-INF/resources/index.html file.

+
+
+
+

Application

+
    +
  • GroupId: io.quarkus
  • +
  • ArtifactId: quarkus-integration-test-elasticsearch
  • +
  • Version: 1.0-SNAPSHOT
  • +
  • Quarkus Version: 1.3.0.Final
  • +
+
+ +
+
+ + + + \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-client/src/main/resources/application.properties b/integration-tests/elasticsearch-rest-client/src/main/resources/application.properties new file mode 100644 index 0000000000000..8395e78244c4d --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/main/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.elasticsearch.hosts=localhost:9200 +quarkus.elasticsearch.socket-timeout=10S +quarkus.elasticsearch.discovery.enabled=true +quarkus.elasticsearch.discovery.refresh-interval=1M \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-client/src/test/java/io/quarkus/it/elasticsearch/FruitResourceTest.java b/integration-tests/elasticsearch-rest-client/src/test/java/io/quarkus/it/elasticsearch/FruitResourceTest.java new file mode 100644 index 0000000000000..6ebed60a37fa9 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/test/java/io/quarkus/it/elasticsearch/FruitResourceTest.java @@ -0,0 +1,69 @@ +package io.quarkus.it.elasticsearch; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +public class FruitResourceTest { + private static final TypeRef> LIST_OF_FRUIT_TYPE_REF = new TypeRef>() { + }; + + @Test + public void testEndpoint() throws InterruptedException { + // create a Fruit + Fruit fruit = new Fruit(); + fruit.id = "1"; + fruit.name = "Apple"; + fruit.color = "Green"; + given() + .contentType("application/json") + .body(fruit) + .when().post("/fruits") + .then() + .statusCode(201); + + // get the Fruit + Fruit result = get("/fruits/1").as(Fruit.class); + Assertions.assertNotNull(result); + Assertions.assertEquals("1", result.id); + Assertions.assertEquals("Apple", result.name); + Assertions.assertEquals("Green", result.color); + + // wait a few ms for the indexing to happened + Thread.sleep(1000); + + // search the Fruit + List results = get("/fruits/search?color=Green").as(LIST_OF_FRUIT_TYPE_REF); + Assertions.assertNotNull(results); + Assertions.assertFalse(results.isEmpty()); + Assertions.assertEquals("1", results.get(0).id); + Assertions.assertEquals("Apple", results.get(0).name); + Assertions.assertEquals("Green", results.get(0).color); + results = get("/fruits/search?name=Apple").as(LIST_OF_FRUIT_TYPE_REF); + Assertions.assertNotNull(results); + Assertions.assertFalse(results.isEmpty()); + Assertions.assertEquals("1", results.get(0).id); + Assertions.assertEquals("Apple", results.get(0).name); + Assertions.assertEquals("Green", results.get(0).color); + } + + @Test + public void testHealth() { + RestAssured.when().get("/health/ready").then() + .body("status", is("UP"), + "checks.status", containsInAnyOrder("UP"), + "checks.name", containsInAnyOrder("Elasticsearch cluster health check")); + } + +} diff --git a/integration-tests/elasticsearch-rest-client/src/test/java/io/quarkus/it/elasticsearch/NativeFruitResourceIT.java b/integration-tests/elasticsearch-rest-client/src/test/java/io/quarkus/it/elasticsearch/NativeFruitResourceIT.java new file mode 100644 index 0000000000000..6d1a5382c051f --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/test/java/io/quarkus/it/elasticsearch/NativeFruitResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.elasticsearch; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeFruitResourceIT extends FruitResourceTest { + + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/elasticsearch.yml b/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/elasticsearch.yml new file mode 100644 index 0000000000000..e15abadf05b9d --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/elasticsearch.yml @@ -0,0 +1,92 @@ +# ======================== Elasticsearch Configuration ========================= +# +# NOTE: Elasticsearch comes with reasonable defaults for most settings. +# Before you set out to tweak and tune the configuration, make sure you +# understand what are you trying to accomplish and the consequences. +# +# The primary way of configuring a node is via this file. This template lists +# the most important settings you may want to configure for a production cluster. +# +# Please consult the documentation for further information on configuration options: +# https://www.elastic.co/guide/en/elasticsearch/reference/index.html +# +# ---------------------------------- Cluster ----------------------------------- +# +# Use a descriptive name for your cluster: +# +#cluster.name: my-application +# +# ------------------------------------ Node ------------------------------------ +# +# Use a descriptive name for the node: +# +#node.name: node-1 +# +# Add custom attributes to the node: +# +#node.attr.rack: r1 +# +# ----------------------------------- Paths ------------------------------------ +# +# Path to directory where to store the data (separate multiple locations by comma): +# +#path.data: /path/to/data +# +# Path to log files: +# +#path.logs: /path/to/logs +# +# ----------------------------------- Memory ----------------------------------- +# +# Lock the memory on startup: +# +bootstrap.memory_lock: true +# +# Make sure that the heap size is set to about half the memory available +# on the system and that the owner of the process is allowed to use this +# limit. +# +# Elasticsearch performs poorly when the system is swapping the memory. +# +# ---------------------------------- Network ----------------------------------- +# +# Set the bind address to a specific IP (IPv4 or IPv6): +# +network.host: _local_ +# +# Set a custom port for HTTP: +# +http.port: 9200 +# +# For more information, consult the network module documentation. +# +# --------------------------------- Discovery ---------------------------------- +# +# Pass an initial list of hosts to perform discovery when this node is started: +# The default list of hosts is ["127.0.0.1", "[::1]"] +# +#discovery.seed_hosts: ["host1", "host2"] +# +# Bootstrap the cluster using an initial set of master-eligible nodes: +# +#cluster.initial_master_nodes: ["node-1", "node-2"] +# +# For more information, consult the discovery and cluster formation module documentation. +# +# ---------------------------------- Gateway ----------------------------------- +# +# Block initial recovery after a full cluster restart until N nodes are started: +# +#gateway.recover_after_nodes: 3 +# +# For more information, consult the gateway module documentation. +# +# ---------------------------------- Various ----------------------------------- +# +# Require explicit names when deleting indices: +# +#action.destructive_requires_name: true +# +# Disable starting multiple nodes on a single system: +# +node.max_local_storage_nodes: 1 diff --git a/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/jvm.options b/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/jvm.options new file mode 100644 index 0000000000000..91f3f0ee7dd56 --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/jvm.options @@ -0,0 +1,125 @@ +## JVM configuration + +################################################################ +## IMPORTANT: JVM heap size +################################################################ +## +## You should always set the min and max JVM heap +## size to the same value. For example, to set +## the heap to 4 GB, set: +## +## -Xms4g +## -Xmx4g +## +## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html +## for more information +## +################################################################ + +# Xms represents the initial size of total heap space +# Xmx represents the maximum size of total heap space + +# For Hibernate Search, we don't need as much as the default 2g +# Let's keep it low, so that we'll be able to run tests +# on memory-constrained CI slaves +-Xms256m +-Xmx256m + +################################################################ +## Expert settings +################################################################ +## +## All settings below this section are considered +## expert settings. Don't tamper with them unless +## you understand what you are doing +## +################################################################ + +## GC configuration +-XX:+UseConcMarkSweepGC +-XX:CMSInitiatingOccupancyFraction=75 +-XX:+UseCMSInitiatingOccupancyOnly + +## G1GC Configuration +# NOTE: G1GC is only supported on JDK version 10 or later. +# To use G1GC uncomment the lines below. +# 10-:-XX:-UseConcMarkSweepGC +# 10-:-XX:-UseCMSInitiatingOccupancyOnly +# 10-:-XX:+UseG1GC +# 10-:-XX:InitiatingHeapOccupancyPercent=75 + +## DNS cache policy +# cache ttl in seconds for positive DNS lookups noting that this overrides the +# JDK security property networkaddress.cache.ttl; set to -1 to cache forever +-Des.networkaddress.cache.ttl=60 +# cache ttl in seconds for negative DNS lookups noting that this overrides the +# JDK security property networkaddress.cache.negative ttl; set to -1 to cache +# forever +-Des.networkaddress.cache.negative.ttl=10 + +## optimizations + +# pre-touch memory pages used by the JVM during initialization +-XX:+AlwaysPreTouch + +## basic + +# explicitly set the stack size +-Xss1m + +# set to headless, just in case +-Djava.awt.headless=true + +# ensure UTF-8 encoding by default (e.g. filenames) +-Dfile.encoding=UTF-8 + +# use our provided JNA always versus the system one +-Djna.nosys=true + +# turn off a JDK optimization that throws away stack traces for common +# exceptions because stack traces are important for debugging +-XX:-OmitStackTraceInFastThrow + +# flags to configure Netty +-Dio.netty.noUnsafe=true +-Dio.netty.noKeySetOptimization=true +-Dio.netty.recycler.maxCapacityPerThread=0 + +# log4j 2 +-Dlog4j.shutdownHookEnabled=false +-Dlog4j2.disable.jmx=true + +-Djava.io.tmpdir=${ES_TMPDIR} + +## heap dumps + +# generate a heap dump when an allocation from the Java heap fails +# heap dumps are created in the working directory of the JVM +-XX:+HeapDumpOnOutOfMemoryError + +# specify an alternative path for heap dumps; ensure the directory exists and +# has sufficient space +-XX:HeapDumpPath=data + +# specify an alternative path for JVM fatal error logs +-XX:ErrorFile=logs/hs_err_pid%p.log + +## JDK 8 GC logging + +8:-XX:+PrintGCDetails +8:-XX:+PrintGCDateStamps +8:-XX:+PrintTenuringDistribution +8:-XX:+PrintGCApplicationStoppedTime +8:-Xloggc:logs/gc.log +8:-XX:+UseGCLogFileRotation +8:-XX:NumberOfGCLogFiles=32 +8:-XX:GCLogFileSize=64m + +# JDK 9+ GC logging +9-:-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m +# due to internationalization enhancements in JDK 9 Elasticsearch need to set the provider to COMPAT otherwise +# time/date parsing will break in an incompatible way for some date patterns and locals +9-:-Djava.locale.providers=COMPAT + +# temporary workaround for C2 bug with JDK 10 on hardware with AVX-512 +10-:-XX:UseAVX=2 \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/log4j2.properties b/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/log4j2.properties new file mode 100644 index 0000000000000..e4789c251b25a --- /dev/null +++ b/integration-tests/elasticsearch-rest-client/src/test/resources/elasticsearch-maven-plugin/configuration/log4j2.properties @@ -0,0 +1,36 @@ +status = error +appenders = console +loggers = action, metadata, cluster, settings, deprecation, slow_search, slow_indexing + +# log action execution errors for easier debugging +logger.action.name = org.elasticsearch.action +logger.action.level = info + +# do not log metadata too much as we generate a log of noise +logger.metadata.name = org.elasticsearch.cluster.metadata +logger.metadata.level = warn + +logger.cluster.name = org.elasticsearch.cluster.routing.allocation +logger.cluster.level = warn + +logger.settings.name = org.elasticsearch.common.settings +logger.settings.level = warn + +logger.deprecation.name = org.elasticsearch.deprecation +logger.deprecation.level = warn + +# Warn us about using inefficient Search operations +logger.slow_search.name = index.search.slowlog +logger.slow_search.level = trace + +# Warn us about using inefficient indexing actions +logger.slow_indexing.name = index.indexing.slowlog +logger.slow_indexing.level = trace + +appender.console.type = Console +appender.console.name = console +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %d{ABSOLUTE} (%t) %5p %c{1}:%L - %m%n + +rootLogger.level = info +rootLogger.appenderRef.console.ref = console diff --git a/integration-tests/elasticsearch-rest-high-level-client/.gitignore b/integration-tests/elasticsearch-rest-high-level-client/.gitignore new file mode 100644 index 0000000000000..087a18358fe57 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/.gitignore @@ -0,0 +1,35 @@ +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-high-level-client/README.md b/integration-tests/elasticsearch-rest-high-level-client/README.md new file mode 100644 index 0000000000000..43570d93bdc2f --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/README.md @@ -0,0 +1,30 @@ +# quarkus-integration-test-elasticsearch-rest-high-level-client project + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +``` +./mvnw quarkus:dev +``` + +## Packaging and running the application + +The application can be packaged using `./mvnw package`. +It produces the `quarkus-integration-test-elasticsearch-rest-high-level-client-1.0-SNAPSHOT-runner.jar` file in the `/target` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory. + +The application is now runnable using `java -jar target/quarkus-integration-test-elasticsearch-rest-high-level-client-1.0-SNAPSHOT-runner.jar`. + +## Creating a native executable + +You can create a native executable using: `./mvnw package -Pnative`. + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: `./mvnw package -Pnative -Dquarkus.native.container-build=true`. + +You can then execute your native executable with: `./target/quarkus-integration-test-elasticsearch-rest-high-level-client-1.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image-guide. \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-high-level-client/pom.xml b/integration-tests/elasticsearch-rest-high-level-client/pom.xml new file mode 100644 index 0000000000000..20f4b06c6f9ba --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/pom.xml @@ -0,0 +1,157 @@ + + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + ../ + + + 4.0.0 + + quarkus-integration-test-elasticsearch-rest-high-level-client + Quarkus - Integration Tests - Elasticsearch REST high level client + + + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-elasticsearch-rest-high-level-client + + + io.quarkus + quarkus-smallrye-health + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + io.quarkus + quarkus-maven-plugin + ${project.version} + + + + build + + + + + + maven-surefire-plugin + + true + + + + maven-failsafe-plugin + + true + + + + + + + + test-elasticsearch + + + test-elasticsearch + + + + + + maven-surefire-plugin + + false + + + + maven-failsafe-plugin + + false + + + + com.github.alexcojocaru + elasticsearch-maven-plugin + + 90 + ${project.build.directory}/test-classes/elasticsearch-maven-plugin/configuration/ + true + + + + + start-elasticsearch + process-test-classes + + runforked + + + + stop-elasticsearch + post-integration-test + + stop + + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/Fruit.java b/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/Fruit.java new file mode 100644 index 0000000000000..07c6e2d45ae96 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/Fruit.java @@ -0,0 +1,7 @@ +package io.quarkus.it.elasticsearch.highlevel; + +public class Fruit { + public String id; + public String name; + public String color; +} diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/FruitResource.java b/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/FruitResource.java new file mode 100644 index 0000000000000..47e8663da7a69 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/FruitResource.java @@ -0,0 +1,54 @@ +package io.quarkus.it.elasticsearch.highlevel; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.UUID; + +import javax.inject.Inject; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/fruits") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class FruitResource { + @Inject + FruitService fruitService; + + @POST + public Response index(Fruit fruit) throws IOException { + if (fruit.id == null) { + fruit.id = UUID.randomUUID().toString(); + } + fruitService.index(fruit); + return Response.created(URI.create("/fruits/" + fruit.id)).build(); + } + + @GET + @Path("/{id}") + public Fruit get(@PathParam("id") String id) throws IOException { + return fruitService.get(id); + } + + @GET + @Path("/search") + public List search(@QueryParam("name") String name, @QueryParam("color") String color) throws IOException { + if (name != null) { + return fruitService.searchByName(name); + } else if (color != null) { + return fruitService.searchByColor(color); + } else { + throw new BadRequestException("Should provide name or color query parameter"); + } + } + +} diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/FruitService.java b/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/FruitService.java new file mode 100644 index 0000000000000..bb1c7c2566fcb --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/main/java/io/quarkus/it/elasticsearch/highlevel/FruitService.java @@ -0,0 +1,72 @@ +package io.quarkus.it.elasticsearch.highlevel; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.elasticsearch.action.get.GetRequest; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.builder.SearchSourceBuilder; + +import io.vertx.core.json.JsonObject; + +@ApplicationScoped +public class FruitService { + @Inject + RestHighLevelClient restHighLevelClient; + + public void index(Fruit fruit) throws IOException { + IndexRequest request = new IndexRequest("fruits"); + request.id(fruit.id); + request.source(JsonObject.mapFrom(fruit).toString(), XContentType.JSON); + restHighLevelClient.index(request, RequestOptions.DEFAULT); + } + + public Fruit get(String id) throws IOException { + GetRequest getRequest = new GetRequest("fruits", id); + GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT); + if (getResponse.isExists()) { + String sourceAsString = getResponse.getSourceAsString(); + JsonObject json = new JsonObject(sourceAsString); + return json.mapTo(Fruit.class); + } + return null; + } + + public List searchByColor(String color) throws IOException { + return search("color", color); + } + + public List searchByName(String name) throws IOException { + return search("name", name); + } + + private List search(String term, String match) throws IOException { + SearchRequest searchRequest = new SearchRequest("fruits"); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(QueryBuilders.matchQuery(term, match)); + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); + SearchHits hits = searchResponse.getHits(); + List results = new ArrayList<>(hits.getHits().length); + for (SearchHit hit : hits.getHits()) { + String sourceAsString = hit.getSourceAsString(); + JsonObject json = new JsonObject(sourceAsString); + results.add(json.mapTo(Fruit.class)); + } + return results; + } +} diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/main/resources/META-INF/resources/fruits.html b/integration-tests/elasticsearch-rest-high-level-client/src/main/resources/META-INF/resources/fruits.html new file mode 100644 index 0000000000000..a2144d6a57be8 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/main/resources/META-INF/resources/fruits.html @@ -0,0 +1,120 @@ + + + + + Fruit REST service + + + + + + + +
+

REST Service - Fruit

+ +

Add a fruit

+
+
+
+
+
+
+
+ +
+ +

Search Fruits

+
+
+
+
+
+
+
+ +
+
+
Name
+
Color
+
+
+
{{ fruit.name }}
+
{{ fruit.color }}
+
+
+ + + diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/main/resources/META-INF/resources/index.html b/integration-tests/elasticsearch-rest-high-level-client/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000000000..f7d857cfe2047 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,152 @@ + + + + + quarkus-integration-test-elasticsearch - 1.0-SNAPSHOT + + + + + + +
+
+

Congratulations, you have created a new Quarkus application.

+ +

Why do you see this?

+ +

This page is served by Quarkus. The source is in + src/main/resources/META-INF/resources/index.html.

+ +

What can I do from here?

+ +

If not already done, run the application in dev mode using: mvn compile quarkus:dev. +

+
    +
  • Add REST resources, Servlets, functions and other services in src/main/java.
  • +
  • Your static assets are located in src/main/resources/META-INF/resources.
  • +
  • Configure your application in src/main/resources/application.properties. +
  • +
+ +

How do I get rid of this page?

+

Just delete the src/main/resources/META-INF/resources/index.html file.

+
+
+
+

Application

+
    +
  • GroupId: io.quarkus
  • +
  • ArtifactId: quarkus-integration-test-elasticsearch
  • +
  • Version: 1.0-SNAPSHOT
  • +
  • Quarkus Version: 1.3.0.Final
  • +
+
+ +
+
+ + + + \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/test/java/io/quarkus/it/elasticsearch/FruitResourceTest.java b/integration-tests/elasticsearch-rest-high-level-client/src/test/java/io/quarkus/it/elasticsearch/FruitResourceTest.java new file mode 100644 index 0000000000000..f6a077535d747 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/test/java/io/quarkus/it/elasticsearch/FruitResourceTest.java @@ -0,0 +1,70 @@ +package io.quarkus.it.elasticsearch; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.it.elasticsearch.highlevel.Fruit; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +public class FruitResourceTest { + private static final TypeRef> LIST_OF_FRUIT_TYPE_REF = new TypeRef>() { + }; + + @Test + public void testEndpoint() throws InterruptedException { + // create a Fruit + Fruit fruit = new Fruit(); + fruit.id = "1"; + fruit.name = "Apple"; + fruit.color = "Green"; + given() + .contentType("application/json") + .body(fruit) + .when().post("/fruits") + .then() + .statusCode(201); + + // get the Fruit + Fruit result = get("/fruits/1").as(Fruit.class); + Assertions.assertNotNull(result); + Assertions.assertEquals("1", result.id); + Assertions.assertEquals("Apple", result.name); + Assertions.assertEquals("Green", result.color); + + // wait a few ms for the indexing to happened + Thread.sleep(1000); + + // search the Fruit + List results = get("/fruits/search?color=Green").as(LIST_OF_FRUIT_TYPE_REF); + Assertions.assertNotNull(results); + Assertions.assertFalse(results.isEmpty()); + Assertions.assertEquals("1", results.get(0).id); + Assertions.assertEquals("Apple", results.get(0).name); + Assertions.assertEquals("Green", results.get(0).color); + results = get("/fruits/search?name=Apple").as(LIST_OF_FRUIT_TYPE_REF); + Assertions.assertNotNull(results); + Assertions.assertFalse(results.isEmpty()); + Assertions.assertEquals("1", results.get(0).id); + Assertions.assertEquals("Apple", results.get(0).name); + Assertions.assertEquals("Green", results.get(0).color); + } + + @Test + public void testHealth() { + RestAssured.when().get("/health/ready").then() + .body("status", is("UP"), + "checks.status", containsInAnyOrder("UP"), + "checks.name", containsInAnyOrder("Elasticsearch cluster health check")); + } + +} diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/test/java/io/quarkus/it/elasticsearch/NativeFruitResourceIT.java b/integration-tests/elasticsearch-rest-high-level-client/src/test/java/io/quarkus/it/elasticsearch/NativeFruitResourceIT.java new file mode 100644 index 0000000000000..6d1a5382c051f --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/test/java/io/quarkus/it/elasticsearch/NativeFruitResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.elasticsearch; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeFruitResourceIT extends FruitResourceTest { + + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/elasticsearch.yml b/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/elasticsearch.yml new file mode 100644 index 0000000000000..e15abadf05b9d --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/elasticsearch.yml @@ -0,0 +1,92 @@ +# ======================== Elasticsearch Configuration ========================= +# +# NOTE: Elasticsearch comes with reasonable defaults for most settings. +# Before you set out to tweak and tune the configuration, make sure you +# understand what are you trying to accomplish and the consequences. +# +# The primary way of configuring a node is via this file. This template lists +# the most important settings you may want to configure for a production cluster. +# +# Please consult the documentation for further information on configuration options: +# https://www.elastic.co/guide/en/elasticsearch/reference/index.html +# +# ---------------------------------- Cluster ----------------------------------- +# +# Use a descriptive name for your cluster: +# +#cluster.name: my-application +# +# ------------------------------------ Node ------------------------------------ +# +# Use a descriptive name for the node: +# +#node.name: node-1 +# +# Add custom attributes to the node: +# +#node.attr.rack: r1 +# +# ----------------------------------- Paths ------------------------------------ +# +# Path to directory where to store the data (separate multiple locations by comma): +# +#path.data: /path/to/data +# +# Path to log files: +# +#path.logs: /path/to/logs +# +# ----------------------------------- Memory ----------------------------------- +# +# Lock the memory on startup: +# +bootstrap.memory_lock: true +# +# Make sure that the heap size is set to about half the memory available +# on the system and that the owner of the process is allowed to use this +# limit. +# +# Elasticsearch performs poorly when the system is swapping the memory. +# +# ---------------------------------- Network ----------------------------------- +# +# Set the bind address to a specific IP (IPv4 or IPv6): +# +network.host: _local_ +# +# Set a custom port for HTTP: +# +http.port: 9200 +# +# For more information, consult the network module documentation. +# +# --------------------------------- Discovery ---------------------------------- +# +# Pass an initial list of hosts to perform discovery when this node is started: +# The default list of hosts is ["127.0.0.1", "[::1]"] +# +#discovery.seed_hosts: ["host1", "host2"] +# +# Bootstrap the cluster using an initial set of master-eligible nodes: +# +#cluster.initial_master_nodes: ["node-1", "node-2"] +# +# For more information, consult the discovery and cluster formation module documentation. +# +# ---------------------------------- Gateway ----------------------------------- +# +# Block initial recovery after a full cluster restart until N nodes are started: +# +#gateway.recover_after_nodes: 3 +# +# For more information, consult the gateway module documentation. +# +# ---------------------------------- Various ----------------------------------- +# +# Require explicit names when deleting indices: +# +#action.destructive_requires_name: true +# +# Disable starting multiple nodes on a single system: +# +node.max_local_storage_nodes: 1 diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/jvm.options b/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/jvm.options new file mode 100644 index 0000000000000..91f3f0ee7dd56 --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/jvm.options @@ -0,0 +1,125 @@ +## JVM configuration + +################################################################ +## IMPORTANT: JVM heap size +################################################################ +## +## You should always set the min and max JVM heap +## size to the same value. For example, to set +## the heap to 4 GB, set: +## +## -Xms4g +## -Xmx4g +## +## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html +## for more information +## +################################################################ + +# Xms represents the initial size of total heap space +# Xmx represents the maximum size of total heap space + +# For Hibernate Search, we don't need as much as the default 2g +# Let's keep it low, so that we'll be able to run tests +# on memory-constrained CI slaves +-Xms256m +-Xmx256m + +################################################################ +## Expert settings +################################################################ +## +## All settings below this section are considered +## expert settings. Don't tamper with them unless +## you understand what you are doing +## +################################################################ + +## GC configuration +-XX:+UseConcMarkSweepGC +-XX:CMSInitiatingOccupancyFraction=75 +-XX:+UseCMSInitiatingOccupancyOnly + +## G1GC Configuration +# NOTE: G1GC is only supported on JDK version 10 or later. +# To use G1GC uncomment the lines below. +# 10-:-XX:-UseConcMarkSweepGC +# 10-:-XX:-UseCMSInitiatingOccupancyOnly +# 10-:-XX:+UseG1GC +# 10-:-XX:InitiatingHeapOccupancyPercent=75 + +## DNS cache policy +# cache ttl in seconds for positive DNS lookups noting that this overrides the +# JDK security property networkaddress.cache.ttl; set to -1 to cache forever +-Des.networkaddress.cache.ttl=60 +# cache ttl in seconds for negative DNS lookups noting that this overrides the +# JDK security property networkaddress.cache.negative ttl; set to -1 to cache +# forever +-Des.networkaddress.cache.negative.ttl=10 + +## optimizations + +# pre-touch memory pages used by the JVM during initialization +-XX:+AlwaysPreTouch + +## basic + +# explicitly set the stack size +-Xss1m + +# set to headless, just in case +-Djava.awt.headless=true + +# ensure UTF-8 encoding by default (e.g. filenames) +-Dfile.encoding=UTF-8 + +# use our provided JNA always versus the system one +-Djna.nosys=true + +# turn off a JDK optimization that throws away stack traces for common +# exceptions because stack traces are important for debugging +-XX:-OmitStackTraceInFastThrow + +# flags to configure Netty +-Dio.netty.noUnsafe=true +-Dio.netty.noKeySetOptimization=true +-Dio.netty.recycler.maxCapacityPerThread=0 + +# log4j 2 +-Dlog4j.shutdownHookEnabled=false +-Dlog4j2.disable.jmx=true + +-Djava.io.tmpdir=${ES_TMPDIR} + +## heap dumps + +# generate a heap dump when an allocation from the Java heap fails +# heap dumps are created in the working directory of the JVM +-XX:+HeapDumpOnOutOfMemoryError + +# specify an alternative path for heap dumps; ensure the directory exists and +# has sufficient space +-XX:HeapDumpPath=data + +# specify an alternative path for JVM fatal error logs +-XX:ErrorFile=logs/hs_err_pid%p.log + +## JDK 8 GC logging + +8:-XX:+PrintGCDetails +8:-XX:+PrintGCDateStamps +8:-XX:+PrintTenuringDistribution +8:-XX:+PrintGCApplicationStoppedTime +8:-Xloggc:logs/gc.log +8:-XX:+UseGCLogFileRotation +8:-XX:NumberOfGCLogFiles=32 +8:-XX:GCLogFileSize=64m + +# JDK 9+ GC logging +9-:-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m +# due to internationalization enhancements in JDK 9 Elasticsearch need to set the provider to COMPAT otherwise +# time/date parsing will break in an incompatible way for some date patterns and locals +9-:-Djava.locale.providers=COMPAT + +# temporary workaround for C2 bug with JDK 10 on hardware with AVX-512 +10-:-XX:UseAVX=2 \ No newline at end of file diff --git a/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/log4j2.properties b/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/log4j2.properties new file mode 100644 index 0000000000000..e4789c251b25a --- /dev/null +++ b/integration-tests/elasticsearch-rest-high-level-client/src/test/resources/elasticsearch-maven-plugin/configuration/log4j2.properties @@ -0,0 +1,36 @@ +status = error +appenders = console +loggers = action, metadata, cluster, settings, deprecation, slow_search, slow_indexing + +# log action execution errors for easier debugging +logger.action.name = org.elasticsearch.action +logger.action.level = info + +# do not log metadata too much as we generate a log of noise +logger.metadata.name = org.elasticsearch.cluster.metadata +logger.metadata.level = warn + +logger.cluster.name = org.elasticsearch.cluster.routing.allocation +logger.cluster.level = warn + +logger.settings.name = org.elasticsearch.common.settings +logger.settings.level = warn + +logger.deprecation.name = org.elasticsearch.deprecation +logger.deprecation.level = warn + +# Warn us about using inefficient Search operations +logger.slow_search.name = index.search.slowlog +logger.slow_search.level = trace + +# Warn us about using inefficient indexing actions +logger.slow_indexing.name = index.indexing.slowlog +logger.slow_indexing.level = trace + +appender.console.type = Console +appender.console.name = console +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %d{ABSOLUTE} (%t) %5p %c{1}:%L - %m%n + +rootLogger.level = info +rootLogger.appenderRef.console.ref = console diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 1536f724933fa..c6146c8e36e42 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -127,6 +127,8 @@ webjars-locator devmode ide-launcher + elasticsearch-rest-client + elasticsearch-rest-high-level-client grpc-plain-text