diff --git a/docs/src/main/asciidoc/redis-reference.adoc b/docs/src/main/asciidoc/redis-reference.adoc index 978b9642b93f9..48fc114179c11 100644 --- a/docs/src/main/asciidoc/redis-reference.adoc +++ b/docs/src/main/asciidoc/redis-reference.adoc @@ -639,6 +639,29 @@ The host provider can be used to configure the redis client like shown below quarkus.redis.hosts-provider-name=hosts-provider ---- +== Customizing the Redis options programmatically + +You can expose a bean implementing the `io.quarkus.redis.client.RedisOptionsCustomizer` interface to customize the Redis client options. +The bean is called for each configured Redis client: + +[source, java] +---- +@ApplicationScoped +public static class MyExampleCustomizer implements RedisOptionsCustomizer { + + @Override + public void customize(String clientName, RedisOptions options) { + if (clientName.equalsIgnoreCase("my-redis") + || clientName.equalsIgnoreCase(RedisConfig.DEFAULT_CLIENT_NAME)) { + // modify the given options + } else { + throw new IllegalStateException("Unknown client name: " + clientName); + } + } +} +---- + + === Dev Services See link:redis-dev-services.adoc[Redis Dev Service]. diff --git a/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java b/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java index dcafca3d7ce0c..f1912459a5450 100644 --- a/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java +++ b/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/RedisClientProcessor.java @@ -33,6 +33,7 @@ import io.quarkus.redis.client.RedisClient; import io.quarkus.redis.client.RedisClientName; import io.quarkus.redis.client.RedisHostsProvider; +import io.quarkus.redis.client.RedisOptionsCustomizer; import io.quarkus.redis.client.reactive.ReactiveRedisClient; import io.quarkus.redis.runtime.client.RedisClientRecorder; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; @@ -89,8 +90,8 @@ List registerRedisClientName() { } @BuildStep - UnremovableBeanBuildItem makeHostsProviderUnremovable() { - return UnremovableBeanBuildItem.beanTypes(RedisHostsProvider.class); + UnremovableBeanBuildItem makeHostsProviderAndOptionsCustomizerUnremovable() { + return UnremovableBeanBuildItem.beanTypes(RedisHostsProvider.class, RedisOptionsCustomizer.class); } @BuildStep diff --git a/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/CustomizerTest.java b/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/CustomizerTest.java new file mode 100644 index 0000000000000..7b972edba2d92 --- /dev/null +++ b/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/CustomizerTest.java @@ -0,0 +1,62 @@ +package io.quarkus.redis.client.deployment; + +import java.util.List; +import java.util.UUID; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.assertj.core.api.Assertions; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.redis.client.RedisClientName; +import io.quarkus.redis.client.RedisOptionsCustomizer; +import io.quarkus.redis.runtime.client.config.RedisConfig; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.QuarkusTestResource; +import io.vertx.mutiny.redis.client.RedisAPI; +import io.vertx.redis.client.RedisOptions; + +@QuarkusTestResource(RedisTestResource.class) +public class CustomizerTest { + + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClass(MyCustomizer.class)) + .overrideConfigKey("quarkus.redis.hosts", "redis://wont-work") + .overrideConfigKey("quarkus.redis.my-redis.hosts", "redis://wont-work"); + + @Inject + RedisAPI api; + + @Inject + @RedisClientName("my-redis") + RedisAPI myapi; + + @Test + public void testCustomization() { + String key = UUID.randomUUID().toString(); + api.setAndAwait(List.of(key, "test-" + key)); + String v = myapi.getAndAwait(key).toString(); + Assertions.assertThat(v).isEqualTo("test-" + key); + } + + @ApplicationScoped + public static class MyCustomizer implements RedisOptionsCustomizer { + + @Override + public void customize(String clientName, RedisOptions options) { + String v = ConfigProviderResolver.instance().getConfig().getValue("quarkus.redis.tr", String.class); + if (clientName.equalsIgnoreCase("my-redis") + || clientName.equalsIgnoreCase(RedisConfig.DEFAULT_CLIENT_NAME)) { + options.setEndpoints(List.of(v)); + } else { + throw new IllegalStateException("Unknown client name: " + clientName); + } + } + } +} diff --git a/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/RedisTestResource.java b/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/RedisTestResource.java index 891178d99de60..142008d8eb148 100644 --- a/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/RedisTestResource.java +++ b/extensions/redis-client/deployment/src/test/java/io/quarkus/redis/client/deployment/RedisTestResource.java @@ -16,11 +16,15 @@ public class RedisTestResource implements QuarkusTestResourceLifecycleManager { @Override public Map start() { server.start(); - return Map.of("quarkus.redis.tr", "redis://" + server.getHost() + ":" + server.getMappedPort(6379)); + return Map.of("quarkus.redis.tr", getEndpoint()); } @Override public void stop() { server.stop(); } + + public static String getEndpoint() { + return String.format("redis://%s:%s", server.getHost(), server.getMappedPort(6379)); + } } diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/RedisOptionsCustomizer.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/RedisOptionsCustomizer.java new file mode 100644 index 0000000000000..c6cfeb1fdb4bd --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/RedisOptionsCustomizer.java @@ -0,0 +1,22 @@ +package io.quarkus.redis.client; + +import io.vertx.redis.client.RedisOptions; + +/** + * Beans exposing the {@code RedisClientOptionsCustomizer} interface has the possibility to extend/modify the + * {@link io.vertx.redis.client.RedisOptions} before they are used to create the {@code RedisClient} or + * {@code RedisDataSource}. + */ +public interface RedisOptionsCustomizer { + + /** + * Allows customizing the options for the client named {@code clientName}. + * The passed {@code options} must be modified in-place. + * + * @param clientName the client name, {@link io.quarkus.redis.runtime.client.config.RedisConfig#DEFAULT_CLIENT_NAME} + * for the default client. + * @param options the options. + */ + void customize(String clientName, RedisOptions options); + +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/RedisClientRecorder.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/RedisClientRecorder.java index eb5f55329ad83..ff7b3eb4f5ec0 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/RedisClientRecorder.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/RedisClientRecorder.java @@ -31,9 +31,8 @@ public class RedisClientRecorder { // Split client and DS recorders private final RedisConfig config; - private final Map clients = new HashMap<>(); - - private final Map dataSources = new HashMap<>(); + private static final Map clients = new HashMap<>(); + private static final Map dataSources = new HashMap<>(); public RedisClientRecorder(RedisConfig rc) { this.config = rc; @@ -44,6 +43,14 @@ public void initialize(RuntimeValue vertx, Set name _initialize(v, names); } + private void closeAllClients() { + for (Map.Entry entry : clients.entrySet()) { + entry.getValue().redis.close(); + } + clients.clear(); + dataSources.clear(); + } + public void _initialize(Vertx vertx, Set names) { for (String name : names) { // Search if we have an associated config: @@ -63,9 +70,11 @@ public ConfigurationException get() { "You must at least configure `quarkus.redis." + name + ".hosts`."); } }); - clients.computeIfAbsent(name, x -> new RedisClientAndApi(VertxRedisClientFactory.create(vertx, actualConfig))); + clients.computeIfAbsent(name, + x -> new RedisClientAndApi(VertxRedisClientFactory.create(name, vertx, actualConfig))); } else if (DEFAULT_CLIENT_NAME.equalsIgnoreCase(name) && maybe.isPresent()) { - clients.computeIfAbsent(name, x -> new RedisClientAndApi(VertxRedisClientFactory.create(vertx, maybe.get()))); + clients.computeIfAbsent(name, + x -> new RedisClientAndApi(VertxRedisClientFactory.create(DEFAULT_CLIENT_NAME, vertx, maybe.get()))); } // Do not throw an error. We would need to check if the default redis client is used. } @@ -188,6 +197,7 @@ public void run() { value.redis.close(); } clients.clear(); + dataSources.clear(); } }); } diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java index e77212f666e82..fa61f05dd122b 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java @@ -14,7 +14,9 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InjectableInstance; +import io.quarkus.arc.InstanceHandle; import io.quarkus.redis.client.RedisHostsProvider; +import io.quarkus.redis.client.RedisOptionsCustomizer; import io.quarkus.redis.runtime.client.config.NetConfig; import io.quarkus.redis.runtime.client.config.RedisClientConfig; import io.quarkus.redis.runtime.client.config.TlsConfig; @@ -38,7 +40,7 @@ private VertxRedisClientFactory() { // Avoid direct instantiation. } - public static Redis create(Vertx vertx, RedisClientConfig config) { + public static Redis create(String name, Vertx vertx, RedisClientConfig config) { RedisOptions options = new RedisOptions(); List hosts = new ArrayList<>(); @@ -81,9 +83,21 @@ public static Redis create(Vertx vertx, RedisClientConfig config) { config.replicas.ifPresent(options::setUseReplicas); options.setNetClientOptions(toNetClientOptions(config)); + + customize(name, options); + return Redis.createClient(vertx, options); } + private static void customize(String name, RedisOptions options) { + if (Arc.container() != null) { + List> customizers = Arc.container().listAll(RedisOptionsCustomizer.class); + for (InstanceHandle customizer : customizers) { + customizer.get().customize(name, options); + } + } + } + private static NetClientOptions toNetClientOptions(RedisClientConfig config) { NetConfig tcp = config.tcp; TlsConfig tls = config.tls;