Skip to content

Commit 68e8081

Browse files
committed
Add a way to deactivate various Elasticsearch clients
1 parent fa5710d commit 68e8081

File tree

11 files changed

+278
-16
lines changed

11 files changed

+278
-16
lines changed

docs/src/main/asciidoc/elasticsearch.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,15 @@ Running it is as simple as executing `./target/elasticsearch-low-level-client-qu
672672

673673
You can then point your browser to `http://localhost:8080/fruits.html` and use your application.
674674

675+
[[client-active]]
676+
=== Activate/deactivate clients
677+
678+
In some cases it may be useful to deactivate some of the REST clients detected at build time.
679+
To deactivate a client at runtime, set the `quarkus.elasticsearch[.optional client name].active` (for the low-level Elasticsearch REST client)
680+
or `quarkus.elasticsearch-java[.optional client name].active` (for the Java Elasticsearch client) to `false`.
681+
Deactivating a low-level Elasticsearch REST client automatically deactivate the corresponding Java Elasticsearch client,
682+
since it is using the low-level one as its transport for communication with the cluster.
683+
675684
== Conclusion
676685

677686
Accessing an Elasticsearch cluster from the low level REST client or the Elasticsearch Java client is easy with Quarkus as it provides easy configuration, CDI integration and native support for it.

extensions/elasticsearch-java-client/deployment/pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
<groupId>io.quarkus</groupId>
3535
<artifactId>quarkus-elasticsearch-java-client</artifactId>
3636
</dependency>
37+
<!-- Test dependencies -->
38+
<dependency>
39+
<groupId>io.quarkus</groupId>
40+
<artifactId>quarkus-junit5-internal</artifactId>
41+
<scope>test</scope>
42+
</dependency>
3743
</dependencies>
3844

3945
<build>
@@ -55,8 +61,35 @@
5561
</execution>
5662
</executions>
5763
</plugin>
64+
<plugin>
65+
<artifactId>maven-surefire-plugin</artifactId>
66+
<configuration>
67+
<skip>true</skip>
68+
</configuration>
69+
</plugin>
5870
</plugins>
5971
</build>
6072

73+
<profiles>
74+
<profile>
75+
<id>test-elasticsearch</id>
76+
<activation>
77+
<property>
78+
<name>test-containers</name>
79+
</property>
80+
</activation>
81+
<build>
82+
<plugins>
83+
<plugin>
84+
<artifactId>maven-surefire-plugin</artifactId>
85+
<configuration>
86+
<skip>false</skip>
87+
</configuration>
88+
</plugin>
89+
</plugins>
90+
</build>
91+
</profile>
92+
</profiles>
93+
6194

