From 45ce70237055fb078f0922061da292473d6f26bf Mon Sep 17 00:00:00 2001 From: altro3 Date: Sun, 8 May 2022 21:05:58 +0700 Subject: [PATCH] Move to elasticsearch-java 8. Removed the use of RestHighLevelClient and xcontent Fixes 305 --- .gitattributes | 1 + config/accepted-api-changes.json | 202 ++++++++++++++++++ elasticsearch/build.gradle | 1 - .../DefaultElasticsearchClientFactory.java | 12 -- .../DefaultHttpAsyncClientBuilderFactory.java | 4 +- .../conditon/RequiresElasticsearch.java | 4 +- .../graalvm/RestClientSubstitutions.java | 4 +- .../ElasticsearchClientHealthIndicator.java | 46 ++-- .../health/ElasticsearchHealthIndicator.java | 118 ---------- .../micronaut/elasticsearch/package-info.java | 2 +- .../elasticsearch/native-image.properties | 2 +- .../elasticsearch/reflect-config.json | 80 +------ .../elasticsearch/resource-config.json | 5 - ...icsearchConfigurationPropertiesSpec.groovy | 8 +- .../ElasticsearchAuthorizationSpec.groovy | 38 ++-- .../ElasticsearchMappingSpec.groovy | 171 +++++++-------- ...ticsearchClientHealthIndicatorSpec.groovy} | 24 +-- .../src/test/resources/logback-test.xml | 15 ++ gradle.properties | 14 +- src/main/docs/guide/configuration.adoc | 8 +- test-suite-groovy/build.gradle | 6 +- .../elasticsearch/ElasticsearchSpec.groovy | 66 ++---- .../src/test/resources/logback-test.xml | 15 ++ .../src/test/resources/logback.xml | 15 -- 24 files changed, 427 insertions(+), 434 deletions(-) create mode 100644 .gitattributes create mode 100644 config/accepted-api-changes.json delete mode 100644 elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicator.java rename elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/{ElasticsearchHealthIndicatorSpec.groovy => ElasticsearchClientHealthIndicatorSpec.groovy} (72%) create mode 100644 elasticsearch/src/test/resources/logback-test.xml create mode 100644 test-suite-groovy/src/test/resources/logback-test.xml delete mode 100644 test-suite-groovy/src/test/resources/logback.xml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..fcadb2cf --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text eol=lf diff --git a/config/accepted-api-changes.json b/config/accepted-api-changes.json new file mode 100644 index 00000000..6596cd3e --- /dev/null +++ b/config/accepted-api-changes.json @@ -0,0 +1,202 @@ +[ + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "member": "Class io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference.getBeanDefinitionType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference.getBeanType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference.load()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference", + "member": "Constructor io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchAsyncClient3$Definition$Reference()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "member": "Class io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference.getBeanDefinitionType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference.getBeanType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference.load()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference", + "member": "Constructor io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchClient2$Definition$Reference()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "member": "Class io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference.getBeanDefinitionType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference.getBeanType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference.load()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference", + "member": "Constructor io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$ElasticsearchTransport4$Definition$Reference()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "member": "Class io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference.getBeanDefinitionType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference.getBeanType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference.load()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference", + "member": "Constructor io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestClient1$Definition$Reference()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Class io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference.getBeanType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference.load()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Constructor io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.$DefaultElasticsearchClientFactory$RestHighLevelClient0$Definition$Reference.getBeanDefinitionType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "member": "Class io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "member": "Field $ANNOTATION_METADATA", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference.getBeanDefinitionType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference.getBeanType()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "member": "Method io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference.load()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference", + "member": "Constructor io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition$Reference()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.ElasticsearchHealthIndicator", + "member": "Class io.micronaut.elasticsearch.health.ElasticsearchHealthIndicator", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.ElasticsearchHealthIndicator", + "member": "Method io.micronaut.elasticsearch.health.ElasticsearchHealthIndicator.getResult()", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + }, + { + "type": "io.micronaut.elasticsearch.health.ElasticsearchHealthIndicator", + "member": "Constructor io.micronaut.elasticsearch.health.ElasticsearchHealthIndicator(org.elasticsearch.client.RestHighLevelClient)", + "reason": "Changes for compatibility with elasticsearch-java 8.2.0" + } +] \ No newline at end of file diff --git a/elasticsearch/build.gradle b/elasticsearch/build.gradle index 8916e9f3..01f1725b 100644 --- a/elasticsearch/build.gradle +++ b/elasticsearch/build.gradle @@ -4,7 +4,6 @@ plugins { dependencies { annotationProcessor mn.micronaut.graal - api "org.elasticsearch.client:elasticsearch-rest-high-level-client:$elasticsearchVersion" api "co.elastic.clients:elasticsearch-java:$elasticsearchVersion" api mn.micronaut.http api mn.micronaut.validation diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultElasticsearchClientFactory.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultElasticsearchClientFactory.java index c9e99503..a4e6859d 100755 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultElasticsearchClientFactory.java +++ b/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultElasticsearchClientFactory.java @@ -25,7 +25,6 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; import co.elastic.clients.elasticsearch.ElasticsearchClient; @@ -44,17 +43,6 @@ @Factory public class DefaultElasticsearchClientFactory { - /** - * Create the {@link RestHighLevelClient} bean for the given configuration. - * - * @param elasticsearchConfiguration The {@link DefaultElasticsearchConfigurationProperties} object - * @return A {@link RestHighLevelClient} bean - */ - @Bean(preDestroy = "close") - RestHighLevelClient restHighLevelClient(DefaultElasticsearchConfigurationProperties elasticsearchConfiguration) { - return new RestHighLevelClient(restClientBuilder(elasticsearchConfiguration)); - } - /** * @param elasticsearchConfiguration The {@link DefaultElasticsearchConfigurationProperties} object * @return The Elasticsearch Rest Client diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultHttpAsyncClientBuilderFactory.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultHttpAsyncClientBuilderFactory.java index 84d362f3..f9a1bdd6 100644 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultHttpAsyncClientBuilderFactory.java +++ b/elasticsearch/src/main/java/io/micronaut/elasticsearch/DefaultHttpAsyncClientBuilderFactory.java @@ -19,9 +19,9 @@ import io.micronaut.context.annotation.Factory; import io.micronaut.context.annotation.Requires; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import jakarta.inject.Singleton; +import org.elasticsearch.client.RestClient; /** * The default factory used to create {@link HttpAsyncClientBuilder} for HTTP client configurations. @@ -29,7 +29,7 @@ * @author Puneet Behl * @since 1.0.0 */ -@Requires(classes = {RestHighLevelClient.class}) +@Requires(classes = {RestClient.class}) @Factory public class DefaultHttpAsyncClientBuilderFactory { diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/conditon/RequiresElasticsearch.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/conditon/RequiresElasticsearch.java index d615b2af..adc6e2b4 100644 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/conditon/RequiresElasticsearch.java +++ b/elasticsearch/src/main/java/io/micronaut/elasticsearch/conditon/RequiresElasticsearch.java @@ -17,7 +17,7 @@ import io.micronaut.elasticsearch.ElasticsearchSettings; import io.micronaut.context.annotation.Requires; -import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.RestClient; import java.lang.annotation.*; @@ -31,6 +31,6 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PACKAGE, ElementType.TYPE}) @Requires(property = ElasticsearchSettings.PREFIX) -@Requires(classes = {RestHighLevelClient.class}) +@Requires(classes = {RestClient.class}) public @interface RequiresElasticsearch { } diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/graalvm/RestClientSubstitutions.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/graalvm/RestClientSubstitutions.java index d4bedbe3..ce5f60f7 100644 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/graalvm/RestClientSubstitutions.java +++ b/elasticsearch/src/main/java/io/micronaut/elasticsearch/graalvm/RestClientSubstitutions.java @@ -47,7 +47,7 @@ * * We substitute it with an implementation which does not use serialization. * - * Forked from Quarkus: https://github.com/quarkusio/quarkus/blob/c9cba824e8812fa3f15474b8382ac5d90f7238aa/extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/runtime/graal/Substitute_RestClient.java + * Forked from Quarkus: https://github.com/quarkusio/quarkus/blob/main/extensions/elasticsearch-rest-client-common/runtime/src/main/java/io/quarkus/elasticsearch/restclient/common/runtime/graal/Substitute_RestClient.java * * @author Iván López * @since 2.0.0 @@ -109,7 +109,7 @@ public NoSerializationBasicAuthCache() { this(null); } - protected HttpHost getKey(final HttpHost host) { + private HttpHost getKey(final HttpHost host) { if (host.getPort() <= 0) { final int port; try { diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicator.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicator.java index c30ff3a3..101d2f97 100644 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicator.java +++ b/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicator.java @@ -15,16 +15,17 @@ */ package io.micronaut.elasticsearch.health; +import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; +import co.elastic.clients.elasticsearch.cluster.HealthResponse; import io.micronaut.context.annotation.Requires; import io.micronaut.health.HealthStatus; import io.micronaut.management.endpoint.health.HealthEndpoint; import io.micronaut.management.health.indicator.HealthIndicator; import io.micronaut.management.health.indicator.HealthResult; +import jakarta.inject.Singleton; import org.reactivestreams.Publisher; -import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; -import jakarta.inject.Singleton; -import java.io.IOException; +import java.util.Locale; import static io.micronaut.health.HealthStatus.DOWN; import static io.micronaut.health.HealthStatus.UP; @@ -39,7 +40,6 @@ */ @Requires(beans = HealthEndpoint.class) @Requires(property = HealthEndpoint.PREFIX + ".elasticsearch.enabled", notEquals = "false") -@Requires(property = HealthEndpoint.PREFIX + ".elasticsearch.rest.high.level.enabled", notEquals = "true") // we don't want to clash with the deprecated check @Singleton public class ElasticsearchClientHealthIndicator implements HealthIndicator { @@ -65,24 +65,40 @@ public ElasticsearchClientHealthIndicator(ElasticsearchAsyncClient client) { */ @Override public Publisher getResult() { - return (subscriber -> { - final HealthResult.Builder resultBuilder = HealthResult.builder(NAME); - try { - client.cluster().health().handle((health, exception) -> { + return (subscriber -> client.cluster().health() + .handle((health, exception) -> { + + final HealthResult.Builder resultBuilder = HealthResult.builder(NAME); if (exception != null) { subscriber.onNext(resultBuilder.status(DOWN).exception(exception).build()); subscriber.onComplete(); } else { HealthStatus status = health.status() == co.elastic.clients.elasticsearch._types.HealthStatus.Red ? DOWN : UP; - subscriber.onNext(resultBuilder.status(status).details(health).build()); + subscriber.onNext(resultBuilder.status(status).details(healthResultDetails(health)).build()); subscriber.onComplete(); } return health; - }); - } catch (IOException e) { - subscriber.onNext(resultBuilder.status(DOWN).exception(e).build()); - subscriber.onComplete(); - } - }); + })); + } + + private String healthResultDetails(HealthResponse response) { + return "{" + + "\"cluster_name\":\"" + response.clusterName() + "\"," + + "\"status\":\"" + response.status().name().toLowerCase(Locale.ENGLISH) + "\"," + + "\"timed_out\":" + response.timedOut() + "," + + "\"number_of_nodes\":" + response.numberOfNodes() + "," + + "\"number_of_data_nodes\":" + response.numberOfDataNodes() + "," + + "\"number_of_pending_tasks\":" + response.numberOfPendingTasks() + "," + + "\"number_of_in_flight_fetch\":" + response.numberOfInFlightFetch() + "," + + "\"task_max_waiting_in_queue\":\"" + response.taskMaxWaitingInQueueMillis().toString() + "\"," + + "\"task_max_waiting_in_queue_millis\":" + response.taskMaxWaitingInQueueMillis().toEpochMilli() + "," + + "\"active_shards_percent_as_number\":\"" + response.activeShardsPercentAsNumber() + "\"," + + "\"active_primary_shards\":" + response.activePrimaryShards() + "," + + "\"active_shards\":" + response.activeShards() + "," + + "\"relocating_shards\":" + response.relocatingShards() + "," + + "\"initializing_shards\":" + response.initializingShards() + "," + + "\"unassigned_shards\":" + response.unassignedShards() + "," + + "\"delayed_unassigned_shards\":" + response.delayedUnassignedShards() + + "}"; } } diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicator.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicator.java deleted file mode 100644 index ad293e69..00000000 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicator.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2017-2020 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.micronaut.elasticsearch.health; - -import io.micronaut.context.annotation.Requires; -import io.micronaut.health.HealthStatus; -import io.micronaut.management.endpoint.health.HealthEndpoint; -import io.micronaut.management.health.indicator.HealthIndicator; -import io.micronaut.management.health.indicator.HealthResult; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.common.Strings; -import org.elasticsearch.xcontent.ToXContent; -import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xcontent.XContentFactory; -import org.reactivestreams.Publisher; - -import jakarta.inject.Singleton; -import java.io.IOException; - -import static io.micronaut.health.HealthStatus.DOWN; -import static io.micronaut.health.HealthStatus.UP; -import static java.util.Collections.emptyMap; -import static org.elasticsearch.cluster.health.ClusterHealthStatus.GREEN; -import static org.elasticsearch.cluster.health.ClusterHealthStatus.YELLOW; - -/** - * A {@link HealthIndicator} for Elasticsearch that uses an automatically-configured high-level REST client, injected as a dependency, to communicate - * with Elasticsearch. - * - * @author Puneet Behl - * @author Robyn Dalgleish - * @since 1.0.0 - */ -@Requires(beans = HealthEndpoint.class) -@Requires(property = HealthEndpoint.PREFIX + ".elasticsearch.rest.high.level.enabled", notEquals = "false") -@Singleton -public class ElasticsearchHealthIndicator implements HealthIndicator { - - private static final String NAME = "elasticsearch"; - - private final RestHighLevelClient esClient; - - /** - * Constructor. - * - * @param esClient The Elasticsearch high level REST client. - */ - public ElasticsearchHealthIndicator(RestHighLevelClient esClient) { - this.esClient = esClient; - } - - /** - * Tries to call the cluster info API on Elasticsearch to obtain information about the cluster. If the call succeeds, the Elasticsearch cluster - * health status (GREEN / YELLOW / RED) will be included in the health indicator details. - * - * @return A positive health result UP if the cluster can be communicated with and is in either GREEN or YELLOW status. A negative health result - * DOWN if the cluster cannot be communicated with or is in RED status. - */ - @Override - public Publisher getResult() { - - return (subscriber -> esClient.cluster().healthAsync(new ClusterHealthRequest(), RequestOptions.DEFAULT, new ActionListener() { - - private final HealthResult.Builder resultBuilder = HealthResult.builder(NAME); - - @Override - public void onResponse(ClusterHealthResponse response) { - - HealthResult result; - - try { - result = resultBuilder - .status(healthResultStatus(response)) - .details(healthResultDetails(response)) - .build(); - } catch (IOException e) { - result = resultBuilder.status(DOWN).exception(e).build(); - } - - subscriber.onNext(result); - subscriber.onComplete(); - } - - @Override - public void onFailure(Exception e) { - subscriber.onNext(resultBuilder.status(DOWN).exception(e).build()); - subscriber.onComplete(); - } - })); - } - - private String healthResultDetails(ClusterHealthResponse response) throws IOException { - XContentBuilder xContentBuilder = XContentFactory.jsonBuilder(); - response.toXContent(xContentBuilder, new ToXContent.MapParams(emptyMap())); - return Strings.toString(xContentBuilder); - } - - private HealthStatus healthResultStatus(ClusterHealthResponse response) { - return response.getStatus() == GREEN || response.getStatus() == YELLOW ? UP : DOWN; - } -} diff --git a/elasticsearch/src/main/java/io/micronaut/elasticsearch/package-info.java b/elasticsearch/src/main/java/io/micronaut/elasticsearch/package-info.java index c8806c91..f62c807c 100644 --- a/elasticsearch/src/main/java/io/micronaut/elasticsearch/package-info.java +++ b/elasticsearch/src/main/java/io/micronaut/elasticsearch/package-info.java @@ -14,7 +14,7 @@ * limitations under the License. */ /** - * Configuration for Elasticsearch RestHighLevelClient. + * Configuration for Elasticsearch RestClient. * refer to https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.3/java-rest-high.html * * @author lishuai diff --git a/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/native-image.properties b/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/native-image.properties index fbf897f2..fd65a437 100644 --- a/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/native-image.properties +++ b/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/native-image.properties @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -Args = --initialize-at-run-time=io.micronaut.elasticsearch.health.$ElasticsearchHealthIndicator$Definition +Args = --initialize-at-run-time=io.micronaut.elasticsearch.health.$ElasticsearchClientHealthIndicator$Definition diff --git a/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/reflect-config.json b/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/reflect-config.json index 517b85c1..7ec5bfa2 100644 --- a/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/reflect-config.json +++ b/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/reflect-config.json @@ -17,84 +17,6 @@ { "name": "org.apache.http.HttpHost[]" }, - { - "name": "org.apache.log4j.Level", - "fields": [{"name": "TRACE"} - ] - }, - { - "name": "org.apache.log4j.Priority" - }, - { - "name": "org.apache.logging.log4j.core.config.json.JsonConfigurationFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.impl.Log4jContextFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.lookup.JmxRuntimeInputArgumentsLookup", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.lookup.JndiLookup", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.core.pattern.DatePatternConverter", - "allDeclaredMethods": true - }, - { - "name": "org.apache.logging.log4j.core.pattern.LevelPatternConverter", - "allDeclaredMethods": true - }, - { - "name": "org.apache.logging.log4j.core.pattern.LineSeparatorPatternConverter", - "allDeclaredMethods": true - }, - { - "name": "org.apache.logging.log4j.core.pattern.LoggerPatternConverter", - "allDeclaredMethods": true - }, - { - "name": "org.apache.logging.log4j.core.pattern.MessagePatternConverter", - "allDeclaredMethods": true - }, - { - "name": "org.apache.logging.log4j.core.pattern.ThreadNamePatternConverter", - "allDeclaredMethods": true - }, - { - "name": "org.apache.logging.log4j.message.DefaultFlowMessageFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.apache.logging.log4j.message.ReusableMessageFactory", - "methods": [{"name": "", "parameterTypes": []}] - }, - { - "name": "org.elasticsearch.Version", - "allPublicFields": true - }, - { - "name" : "org.apache.logging.log4j.core.util.ExecutorServices", - "allDeclaredFields" : true, - "allPublicMethods" : true, - "allDeclaredConstructors" : true - }, { "name" : "java.lang.Thread", "allDeclaredFields" : true, @@ -107,4 +29,4 @@ "allPublicMethods" : true, "allDeclaredConstructors" : true } -] \ No newline at end of file +] diff --git a/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/resource-config.json b/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/resource-config.json index f0545c41..9f42769f 100644 --- a/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/resource-config.json +++ b/elasticsearch/src/main/resources/META-INF/native-image/io.micronaut.elasticsearch/elasticsearch/resource-config.json @@ -1,11 +1,6 @@ { "resources":[ - {"pattern":"\\QMETA-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat\\E"}, {"pattern":"\\QMETA-INF/services/javax.script.ScriptEngineFactory\\E"}, - {"pattern":"\\QMETA-INF/services/org.apache.logging.log4j.spi.Provider\\E"}, - {"pattern":"\\QMETA-INF/services/org.apache.logging.log4j.util.PropertySource\\E"}, - {"pattern":"\\QMETA-INF/services/org.elasticsearch.common.xcontent.XContentBuilderExtension\\E"}, - {"pattern":"\\QMETA-INF/services/org.elasticsearch.plugins.spi.NamedXContentProvider\\E"}, {"pattern":"\\Qjavax/crypto/Cipher.class\\E"}, {"pattern":"\\Qmozilla/public-suffix-list.txt\\E"}, {"pattern":"\\Qorg/apache/http/nio/client/version.properties\\E"}, diff --git a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/DefaultElasticsearchConfigurationPropertiesSpec.groovy b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/DefaultElasticsearchConfigurationPropertiesSpec.groovy index d70b66df..5b452050 100644 --- a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/DefaultElasticsearchConfigurationPropertiesSpec.groovy +++ b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/DefaultElasticsearchConfigurationPropertiesSpec.groovy @@ -16,6 +16,7 @@ package io.micronaut.elasticsearch +import co.elastic.clients.elasticsearch.ElasticsearchClient import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Factory import io.micronaut.context.annotation.Replaces @@ -27,7 +28,6 @@ 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.elasticsearch.client.NodeSelector -import org.elasticsearch.client.RestHighLevelClient import spock.lang.Specification import jakarta.inject.Singleton @@ -38,7 +38,7 @@ import jakarta.inject.Singleton */ class DefaultElasticsearchConfigurationPropertiesSpec extends Specification { - void "Test Elasticsearch high level rest client configrations"() { + void "Test Elasticsearch rest client configrations"() { when: ApplicationContext applicationContext = ApplicationContext.run( @@ -87,8 +87,6 @@ class DefaultElasticsearchConfigurationPropertiesSpec extends Specification { "elasticsearch.maxRetryTimeoutMillis": 1000, "elasticsearch.request.default.localAddress": "198.57.151.22", "elasticsearch.request.default.expectContinueEnabled": true - - ) then: @@ -108,7 +106,7 @@ class DefaultElasticsearchConfigurationPropertiesSpec extends Specification { expect: applicationContext.containsBean(DefaultElasticsearchConfigurationProperties) - applicationContext.containsBean(RestHighLevelClient) + applicationContext.containsBean(ElasticsearchClient) applicationContext.getBean(DefaultElasticsearchConfigurationProperties).httpHosts.size() == 2 } diff --git a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchAuthorizationSpec.groovy b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchAuthorizationSpec.groovy index 7f8970c5..425aba82 100644 --- a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchAuthorizationSpec.groovy +++ b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchAuthorizationSpec.groovy @@ -16,44 +16,54 @@ package io.micronaut.elasticsearch +import co.elastic.clients.elasticsearch.ElasticsearchClient +import co.elastic.clients.elasticsearch.core.InfoResponse import io.micronaut.context.ApplicationContext -import org.elasticsearch.client.RequestOptions -import org.elasticsearch.client.RestHighLevelClient -import org.elasticsearch.client.core.MainResponse +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy import org.testcontainers.elasticsearch.ElasticsearchContainer +import org.testcontainers.spock.Testcontainers import spock.lang.Requires import spock.lang.Specification +@Testcontainers @Requires({ sys['elasticsearch.version'] }) class ElasticsearchAuthorizationSpec extends Specification { static final String ELASTICSEARCH_VERSION = System.getProperty("elasticsearch.version") static final String ELASTICSEARCH_USERNAME = "elastic" static final String ELASTICSEARCH_PASSWORD = "changeme" + static final ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") + .withExposedPorts(9200) + .withEnv("xpack.security.enabled", "false") + .withPassword(ELASTICSEARCH_PASSWORD) + .waitingFor(new LogMessageWaitStrategy().withRegEx(".*\"message\":\"started\".*")) + + void setupSpec() { + container.start() + } + + void cleanupSpec() { + container.stop() + } void "Test Elasticsearch authorization"() { given: - ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") - container.withPassword(ELASTICSEARCH_PASSWORD) - container.start() - String token = new String(Base64.getEncoder().encode((ELASTICSEARCH_USERNAME + ':' + ELASTICSEARCH_PASSWORD).getBytes())) ApplicationContext applicationContext = ApplicationContext.run( - 'elasticsearch.httpHosts': 'http://' + container.getHttpHostAddress(), - 'elasticsearch.default-headers': "Authorization:Basic ${token}" + 'elasticsearch.httpHosts': 'http://' + container.httpHostAddress, + 'elasticsearch.default-headers': "Authorization:Basic $token" ) expect: - applicationContext.containsBean(RestHighLevelClient) - applicationContext.getBean(RestHighLevelClient).ping(RequestOptions.DEFAULT) - MainResponse response = applicationContext.getBean(RestHighLevelClient).info(RequestOptions.DEFAULT) - System.out.println(String.format("cluster: %s, node: %s, version: %s %s", response.getClusterName(), response.getNodeName(), response.getVersion().getNumber(), response.getVersion().getBuildDate())) + applicationContext.containsBean(ElasticsearchClient) + applicationContext.getBean(ElasticsearchClient).ping() + InfoResponse response = applicationContext.getBean(ElasticsearchClient).info() + System.out.println(String.format("cluster: %s, node: %s, version: %s %s", response.clusterName(), response.name(), response.version().number(), response.version().buildDate())) cleanup: applicationContext.close() - container.stop() } } diff --git a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchMappingSpec.groovy b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchMappingSpec.groovy index d4f6ce49..3fc698e9 100755 --- a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchMappingSpec.groovy +++ b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/ElasticsearchMappingSpec.groovy @@ -16,22 +16,14 @@ package io.micronaut.elasticsearch +import co.elastic.clients.elasticsearch.ElasticsearchClient +import co.elastic.clients.elasticsearch._types.Result +import co.elastic.clients.elasticsearch.core.* +import co.elastic.clients.elasticsearch.indices.ExistsRequest import io.micronaut.context.ApplicationContext -import org.elasticsearch.Version -import org.elasticsearch.action.DocWriteResponse -import org.elasticsearch.action.delete.DeleteRequest -import org.elasticsearch.action.delete.DeleteResponse -import org.elasticsearch.action.get.GetRequest -import org.elasticsearch.action.get.GetResponse -import org.elasticsearch.action.index.IndexRequest -import org.elasticsearch.action.index.IndexResponse -import org.elasticsearch.client.RequestOptions -import org.elasticsearch.client.RestHighLevelClient -import org.elasticsearch.client.core.MainResponse -import org.elasticsearch.client.indices.GetIndexRequest -import org.elasticsearch.search.fetch.subphase.FetchSourceContext -import org.elasticsearch.xcontent.XContentType +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy import org.testcontainers.elasticsearch.ElasticsearchContainer +import org.testcontainers.spock.Testcontainers import spock.lang.Requires import spock.lang.Specification @@ -40,136 +32,129 @@ import spock.lang.Specification * @author Puneet Behl * @since 1.0.1 */ +@Testcontainers @Requires({ sys['elasticsearch.version'] }) class ElasticsearchMappingSpec extends Specification { - final static String ELASTICSEARCH_VERSION = System.getProperty("elasticsearch.version") + static final String ELASTICSEARCH_VERSION = System.getProperty("elasticsearch.version") + static final ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") + .withExposedPorts(9200) + .withEnv("xpack.security.enabled", "false") + .waitingFor(new LogMessageWaitStrategy().withRegEx(".*\"message\":\"started\".*")) + + void setupSpec() { + container.start() + } + + void cleanupSpec() { + container.stop() + } void "Test Elasticsearch connection"() { given: - ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") - container.start() - - ApplicationContext applicationContext = ApplicationContext.run('elasticsearch.httpHosts': 'http://' + container.getHttpHostAddress()) + ApplicationContext applicationContext = ApplicationContext.run('elasticsearch.httpHosts': 'http://' + container.httpHostAddress) expect: - applicationContext.containsBean(RestHighLevelClient) - applicationContext.getBean(RestHighLevelClient).ping(RequestOptions.DEFAULT) - MainResponse response = applicationContext.getBean(RestHighLevelClient).info(RequestOptions.DEFAULT) - System.out.println(String.format("cluser: %s, node: %s, version: %s", response.getClusterName(), response.getNodeName(), response.getVersion())) + applicationContext.containsBean(ElasticsearchClient) + applicationContext.getBean(ElasticsearchClient).ping() + InfoResponse response = applicationContext.getBean(ElasticsearchClient).info() + System.out.println(String.format("cluser: %s, node: %s, version: %s %s", response.clusterName(), response.name(), response.version().number(), response.version().buildDate())) cleanup: applicationContext.close() - container.stop() } - void "Test Elasticsearch(7.x) Mapping API"() { + void "Test Elasticsearch(8.x) Mapping API"() { given: - ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") - container.start() - ApplicationContext applicationContext = ApplicationContext.run('elasticsearch.httpHosts': 'http://' + container.getHttpHostAddress()) - RestHighLevelClient client = applicationContext.getBean(RestHighLevelClient) + ElasticsearchClient client = applicationContext.getBean(ElasticsearchClient) expect: "Make sure the version of ES is up to date because these tests may cause unexpected results" - client.info(RequestOptions.DEFAULT).getVersion().getNumber().equals(Version.fromString(ELASTICSEARCH_VERSION).toString()) + ELASTICSEARCH_VERSION == client.info().version().number() when: - GetIndexRequest getIndexRequest = new GetIndexRequest("posts") + ExistsRequest existsRequest = new ExistsRequest.Builder().index("posts").build() then: "index does not exists" - !client.indices().exists(getIndexRequest, RequestOptions.DEFAULT) + !client.indices().exists(existsRequest).value() when: "create index request" - IndexRequest request = new IndexRequest( - "posts", - "doc", - "1") - String jsonString = "{" + - "\"user\":\"kimchy\"," + - "\"postDate\":\"2013-01-30\"," + - "\"message\":\"trying out Elasticsearch\"" + - "}" - - request.source(jsonString, XContentType.JSON) - IndexResponse response = client.index(request, RequestOptions.DEFAULT) + IndexRequest.Builder requestBuilder = new IndexRequest.Builder<>() + .index("posts") + .id("1") + + Map document = new HashMap<>() + document.put("user", "kimchy") + document.put("postDate", "2013-01-30") + document.put("message", "trying out Elasticsearch") + requestBuilder.document(document) + + IndexResponse response = client.index(requestBuilder.build()) then: "verify version and result" - response.getIndex() == "posts" - response.getVersion() == 1 - response.getResult() == DocWriteResponse.Result.CREATED + response.index() == "posts" + response.version() == 1 + response.result() == Result.Created when: "update index request" - request = new IndexRequest( - "posts", - "doc", - "1") - jsonString = "{" + - "\"user\":\"kimchy1\"," + - "\"postDate\":\"2018-10-30\"," + - "\"message\":\"Trying out Elasticsearch6\"" + - "}" - - request.source(jsonString, XContentType.JSON) - response = client.index(request, RequestOptions.DEFAULT) + requestBuilder = new IndexRequest.Builder<>() + .index("posts") + .id("1") + + document = new HashMap<>() + document.put("user", "kimchy1") + document.put("postDate", "2018-10-30") + document.put("message", "Trying out Elasticsearch6") + requestBuilder.document(document) + + response = client.index(requestBuilder.build()) then: "verify version and result" - response.getIndex() == "posts" - response.getVersion() == 2 - response.getResult() == DocWriteResponse.Result.UPDATED + response.index() == "posts" + response.version() == 2 + response.result() == Result.Updated when: "get request" - GetRequest getRequest = new GetRequest( - "posts", - "doc", - "1"). - fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) - String[] includes = ["message", "*Date"] - String[] excludes = [] - FetchSourceContext fetchSourceContext = - new FetchSourceContext(true, includes, excludes) - getRequest.fetchSourceContext(fetchSourceContext) + GetRequest getRequest = new GetRequest.Builder() + .index("posts") + .id("1") + .sourceIncludes("message", "*Date") + .build() - - GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT) + GetResponse getResponse = client.get(getRequest, Map.class) then: "verify source" - getResponse.getType() == "doc" - getResponse.getIndex() == "posts" - getResponse.isExists() - getResponse.getVersion() == 2 - getResponse.getSourceAsMap() == [postDate:"2018-10-30", message:"Trying out Elasticsearch6"] + getResponse.index() == "posts" + getResponse.version() == 2 + getResponse.source() == [postDate: "2018-10-30", message: "Trying out Elasticsearch6"] when: "exits request" - getRequest = new GetRequest( - "posts", - "doc", - "1") - getRequest.fetchSourceContext(new FetchSourceContext(false)) - getRequest.storedFields("_none_") + co.elastic.clients.elasticsearch.core.ExistsRequest existsRequest2 = new co.elastic.clients.elasticsearch.core.ExistsRequest.Builder() + .index("posts") + .id("1") + .build() then: - client.exists(getRequest, RequestOptions.DEFAULT) + client.exists(existsRequest2) when: "delete request" - DeleteRequest deleteRequest = new DeleteRequest( - "posts", - "doc", - "1") + DeleteRequest deleteRequest = new DeleteRequest.Builder() + .index("posts") + .id("1") + .build() - DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT) + DeleteResponse deleteResponse = client.delete(deleteRequest) then: - deleteResponse.getIndex() == "posts" + deleteResponse.index() == "posts" cleanup: applicationContext.close() - container.stop() } } diff --git a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicatorSpec.groovy b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicatorSpec.groovy similarity index 72% rename from elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicatorSpec.groovy rename to elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicatorSpec.groovy index be5b4715..6b13a592 100644 --- a/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/ElasticsearchHealthIndicatorSpec.groovy +++ b/elasticsearch/src/test/groovy/io/micronaut/elasticsearch/health/ElasticsearchClientHealthIndicatorSpec.groovy @@ -26,7 +26,9 @@ import org.apache.http.auth.AuthScope import org.apache.http.auth.UsernamePasswordCredentials import org.apache.http.client.CredentialsProvider import org.apache.http.impl.client.BasicCredentialsProvider +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy import org.testcontainers.elasticsearch.ElasticsearchContainer +import org.testcontainers.spock.Testcontainers import spock.lang.Requires import spock.lang.Specification import reactor.core.publisher.Flux @@ -36,30 +38,30 @@ import reactor.core.publisher.Flux * @since 1.0.0 */ @Requires({ sys['elasticsearch.version'] }) -class ElasticsearchHealthIndicatorSpec extends Specification { +class ElasticsearchClientHealthIndicatorSpec extends Specification { final static String ELASTICSEARCH_VERSION = System.getProperty("elasticsearch.version") void "test elasticsearch health indicator"() { given: ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") + .withExposedPorts(9200) + .withEnv("xpack.security.enabled", "false") + .waitingFor(new LogMessageWaitStrategy().withRegEx(".*\"message\":\"started\".*")) container.start() - final CredentialsProvider credentialsProvider = new BasicCredentialsProvider() - credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme")) - - ApplicationContext applicationContext = ApplicationContext.run('elasticsearch.httpHosts': "http://${container.getHttpHostAddress()}") + ApplicationContext applicationContext = ApplicationContext.run('elasticsearch.httpHosts': "http://$container.httpHostAddress") expect: applicationContext.containsBean(DefaultElasticsearchConfigurationProperties) when: - ElasticsearchHealthIndicator indicator = applicationContext.getBean(ElasticsearchHealthIndicator) + ElasticsearchClientHealthIndicator indicator = applicationContext.getBean(ElasticsearchClientHealthIndicator) HealthResult result = Flux.from(indicator.getResult()).blockFirst() then: result.status == HealthStatus.UP - new JsonSlurper().parseText((String) result.details).status == "green" + new JsonSlurper().parseText((String) result.details).status == co.elastic.clients.elasticsearch._types.HealthStatus.Green.name().toLowerCase(Locale.ENGLISH) when: container.stop() @@ -68,20 +70,18 @@ class ElasticsearchHealthIndicatorSpec extends Specification { then: result.status == HealthStatus.DOWN - cleanup: applicationContext?.stop() } - void "test that ElasticsearchHealthIndicator is not created when the endpoints.health.elasticsearch.rest.high.level.enabled is set to false "() { + void "test that ElasticsearchClientHealthIndicator is not created when the endpoints.health.elasticsearch.rest.high.level.enabled is set to false "() { ApplicationContext applicationContext = ApplicationContext.run( 'elasticsearch.httpHosts': "http://localhost:9200", - 'endpoints.health.elasticsearch.rest.high.level.enabled': "false" - + 'endpoints.health.elasticsearch.enabled': "false" ) when: - applicationContext.getBean(ElasticsearchHealthIndicator) + applicationContext.getBean(ElasticsearchClientHealthIndicator) then: thrown(NoSuchBeanException) diff --git a/elasticsearch/src/test/resources/logback-test.xml b/elasticsearch/src/test/resources/logback-test.xml new file mode 100644 index 00000000..3d2478ba --- /dev/null +++ b/elasticsearch/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level [%thread] %logger{25} [%file:%line] - %msg%n + UTF-8 + + + + + + + + diff --git a/gradle.properties b/gradle.properties index a6219d8c..ae4f06ed 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,12 @@ -projectVersion=4.2.1-SNAPSHOT +projectVersion=5.0.0-SNAPSHOT projectGroup=io.micronaut.elasticsearch micronautDocsVersion=2.0.0 -micronautVersion=3.2.3 -micronautTestVersion=3.0.5 -groovyVersion=3.0.9 -spockVersion=2.0-groovy-3.0 +micronautVersion=3.4.3 +micronautTestVersion=3.1.1 +groovyVersion=3.0.10 +spockVersion=2.1-groovy-3.0 -elasticsearchVersion=7.16.3 +elasticsearchVersion=8.2.0 title=Micronaut Elasticsearch projectDesc=Integration between Micronaut and Elasticsearch @@ -14,7 +14,7 @@ projectUrl=https://micronaut.io githubSlug=micronaut-projects/micronaut-elasticsearch developers=Puneet Behl -githubCoreBranch=3.3.x +githubCoreBranch=4.0.x bomProperty=micronautElasticsearchVersion bomProperties=elasticsearchVersion diff --git a/src/main/docs/guide/configuration.adoc b/src/main/docs/guide/configuration.adoc index 9d598c95..b303a74d 100644 --- a/src/main/docs/guide/configuration.adoc +++ b/src/main/docs/guide/configuration.adoc @@ -2,18 +2,18 @@ [TIP] .Using the CLI ==== -If you are creating your project using the Micronaut CLI, supply the `elasticsearch` feature to configure the Elasticsearch Java REST Client in your project: +If you are creating your project using the Micronaut CLI, supply the `elasticsearch` feature to configure the https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html[ElasticsearchClient^] in your project: ---- $ mn create-app my-app --features elasticsearch ---- ==== -To configure the Elasticsearch Java REST Client you should first add `elasticsearch` module to your classpath: +To configure the https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html[ElasticsearchClient^] you should first add `elasticsearch` module to your classpath: [source,groovy] .build.gradle ---- -compile "io.micronaut.elasticsearch:micronaut-elasticsearch" +implementation "io.micronaut.elasticsearch:micronaut-elasticsearch" ---- You should then configure the `httpHosts` of the Elasticsearch server you wish to communicate with in `application.yml` as: @@ -27,7 +27,7 @@ elasticsearch: See the API for api:configuration.elasticsearch.DefaultElasticsearchConfigurationProperties[] for more information on the available configuration options. -Once you have the above configuration in place then you can inject the `co.elastic.clients.elasticsearch.ElasticsearchClient`, the `co.elastic.clients.elasticsearch.ElasticsearchAsyncClient`, the `org.elasticsearch.client.RestClient` or the deprecated `org.elasticsearch.client.RestHighLevelClient` bean. The following is the simplest way to get Elasticsearch information using the ElasticsearchClient: +Once you have the above configuration in place then you can inject the `co.elastic.clients.elasticsearch.ElasticsearchClient`, the `co.elastic.clients.elasticsearch.ElasticsearchAsyncClient` or the `org.elasticsearch.client.RestClient` bean. The following is the simplest way to get Elasticsearch information using the ElasticsearchClient: [source,groovy] ---- diff --git a/test-suite-groovy/build.gradle b/test-suite-groovy/build.gradle index 8da964dd..56be7e14 100644 --- a/test-suite-groovy/build.gradle +++ b/test-suite-groovy/build.gradle @@ -19,6 +19,8 @@ dependencies { // end::testcontainers-dependencies[] testImplementation projects.elasticsearch + + testRuntimeOnly mn.logback } tasks.named('test') { @@ -27,6 +29,6 @@ tasks.named('test') { } java { - sourceCompatibility = JavaVersion.toVersion('1.8') - targetCompatibility = JavaVersion.toVersion('1.8') + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/docs/configuration/elasticsearch/ElasticsearchSpec.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/docs/configuration/elasticsearch/ElasticsearchSpec.groovy index c967e2ce..9184be58 100644 --- a/test-suite-groovy/src/test/groovy/io/micronaut/docs/configuration/elasticsearch/ElasticsearchSpec.groovy +++ b/test-suite-groovy/src/test/groovy/io/micronaut/docs/configuration/elasticsearch/ElasticsearchSpec.groovy @@ -16,33 +16,31 @@ package io.micronaut.docs.configuration.elasticsearch -import io.micronaut.elasticsearch.DefaultElasticsearchConfigurationProperties +import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient +import co.elastic.clients.elasticsearch.ElasticsearchClient +import co.elastic.clients.elasticsearch.core.InfoResponse import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Replaces +import io.micronaut.elasticsearch.DefaultElasticsearchConfigurationProperties //tag::httpClientFactoryImports[] -import io.micronaut.context.annotation.Replaces + +import jakarta.inject.Singleton import org.apache.http.auth.AuthScope //end::httpClientFactoryImports[] -import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient -import co.elastic.clients.elasticsearch.ElasticsearchClient -import co.elastic.clients.elasticsearch.core.InfoResponse + import org.apache.http.auth.UsernamePasswordCredentials import org.apache.http.client.CredentialsProvider import org.apache.http.impl.client.BasicCredentialsProvider import org.apache.http.impl.nio.client.HttpAsyncClientBuilder -import org.elasticsearch.Version -import org.elasticsearch.client.RequestOptions -import org.elasticsearch.client.RestHighLevelClient -import org.elasticsearch.client.core.MainResponse +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy import org.testcontainers.elasticsearch.ElasticsearchContainer import spock.lang.Requires import spock.lang.Shared import spock.lang.Specification -import jakarta.inject.Singleton - //tag::singletonImports[] //end::singletonImports[] /** @@ -56,41 +54,24 @@ class ElasticsearchSpec extends Specification { final static String ELASTICSEARCH_VERSION = System.getProperty("elasticsearch.version") @Shared - ElasticsearchContainer elasticsearch = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") + static final ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION") + .withExposedPorts(9200) + .withEnv("xpack.security.enabled", "false") + .waitingFor(new LogMessageWaitStrategy().withRegEx(".*\"message\":\"started\".*")) // end::es-testcontainer[] + void setupSpec() { + container.start() + } - //tag::es-stats[] - void "Test simple info for Elasticsearch stats using the High Level REST Client"() { - given: - //tag::es-conf[] - elasticsearch.start() - ApplicationContext applicationContext = ApplicationContext.run("elasticsearch.httpHosts": "http://${elasticsearch.getHttpHostAddress()}", "test") - //end::es-conf - String stats - - when: - //tag::es-bean[] - RestHighLevelClient client = applicationContext.getBean(RestHighLevelClient) - //end::es-bean[] - MainResponse response = - client.info(RequestOptions.DEFAULT) // <1> - - then: - "docker-cluster" == response.getClusterName() - Version.fromString(ELASTICSEARCH_VERSION).toString() == response.getVersion().getNumber() - - cleanup: - applicationContext.close() - elasticsearch.stop() + void cleanupSpec() { + container.stop() } - //end::es-dbstats[] void "Test simple info for Elasticsearch stats using the ElasticsearchClient"() { given: - elasticsearch.start() - ApplicationContext applicationContext = ApplicationContext.run("elasticsearch.httpHosts": "http://${elasticsearch.getHttpHostAddress()}", "test") + ApplicationContext applicationContext = ApplicationContext.run("elasticsearch.httpHosts": "http://$container.httpHostAddress", "test") String stats when: @@ -102,17 +83,15 @@ class ElasticsearchSpec extends Specification { then: "docker-cluster" == response.clusterName() - Version.fromString(ELASTICSEARCH_VERSION).toString() == response.version().number() + ELASTICSEARCH_VERSION == response.version().number() cleanup: applicationContext.close() - elasticsearch.stop() } void "Test simple info for Elasticsearch stats using the ElasticsearchAsyncClient"() { given: - elasticsearch.start() - ApplicationContext applicationContext = ApplicationContext.run("elasticsearch.httpHosts": "http://${elasticsearch.getHttpHostAddress()}", "test") + ApplicationContext applicationContext = ApplicationContext.run("elasticsearch.httpHosts": "http://$container.httpHostAddress", "test") String stats when: @@ -122,11 +101,10 @@ class ElasticsearchSpec extends Specification { then: "docker-cluster" == response.clusterName() - Version.fromString(ELASTICSEARCH_VERSION).toString() == response.version().number() + ELASTICSEARCH_VERSION == response.version().number() cleanup: applicationContext.close() - elasticsearch.stop() } void "Test overiding HttpAsyncClientBuilder bean"() { diff --git a/test-suite-groovy/src/test/resources/logback-test.xml b/test-suite-groovy/src/test/resources/logback-test.xml new file mode 100644 index 00000000..3d2478ba --- /dev/null +++ b/test-suite-groovy/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level [%thread] %logger{25} [%file:%line] - %msg%n + UTF-8 + + + + + + + + diff --git a/test-suite-groovy/src/test/resources/logback.xml b/test-suite-groovy/src/test/resources/logback.xml deleted file mode 100644 index da3fbd21..00000000 --- a/test-suite-groovy/src/test/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - \ No newline at end of file