host();
+
+ /**
+ * Set proxy type.
+ * Accepted values are: {@code HTTP} (default), {@code SOCKS4} and {@code SOCKS5}.
+ */
+ @WithDefault("http")
+ ProxyType type();
+
+}
diff --git a/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/config/RedisConfig.java b/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/config/RedisConfig.java
new file mode 100644
index 0000000000..1af2377a34
--- /dev/null
+++ b/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/config/RedisConfig.java
@@ -0,0 +1,197 @@
+/**
+ * TODO.
+ */
+package org.eclipse.hono.deviceconnection.redis.client.config;
+
+import java.net.URI;
+import java.time.Duration;
+import java.util.Optional;
+import java.util.Set;
+
+import io.smallrye.config.ConfigMapping;
+import io.smallrye.config.WithDefault;
+import io.vertx.redis.client.RedisClientType;
+import io.vertx.redis.client.RedisReplicas;
+import io.vertx.redis.client.RedisRole;
+
+/**
+ * TODO.
+ */
+@SuppressWarnings("checkstyle:JavadocMethod")
+@ConfigMapping(prefix = "hono.cache.redis") //, namingStrategy = ConfigMapping.NamingStrategy.VERBATIM)
+public interface RedisConfig {
+
+ /**
+ * The redis hosts to use while connecting to the redis server. Only the cluster and sentinel modes will consider more than
+ * 1 element.
+ *
+ * The URI provided uses the following schema `redis://[username:password@][host][:port][/database]`
+ * Use `quarkus.redis.hosts-provider-name` to provide the hosts programmatically.
+ *
+ * @see Redis scheme on www.iana.org
+ */
+ Optional> hosts();
+
+ /**
+ * The hosts provider bean name.
+ *
+ * It is the {@code @Named} value of the hosts provider bean. It is used to discriminate if multiple
+ * `io.quarkus.redis.client.RedisHostsProvider` beans are available.
+ *
+ *
+ * Used when `quarkus.redis.hosts` is not set.
+ */
+ Optional hostsProviderName();
+
+ /**
+ * The maximum delay to wait before a blocking command to redis server times out.
+ */
+ @WithDefault("10s")
+ Duration timeout();
+
+ /**
+ * The redis client type.
+ * Accepted values are: {@code STANDALONE} (default), {@code CLUSTER}, {@code REPLICATION}, {@code SENTINEL}.
+ */
+ @WithDefault("standalone")
+ RedisClientType clientType();
+
+ /**
+ * The master name (only considered in HA mode).
+ */
+ Optional masterName();
+
+ /**
+ * The role name (only considered in Sentinel / HA mode).
+ * Accepted values are: {@code MASTER}, {@code REPLICA}, {@code SENTINEL}.
+ */
+ Optional role();
+
+ /**
+ * Whether to use replicas nodes (only considered in Cluster mode).
+ * Accepted values are: {@code ALWAYS}, {@code NEVER}, {@code SHARE}.
+ */
+ Optional replicas();
+
+ /**
+ * The default password for cluster/sentinel connections.
+ *
+ * If not set it will try to extract the value from the current default {@code #hosts}.
+ */
+ Optional password();
+
+ /**
+ * The maximum size of the connection pool. When working with cluster or sentinel.
+ *
+ * This value should be at least the total number of cluster member (or number of sentinels + 1)
+ */
+ @WithDefault("6")
+ int maxPoolSize();
+
+ /**
+ * The maximum waiting requests for a connection from the pool.
+ */
+ @WithDefault("24")
+ int maxPoolWaiting();
+
+ /**
+ * The duration indicating how often should the connection pool cleaner executes.
+ */
+ Optional poolCleanerInterval();
+
+ /**
+ * The timeout for a connection recycling.
+ */
+ @WithDefault("15")
+ Duration poolRecycleTimeout();
+
+ /**
+ * Sets how many handlers is the client willing to queue.
+ *
+ * The client will always work on pipeline mode, this means that messages can start queueing.
+ * Using this configuration option, you can control how much backlog you're willing to accept.
+ */
+ @WithDefault("2048")
+ int maxWaitingHandlers();
+
+ /**
+ * Tune how much nested arrays are allowed on a redis response. This affects the parser performance.
+ */
+ @WithDefault("32")
+ int maxNestedArrays();
+
+ /**
+ * The number of reconnection attempts when a pooled connection cannot be established on first try.
+ */
+ @WithDefault("0")
+ int reconnectAttempts();
+
+ /**
+ * The interval between reconnection attempts when a pooled connection cannot be established on first try.
+ */
+ @WithDefault("1")
+ Duration reconnectInterval();
+
+ /**
+ * Should the client perform {@code RESP} protocol negotiation during the connection handshake.
+ */
+ @WithDefault("true")
+ boolean protocolNegotiation();
+
+ /*
+ * The preferred protocol version to be used during protocol negotiation. When not set,
+ * defaults to RESP 3. When protocol negotiation is disabled, this setting has no effect.
+ */
+ //Optional preferredProtocolVersion();
+
+ /**
+ * The TTL of the hash slot cache. A hash slot cache is used by the clustered Redis client
+ * to prevent constantly sending {@code CLUSTER SLOTS} commands to the first statically
+ * configured cluster node.
+ *
+ * This setting is only meaningful in case of a clustered Redis client and has no effect
+ * otherwise.
+ */
+ @WithDefault("1s")
+ Duration hashSlotCacheTtl();
+
+ /**
+ * TCP config.
+ */
+ NetConfig tcp();
+
+ /**
+ * SSL/TLS config.
+ */
+ TlsConfig tls();
+
+ /**
+ * TODO.
+ * @return TODO.
+ */
+ default String toDebugString() {
+ return "RedisClientConfig{" +
+ "hosts=" + hosts() +
+ ", hostsProviderName=" + hostsProviderName() +
+ ", timeout=" + timeout() +
+ ", clientType=" + clientType() +
+ ", masterName=" + masterName() +
+ ", role=" + role() +
+ ", replicas=" + replicas() +
+ ", password=" + password() +
+ ", maxPoolSize=" + maxPoolSize() +
+ ", maxPoolWaiting=" + maxPoolWaiting() +
+ ", poolCleanerInterval=" + poolCleanerInterval() +
+ ", poolRecycleTimeout=" + poolRecycleTimeout() +
+ ", maxWaitingHandlers=" + maxWaitingHandlers() +
+ ", maxNestedArrays=" + maxNestedArrays() +
+ ", reconnectAttempts=" + reconnectAttempts() +
+ ", reconnectInterval=" + reconnectInterval() +
+ ", protocolNegotiation=" + protocolNegotiation() +
+ //", preferredProtocolVersion=" + preferredProtocolVersion() +
+ ", hashSlotCacheTtl=" + hashSlotCacheTtl() +
+ ", tcp=" + tcp() +
+ ", tls=" + tls() +
+ '}';
+ }
+}
diff --git a/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/config/TlsConfig.java b/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/config/TlsConfig.java
new file mode 100644
index 0000000000..bbbc652510
--- /dev/null
+++ b/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/config/TlsConfig.java
@@ -0,0 +1,214 @@
+/**
+ * TODO.
+ */
+package org.eclipse.hono.deviceconnection.redis.client.config;
+
+import java.util.List;
+import java.util.Optional;
+
+import io.quarkus.runtime.annotations.ConfigGroup;
+import io.quarkus.vertx.core.runtime.config.JksConfiguration;
+import io.quarkus.vertx.core.runtime.config.PemKeyCertConfiguration;
+import io.quarkus.vertx.core.runtime.config.PemTrustCertConfiguration;
+import io.quarkus.vertx.core.runtime.config.PfxConfiguration;
+import io.smallrye.config.WithDefault;
+import io.smallrye.config.WithParentName;
+
+/**
+ * TODO.
+ */
+@SuppressWarnings("checkstyle:JavadocMethod")
+@ConfigGroup
+public interface TlsConfig {
+
+ /**
+ * Whether SSL/TLS is enabled.
+ */
+ @WithDefault("false")
+ boolean enabled();
+
+ /**
+ * Enable trusting all certificates. Disabled by default.
+ */
+ @WithDefault("false")
+ boolean trustAll();
+
+ /**
+ * TODO.
+ */
+ interface PemTrustCertificate {
+ /**
+ * TODO.
+ */
+ @WithParentName
+ @WithDefault("false")
+ boolean enabled();
+
+ /**
+ * TODO.
+ */
+ Optional> certs();
+
+ /**
+ * TODO.
+ */
+ default PemTrustCertConfiguration convert() {
+ final PemTrustCertConfiguration trustCertificatePem = new PemTrustCertConfiguration();
+ trustCertificatePem.enabled = enabled();
+ trustCertificatePem.certs = certs();
+ return trustCertificatePem;
+ }
+ }
+
+ /**
+ * Trust configuration in the PEM format.
+ *
+ * When enabled, {@code #trust-certificate-jks} and {@code #trust-certificate-pfx} must be disabled.
+ */
+ PemTrustCertificate trustCertificatePem();
+
+ /**
+ * TODO.
+ */
+ interface Jks {
+ /**
+ * TODO.
+ */
+ @WithParentName
+ @WithDefault("false")
+ boolean enabled();
+
+ /**
+ * TODO.
+ */
+ Optional path();
+
+ /**
+ * TODO.
+ */
+ Optional password();
+
+ /**
+ * TODO.
+ */
+ default JksConfiguration convert() {
+ final JksConfiguration jks = new JksConfiguration();
+ jks.enabled = enabled();
+ jks.path = path();
+ jks.password = password();
+ return jks;
+ }
+ }
+
+ /**
+ * Trust configuration in the JKS format.
+ *
+ * When enabled, {@code #trust-certificate-pem} and {@code #trust-certificate-pfx} must be disabled.
+ */
+ Jks trustCertificateJks();
+
+ /**
+ * TODO.
+ */
+ interface Pfx {
+ /**
+ * TODO.
+ */
+ @WithParentName
+ @WithDefault("false")
+ boolean enabled();
+
+ /**
+ * TODO.
+ */
+ Optional path();
+
+ /**
+ * TODO.
+ */
+ Optional password();
+
+ /**
+ * TODO.
+ */
+ default PfxConfiguration convert() {
+ final PfxConfiguration jks = new PfxConfiguration();
+ jks.enabled = enabled();
+ jks.path = path();
+ jks.password = password();
+ return jks;
+ }
+ }
+
+ /**
+ * Trust configuration in the PFX format.
+ *
+ * When enabled, {@code #trust-certificate-jks} and {@code #trust-certificate-pem} must be disabled.
+ */
+ Pfx trustCertificatePfx();
+
+ /**
+ * TODO.
+ */
+ interface PemKeyCert {
+ /**
+ * TODO.
+ */
+ @WithParentName
+ @WithDefault("false")
+ boolean enabled();
+
+ /**
+ * TODO.
+ */
+ Optional> keys();
+
+ /**
+ * TODO.
+ */
+ Optional> certs();
+
+ /**
+ * TODO.
+ */
+ default PemKeyCertConfiguration convert() {
+ final PemKeyCertConfiguration pemKeyCert = new PemKeyCertConfiguration();
+ pemKeyCert.enabled = enabled();
+ pemKeyCert.keys = keys();
+ pemKeyCert.certs = certs();
+ return pemKeyCert;
+ }
+ }
+
+ /**
+ * Key/cert configuration in the PEM format.
+ *
+ * When enabled, {@code key-certificate-jks} and {@code #key-certificate-pfx} must be disabled.
+ */
+ PemKeyCert keyCertificatePem();
+
+ /**
+ * Key/cert configuration in the JKS format.
+ *
+ * When enabled, {@code #key-certificate-pem} and {@code #key-certificate-pfx} must be disabled.
+ */
+ Jks keyCertificateJks();
+
+ /**
+ * Key/cert configuration in the PFX format.
+ *
+ * When enabled, {@code key-certificate-jks} and {@code #key-certificate-pem} must be disabled.
+ */
+ Pfx keyCertificatePfx();
+
+ /**
+ * The hostname verification algorithm to use in case the server's identity should be checked.
+ * Should be {@code HTTPS}, {@code LDAPS} or an {@code NONE} (default).
+ *
+ * If set to {@code NONE}, it does not verify the hostname.
+ *
+ */
+ @WithDefault("NONE")
+ String hostnameVerificationAlgorithm();
+
+}
diff --git a/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/vertx/VertxRedisClientFactory.java b/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/vertx/VertxRedisClientFactory.java
new file mode 100644
index 0000000000..31866c4c66
--- /dev/null
+++ b/client-device-connection-redis/src/main/java/org/eclipse/hono/deviceconnection/redis/client/vertx/VertxRedisClientFactory.java
@@ -0,0 +1,153 @@
+/**
+ * TODO.
+ */
+package org.eclipse.hono.deviceconnection.redis.client.vertx;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hono.deviceconnection.redis.client.config.NetConfig;
+import org.eclipse.hono.deviceconnection.redis.client.config.RedisConfig;
+import org.eclipse.hono.deviceconnection.redis.client.config.TlsConfig;
+
+import io.quarkus.runtime.configuration.ConfigurationException;
+import io.quarkus.vertx.core.runtime.SSLConfigHelper;
+import io.vertx.core.Vertx;
+import io.vertx.core.net.NetClientOptions;
+import io.vertx.core.net.ProxyOptions;
+import io.vertx.redis.client.Redis;
+import io.vertx.redis.client.RedisClientType;
+import io.vertx.redis.client.RedisOptions;
+
+/**
+ * Creates Vert.x Redis client for a given {@link RedisConfig}.
+ */
+public class VertxRedisClientFactory {
+
+ public static final String CLIENT_NAME = "hono-deviceconnection";
+
+ private VertxRedisClientFactory() {
+ // Avoid direct instantiation.
+ }
+
+ /**
+ * TODO.
+ * @param vertx TODO.
+ * @param config TODO.
+ * @return TODO.
+ * @throws ConfigurationException TODO.
+ */
+ public static Redis create(final Vertx vertx, final RedisConfig config) throws ConfigurationException {
+ final RedisOptions options = new RedisOptions();
+
+ final List hosts;
+ if (config.hosts().isPresent()) {
+ hosts = new ArrayList<>(config.hosts().get());
+ for (URI uri : config.hosts().get()) {
+ options.addConnectionString(uri.toString().trim());
+ }
+ } else {
+ throw new ConfigurationException("Redis host not configured");
+ }
+
+ if (RedisClientType.STANDALONE == config.clientType()) {
+ if (hosts.size() > 1) {
+ throw new ConfigurationException("Multiple Redis hosts supplied for non-clustered configuration");
+ }
+ }
+
+ config.masterName().ifPresent(options::setMasterName);
+ options.setMaxNestedArrays(config.maxNestedArrays());
+ options.setMaxPoolSize(config.maxPoolSize());
+ options.setMaxPoolWaiting(config.maxPoolWaiting());
+ options.setMaxWaitingHandlers(config.maxWaitingHandlers());
+
+ options.setProtocolNegotiation(config.protocolNegotiation());
+ //config.preferredProtocolVersion().ifPresent(options::setPreferredProtocolVersion);
+ options.setPassword(config.password().orElse(null));
+ config.poolCleanerInterval().ifPresent(d -> options.setPoolCleanerInterval((int) d.toMillis()));
+ options.setPoolRecycleTimeout((int) config.poolRecycleTimeout().toMillis());
+ options.setHashSlotCacheTTL(config.hashSlotCacheTtl().toMillis());
+
+ config.role().ifPresent(options::setRole);
+ options.setType(config.clientType());
+ config.replicas().ifPresent(options::setUseReplicas);
+
+ options.setNetClientOptions(toNetClientOptions(config));
+
+ options.setPoolName(CLIENT_NAME);
+ // Use the convention defined by Quarkus Micrometer Vert.x metrics to create metrics prefixed with redis.
+ // and the client_name as tag.
+ // See io.quarkus.micrometer.runtime.binder.vertx.VertxMeterBinderAdapter.extractPrefix and
+ // io.quarkus.micrometer.runtime.binder.vertx.VertxMeterBinderAdapter.extractClientName
+ options.getNetClientOptions().setMetricsName("redis|" + CLIENT_NAME);
+
+ return Redis.createClient(vertx, options);
+ }
+
+ private static NetClientOptions toNetClientOptions(final RedisConfig config) {
+ final NetConfig tcp = config.tcp();
+ final TlsConfig tls = config.tls();
+ final NetClientOptions net = new NetClientOptions();
+
+ tcp.alpn().ifPresent(net::setUseAlpn);
+ tcp.applicationLayerProtocols().ifPresent(net::setApplicationLayerProtocols);
+ tcp.connectionTimeout().ifPresent(d -> net.setConnectTimeout((int) d.toMillis()));
+
+ final String verificationAlgorithm = tls.hostnameVerificationAlgorithm();
+ if ("NONE".equalsIgnoreCase(verificationAlgorithm)) {
+ net.setHostnameVerificationAlgorithm("");
+ } else {
+ net.setHostnameVerificationAlgorithm(verificationAlgorithm);
+ }
+
+ tcp.idleTimeout().ifPresent(d -> net.setIdleTimeout((int) d.toSeconds()));
+
+ tcp.keepAlive().ifPresent(b -> net.setTcpKeepAlive(true));
+ tcp.noDelay().ifPresent(b -> net.setTcpNoDelay(true));
+
+ net.setSsl(tls.enabled()).setTrustAll(tls.trustAll());
+
+ SSLConfigHelper.configurePemTrustOptions(net, tls.trustCertificatePem().convert());
+ SSLConfigHelper.configureJksTrustOptions(net, tls.trustCertificateJks().convert());
+ SSLConfigHelper.configurePfxTrustOptions(net, tls.trustCertificatePfx().convert());
+
+ SSLConfigHelper.configurePemKeyCertOptions(net, tls.keyCertificatePem().convert());
+ SSLConfigHelper.configureJksKeyCertOptions(net, tls.keyCertificateJks().convert());
+ SSLConfigHelper.configurePfxKeyCertOptions(net, tls.keyCertificatePfx().convert());
+
+ net.setReconnectAttempts(config.reconnectAttempts());
+ net.setReconnectInterval(config.reconnectInterval().toMillis());
+
+ tcp.localAddress().ifPresent(net::setLocalAddress);
+ tcp.nonProxyHosts().ifPresent(net::setNonProxyHosts);
+ if (tcp.proxyOptions().host().isPresent()) {
+ final ProxyOptions po = new ProxyOptions();
+ po.setHost(tcp.proxyOptions().host().get());
+ po.setType(tcp.proxyOptions().type());
+ po.setPort(tcp.proxyOptions().port());
+ tcp.proxyOptions().username().ifPresent(po::setUsername);
+ tcp.proxyOptions().password().ifPresent(po::setPassword);
+ net.setProxyOptions(po);
+ }
+ tcp.readIdleTimeout().ifPresent(d -> net.setReadIdleTimeout((int) d.toSeconds()));
+ tcp.reconnectAttempts().ifPresent(net::setReconnectAttempts);
+ tcp.reconnectInterval().ifPresent(v -> net.setReconnectInterval(v.toMillis()));
+ tcp.reuseAddress().ifPresent(net::setReuseAddress);
+ tcp.reusePort().ifPresent(net::setReusePort);
+ tcp.receiveBufferSize().ifPresent(net::setReceiveBufferSize);
+ tcp.sendBufferSize().ifPresent(net::setSendBufferSize);
+ tcp.soLinger().ifPresent(d -> net.setSoLinger((int) d.toMillis()));
+ tcp.secureTransportProtocols().ifPresent(net::setEnabledSecureTransportProtocols);
+ tcp.trafficClass().ifPresent(net::setTrafficClass);
+ tcp.noDelay().ifPresent(net::setTcpNoDelay);
+ tcp.cork().ifPresent(net::setTcpCork);
+ tcp.keepAlive().ifPresent(net::setTcpKeepAlive);
+ tcp.fastOpen().ifPresent(net::setTcpFastOpen);
+ tcp.quickAck().ifPresent(net::setTcpQuickAck);
+ tcp.writeIdleTimeout().ifPresent(d -> net.setWriteIdleTimeout((int) d.toSeconds()));
+
+ return net;
+ }
+}
diff --git a/client-device-connection-redis/src/main/resources/META-INF/native-image/org.eclipse.hono/client-device-connection-infinispan/native-image.properties b/client-device-connection-redis/src/main/resources/META-INF/native-image/org.eclipse.hono/client-device-connection-infinispan/native-image.properties
new file mode 100644
index 0000000000..2879f33e15
--- /dev/null
+++ b/client-device-connection-redis/src/main/resources/META-INF/native-image/org.eclipse.hono/client-device-connection-infinispan/native-image.properties
@@ -0,0 +1,15 @@
+# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+Args = -H:ResourceConfigurationResources=${.}/resources-config.json \
+ -H:AdditionalSecurityProviders=org.wildfly.security.sasl.digest.WildFlyElytronSaslDigestProvider \
+ -H:AdditionalSecurityProviders=org.wildfly.security.sasl.external.WildFlyElytronSaslExternalProvider \
+ -H:AdditionalSecurityProviders=org.wildfly.security.sasl.plain.WildFlyElytronSaslPlainProvider \
+ -H:AdditionalSecurityProviders=org.wildfly.security.sasl.scram.WildFlyElytronSaslScramProvider
diff --git a/client-device-connection-redis/src/main/resources/META-INF/native-image/org.eclipse.hono/client-device-connection-infinispan/resources-config.json b/client-device-connection-redis/src/main/resources/META-INF/native-image/org.eclipse.hono/client-device-connection-infinispan/resources-config.json
new file mode 100644
index 0000000000..ef9f0db4ab
--- /dev/null
+++ b/client-device-connection-redis/src/main/resources/META-INF/native-image/org.eclipse.hono/client-device-connection-infinispan/resources-config.json
@@ -0,0 +1,37 @@
+{
+ "resources": {
+ "includes": [
+ {
+ "pattern": ".*\\.properties$"
+ },
+ {
+ "pattern": ".*\\.proto$"
+ },
+ {
+ "pattern": "default-configs\\/.*$"
+ },
+ {
+ "pattern": "META-INF\\/services\\/java\\.security\\.Provider"
+ },
+ {
+ "pattern": "META-INF\\/services\\/javax\\.security\\.sasl\\.SaslClientFactory"
+ },
+ {
+ "pattern": "META-INF\\/services\\/org\\.infinispan\\.configuration\\.parsing\\.ConfigurationParser"
+ },
+ {
+ "pattern": "META-INF\\/services\\/org\\.infinispan\\.factories\\.impl\\..*$"
+ },
+ {
+ "pattern": "META-INF\\/services\\/org\\.infinispan\\.protostream\\..*$"
+ }
+ ],
+ "excludes": [
+ {
+ "pattern": "META-INF\\/maven\\/.*$"
+ },{
+ "pattern": "META-INF\\/native-image\\/.*$"
+ }
+ ]
+ }
+}
diff --git a/client-device-connection-redis/src/main/resources/application.properties b/client-device-connection-redis/src/main/resources/application.properties
new file mode 100644
index 0000000000..7fce6fc1be
--- /dev/null
+++ b/client-device-connection-redis/src/main/resources/application.properties
@@ -0,0 +1,11 @@
+# Create a Jandex index of beans contained in Google Guava
+# This prevents warnings when building downstream modules that use for example
+# the com.google.common.base.MoreObjects$ToStringHelper method.
+quarkus.index-dependency.guava.group-id=com.google.guava
+quarkus.index-dependency.guava.artifact-id=guava
+# Create a Jandex index of beans contained in the Infinispan Hotrod client
+# This is necessary in order to be able to configure the Hotrod client by
+# means of the org.eclipse.hono.deviceconnection.infinispan.client.InfinispanRemoteConfigurationOptions
+# class.
+quarkus.index-dependency.infinispan.group-id=org.infinispan
+quarkus.index-dependency.infinispan.artifact-id=infinispan-client-hotrod
diff --git a/client-device-connection-redis/src/test/resources/logback-test.xml b/client-device-connection-redis/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..fb0c54d42d
--- /dev/null
+++ b/client-device-connection-redis/src/test/resources/logback-test.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client-device-connection-redis/src/test/resources/remote-cache-options.yaml b/client-device-connection-redis/src/test/resources/remote-cache-options.yaml
new file mode 100644
index 0000000000..19b061cf42
--- /dev/null
+++ b/client-device-connection-redis/src/test/resources/remote-cache-options.yaml
@@ -0,0 +1,34 @@
+hono:
+ cache:
+ infinispan:
+ serverList: "data-grid:11222"
+ authServerName: "data-grid"
+ authUsername: "user"
+ authPassword: "secret"
+ authRealm: "ApplicationRealm"
+ cluster:
+ siteA: "hostA1:11222; hostA2:11223"
+ siteB: "hostB1:11222; hostB2:11223"
+ connectionPool:
+ minIdle: 10
+ maxActive: 10
+ maxPendingRequests: 400
+ maxWait: 500
+ defaultExecutorFactory:
+ poolSize: 200
+ saslMechanism: "DIGEST-MD5"
+ saslProperties:
+ "javax.security.sasl.qop": "auth"
+ socketTimeout: 5000
+ connectTimeout: 5000
+ keyStoreFileName: "/etc/hono/key-store.p12"
+ keyStoreType: "PKCS12"
+ keyStorePassword: "key-store-secret"
+ keyAlias: "infinispan"
+ keyStoreCertificatePassword: "cert-secret"
+ trustStorePath: "/etc/hono/trust-store.p12"
+ trustStoreFileName: "/etc/hono/trust-store-file.p12"
+ trustStoreType: "PKCS12"
+ trustStorePassword: "trust-store-secret"
+ useSsl: true
+ sslCiphers: "TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256"
diff --git a/client-device-connection/pom.xml b/client-device-connection/pom.xml
new file mode 100644
index 0000000000..6a3848e74f
--- /dev/null
+++ b/client-device-connection/pom.xml
@@ -0,0 +1,164 @@
+
+
+
+ 4.0.0
+
+ org.eclipse.hono
+ hono-bom
+ 2.6.0-SNAPSHOT
+ ../bom
+
+ client-device-connection
+
+ Device Connection client
+ Base classes for client for accessing device connection information in a remote cache / data grid.
+
+
+
+ org.eclipse.hono
+ hono-legal
+
+
+ org.eclipse.hono
+ hono-core
+
+
+ org.eclipse.hono
+ hono-client-common
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ io.opentracing
+ opentracing-api
+
+
+
+ io.smallrye.config
+ smallrye-config-core
+
+
+
+ io.vertx
+ vertx-core
+
+
+
+ io.vertx
+ vertx-health-check
+
+
+
+ com.google.guava
+ guava
+
+
+ io.quarkus
+ quarkus-core
+ true
+
+
+ org.jboss.logmanager
+ jboss-logmanager-embedded
+
+
+ org.jboss.logging
+ jboss-logging-annotations
+
+
+ io.quarkus
+ quarkus-development-mode-spi
+
+
+ io.quarkus
+ quarkus-bootstrap-runner
+
+
+ org.jboss.slf4j
+ slf4j-jboss-logmanager
+
+
+ org.graalvm.sdk
+ graal-sdk
+
+
+ io.quarkus
+ quarkus-fs-util
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ com.google.truth
+ truth
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ io.vertx
+ vertx-junit5
+ test
+
+
+ org.eclipse.hono
+ core-test-utils
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ org.jboss.jandex
+ jandex-maven-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+
+
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/AdapterInstanceStatusProvider.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/AdapterInstanceStatusProvider.java
similarity index 97%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/AdapterInstanceStatusProvider.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/AdapterInstanceStatusProvider.java
index 2c98c8a2c0..64a18cf0f6 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/AdapterInstanceStatusProvider.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/AdapterInstanceStatusProvider.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import java.util.Collection;
import java.util.Set;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/Cache.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/Cache.java
similarity index 98%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/Cache.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/Cache.java
index eec68f8f7c..42d28a015c 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/Cache.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/Cache.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import java.util.Map;
import java.util.Set;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CacheBasedDeviceConnectionInfo.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CacheBasedDeviceConnectionInfo.java
similarity index 99%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CacheBasedDeviceConnectionInfo.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CacheBasedDeviceConnectionInfo.java
index 21f3bd28a7..caa3e24172 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CacheBasedDeviceConnectionInfo.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CacheBasedDeviceConnectionInfo.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import java.net.HttpURLConnection;
import java.time.Duration;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CommonCacheConfig.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CommonCacheConfig.java
similarity index 97%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CommonCacheConfig.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CommonCacheConfig.java
index ec4109424c..c969ab3c49 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CommonCacheConfig.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CommonCacheConfig.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import com.google.common.base.MoreObjects;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CommonCacheOptions.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CommonCacheOptions.java
similarity index 95%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CommonCacheOptions.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CommonCacheOptions.java
index 8118a198f5..bbd69c036d 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/CommonCacheOptions.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/CommonCacheOptions.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import org.eclipse.hono.util.CommandRouterConstants;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/DeviceConnectionInfo.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/DeviceConnectionInfo.java
similarity index 99%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/DeviceConnectionInfo.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/DeviceConnectionInfo.java
index 4b7e800298..f3c2d2a35c 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/DeviceConnectionInfo.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/DeviceConnectionInfo.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import java.time.Duration;
import java.util.Map;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/DeviceToAdapterMappingErrorListener.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/DeviceToAdapterMappingErrorListener.java
similarity index 95%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/DeviceToAdapterMappingErrorListener.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/DeviceToAdapterMappingErrorListener.java
index bdb45e069b..0fa4d7fb95 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/DeviceToAdapterMappingErrorListener.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/DeviceToAdapterMappingErrorListener.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import io.opentracing.Span;
import io.vertx.core.Future;
diff --git a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/UnknownStatusProvider.java b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/UnknownStatusProvider.java
similarity index 94%
rename from client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/UnknownStatusProvider.java
rename to client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/UnknownStatusProvider.java
index d52468ce44..861ecfda79 100644
--- a/client-device-connection-infinispan/src/main/java/org/eclipse/hono/deviceconnection/infinispan/client/UnknownStatusProvider.java
+++ b/client-device-connection/src/main/java/org/eclipse/hono/deviceconnection/common/UnknownStatusProvider.java
@@ -12,7 +12,7 @@
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import java.util.Collection;
import java.util.Set;
diff --git a/client-device-connection-infinispan/src/test/java/org/eclipse/hono/deviceconnection/infinispan/client/CacheBasedDeviceConnectionInfoTest.java b/client-device-connection/src/test/java/org/eclipse/hono/deviceconnection/common/CacheBasedDeviceConnectionInfoTest.java
similarity index 99%
rename from client-device-connection-infinispan/src/test/java/org/eclipse/hono/deviceconnection/infinispan/client/CacheBasedDeviceConnectionInfoTest.java
rename to client-device-connection/src/test/java/org/eclipse/hono/deviceconnection/common/CacheBasedDeviceConnectionInfoTest.java
index 744858a2be..a6efb9126d 100644
--- a/client-device-connection-infinispan/src/test/java/org/eclipse/hono/deviceconnection/infinispan/client/CacheBasedDeviceConnectionInfoTest.java
+++ b/client-device-connection/src/test/java/org/eclipse/hono/deviceconnection/common/CacheBasedDeviceConnectionInfoTest.java
@@ -11,7 +11,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.hono.deviceconnection.infinispan.client;
+package org.eclipse.hono.deviceconnection.common;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
diff --git a/client-device-connection/src/test/java/org/eclipse/hono/deviceconnection/common/CommonCacheQuarkusPropertyBindingTest.java b/client-device-connection/src/test/java/org/eclipse/hono/deviceconnection/common/CommonCacheQuarkusPropertyBindingTest.java
new file mode 100644
index 0000000000..753711f531
--- /dev/null
+++ b/client-device-connection/src/test/java/org/eclipse/hono/deviceconnection/common/CommonCacheQuarkusPropertyBindingTest.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2021, 2022 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.eclipse.hono.deviceconnection.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.eclipse.hono.test.ConfigMappingSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests verifying binding of configuration properties to {@link CommonCacheConfig}.
+ *
+ */
+public class CommonCacheQuarkusPropertyBindingTest {
+
+ @Test
+ void testCommonCacheConfigurationPropertiesArePickedUp() {
+ final var commonCacheConfig = new CommonCacheConfig(
+ ConfigMappingSupport.getConfigMapping(
+ CommonCacheOptions.class,
+ this.getClass().getResource("/common-cache-options.yaml")));
+
+ assertThat(commonCacheConfig.getCacheName()).isEqualTo("the-cache");
+ assertThat(commonCacheConfig.getCheckKey()).isEqualTo("the-key");
+ assertThat(commonCacheConfig.getCheckValue()).isEqualTo("the-value");
+ }
+}
diff --git a/client-device-connection-infinispan/src/test/resources/common-cache-options.yaml b/client-device-connection/src/test/resources/common-cache-options.yaml
similarity index 100%
rename from client-device-connection-infinispan/src/test/resources/common-cache-options.yaml
rename to client-device-connection/src/test/resources/common-cache-options.yaml
diff --git a/client-device-connection/src/test/resources/logback-test.xml b/client-device-connection/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..42747dae3b
--- /dev/null
+++ b/client-device-connection/src/test/resources/logback-test.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index fdcafbacd4..56facc63da 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,7 +178,9 @@
bom
core
cli
+ client-device-connection
client-device-connection-infinispan
+ client-device-connection-redis
clients
demo-certs
examples
diff --git a/services/command-router/pom.xml b/services/command-router/pom.xml
index aeaa5a4738..fb530d9aab 100644
--- a/services/command-router/pom.xml
+++ b/services/command-router/pom.xml
@@ -24,10 +24,19 @@
A Quarkus based implementation of Hono's Command Router API that is using Infinispan for storing data.
+
+ org.eclipse.hono
+ client-device-connection
+
org.eclipse.hono
client-device-connection-infinispan
+
+ org.eclipse.hono
+ client-device-connection-redis
+
+
org.eclipse.hono
hono-client-command-amqp
diff --git a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/AdapterInstanceStatusService.java b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/AdapterInstanceStatusService.java
index bf3dec738a..16eb5653df 100644
--- a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/AdapterInstanceStatusService.java
+++ b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/AdapterInstanceStatusService.java
@@ -13,7 +13,7 @@
package org.eclipse.hono.commandrouter;
-import org.eclipse.hono.deviceconnection.infinispan.client.AdapterInstanceStatusProvider;
+import org.eclipse.hono.deviceconnection.common.AdapterInstanceStatusProvider;
import org.eclipse.hono.util.Lifecycle;
/**
diff --git a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/CommandTargetMapper.java b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/CommandTargetMapper.java
index d39fc6eb45..482efeb4da 100644
--- a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/CommandTargetMapper.java
+++ b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/CommandTargetMapper.java
@@ -15,7 +15,7 @@
import org.eclipse.hono.client.registry.DeviceRegistrationClient;
import org.eclipse.hono.commandrouter.impl.CommandTargetMapperImpl;
-import org.eclipse.hono.deviceconnection.infinispan.client.DeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.DeviceConnectionInfo;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
diff --git a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/Application.java b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/Application.java
index 81c1309fb8..44d98f5da5 100644
--- a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/Application.java
+++ b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/Application.java
@@ -61,7 +61,7 @@
import org.eclipse.hono.commandrouter.impl.pubsub.PubSubBasedCommandConsumerFactoryImpl;
import org.eclipse.hono.config.ServiceConfigProperties;
import org.eclipse.hono.config.ServiceOptions;
-import org.eclipse.hono.deviceconnection.infinispan.client.DeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.DeviceConnectionInfo;
import org.eclipse.hono.service.HealthCheckProvider;
import org.eclipse.hono.service.NotificationSupportingServiceApplication;
import org.eclipse.hono.service.amqp.AmqpEndpoint;
diff --git a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/DeviceConnectionInfoProducer.java b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/DeviceConnectionInfoProducer.java
index 07b1482bbb..971d013aea 100644
--- a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/DeviceConnectionInfoProducer.java
+++ b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/app/DeviceConnectionInfoProducer.java
@@ -14,6 +14,7 @@
package org.eclipse.hono.commandrouter.app;
+import io.quarkus.runtime.configuration.ConfigurationException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -23,15 +24,18 @@
import org.eclipse.hono.commandrouter.CommandRouterServiceOptions;
import org.eclipse.hono.commandrouter.impl.KubernetesBasedAdapterInstanceStatusService;
import org.eclipse.hono.commandrouter.impl.UnknownStatusProvidingService;
-import org.eclipse.hono.deviceconnection.infinispan.client.BasicCache;
-import org.eclipse.hono.deviceconnection.infinispan.client.CacheBasedDeviceConnectionInfo;
-import org.eclipse.hono.deviceconnection.infinispan.client.CommonCacheConfig;
-import org.eclipse.hono.deviceconnection.infinispan.client.CommonCacheOptions;
-import org.eclipse.hono.deviceconnection.infinispan.client.DeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.Cache;
+import org.eclipse.hono.deviceconnection.common.CacheBasedDeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.CommonCacheConfig;
+import org.eclipse.hono.deviceconnection.common.CommonCacheOptions;
+import org.eclipse.hono.deviceconnection.common.DeviceConnectionInfo;
import org.eclipse.hono.deviceconnection.infinispan.client.EmbeddedCache;
import org.eclipse.hono.deviceconnection.infinispan.client.HotrodCache;
import org.eclipse.hono.deviceconnection.infinispan.client.InfinispanRemoteConfigurationOptions;
import org.eclipse.hono.deviceconnection.infinispan.client.InfinispanRemoteConfigurationProperties;
+import org.eclipse.hono.deviceconnection.redis.client.RedisCacheVertx;
+import org.eclipse.hono.deviceconnection.redis.client.config.RedisConfig;
+import org.eclipse.hono.deviceconnection.redis.client.vertx.VertxRedisClientFactory;
import org.eclipse.hono.util.Strings;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
@@ -44,6 +48,7 @@
import io.opentracing.Tracer;
import io.smallrye.config.ConfigMapping;
import io.vertx.core.Vertx;
+import io.vertx.redis.client.RedisAPI;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Singleton;
@@ -64,35 +69,50 @@ public class DeviceConnectionInfoProducer {
@Produces
DeviceConnectionInfo deviceConnectionInfo(
- final BasicCache cache,
+ final Cache cache,
final Tracer tracer,
final AdapterInstanceStatusService adapterInstanceStatusService) {
return new CacheBasedDeviceConnectionInfo(cache, tracer, adapterInstanceStatusService);
}
@Produces
- BasicCache cache(
+ Cache cache(
final Vertx vertx,
@ConfigMapping(prefix = "hono.commandRouter.cache.common")
final CommonCacheOptions commonCacheOptions,
@ConfigMapping(prefix = "hono.commandRouter.cache.remote")
- final InfinispanRemoteConfigurationOptions remoteCacheConfigurationOptions) {
+ final InfinispanRemoteConfigurationOptions remoteCacheConfigurationOptions,
+ @ConfigMapping(prefix = "hono.commandRouter.cache.redis")
+ final RedisConfig redisCacheConfiguration
+ ) {
final var commonCacheConfig = new CommonCacheConfig(commonCacheOptions);
final var infinispanCacheConfig = new InfinispanRemoteConfigurationProperties(remoteCacheConfigurationOptions);
- if (Strings.isNullOrEmpty(infinispanCacheConfig.getServerList())) {
- LOG.info("configuring embedded cache");
- return new EmbeddedCache<>(
- vertx,
- embeddedCacheManager(commonCacheConfig),
- commonCacheConfig.getCacheName());
- } else {
+
+ if (!Strings.isNullOrEmpty(infinispanCacheConfig.getServerList()) &&
+ redisCacheConfiguration.hosts().isPresent()) {
+ throw new ConfigurationException(
+ "Both hotrod (remote) and redis cache configuration exists. Only one should be provided.");
+ }
+
+ if (!Strings.isNullOrEmpty(infinispanCacheConfig.getServerList())) {
LOG.info("configuring remote cache");
return HotrodCache.from(
vertx,
infinispanCacheConfig,
commonCacheConfig);
}
+
+ if (redisCacheConfiguration.hosts().isPresent()) {
+ LOG.info("configuring redis cache");
+ return RedisCacheVertx.from(RedisAPI.api(VertxRedisClientFactory.create(vertx, redisCacheConfiguration)));
+ }
+
+ LOG.info("configuring embedded cache");
+ return new EmbeddedCache<>(
+ vertx,
+ embeddedCacheManager(commonCacheConfig),
+ commonCacheConfig.getCacheName());
}
private EmbeddedCacheManager embeddedCacheManager(final CommonCacheConfig cacheConfig) {
diff --git a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImpl.java b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImpl.java
index 6d53adac9f..713ff373d6 100644
--- a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImpl.java
+++ b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImpl.java
@@ -38,7 +38,7 @@
import org.eclipse.hono.commandrouter.CommandRouterResult;
import org.eclipse.hono.commandrouter.CommandRouterService;
import org.eclipse.hono.config.ServiceConfigProperties;
-import org.eclipse.hono.deviceconnection.infinispan.client.DeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.DeviceConnectionInfo;
import org.eclipse.hono.service.HealthCheckProvider;
import org.eclipse.hono.tracing.TracingHelper;
import org.eclipse.hono.util.CommandConstants;
diff --git a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandTargetMapperImpl.java b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandTargetMapperImpl.java
index 17e60de2a5..1f4a863fc6 100644
--- a/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandTargetMapperImpl.java
+++ b/services/command-router/src/main/java/org/eclipse/hono/commandrouter/impl/CommandTargetMapperImpl.java
@@ -23,7 +23,7 @@
import org.eclipse.hono.client.registry.DeviceDisabledOrNotRegisteredException;
import org.eclipse.hono.client.registry.DeviceRegistrationClient;
import org.eclipse.hono.commandrouter.CommandTargetMapper;
-import org.eclipse.hono.deviceconnection.infinispan.client.DeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.DeviceConnectionInfo;
import org.eclipse.hono.tracing.TracingHelper;
import org.eclipse.hono.util.DeviceConnectionConstants;
import org.eclipse.hono.util.MessageHelper;
diff --git a/services/command-router/src/test/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImplTest.java b/services/command-router/src/test/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImplTest.java
index 929d2bf701..5fa82469be 100644
--- a/services/command-router/src/test/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImplTest.java
+++ b/services/command-router/src/test/java/org/eclipse/hono/commandrouter/impl/CommandRouterServiceImplTest.java
@@ -41,7 +41,7 @@
import org.eclipse.hono.client.util.MessagingClientProvider;
import org.eclipse.hono.commandrouter.CommandConsumerFactory;
import org.eclipse.hono.config.ServiceConfigProperties;
-import org.eclipse.hono.deviceconnection.infinispan.client.DeviceConnectionInfo;
+import org.eclipse.hono.deviceconnection.common.DeviceConnectionInfo;
import org.eclipse.hono.test.VertxMockSupport;
import org.eclipse.hono.util.CommandConstants;
import org.eclipse.hono.util.EventConstants;
diff --git a/tests/pom.xml b/tests/pom.xml
index b7466c33bc..c1b956fa05 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -466,6 +466,22 @@
350000000
+
+
+ redis_cache
+
+
+ hono.commandrouting.cache
+ redis
+
+
+
+ true
+ redis-cache
+ 350000000
+
+
+
amqp
@@ -764,6 +780,7 @@
27017
9094
+ 6379
15671
15672
@@ -1262,6 +1279,35 @@
+
+
+ bitnami/redis
+ hono-redis-test
+
+ false
+
+ 6379:6379
+
+
+ custom
+ ${custom.network.name}
+ redis
+
+ 1500000000
+ 1500000000
+
+ Redis
+ ${log.color.extra-services}
+
+
+
+ .*(Ready to accept connections).*
+
+
+ yes
+
+
+
${docker.repository}/hono-mongodb-test:${project.version}
@@ -2227,7 +2273,7 @@
true
**/hono-*-test:*
- hono-*-test-*,all-in-one-*,cp-*,postgres-*
+ hono-*-test-*,all-in-one-*,cp-*,postgres-*,redis-*
diff --git a/tests/src/test/resources/commandrouter/redis-cache/application.yml b/tests/src/test/resources/commandrouter/redis-cache/application.yml
new file mode 100644
index 0000000000..7c5eada006
--- /dev/null
+++ b/tests/src/test/resources/commandrouter/redis-cache/application.yml
@@ -0,0 +1,92 @@
+hono:
+ app:
+ maxInstances: 1
+ amqpMessagingDisabled: ${hono.amqp-messaging.disabled}
+ kafkaMessagingDisabled: ${hono.kafka-messaging.disabled}
+ auth:
+ host: "${hono.auth.host}"
+ port: 5671
+ name: "command-router"
+ trustStorePath: "/opt/hono/config/certs/trusted-certs.pem"
+ jwksPollingInterval: "PT20S"
+ commandRouter:
+ amqp:
+ insecurePortEnabled: true
+ insecurePortBindAddress: "0.0.0.0"
+ cache:
+ redis:
+ hosts: "redis://redis:6379"
+ messaging:
+ name: 'Hono Command Router'
+ host: "${hono.amqp-network.host}"
+ port: 5673
+ amqpHostname: "hono-internal"
+ keyPath: "/opt/hono/config/certs/command-router-key.pem"
+ certPath: "/opt/hono/config/certs/command-router-cert.pem"
+ trustStorePath: "/opt/hono/config/certs/trusted-certs.pem"
+ linkEstablishmentTimeout: ${link.establishment.timeout}
+ flowLatency: ${flow.latency}
+ requestTimeout: ${request.timeout}
+ registration:
+ name: 'Hono Command Router'
+ host: "${hono.registration.host}"
+ port: 5672
+ username: "command-router@HONO"
+ password: "cmd-router-secret"
+ linkEstablishmentTimeout: ${link.establishment.timeout}
+ flowLatency: ${flow.latency}
+ requestTimeout: ${request.timeout}
+ tenant:
+ name: 'Hono Command Router'
+ host: "${hono.registration.host}"
+ port: 5672
+ username: "command-router@HONO"
+ password: "cmd-router-secret"
+ linkEstablishmentTimeout: ${link.establishment.timeout}
+ flowLatency: ${flow.latency}
+ requestTimeout: ${request.timeout}
+ command:
+ name: 'Hono Command Router'
+ host: "${hono.amqp-network.host}"
+ port: 5673
+ amqpHostname: "hono-internal"
+ keyPath: "/opt/hono/config/certs/command-router-key.pem"
+ certPath: "/opt/hono/config/certs/command-router-cert.pem"
+ trustStorePath: "/opt/hono/config/certs/trusted-certs.pem"
+ linkEstablishmentTimeout: ${link.establishment.timeout}
+ flowLatency: ${flow.latency}
+ requestTimeout: ${request.timeout}
+ kafka:
+ commonClientConfig:
+ bootstrap.servers: "${hono.kafka.bootstrap.servers}"
+ commandInternal:
+ producerConfig:
+ max.block.ms: ${kafka-client.producer.max-block-ms}
+ request.timeout.ms: ${kafka-client.producer.request-timeout-ms}
+ delivery.timeout.ms: ${kafka-client.producer.delivery-timeout-ms}
+ commandResponse:
+ producerConfig:
+ max.block.ms: ${kafka-client.producer.max-block-ms}
+ request.timeout.ms: ${kafka-client.producer.request-timeout-ms}
+ delivery.timeout.ms: ${kafka-client.producer.delivery-timeout-ms}
+ event:
+ producerConfig:
+ max.block.ms: ${kafka-client.producer.max-block-ms}
+ request.timeout.ms: ${kafka-client.producer.request-timeout-ms}
+ delivery.timeout.ms: ${kafka-client.producer.delivery-timeout-ms}
+
+quarkus:
+ otel:
+ exporter:
+ otlp:
+ endpoint: "${otel-collector.endpoint}"
+ console:
+ color: true
+ log:
+ level: INFO
+ min-level: TRACE
+ category:
+ "io.quarkus.vertx.core.runtime":
+ level: DEBUG
+ vertx:
+ max-event-loop-execute-time: ${max.event-loop.execute-time}