6295
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.quarkus.elasticsearch.javaclient.deployment;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
import jakarta.inject.Inject;
8+
9+
import org.elasticsearch.client.RestClient;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.RegisterExtension;
12+
13+
import co.elastic.clients.elasticsearch.ElasticsearchClient;
14+
import io.quarkus.arc.Arc;
15+
import io.quarkus.arc.InjectableInstance;
16+
import io.quarkus.test.QuarkusUnitTest;
17+
import io.smallrye.common.annotation.Identifier;
18+
19+
public class ActiveInactiveClientTest {
20+
21+
@RegisterExtension
22+
static final QuarkusUnitTest test = new QuarkusUnitTest()
23+
.withApplicationRoot((jar) -> jar
24+
.addAsResource("active-inactive.properties", "application.properties")
25+
.addClass(SillyService.class));
26+
27+
// So that we reference the clients somewhere ...
28+
@ApplicationScoped
29+
public static class SillyService {
30+
@Inject
31+
InjectableInstance<RestClient> restClient1;
32+
@Inject
33+
@Identifier("client2")
34+
InjectableInstance<RestClient> restClient2;
35+
@Inject
36+
@Identifier("client3")
37+
InjectableInstance<RestClient> restClient3;
38+
}
39+
40+
@Test
41+
void smoke() {
42+
// low level clients should be there as well
43+
assertTrue(Arc.container().select(RestClient.class).getHandle().getBean().isActive());
44+
assertFalse(
45+
Arc.container().select(RestClient.class, Identifier.Literal.of("client2")).getHandle().getBean().isActive());
46+
assertTrue(Arc.container().select(RestClient.class, Identifier.Literal.of("client3")).getHandle().getBean().isActive());
47+
// as the Java clients:
48+
assertTrue(Arc.container().select(ElasticsearchClient.class).getHandle().getBean().isActive());
49+
assertFalse(Arc.container().select(ElasticsearchClient.class, Identifier.Literal.of("client2")).getHandle().getBean()
50+
.isActive());
51+
assertFalse(Arc.container().select(ElasticsearchClient.class, Identifier.Literal.of("client3")).getHandle().getBean()
52+
.isActive());
53+
}
54+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
quarkus.elasticsearch.devservices.enabled=false
2+
3+
quarkus.elasticsearch.active=true
4+
quarkus.elasticsearch."client2".active=false
5+
quarkus.elasticsearch."client3".active=true
6+
7+
quarkus.elasticsearch-java.active=true
8+
quarkus.elasticsearch-java."client2".active=false
9+
quarkus.elasticsearch-java."client3".active=false

extensions/elasticsearch-java-client/runtime/src/main/java/io/quarkus/elasticsearch/javaclient/runtime/ElasticsearchJavaClientRecorder.java

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.quarkus.elasticsearch.javaclient.runtime;
22

3+
import java.util.Locale;
34
import java.util.function.Function;
45
import java.util.function.Supplier;
56

@@ -14,11 +15,24 @@
1415
import co.elastic.clients.transport.rest_client.RestClientTransport;
1516
import io.quarkus.arc.ActiveResult;
1617
import io.quarkus.arc.SyntheticCreationalContext;
18+
import io.quarkus.elasticsearch.restclient.common.runtime.ElasticsearchClientBeanUtil;
19+
import io.quarkus.elasticsearch.restclient.lowlevel.runtime.ElasticsearchClientRuntimeConfig;
20+
import io.quarkus.elasticsearch.restclient.lowlevel.runtime.ElasticsearchClientsRuntimeConfig;
21+
import io.quarkus.runtime.RuntimeValue;
1722
import io.quarkus.runtime.annotations.Recorder;
1823

1924
@Recorder
2025
public class ElasticsearchJavaClientRecorder {
2126

27+
private final RuntimeValue<ElasticsearchJavaClientsRuntimeConfig> runtimeConfig;
28+
private final RuntimeValue<ElasticsearchClientsRuntimeConfig> lowLevelRuntimeConfig;
29+
30+
public ElasticsearchJavaClientRecorder(RuntimeValue<ElasticsearchJavaClientsRuntimeConfig> runtimeConfig,
31+
RuntimeValue<ElasticsearchClientsRuntimeConfig> lowLevelRuntimeConfig) {
32+
this.runtimeConfig = runtimeConfig;
33+
this.lowLevelRuntimeConfig = lowLevelRuntimeConfig;
34+
}
35+
2236
public Function<SyntheticCreationalContext<ElasticsearchTransport>, ElasticsearchTransport> elasticsearchTransportSupplier(
2337
String clientName) {
2438
return new Function<SyntheticCreationalContext<ElasticsearchTransport>, ElasticsearchTransport>() {
@@ -53,11 +67,40 @@ public ElasticsearchAsyncClient apply(SyntheticCreationalContext<ElasticsearchAs
5367
};
5468
}
5569

56-
public Supplier<ActiveResult> checkActiveHealthCheckSupplier(String clientName) {
57-
return ActiveResult::active;
70+
public Supplier<ActiveResult> checkActiveElasticsearchTransportSupplier(String clientName) {
71+
return () -> {
72+
ElasticsearchClientRuntimeConfig lowLevelClient = lowLevelRuntimeConfig.getValue().clients().get(clientName);
73+
ElasticsearchJavaClientRuntimeConfig javaClient = runtimeConfig.getValue().clients().get(clientName);
74+
if (!lowLevelClient.active()) {
75+
if (javaClient.active().isPresent() && javaClient.active().get()) {
76+
throw new IllegalStateException("Elasticsearch Java client [" + clientName + "] is misconfigured. "
77+
+ "Explicitly enabling this Elasticsearch Java client, "
78+
+ "while its corresponding low-level Elasticsearch REST client is deactivated ("
79+
+ enableKey("quarkus.elasticsearch-java.", clientName) + "=false) "
80+
+ "is not allowed.");
81+
}
82+
return ActiveResult.inactive("Elasticsearch Java client [" + clientName + "] is not active, " +
83+
"because the corresponding low-level REST client is deactivated through the configuration properties. "
84+
+ "To activate this client, make sure that both "
85+
+ enableKey("quarkus.elasticsearch-java.", clientName) + " and "
86+
+ enableKey("quarkus.elasticsearch.", clientName)
87+
+ " are set to true either implicitly (their default values) or explicitly.");
88+
}
89+
if (!javaClient.active().orElse(true)) {
90+
return ActiveResult.inactive("Elasticsearch Java client [" + clientName + "] is not active, "
91+
+ "because it is deactivated through the configuration properties. "
92+
+ "To activate this client, make sure that "
93+
+ enableKey("quarkus.elasticsearch-java.", clientName)
94+
+ " is set to true either implicitly (its default value) or explicitly.");
95+
}
96+
return ActiveResult.active();
97+
};
5898
}
5999

60-
public Supplier<ActiveResult> checkActiveElasticsearchTransportSupplier(String clientName) {
61-
return ActiveResult::active;
100+
static String enableKey(String radical, String client) {
101+
return String.format(
102+
Locale.ROOT, "%s%sactive",
103+
radical,
104+
ElasticsearchClientBeanUtil.isDefault(client) ? "" : "\"" + client + "\".");
62105
}
63106
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.quarkus.elasticsearch.javaclient.runtime;
2+
3+
import java.util.Optional;
4+
5+
import io.quarkus.runtime.annotations.ConfigDocDefault;
6+
import io.quarkus.runtime.annotations.ConfigGroup;
7+
8+
@ConfigGroup
9+
public interface ElasticsearchJavaClientRuntimeConfig {
10+
11+
/**
12+
* Defines whether the client should be active at runtime.
13+
*/
14+
@ConfigDocDefault("`true` if the underlying low-level Elasticsearch REST client is also active; `false` otherwise")
15+
Optional<Boolean> active();
16+
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.quarkus.elasticsearch.javaclient.runtime;
2+
3+
import java.util.Map;
4+
5+
import io.quarkus.elasticsearch.restclient.common.runtime.ElasticsearchClientBeanUtil;
6+
import io.quarkus.runtime.annotations.ConfigDocMapKey;
7+
import io.quarkus.runtime.annotations.ConfigPhase;
8+
import io.quarkus.runtime.annotations.ConfigRoot;
9+
import io.smallrye.config.ConfigMapping;
10+
import io.smallrye.config.WithDefaults;
11+
import io.smallrye.config.WithParentName;
12+
import io.smallrye.config.WithUnnamedKey;
13+
14+
@ConfigMapping(prefix = "quarkus.elasticsearch-java")
15+
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
16+
public interface ElasticsearchJavaClientsRuntimeConfig {
17+
18+
/**
19+
* Configuration for additional, named Elasticsearch Java clients.
20+
*/
21+
@ConfigDocMapKey("client-name")
22+
@WithParentName
23+
@WithDefaults
24+
@WithUnnamedKey(ElasticsearchClientBeanUtil.DEFAULT_ELASTICSEARCH_CLIENT_NAME)
25+
Map<String, ElasticsearchJavaClientRuntimeConfig> clients();
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.quarkus.elasticsearch.restclient.lowlevel.runtime;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
import jakarta.inject.Inject;
8+
9+
import org.elasticsearch.client.RestClient;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.RegisterExtension;
12+
13+
import io.quarkus.arc.Arc;
14+
import io.quarkus.arc.InjectableInstance;
15+
import io.quarkus.test.QuarkusUnitTest;
16+
import io.smallrye.common.annotation.Identifier;
17+
18+
public class ActiveInactiveClientTest {
19+
20+
@RegisterExtension
21+
static final QuarkusUnitTest test = new QuarkusUnitTest()
22+
.withApplicationRoot((jar) -> jar
23+
.addAsResource("active-inactive.properties", "application.properties")
24+
.addClass(SillyService.class));
25+
26+
// So that we reference the clients somewhere ...
27+
@ApplicationScoped
28+
public static class SillyService {
29+
@Inject
30+
InjectableInstance<RestClient> restClient1;
31+
@Inject
32+
@Identifier("client2")
33+
InjectableInstance<RestClient> restClient2;
34+
@Inject
35+
@Identifier("client3")
36+
InjectableInstance<RestClient> restClient3;
37+
}
38+
39+
@Test
40+
void smoke() {
41+
assertTrue(Arc.container().select(RestClient.class).getHandle().getBean().isActive());
42+
assertFalse(
43+
Arc.container().select(RestClient.class, Identifier.Literal.of("client2")).getHandle().getBean().isActive());
44+
assertTrue(Arc.container().select(RestClient.class, Identifier.Literal.of("client3")).getHandle().getBean().isActive());
45+
}
46+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
quarkus.elasticsearch.active=true
2+
quarkus.elasticsearch."client2".active=false
3+
quarkus.elasticsearch."client3".active=true

extensions/elasticsearch-rest-client/runtime/src/main/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/ElasticsearchClientRuntimeConfig.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
@ConfigGroup
1212
public interface ElasticsearchClientRuntimeConfig {
1313

14-
// TODO: MB#1 implement this ?
15-
// /**
16-
// * Defines if the client is enabled.
17-
// */
18-
// @WithDefault("true")
19-
// boolean enabled();
14+
/**
15+
* Defines whether the client should be active at runtime.
16+
*/
17+
@WithDefault("true")
18+
boolean active();
2019

2120
/**
2221
* The list of hosts of the Elasticsearch servers.

0 commit comments

Comments
 (0)