diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java index 06852af106..95a4652b82 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java @@ -46,9 +46,12 @@ import org.keycloak.models.cache.infinispan.events.RealmUpdatedEvent; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.provider.InvalidationHandler.ObjectType; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; import org.keycloak.provider.ProviderEvent; import java.util.Iterator; +import java.util.List; import java.util.ServiceLoader; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; @@ -225,7 +228,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries)); cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true); - this.topologyInfo = new TopologyInfo(cacheManager, config, false); + this.topologyInfo = new TopologyInfo(cacheManager, config, false, getId()); logger.debugv("Using container managed Infinispan cache container, lookup={0}", cacheManager); @@ -239,7 +242,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon boolean async = config.getBoolean("async", false); boolean useKeycloakTimeService = config.getBoolean("useKeycloakTimeService", false); - this.topologyInfo = new TopologyInfo(cacheManager, config, true); + this.topologyInfo = new TopologyInfo(cacheManager, config, true, getId()); if (clustered) { String jgroupsUdpMcastAddr = config.get("jgroupsUdpMcastAddr", System.getProperty(InfinispanConnectionProvider.JGROUPS_UDP_MCAST_ADDR)); diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/TopologyInfo.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/TopologyInfo.java index 25a6ad1afd..cefe17d367 100644 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/TopologyInfo.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/TopologyInfo.java @@ -20,7 +20,6 @@ package org.keycloak.connections.infinispan; import java.net.InetSocketAddress; import java.security.SecureRandom; import java.util.Objects; -import java.util.Optional; import org.infinispan.Cache; import org.infinispan.distribution.DistributionManager; @@ -55,22 +54,28 @@ public class TopologyInfo { private final boolean isGeneratedNodeName; - public TopologyInfo(EmbeddedCacheManager cacheManager, Config.Scope config, boolean embedded) { + public TopologyInfo(EmbeddedCacheManager cacheManager, Config.Scope config, boolean embedded, String providerId) { String siteName; String nodeName; boolean isGeneratedNodeName = false; + if (System.getProperty(InfinispanConnectionProvider.JBOSS_SITE_NAME) != null) { + throw new IllegalArgumentException( + String.format("System property %s is in use. Use --spi-connections-infinispan-%s-site-name config option instead", + InfinispanConnectionProvider.JBOSS_SITE_NAME, providerId)); + } + if (!embedded) { Transport transport = cacheManager.getTransport(); if (transport != null) { nodeName = transport.getAddress().toString(); siteName = cacheManager.getCacheManagerConfiguration().transport().siteId(); if (siteName == null) { - siteName = System.getProperty(InfinispanConnectionProvider.JBOSS_SITE_NAME); + siteName = config.get("siteName"); } } else { nodeName = System.getProperty(InfinispanConnectionProvider.JBOSS_NODE_NAME); - siteName = System.getProperty(InfinispanConnectionProvider.JBOSS_SITE_NAME); + siteName = config.get("siteName"); } if (nodeName == null || nodeName.equals("localhost")) { isGeneratedNodeName = true; @@ -84,7 +89,7 @@ public class TopologyInfo { nodeName = null; } - siteName = config.get("siteName", System.getProperty(InfinispanConnectionProvider.JBOSS_SITE_NAME)); + siteName = config.get("siteName"); if (siteName != null && siteName.isEmpty()) { siteName = null; } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java index 83c7a4f5d1..12dabba584 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java @@ -19,6 +19,10 @@ package org.keycloak.quarkus.runtime.storage.legacy.infinispan; import org.infinispan.manager.EmbeddedCacheManager; import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; + +import java.util.List; /** * @author Pedro Igor @@ -43,4 +47,15 @@ public class QuarkusInfinispanConnectionFactory extends DefaultInfinispanConnect public String getId() { return "quarkus"; } + + @Override + public List getConfigMetadata() { + return ProviderConfigurationBuilder.create() + .property() + .name("site-name") + .helpText("Site name for multi-site deployments") + .type("string") + .add() + .build(); + } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java index c13cd89703..646048f11b 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java @@ -18,8 +18,10 @@ package org.keycloak.it.storage.database; import io.quarkus.test.junit.main.Launch; +import io.quarkus.test.junit.main.LaunchResult; import org.junit.jupiter.api.Test; import org.keycloak.common.util.Retry; +import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.InfinispanContainer; import org.keycloak.it.junit5.extension.WithExternalInfinispan; @@ -31,12 +33,12 @@ import static io.restassured.RestAssured.when; public class ExternalInfinispanTest { @Test - @Launch({ "start-dev", "--features=multi-site", "--cache=ispn", "--cache-config-file=../../../test-classes/ExternalInfinispan/kcb-infinispan-cache-remote-store-config.xml", "-Djboss.site.name=ISPN" }) + @Launch({ "start-dev", "--features=multi-site", "--cache=ispn", "--cache-config-file=../../../test-classes/ExternalInfinispan/kcb-infinispan-cache-remote-store-config.xml", "--spi-connections-infinispan-quarkus-site-name=ISPN" }) void testLoadBalancerCheckFailure() { when().get("/lb-check").then() .statusCode(200); - InfinispanContainer.remoteCacheManager.administration().removeCache("sessions"); + InfinispanContainer.removeCache("sessions"); // The `lb-check` relies on the Infinispan's persistence check status. By default, Infinispan checks in the background every second that the remote store is available. // So we'll wait on average about one second here for the check to switch its state. @@ -45,4 +47,10 @@ public class ExternalInfinispanTest { .statusCode(503); }, 10, 200); } + + @Test + @Launch({ "start-dev", "--features=multi-site", "--cache=ispn", "--cache-config-file=../../../test-classes/ExternalInfinispan/kcb-infinispan-cache-remote-store-config.xml", "-Djboss.site.name=ISPN" }) + void testSiteNameAsSystemProperty(LaunchResult result) { + ((CLIResult) result).assertMessage("System property jboss.site.name is in use. Use --spi-connections-infinispan-quarkus-site-name config option instead"); + } } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java index 7a7b5e65ec..49a3bd6699 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java @@ -199,7 +199,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { databaseContainer.stop(); databaseContainer = null; } - if (infinispanContainer != null && infinispanContainer.isRunning()) { + if (infinispanContainer != null) { infinispanContainer.stop(); } result = null; diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/InfinispanContainer.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/InfinispanContainer.java index c0f73c4d3b..14760decf0 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/InfinispanContainer.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/InfinispanContainer.java @@ -67,6 +67,13 @@ public class InfinispanContainer extends GenericContainer { return INFINISPAN_IMAGE; } + public static void removeCache(String cache) { + // first stop the cache to avoid leaking MBeans for the HotRodClient + // see: https://issues.redhat.com/browse/ISPN-15606 + remoteCacheManager.getCache(cache).stop(); + remoteCacheManager.administration().removeCache(cache); + } + private void establishHotRodConnection() { ConfigurationBuilder configBuilder = new ConfigurationBuilder() .addServers(getContainerIpAddress() + ":11222") @@ -84,6 +91,8 @@ public class InfinispanContainer extends GenericContainer { @Override public void start() { + logger().info("Starting ISPN container"); + super.start(); establishHotRodConnection(); @@ -95,6 +104,17 @@ public class InfinispanContainer extends GenericContainer { }); } + @Override + public void stop() { + logger().info("Stopping ISPN container"); + + if (remoteCacheManager != null) { + remoteCacheManager.stop(); + } + + super.stop(); + } + public void createCache(RemoteCacheManager remoteCacheManager, String cacheName) { String xml = String.format("" , cacheName); remoteCacheManager.administration().getOrCreateCache(cacheName, new XMLStringConfiguration(xml)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index e8831447fb..f950e18fae 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -185,7 +185,7 @@ "default": { "jgroupsUdpMcastAddr": "${keycloak.connectionsInfinispan.jgroupsUdpMcastAddr:234.56.78.90}", "nodeName": "${keycloak.connectionsInfinispan.nodeName,jboss.node.name:}", - "siteName": "${keycloak.connectionsInfinispan.siteName,jboss.site.name:}", + "siteName": "${keycloak.connectionsInfinispan.siteName:}", "clustered": "${keycloak.connectionsInfinispan.clustered:false}", "async": "${keycloak.connectionsInfinispan.async:false}", "sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:1}", diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml index a872b16c1e..54a0486a05 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml @@ -519,7 +519,7 @@ -Djboss.socket.binding.port-offset=${auth.server.crossdc01.port.offset} -Djboss.default.multicast.address=234.56.78.1 -Dremote.cache.port=12232 - -Djboss.site.name=dc0 + -Dkeycloak.connectionsInfinispan.siteName=dc0 -Djboss.node.name=auth-server-${node.name}-cross-dc-0_1 -Dauth.server.truststore=${auth.server.truststore} -Dauth.server.truststore.password=${auth.server.truststore.password} @@ -551,7 +551,7 @@ -Djboss.socket.binding.port-offset=${auth.server.crossdc02.port.offset} -Djboss.default.multicast.address=234.56.78.1 -Dremote.cache.port=12232 - -Djboss.site.name=dc0 + -Dkeycloak.connectionsInfinispan.siteName=dc0 -Djboss.node.name=auth-server-${node.name}-cross-dc-0_2-manual -Dauth.server.truststore=${auth.server.truststore} -Dauth.server.truststore.password=${auth.server.truststore.password} @@ -584,7 +584,7 @@ -Djboss.socket.binding.port-offset=${auth.server.crossdc11.port.offset} -Djboss.default.multicast.address=234.56.78.2 -Dremote.cache.port=13232 - -Djboss.site.name=dc1 + -Dkeycloak.connectionsInfinispan.siteName=dc1 -Djboss.node.name=auth-server-${node.name}-cross-dc-1_1 -Dauth.server.truststore=${auth.server.truststore} -Dauth.server.truststore.password=${auth.server.truststore.password} @@ -616,7 +616,7 @@ -Djboss.socket.binding.port-offset=${auth.server.crossdc12.port.offset} -Djboss.default.multicast.address=234.56.78.2 -Dremote.cache.port=13232 - -Djboss.site.name=dc1 + -Dkeycloak.connectionsInfinispan.siteName=dc1 -Djboss.node.name=auth-server-${node.name}-cross-dc-1_2-manual -Dauth.server.truststore=${auth.server.truststore} -Dauth.server.truststore.password=${auth.server.truststore.password} diff --git a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json index ba420b26dc..0bb1a40763 100755 --- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json @@ -137,7 +137,7 @@ "default": { "jgroupsUdpMcastAddr": "${keycloak.connectionsInfinispan.jgroupsUdpMcastAddr:234.56.78.90}", "nodeName": "${keycloak.connectionsInfinispan.nodeName,jboss.node.name:}", - "siteName": "${keycloak.connectionsInfinispan.siteName,jboss.site.name:}", + "siteName": "${keycloak.connectionsInfinispan.siteName:}", "clustered": "${keycloak.connectionsInfinispan.clustered:}", "async": "${keycloak.connectionsInfinispan.async:}", "sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:}",