diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 4888078c5e..088d48f1d1 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -48,6 +48,10 @@
org.keycloak
keycloak-model-infinispan
+
+ org.keycloak
+ keycloak-model-map-hot-rod
+
org.twitter4j
diff --git a/distribution/feature-packs/server-feature-pack-dependencies/pom.xml b/distribution/feature-packs/server-feature-pack-dependencies/pom.xml
index 9f3527e204..a8041d0564 100644
--- a/distribution/feature-packs/server-feature-pack-dependencies/pom.xml
+++ b/distribution/feature-packs/server-feature-pack-dependencies/pom.xml
@@ -200,6 +200,16 @@
+
+ org.keycloak
+ keycloak-model-map-hot-rod
+
+
+ *
+ *
+
+
+
org.keycloak
keycloak-saml-core
diff --git a/model/hot-rod/pom.xml b/model/hot-rod/pom.xml
new file mode 100644
index 0000000000..74e27e0a04
--- /dev/null
+++ b/model/hot-rod/pom.xml
@@ -0,0 +1,52 @@
+
+
+
+ keycloak-model-pom
+ org.keycloak
+ 16.0.0-SNAPSHOT
+
+ 4.0.0
+
+ keycloak-model-map-hot-rod
+ Keycloak Model Hot Rod
+
+
+
+
+ org.keycloak
+ keycloak-model-map
+
+
+ org.infinispan
+ infinispan-client-hotrod
+
+
+ org.infinispan
+ infinispan-query-dsl
+
+
+ org.infinispan
+ infinispan-remote-query-client
+
+
+ org.infinispan
+ infinispan-server-router
+
+
+ org.infinispan
+ infinispan-server-rest
+
+
+ org.infinispan
+ infinispan-server-runtime
+
+
+ org.infinispan.protostream
+ protostream-processor
+ provided
+
+
+
+
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java b/model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java
new file mode 100644
index 0000000000..a6e7a2ff4f
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.common;
+
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.rest.RestServer;
+import org.infinispan.rest.configuration.RestServerConfigurationBuilder;
+import org.infinispan.server.configuration.endpoint.SinglePortServerConfigurationBuilder;
+import org.infinispan.server.hotrod.HotRodServer;
+import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder;
+import org.infinispan.server.router.RoutingTable;
+import org.infinispan.server.router.configuration.SinglePortRouterConfiguration;
+import org.infinispan.server.router.router.impl.singleport.SinglePortEndpointRouter;
+import org.infinispan.server.router.routes.Route;
+import org.infinispan.server.router.routes.RouteDestination;
+import org.infinispan.server.router.routes.RouteSource;
+import org.infinispan.server.router.routes.hotrod.HotRodServerRouteDestination;
+import org.infinispan.server.router.routes.rest.RestServerRouteDestination;
+import org.infinispan.server.router.routes.singleport.SinglePortRouteSource;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Martin Kanis
+ */
+public class HotRodUtils {
+ /**
+ * Not suitable for a production usage. Only for development and test purposes.
+ * Also do not use in clustered environment.
+ * @param hotRodServer HotRodServer
+ * @param hotRodCacheManager DefaultCacheManager
+ * @param embeddedPort int
+ */
+ public static void createHotRodMapStoreServer(HotRodServer hotRodServer, DefaultCacheManager hotRodCacheManager, int embeddedPort) {
+ HotRodServerConfigurationBuilder hotRodServerConfigurationBuilder = new HotRodServerConfigurationBuilder();
+ hotRodServerConfigurationBuilder.startTransport(false);
+ hotRodServerConfigurationBuilder.port(embeddedPort);
+ hotRodServer.start(hotRodServerConfigurationBuilder.build(), hotRodCacheManager);
+
+ RestServerConfigurationBuilder restServerConfigurationBuilder = new RestServerConfigurationBuilder();
+ restServerConfigurationBuilder.startTransport(false);
+ restServerConfigurationBuilder.port(embeddedPort);
+ RestServer restServer = new RestServer();
+ restServer.start(restServerConfigurationBuilder.build(), hotRodCacheManager);
+
+ SinglePortRouteSource routeSource = new SinglePortRouteSource();
+ Set> routes = new HashSet<>();
+ routes.add(new Route<>(routeSource, new HotRodServerRouteDestination("hotrod", hotRodServer)));
+ routes.add(new Route<>(routeSource, new RestServerRouteDestination("rest", restServer)));
+
+ SinglePortRouterConfiguration singlePortRouter = new SinglePortServerConfigurationBuilder().port(embeddedPort).build();
+ SinglePortEndpointRouter endpointServer = new SinglePortEndpointRouter(singlePortRouter);
+ endpointServer.start(new RoutingTable(routes));
+ }
+
+ /**
+ * Not suitable for a production usage. Only for development and test purposes.
+ * Also do not use in clustered environment.
+ * @param embeddedPort int
+ */
+ public static void createHotRodMapStoreServer(int embeddedPort) {
+ DefaultCacheManager hotRodCacheManager = null;
+ try {
+ hotRodCacheManager = new DefaultCacheManager("config/infinispan.xml");
+ } catch (IOException e) {
+ new RuntimeException("Cannot initialize cache manager!", e);
+ }
+
+ HotRodUtils.createHotRodMapStoreServer(new HotRodServer(), hotRodCacheManager, embeddedPort);
+ }
+}
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java b/model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java
new file mode 100644
index 0000000000..ec19172ae0
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.common;
+
+import org.infinispan.protostream.GeneratedSchema;
+import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
+//import org.keycloak.models.map.client.HotRodAttributeEntity;
+//import org.keycloak.models.map.client.HotRodClientEntity;
+//import org.keycloak.models.map.client.HotRodPair;
+//import org.keycloak.models.map.client.HotRodProtocolMapperEntity;
+
+/**
+ * @author Martin Kanis
+ */
+@AutoProtoSchemaBuilder(
+ includeClasses = {
+ //HotRodAttributeEntity.class,
+ //HotRodClientEntity.class,
+ //HotRodProtocolMapperEntity.class,
+ //HotRodPair.class
+ },
+ schemaFileName = "KeycloakHotRodMapStorage.proto",
+ schemaFilePath = "proto/",
+ schemaPackageName = "org.keycloak.models.map.storage.hotrod")
+public interface ProtoSchemaInitializer extends GeneratedSchema {
+ ProtoSchemaInitializer INSTANCE = new ProtoSchemaInitializerImpl();
+}
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java
new file mode 100644
index 0000000000..1b289f1ac1
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.connections;
+
+import org.infinispan.client.hotrod.RemoteCache;
+import org.infinispan.client.hotrod.RemoteCacheManager;
+
+/**
+ * @author Martin Kanis
+ */
+public class DefaultHotRodConnectionProvider implements HotRodConnectionProvider {
+
+ private RemoteCacheManager remoteCacheManager;
+
+ public DefaultHotRodConnectionProvider(RemoteCacheManager remoteCacheManager) {
+ this.remoteCacheManager = remoteCacheManager;
+ }
+
+ @Override
+ public RemoteCache getRemoteCache(String name) {
+ return remoteCacheManager.getCache(name);
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java
new file mode 100644
index 0000000000..4508095af2
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.connections;
+
+import org.infinispan.client.hotrod.RemoteCache;
+import org.infinispan.client.hotrod.RemoteCacheManager;
+import org.infinispan.client.hotrod.configuration.ClientIntelligence;
+import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
+import org.infinispan.commons.marshall.ProtoStreamMarshaller;
+import org.infinispan.protostream.GeneratedSchema;
+import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants;
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+//import org.keycloak.models.map.common.HotRodEntityDescriptor;
+import org.keycloak.models.map.common.HotRodUtils;
+import org.keycloak.models.map.common.ProtoSchemaInitializer;
+//import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * @author Martin Kanis
+ */
+public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionProviderFactory {
+
+ public static final String PROVIDER_ID = "default";
+
+ private static final Logger LOG = Logger.getLogger(DefaultHotRodConnectionProviderFactory.class);
+
+ private org.keycloak.Config.Scope config;
+
+ private RemoteCacheManager remoteCacheManager;
+
+ @Override
+ public HotRodConnectionProvider create(KeycloakSession session) {
+ if (remoteCacheManager == null) {
+ lazyInit();
+ }
+ return new DefaultHotRodConnectionProvider(remoteCacheManager);
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public void init(org.keycloak.Config.Scope config) {
+ this.config = config;
+ }
+
+ public void lazyInit() {
+ if (config.getBoolean("embedded", false)) {
+ HotRodUtils.createHotRodMapStoreServer(config.getInt("embeddedPort", 11444));
+ }
+
+ ConfigurationBuilder remoteBuilder = new ConfigurationBuilder();
+ remoteBuilder.addServer()
+ .host(config.get("host", "localhost"))
+ .port(config.getInt("port", 11444))
+ .clientIntelligence(ClientIntelligence.HASH_DISTRIBUTION_AWARE)
+ .marshaller(new ProtoStreamMarshaller());
+
+ if (config.getBoolean("enableSecurity", true)) {
+ remoteBuilder.security()
+ .authentication()
+ .saslMechanism("SCRAM-SHA-512")
+ .username(config.get("username", "admin"))
+ .password(config.get("password", "admin"))
+ .realm(config.get("realm", "default"));
+ }
+
+ boolean configureRemoteCaches = config.getBoolean("configureRemoteCaches", false);
+ if (configureRemoteCaches) {
+ configureRemoteCaches(remoteBuilder);
+ }
+
+ remoteBuilder.addContextInitializer(ProtoSchemaInitializer.INSTANCE);
+ remoteCacheManager = new RemoteCacheManager(remoteBuilder.build());
+
+ if (configureRemoteCaches) {
+ // access the caches to force their creation
+ /*HotRodMapStorageProviderFactory.ENTITY_DESCRIPTOR_MAP.values().stream()
+ .map(HotRodEntityDescriptor::getCacheName)
+ .forEach(remoteCacheManager::getCache);*/
+ }
+
+ registerSchemata(ProtoSchemaInitializer.INSTANCE);
+ }
+
+ private void registerSchemata(GeneratedSchema initializer) {
+ final RemoteCache protoMetadataCache = remoteCacheManager.getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
+
+ protoMetadataCache.put(initializer.getProtoFileName(), initializer.getProtoFile());
+
+ String errors = protoMetadataCache.get(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX);
+ if (errors != null) {
+ throw new IllegalStateException("Some Protobuf schema files contain errors: " + errors + "\nSchema :\n" + initializer.getProtoFileName());
+ }
+ }
+
+ private void configureRemoteCaches(ConfigurationBuilder builder) {
+ URI uri;
+ try {
+ uri = DefaultHotRodConnectionProviderFactory.class.getClassLoader().getResource("config/cacheConfig.xml").toURI();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Cannot read the cache configuration!", e);
+ }
+
+ /*HotRodMapStorageProviderFactory.ENTITY_DESCRIPTOR_MAP.values().stream()
+ .map(HotRodEntityDescriptor::getCacheName)
+ .forEach(name -> builder.remoteCache(name).configurationURI(uri));*/
+ }
+}
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java
new file mode 100644
index 0000000000..a7268dab6f
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.connections;
+
+import org.infinispan.client.hotrod.RemoteCache;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author Martin Kanis
+ */
+public interface HotRodConnectionProvider extends Provider {
+
+ /**
+ * Returns a remote Infinispan cache specified by the given name.
+ * @param name String Name of the remote cache.
+ * @param key
+ * @param value
+ * @return A remote Infinispan cache specified by name.
+ */
+ RemoteCache getRemoteCache(String name);
+}
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java
new file mode 100644
index 0000000000..cba9798e94
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.connections;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author Martin Kanis
+ */
+public interface HotRodConnectionProviderFactory extends ProviderFactory {
+}
diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java
new file mode 100644
index 0000000000..54be770f92
--- /dev/null
+++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.models.map.connections;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Martin Kanis
+ */
+public class HotRodConnectionSpi implements Spi {
+
+ public static final String NAME = "connectionsHotRod";
+
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return HotRodConnectionProvider.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory> getProviderFactoryClass() {
+ return HotRodConnectionProviderFactory.class;
+ }
+}
diff --git a/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory
new file mode 100644
index 0000000000..1d08505e2b
--- /dev/null
+++ b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2021 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# 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
+#
+# http://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.
+#
+
+org.keycloak.models.map.connections.DefaultHotRodConnectionProviderFactory
\ No newline at end of file
diff --git a/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000000..bc573c7fd3
--- /dev/null
+++ b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1,18 @@
+#
+# Copyright 2021 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# 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
+#
+# http://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.
+#
+
+org.keycloak.models.map.connections.HotRodConnectionSpi
\ No newline at end of file
diff --git a/model/hot-rod/src/main/resources/config/cacheConfig.xml b/model/hot-rod/src/main/resources/config/cacheConfig.xml
new file mode 100644
index 0000000000..351576ee74
--- /dev/null
+++ b/model/hot-rod/src/main/resources/config/cacheConfig.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/model/hot-rod/src/main/resources/config/infinispan.xml b/model/hot-rod/src/main/resources/config/infinispan.xml
new file mode 100644
index 0000000000..ce1efc99cf
--- /dev/null
+++ b/model/hot-rod/src/main/resources/config/infinispan.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml
index d8c7f060b8..97558e91ac 100755
--- a/model/infinispan/pom.xml
+++ b/model/infinispan/pom.xml
@@ -75,10 +75,5 @@
microprofile-metrics-api
test
-
- org.infinispan
- infinispan-server-hotrod
- ${infinispan.version}
-
diff --git a/model/pom.xml b/model/pom.xml
index bbf98ce10e..a3932630ce 100755
--- a/model/pom.xml
+++ b/model/pom.xml
@@ -35,5 +35,6 @@
infinispan
map
build-processor
+ hot-rod
diff --git a/pom.xml b/pom.xml
index 676a95aa75..84d12637c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,6 +78,7 @@
5.3.20.Final
5.3.20.Final
11.0.9.Final
+ 4.3.4.Final
2.12.1
${jackson.version}
${jackson.databind.version}
@@ -908,6 +909,53 @@
infinispan-jboss-marshalling
${infinispan.version}
+
+ org.infinispan
+ infinispan-server-core
+ ${infinispan.version}
+
+
+ org.infinispan
+ infinispan-server-router
+ ${infinispan.version}
+
+
+ org.infinispan
+ infinispan-server-runtime
+ ${infinispan.version}
+
+
+ *
+ *
+
+
+
+
+ org.infinispan
+ infinispan-server-rest
+ ${infinispan.version}
+
+
+ org.infinispan
+ infinispan-client-hotrod
+ ${infinispan.version}
+
+
+ org.infinispan
+ infinispan-query-dsl
+ ${infinispan.version}
+
+
+ org.infinispan
+ infinispan-remote-query-client
+ ${infinispan.version}
+
+
+ org.infinispan.protostream
+ protostream-processor
+ provided
+ ${infinispan.protostream.processor.version}
+
org.jboss.marshalling
jboss-marshalling
@@ -1243,6 +1291,11 @@
keycloak-model-infinispan
${project.version}
+
+ org.keycloak
+ keycloak-model-map-hot-rod
+ ${project.version}
+
org.keycloak
launcher
@@ -2036,4 +2089,4 @@
-
\ No newline at end of file
+
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 72957ed3c0..54323ee1f1 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
@@ -48,42 +48,90 @@
},
"realm": {
- "provider": "${keycloak.realm.provider:jpa}"
+ "provider": "${keycloak.realm.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.realm.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"user": {
- "provider": "${keycloak.user.provider:jpa}"
+ "provider": "${keycloak.user.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.user.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"client": {
- "provider": "${keycloak.client.provider:jpa}"
+ "provider": "${keycloak.client.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.client.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"clientScope": {
- "provider": "${keycloak.clientScope.provider:jpa}"
+ "provider": "${keycloak.clientScope.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.clientScope.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"group": {
- "provider": "${keycloak.group.provider:jpa}"
+ "provider": "${keycloak.group.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.group.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"role": {
- "provider": "${keycloak.role.provider:jpa}"
+ "provider": "${keycloak.role.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.role.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"authenticationSessions": {
"provider": "${keycloak.authSession.provider:infinispan}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.authSession.map.storage.provider:concurrenthashmap}"
+ }
+ },
"infinispan": {
"authSessionsLimit": "${keycloak.authSessions.limit:300}"
}
},
"userSessions": {
- "provider": "${keycloak.userSession.provider:infinispan}"
+ "provider": "${keycloak.userSession.provider:infinispan}",
+ "map": {
+ "storage-user-sessions": {
+ "provider": "${keycloak.userSession.map.storage.provider:concurrenthashmap}"
+ },
+ "storage-client-sessions": {
+ "provider": "${keycloak.userSession.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"loginFailure": {
- "provider": "${keycloak.loginFailure.provider:infinispan}"
+ "provider": "${keycloak.loginFailure.provider:infinispan}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.loginFailure.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"mapStorage": {
@@ -104,7 +152,12 @@
},
"authorizationPersister": {
- "provider": "${keycloak.authorization.provider:jpa}"
+ "provider": "${keycloak.authorization.provider:jpa}",
+ "map": {
+ "storage": {
+ "provider": "${keycloak.authorization.map.storage.provider:concurrenthashmap}"
+ }
+ }
},
"userCache": {
@@ -192,6 +245,14 @@
}
},
+ "connectionsHotRod": {
+ "default": {
+ "embedded": "${keycloak.connectionsHotRod.embedded:true}",
+ "embeddedPort": "${keycloak.connectionsHotRod.embeddedPort:11444}",
+ "enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:false}"
+ }
+ },
+
"truststore": {
"file": {
"file": "${keycloak.truststore.file:target/dependency/keystore/keycloak.truststore}",
diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml
index 93719dc98a..2bc5554c2e 100644
--- a/testsuite/model/pom.xml
+++ b/testsuite/model/pom.xml
@@ -75,7 +75,6 @@
${jdbc.mvn.version}
test
-
org.keycloak
keycloak-model-jpa
@@ -93,6 +92,14 @@
integration-arquillian-testsuite-providers
${project.version}
+
+ org.keycloak
+ keycloak-model-map-hot-rod
+
+
+ org.infinispan
+ infinispan-server-core
+
@@ -266,6 +273,14 @@
Jpa,Map,ConcurrentHashMapStorage
+
+
+ hot-rod
+
+ enabled
+ Jpa,Map,HotRodMapStorage
+
+
diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java
index ca8b9d690a..b65713f8fe 100644
--- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java
+++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java
@@ -133,7 +133,7 @@ public class Config implements ConfigProvider {
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
- sb.append(prefix).append(".");
+ sb.append(prefix);
for (String s : scope) {
sb.append(s);
sb.append(".");
diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java
index 7313dc20c3..87a2cc7bfa 100644
--- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java
+++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java
@@ -14,6 +14,7 @@ import org.infinispan.server.hotrod.configuration.HotRodServerConfiguration;
import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder;
import org.junit.rules.ExternalResource;
import org.keycloak.Config;
+import org.keycloak.models.map.common.HotRodUtils;
import java.io.IOException;
@@ -39,7 +40,9 @@ public class HotRodServerRule extends ExternalResource {
@Override
protected void after() {
- remoteCacheManager.stop();
+ if (remoteCacheManager != null) {
+ remoteCacheManager.stop();
+ }
}
public void createEmbeddedHotRodServer(Config.Scope config) {
@@ -76,6 +79,29 @@ public class HotRodServerRule extends ExternalResource {
LOGIN_FAILURE_CACHE_NAME, WORK_CACHE_NAME, ACTION_TOKEN_CACHE);
}
+ public void createHotRodMapStoreServer() {
+ hotRodCacheManager = configureHotRodCacheManager("hotrod/infinispan.xml");
+ hotRodServer = new HotRodServer();
+
+ HotRodUtils.createHotRodMapStoreServer(hotRodServer, hotRodCacheManager, 11444);
+
+ org.infinispan.client.hotrod.configuration.ConfigurationBuilder remoteBuilder = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
+ org.infinispan.client.hotrod.configuration.Configuration cfg = remoteBuilder
+ .addServers(hotRodServer.getHost() + ":" + hotRodServer.getPort()).build();
+ remoteCacheManager = new RemoteCacheManager(cfg);
+ }
+
+ private DefaultCacheManager configureHotRodCacheManager(String configPath) {
+ DefaultCacheManager manager = null;
+ try {
+ manager = new DefaultCacheManager(configPath);
+ } catch (IOException e) {
+ new RuntimeException(e);
+ }
+
+ return manager;
+ }
+
private void getCaches(String... cache) {
for (String c: cache) {
hotRodCacheManager.getCache(c, true);
diff --git a/testsuite/model/src/main/resources/hotrod/infinispan.xml b/testsuite/model/src/main/resources/hotrod/infinispan.xml
new file mode 100644
index 0000000000..00a78521ec
--- /dev/null
+++ b/testsuite/model/src/main/resources/hotrod/infinispan.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java
new file mode 100644
index 0000000000..5c989aa7ad
--- /dev/null
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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 org.keycloak.testsuite.model.parameters;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.keycloak.authorization.store.StoreFactorySpi;
+import org.keycloak.models.DeploymentStateSpi;
+import org.keycloak.models.UserLoginFailureSpi;
+import org.keycloak.models.UserSessionSpi;
+import org.keycloak.models.dblock.NoLockingDBLockProviderFactory;
+import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
+import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
+import org.keycloak.models.map.client.MapClientProviderFactory;
+import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
+import org.keycloak.models.map.connections.DefaultHotRodConnectionProviderFactory;
+import org.keycloak.models.map.connections.HotRodConnectionProviderFactory;
+import org.keycloak.models.map.connections.HotRodConnectionSpi;
+import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
+import org.keycloak.models.map.group.MapGroupProviderFactory;
+import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
+import org.keycloak.models.map.realm.MapRealmProviderFactory;
+import org.keycloak.models.map.role.MapRoleProviderFactory;
+import org.keycloak.models.map.storage.MapStorageSpi;
+import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
+//import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory;
+import org.keycloak.models.map.user.MapUserProviderFactory;
+import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+import org.keycloak.sessions.AuthenticationSessionSpi;
+import org.keycloak.testsuite.model.Config;
+import org.keycloak.testsuite.model.HotRodServerRule;
+import org.keycloak.testsuite.model.KeycloakModelParameters;
+
+import java.util.Set;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class HotRodMapStorage extends KeycloakModelParameters {
+
+ static final Set> ALLOWED_SPIS = ImmutableSet.>builder()
+ .add(HotRodConnectionSpi.class)
+ .build();
+
+ static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder()
+ //.add(HotRodMapStorageProviderFactory.class)
+ .add(HotRodConnectionProviderFactory.class)
+ .add(ConcurrentHashMapStorageProviderFactory.class)
+ .build();
+
+ private static final String STORAGE_CONFIG = "storage.provider";
+
+ private HotRodServerRule hotRodServerRule = new HotRodServerRule();
+
+ @Override
+ public void updateConfig(Config cf) {
+ cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ //.spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
+ .spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi("group").provider(MapGroupProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi("role").provider(MapRoleProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi("user").provider(MapUserProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-user-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .config("storage-client-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID);
+
+ cf.spi(MapStorageSpi.NAME)
+ .provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+ .config("dir", "${project.build.directory:target}");
+
+ cf.spi(HotRodConnectionSpi.NAME).provider(DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
+ .config("enableSecurity", "false")
+ .config("configureRemoteCaches", "false");
+ }
+
+ @Override
+ public void beforeSuite(Config cf) {
+ hotRodServerRule.createHotRodMapStoreServer();
+ }
+
+ @Override
+ public Statement classRule(Statement base, Description description) {
+ return hotRodServerRule.apply(base, description);
+ }
+
+ public HotRodMapStorage() {
+ super(ALLOWED_SPIS, ALLOWED_FACTORIES);
+ }
+
+}
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 396aecb071..6afe42d6d8 100755
--- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json
@@ -124,6 +124,14 @@
}
},
+ "connectionsHotRod": {
+ "default": {
+ "embedded": "${keycloak.connectionsHotRod.embedded:true}",
+ "embeddedPort": "${keycloak.connectionsHotRod.embeddedPort:11444}",
+ "enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:false}"
+ }
+ },
+
"scripting": {
},