From 6d683824a4f216aa250f90d12f10c704cebe1eed Mon Sep 17 00:00:00 2001 From: Michal Hajas Date: Wed, 5 Oct 2022 14:13:10 +0200 Subject: [PATCH] Deprecate DBLockProvider and replace it with new GlobalLockProvider Closes #9388 --- .../dblock/DBLockGlobalLockProvider.java | 81 +++++++++++++++++++ .../DBLockGlobalLockProviderFactory.java | 61 ++++++++++++++ .../keycloak/models/dblock/DBLockManager.java | 0 .../models/dblock/DBLockProvider.java | 0 .../models/dblock/DBLockProviderFactory.java | 0 .../org/keycloak/models/dblock/DBLockSpi.java | 0 ...k.models.locking.GlobalLockProviderFactory | 18 +++++ .../services/org.keycloak.provider.Spi | 1 + .../jpa/JpaMapStorageProviderFactory.java | 15 ++-- .../org/keycloak/config/StorageOptions.java | 2 +- .../mappers/StoragePropertyMappers.java | 10 +-- .../LegacyJpaConnectionProviderFactory.java | 27 +++---- .../keycloak/models/locking/GlobalLock.java | 34 ++++++++ .../models/locking/GlobalLockProvider.java | 79 ++++++++++++++++++ .../locking/GlobalLockProviderFactory.java | 23 ++++++ .../models/locking/GlobalLockProviderSpi.java | 47 +++++++++++ .../LockAcquiringTimeoutException.java | 49 +++++++++++ .../NoneGlobalLockProviderFactory.java} | 53 +++++------- ....models.locking.GlobalLockProviderFactory} | 2 +- .../services/org.keycloak.provider.Spi | 2 +- .../resources/KeycloakApplication.java | 30 ++++--- .../integration-arquillian/tests/base/pom.xml | 2 +- .../resources/META-INF/keycloak-server.json | 4 +- testsuite/model/pom.xml | 6 +- .../testsuite/model/KeycloakModelTest.java | 4 +- .../model/parameters/HotRodMapStorage.java | 2 - .../model/parameters/JpaMapStorage.java | 2 - .../testsuite/model/parameters/LegacyJpa.java | 7 ++ .../testsuite/model/parameters/Map.java | 7 +- testsuite/utils/pom.xml | 2 +- .../resources/META-INF/keycloak-server.json | 4 +- 31 files changed, 486 insertions(+), 88 deletions(-) create mode 100644 model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProvider.java create mode 100644 model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProviderFactory.java rename {server-spi-private => model/legacy-private}/src/main/java/org/keycloak/models/dblock/DBLockManager.java (100%) rename {server-spi-private => model/legacy-private}/src/main/java/org/keycloak/models/dblock/DBLockProvider.java (100%) rename {server-spi-private => model/legacy-private}/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java (100%) rename {server-spi-private => model/legacy-private}/src/main/java/org/keycloak/models/dblock/DBLockSpi.java (100%) create mode 100644 model/legacy-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory create mode 100644 server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLock.java create mode 100644 server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProvider.java create mode 100644 server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderFactory.java create mode 100644 server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderSpi.java create mode 100644 server-spi-private/src/main/java/org/keycloak/models/locking/LockAcquiringTimeoutException.java rename server-spi-private/src/main/java/org/keycloak/models/{dblock/NoLockingDBLockProviderFactory.java => locking/NoneGlobalLockProviderFactory.java} (64%) rename server-spi-private/src/main/resources/META-INF/services/{org.keycloak.models.dblock.DBLockProviderFactory => org.keycloak.models.locking.GlobalLockProviderFactory} (91%) diff --git a/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProvider.java b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProvider.java new file mode 100644 index 0000000000..1ff4caae03 --- /dev/null +++ b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProvider.java @@ -0,0 +1,81 @@ +/* + * Copyright 2022 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.dblock; + +import org.jboss.logging.Logger; +import org.keycloak.models.locking.GlobalLock; +import org.keycloak.models.locking.GlobalLockProvider; + +import java.time.Duration; +import java.util.Objects; + +import static org.keycloak.models.locking.GlobalLock.Constants.KEYCLOAK_BOOT; + +public class DBLockGlobalLockProvider implements GlobalLockProvider { + + private static final Logger LOG = Logger.getLogger(DBLockGlobalLockProvider.class); + public static final String DATABASE = "database"; + private final DBLockProvider dbLockProvider; + public DBLockGlobalLockProvider(DBLockProvider dbLockProvider) { + this.dbLockProvider = dbLockProvider; + } + + private static DBLockProvider.Namespace stringToNamespace(String lockName) { + switch (lockName) { + case DATABASE: + return DBLockProvider.Namespace.DATABASE; + case KEYCLOAK_BOOT: + return DBLockProvider.Namespace.KEYCLOAK_BOOT; + default: + throw new RuntimeException("Lock with name " + lockName + " not supported by DBLockGlobalLockProvider."); + } + } + + @Override + public GlobalLock acquire(String lockName, Duration timeToWaitForLock) { + Objects.requireNonNull(lockName, "lockName cannot be null"); + + if (timeToWaitForLock != null) { + LOG.debug("DBLockGlobalLockProvider does not support setting timeToWaitForLock per lock."); + } + dbLockProvider.waitForLock(stringToNamespace(lockName)); + return () -> releaseLock(lockName); + } + + private void releaseLock(String lockName) { + if (dbLockProvider.getCurrentLock() != stringToNamespace(lockName)) { + throw new RuntimeException("Requested releasing lock with name " + lockName + ", but lock is currently acquired for " + dbLockProvider.getCurrentLock() + "."); + } + + dbLockProvider.releaseLock(); + } + + @Override + public void forceReleaseAllLocks() { + if (dbLockProvider.supportsForcedUnlock()) { + dbLockProvider.releaseLock(); + } else { + throw new IllegalStateException("Forced unlock requested, but provider " + dbLockProvider + " does not support it."); + } + } + + @Override + public void close() { + + } +} diff --git a/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProviderFactory.java b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProviderFactory.java new file mode 100644 index 0000000000..32a6d68d8a --- /dev/null +++ b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockGlobalLockProviderFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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.dblock; + +import org.keycloak.Config; +import org.keycloak.common.Profile; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.locking.GlobalLockProvider; +import org.keycloak.models.locking.GlobalLockProviderFactory; +import org.keycloak.provider.EnvironmentDependentProviderFactory; + +public class DBLockGlobalLockProviderFactory implements GlobalLockProviderFactory, EnvironmentDependentProviderFactory { + + public static final String PROVIDER_ID = "dblock"; + private DBLockManager dbLockManager; + + @Override + public GlobalLockProvider create(KeycloakSession session) { + if (dbLockManager == null) { + dbLockManager = new DBLockManager(session); + dbLockManager.checkForcedUnlock(); + } + + return new DBLockGlobalLockProvider(dbLockManager.getDBLock()); + } + + @Override + public void init(Config.Scope config) { } + + @Override + public void postInit(KeycloakSessionFactory factory) { } + + @Override + public void close() { } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public boolean isSupported() { + return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE); + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java rename to model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java rename to model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java rename to model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java b/model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java rename to model/legacy-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java diff --git a/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory b/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory new file mode 100644 index 0000000000..1aa57e0905 --- /dev/null +++ b/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory @@ -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.dblock.DBLockGlobalLockProviderFactory diff --git a/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index 08a9646d4e..4b01912bc6 100644 --- a/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/model/legacy-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -18,6 +18,7 @@ org.keycloak.models.cache.CacheUserProviderSpi org.keycloak.models.cache.CacheRealmProviderSpi org.keycloak.models.cache.CachePublicKeyProviderSpi +org.keycloak.models.dblock.DBLockSpi org.keycloak.storage.client.ClientStorageProviderSpi org.keycloak.storage.group.GroupStorageProviderSpi org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java index faf437c273..054cd0013a 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java @@ -27,6 +27,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; @@ -64,7 +65,9 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.UserLoginFailureModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; -import org.keycloak.models.dblock.DBLockProvider; +import org.keycloak.models.locking.GlobalLock; +import org.keycloak.models.locking.GlobalLockProvider; +import org.keycloak.models.locking.LockAcquiringTimeoutException; import org.keycloak.models.map.client.MapProtocolMapperEntity; import org.keycloak.models.map.client.MapProtocolMapperEntityImpl; import org.keycloak.models.map.common.DeepCloner; @@ -433,13 +436,11 @@ public class JpaMapStorageProviderFactory implements private void update(Class modelType, Connection connection, KeycloakSession session) { KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession lockSession) -> { - // TODO locking tables based on modelType: https://github.com/keycloak/keycloak/issues/9388 - DBLockProvider dbLock = session.getProvider(DBLockProvider.class); - dbLock.waitForLock(DBLockProvider.Namespace.DATABASE); - try { + GlobalLockProvider globalLock = session.getProvider(GlobalLockProvider.class); + try (GlobalLock l = globalLock.acquireLock(modelType.getName())) { session.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, config.get("schema")); - } finally { - dbLock.releaseLock(); + } catch (LockAcquiringTimeoutException e) { + throw new RuntimeException("Acquiring " + modelType.getName() + " failed.", e); } }); } diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java index 7e9d8fdba6..19ab78af2f 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java @@ -217,7 +217,7 @@ public class StorageOptions { .buildTime(true) .build(); - public static final Option STORAGE_DBLOCK = new OptionBuilder<>("storage-dblock", String.class) + public static final Option STORAGE_GLOBAL_LOCK_PROVIDER = new OptionBuilder<>("storage-global-lock-provider", String.class) .category(OptionCategory.STORAGE) .hidden() .buildTime(true) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java index 59671587d2..0996cd05dd 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java @@ -195,10 +195,10 @@ final class StoragePropertyMappers { .transformer(StoragePropertyMappers::resolveMapStorageProvider) .paramLabel("type") .build(), - fromOption(StorageOptions.STORAGE_DBLOCK) - .to("kc.spi-dblock-provider") + fromOption(StorageOptions.STORAGE_GLOBAL_LOCK_PROVIDER) + .to("kc.spi-global-lock-provider") .mapFrom("storage") - .transformer(StoragePropertyMappers::getDbLockProvider) + .transformer(StoragePropertyMappers::getGlobalLockProvider) .paramLabel("type") .build(), fromOption(StorageOptions.STORAGE_CACHE_REALM_ENABLED) @@ -308,8 +308,8 @@ final class StoragePropertyMappers { return of(storage.isEmpty() ? "infinispan" : "map"); } - private static Optional getDbLockProvider(Optional storage, ConfigSourceInterceptorContext context) { - return of(storage.isEmpty() ? "jpa" : "none"); + private static Optional getGlobalLockProvider(Optional storage, ConfigSourceInterceptorContext context) { + return of(storage.isEmpty() ? "dblock" : "none"); } private static Optional getUserSessionPersisterStorage(Optional storage, ConfigSourceInterceptorContext context) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java index 0a1b1128a5..70ac9355e6 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java @@ -30,6 +30,7 @@ import java.sql.Statement; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeoutException; import javax.enterprise.inject.Instance; import javax.persistence.EntityManager; @@ -50,8 +51,10 @@ import org.keycloak.migration.MigrationModelManager; import org.keycloak.migration.ModelVersion; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.dblock.DBLockManager; -import org.keycloak.models.dblock.DBLockProvider; +import org.keycloak.models.dblock.DBLockGlobalLockProvider; +import org.keycloak.models.locking.GlobalLock; +import org.keycloak.models.locking.GlobalLockProvider; +import org.keycloak.models.locking.LockAcquiringTimeoutException; import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigurationBuilder; @@ -291,25 +294,21 @@ public class LegacyJpaConnectionProviderFactory extends AbstractJpaConnectionPro } private void update(Connection connection, String schema, KeycloakSession session, JpaUpdaterProvider updater) { - DBLockManager dbLockManager = new DBLockManager(session); - DBLockProvider dbLock2 = dbLockManager.getDBLock(); - dbLock2.waitForLock(DBLockProvider.Namespace.DATABASE); - try { + GlobalLockProvider globalLock = session.getProvider(GlobalLockProvider.class); + try (GlobalLock l = globalLock.acquireLock(DBLockGlobalLockProvider.DATABASE)) { updater.update(connection, schema); - } finally { - dbLock2.releaseLock(); + } catch (LockAcquiringTimeoutException e) { + throw new RuntimeException("Acquiring database failed.", e); } } private void export(Connection connection, String schema, File databaseUpdateFile, KeycloakSession session, JpaUpdaterProvider updater) { - DBLockManager dbLockManager = new DBLockManager(session); - DBLockProvider dbLock2 = dbLockManager.getDBLock(); - dbLock2.waitForLock(DBLockProvider.Namespace.DATABASE); - try { + GlobalLockProvider globalLock = session.getProvider(GlobalLockProvider.class); + try (GlobalLock l = globalLock.acquireLock(DBLockGlobalLockProvider.DATABASE)) { updater.export(connection, schema, databaseUpdateFile); - } finally { - dbLock2.releaseLock(); + } catch (LockAcquiringTimeoutException e) { + throw new RuntimeException("Acquiring database failed.", e); } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLock.java b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLock.java new file mode 100644 index 0000000000..7ed5378081 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLock.java @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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.locking; + +/** + * An object of this type represents a successfully acquired global lock provided by {@link GlobalLockProvider} + */ +public interface GlobalLock extends AutoCloseable { + + /** + * Releases the lock represented by this + */ + @Override + void close(); + + public static class Constants { + public static final String KEYCLOAK_BOOT = "keycloak-boot"; + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProvider.java b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProvider.java new file mode 100644 index 0000000000..bfc7467002 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProvider.java @@ -0,0 +1,79 @@ +/* + * Copyright 2022 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.locking; + +import org.keycloak.provider.Provider; + +import java.time.Duration; + +public interface GlobalLockProvider extends Provider { + + /** + * Effectively the same as {@code acquire(lockName, null)} + *

+ * This method is intended to be used in a {@code try}-with-resources block. + * + * @param lockName Identifier used for acquiring lock. Can be any non-null string. + * @return Instance of {@link GlobalLock} representing successfully acquired global lock. + * @throws LockAcquiringTimeoutException When acquiring the global lock times out + * (see Javadoc of {@link #acquire(String, Duration)} for more details on how the time + * duration is determined) + * @throws NullPointerException When lockName is {@code null}. + */ + default GlobalLock acquireLock(String lockName) throws LockAcquiringTimeoutException { + return acquire(lockName, null); + } + + /** + * Acquires a new global lock that is visible to all Keycloak nodes. The lock is non-reentrant. + *

+ * The lock is guaranteed to be kept until the returned {@link GlobalLock} is closed + * using the {@link GlobalLock#close} method. + *

+ * Some implementations may benefit from locks that are released at the end of transaction. + * For this purpose, the lifespan of the returned lock is limited by the transaction lifespan + * of the session which acquired this lock. + *

+ * This method is intended to be used in a {@code try}-with-resources block. + *

+ * If there is another global lock with the same identifier ({@code lockName}) already acquired, this method waits + * until the lock is released, however, not more than {@code timeToWaitForLock} duration. If the lock is not + * acquired after {@code timeToWaitForLock} duration, the method throws {@link LockAcquiringTimeoutException} + *

+ * Releasing of the lock is done using instance of {@link GlobalLock} returned by this method. + * + * @param lockName Identifier used for acquiring lock. Can be any non-null string. + * @param timeToWaitForLock Duration this method waits until it gives up acquiring the lock. If {@code null}, + * each implementation should provide some default duration, for example using + * configuration option. + * @return Instance of {@link GlobalLock} representing successfully acquired global lock. + * + * @throws LockAcquiringTimeoutException When the method waits for {@code timeToWaitForLock} duration and the lock is still + * not available to acquire. + * @throws NullPointerException When {@code lockName} is {@code null}. + */ + GlobalLock acquire(String lockName, Duration timeToWaitForLock) throws LockAcquiringTimeoutException; + + /** + * Releases all locks acquired by this GlobalLockProvider. + *

+ * This method must unlock all existing locks acquired by this provider regardless of the thread + * or Keycloak instance that originally acquired them. + */ + void forceReleaseAllLocks(); +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderFactory.java new file mode 100644 index 0000000000..30575bd498 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderFactory.java @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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.locking; + +import org.keycloak.provider.ProviderFactory; + +public interface GlobalLockProviderFactory extends ProviderFactory { +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderSpi.java new file mode 100644 index 0000000000..b99cf05920 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/locking/GlobalLockProviderSpi.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 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.locking; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +public class GlobalLockProviderSpi implements Spi { + + public static final String GLOBAL_LOCK = "globalLock"; + + @Override + public boolean isInternal() { + return true; + } + + @Override + public String getName() { + return GLOBAL_LOCK; + } + + @Override + public Class getProviderClass() { + return GlobalLockProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return GlobalLockProviderFactory.class; + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/locking/LockAcquiringTimeoutException.java b/server-spi-private/src/main/java/org/keycloak/models/locking/LockAcquiringTimeoutException.java new file mode 100644 index 0000000000..6e4402ea31 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/locking/LockAcquiringTimeoutException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 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.locking; + +import java.time.Instant; + +/** + * This exception is thrown when acquiring a lock times out. + */ +public final class LockAcquiringTimeoutException extends Exception { + + private LockAcquiringTimeoutException() {} + + /** + * + * @param lockName Identifier of a lock whose acquiring was unsuccessful. + * @param keycloakInstanceIdentifier Identifier of a Keycloak instance that is currently holding the lock. + * @param timeWhenAcquired Time instant when the lock held by {@code keycloakInstanceIdentifier} was acquired. + */ + public LockAcquiringTimeoutException(String lockName, String keycloakInstanceIdentifier, Instant timeWhenAcquired) { + super(String.format("Lock [%s] already acquired by keycloak instance [%s] at the time [%s]", lockName, keycloakInstanceIdentifier, timeWhenAcquired.toString())); + } + + /** + * + * @param lockName Identifier of a lock whose acquiring was unsuccessful. + * @param keycloakInstanceIdentifier Identifier of a Keycloak instance that is currently holding the lock. + * @param timeWhenAcquired Time instant when the lock held by {@code keycloakInstanceIdentifier} was acquired. + * @param cause The cause. + */ + public LockAcquiringTimeoutException(String lockName, String keycloakInstanceIdentifier, Instant timeWhenAcquired, Throwable cause) { + super(String.format("Lock [%s] already acquired by keycloak instance [%s] at the time [%s]", lockName, keycloakInstanceIdentifier, timeWhenAcquired.toString()), cause); + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/dblock/NoLockingDBLockProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/locking/NoneGlobalLockProviderFactory.java similarity index 64% rename from server-spi-private/src/main/java/org/keycloak/models/dblock/NoLockingDBLockProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/locking/NoneGlobalLockProviderFactory.java index e2945c6fda..bc29cc2927 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/dblock/NoLockingDBLockProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/locking/NoneGlobalLockProviderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 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"); @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.models.dblock; +package org.keycloak.models.locking; import org.keycloak.Config; import org.keycloak.common.Profile; @@ -23,29 +23,30 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.provider.EnvironmentDependentProviderFactory; -public class NoLockingDBLockProviderFactory implements DBLockProviderFactory, EnvironmentDependentProviderFactory { +import java.time.Duration; + +public class NoneGlobalLockProviderFactory implements GlobalLockProviderFactory, EnvironmentDependentProviderFactory { public static final String PROVIDER_ID = "none"; @Override - public void setTimeouts(long lockRecheckTimeMillis, long lockWaitTimeoutMillis) { - } - - @Override - public DBLockProvider create(KeycloakSession session) { + public GlobalLockProvider create(KeycloakSession session) { return INSTANCE; } @Override public void init(Config.Scope config) { + } @Override public void postInit(KeycloakSessionFactory factory) { + } @Override public void close() { + } @Override @@ -58,32 +59,20 @@ public class NoLockingDBLockProviderFactory implements DBLockProviderFactory, En return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE); } - private static final DBLockProvider INSTANCE = new DBLockProvider() { - @Override - public void waitForLock(DBLockProvider.Namespace lock) { - } - - @Override - public void releaseLock() { - } - - @Override - public DBLockProvider.Namespace getCurrentLock() { - return null; - } - - @Override - public boolean supportsForcedUnlock() { - return false; - } - - @Override - public void destroyLockInfo() { - } - + private static final GlobalLockProvider INSTANCE = new GlobalLockProvider() { @Override public void close() { + + } + + @Override + public GlobalLock acquire(String lockName, Duration timeToWaitForLock) { + return () -> {}; + } + + @Override + public void forceReleaseAllLocks() { + } }; - } diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.dblock.DBLockProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory similarity index 91% rename from server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.dblock.DBLockProviderFactory rename to server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory index ef4d96a73e..b0c6891c30 100644 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.dblock.DBLockProviderFactory +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.locking.GlobalLockProviderFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.keycloak.models.dblock.NoLockingDBLockProviderFactory +org.keycloak.models.locking.NoneGlobalLockProviderFactory diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index e460f50021..8defe02362 100755 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -28,7 +28,7 @@ org.keycloak.models.SingleUseObjectSpi org.keycloak.models.UserSessionSpi org.keycloak.models.UserLoginFailureSpi org.keycloak.models.UserSpi -org.keycloak.models.dblock.DBLockSpi +org.keycloak.models.locking.GlobalLockProviderSpi org.keycloak.migration.MigrationSpi org.keycloak.events.EventListenerSpi org.keycloak.events.EventStoreSpi diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index eae9a2ea35..0f4ddbb601 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -32,8 +32,9 @@ import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; -import org.keycloak.models.dblock.DBLockManager; -import org.keycloak.models.dblock.DBLockProvider; +import org.keycloak.models.locking.GlobalLock; +import org.keycloak.models.locking.GlobalLockProvider; +import org.keycloak.models.locking.LockAcquiringTimeoutException; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.models.utils.RepresentationToModel; @@ -70,6 +71,7 @@ import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -131,17 +133,27 @@ public class KeycloakApplication extends Application { ExportImportManager[] exportImportManager = new ExportImportManager[1]; + // Release all locks acquired by currently used GlobalLockProvider if keycloak.globalLock.forceUnlock is equal + // to true. This can be used to recover from a state where there are some stale locks that were not correctly + // unlocked + if (Boolean.getBoolean("keycloak.globalLock.forceUnlock")) { + KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { + @Override + public void run(KeycloakSession session) { + GlobalLockProvider locks = session.getProvider(GlobalLockProvider.class); + locks.forceReleaseAllLocks(); + } + }); + } + KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { @Override public void run(KeycloakSession session) { - DBLockManager dbLockManager = new DBLockManager(session); - dbLockManager.checkForcedUnlock(); - DBLockProvider dbLock = dbLockManager.getDBLock(); - dbLock.waitForLock(DBLockProvider.Namespace.KEYCLOAK_BOOT); - try { + GlobalLockProvider locks = session.getProvider(GlobalLockProvider.class); + try (GlobalLock l = locks.acquireLock(GlobalLock.Constants.KEYCLOAK_BOOT)) { exportImportManager[0] = bootstrap(); - } finally { - dbLock.releaseLock(); + } catch (LockAcquiringTimeoutException e) { + throw new RuntimeException("Acquiring keycloak-boot lock failed.", e); } } }); diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 913bc4774f..4bc211e251 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -784,7 +784,7 @@ enabled concurrenthashmap - none + none map map map 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 1fae3debdf..e05842133e 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 @@ -51,8 +51,8 @@ } }, - "dblock": { - "provider": "${keycloak.dblock.provider:jpa}" + "globalLock": { + "provider": "${keycloak.globalLock.provider:dblock}" }, "realm": { diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index 9e6bd980f8..ee8b4d8fd7 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -316,7 +316,7 @@ map enabled - LegacyJpa,Map,ConcurrentHashMapStorage + Map,ConcurrentHashMapStorage @@ -324,7 +324,7 @@ hot-rod enabled - LegacyJpa,Map,HotRodMapStorage + Map,HotRodMapStorage @@ -332,7 +332,7 @@ map-ldap enabled - Jpa,Map,LdapMapStorage + Map,LdapMapStorage diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java index e2f4b8fe4e..f8fc1ffeec 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java @@ -45,6 +45,7 @@ import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.UserLoginFailureSpi; import org.keycloak.models.UserSessionSpi; import org.keycloak.models.UserSpi; +import org.keycloak.models.locking.GlobalLockProviderSpi; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.provider.Provider; @@ -102,7 +103,6 @@ import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.keycloak.models.DeploymentStateProviderFactory; -import org.keycloak.models.dblock.DBLockSpi; /** * Base of testcases that operate on session level. The tests derived from this class @@ -219,7 +219,7 @@ public abstract class KeycloakModelTest { .add(ClientSpi.class) .add(ComponentFactorySpi.class) .add(ClusterSpi.class) - .add(DBLockSpi.class) + .add(GlobalLockProviderSpi.class) .add(EventStoreSpi.class) .add(ExecutorsSpi.class) .add(GroupSpi.class) 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 index 34fcdc219f..c2e1447412 100644 --- 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 @@ -24,7 +24,6 @@ import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.SingleUseObjectSpi; 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; @@ -92,7 +91,6 @@ public class HotRodMapStorage extends KeycloakModelParameters { .spi("user").provider(MapUserProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) .spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) - .spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-admin-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID) .config("storage-auth-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java index c7d0d9ddf9..f68fb2d833 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java @@ -25,7 +25,6 @@ import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.SingleUseObjectSpi; 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; @@ -107,7 +106,6 @@ public class JpaMapStorage extends KeycloakModelParameters { .spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi("user").provider(MapUserProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) - .spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java index add3ee2680..74ae77c322 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java @@ -25,7 +25,10 @@ import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionPr import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionSpi; import org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProviderFactory; import org.keycloak.events.jpa.JpaEventStoreProviderFactory; +import org.keycloak.models.dblock.DBLockGlobalLockProviderFactory; +import org.keycloak.models.dblock.DBLockSpi; import org.keycloak.models.jpa.session.JpaUserSessionPersisterProviderFactory; +import org.keycloak.models.locking.GlobalLockProviderSpi; import org.keycloak.models.session.UserSessionPersisterSpi; import org.keycloak.migration.MigrationProviderFactory; import org.keycloak.migration.MigrationSpi; @@ -65,6 +68,8 @@ public class LegacyJpa extends KeycloakModelParameters { .add(MigrationSpi.class) .add(LoginProtocolSpi.class) + .add(DBLockSpi.class) + .build(); static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() @@ -83,6 +88,7 @@ public class LegacyJpa extends KeycloakModelParameters { .add(JpaUserProviderFactory.class) .add(LiquibaseConnectionProviderFactory.class) .add(LiquibaseDBLockProviderFactory.class) + .add(DBLockGlobalLockProviderFactory.class) .add(JpaUserSessionPersisterProviderFactory.class) //required for migrateModel @@ -110,6 +116,7 @@ public class LegacyJpa extends KeycloakModelParameters { .spi("realm").defaultProvider("jpa") .spi("deploymentState").defaultProvider("jpa") .spi("dblock").defaultProvider("jpa") + .spi(GlobalLockProviderSpi.GLOBAL_LOCK).defaultProvider(DBLockGlobalLockProviderFactory.PROVIDER_ID) ; } } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java index 169d80f061..0f140cd757 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java @@ -24,7 +24,8 @@ import org.keycloak.models.SingleUseObjectProviderFactory; import org.keycloak.models.SingleUseObjectSpi; import org.keycloak.models.UserLoginFailureSpi; import org.keycloak.models.UserSessionSpi; -import org.keycloak.models.dblock.NoLockingDBLockProviderFactory; +import org.keycloak.models.locking.GlobalLockProviderSpi; +import org.keycloak.models.locking.NoneGlobalLockProviderFactory; import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory; import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory; import org.keycloak.models.map.events.MapEventStoreProviderFactory; @@ -75,7 +76,7 @@ public class Map extends KeycloakModelParameters { .add(MapUserProviderFactory.class) .add(MapUserSessionProviderFactory.class) .add(MapUserLoginFailureProviderFactory.class) - .add(NoLockingDBLockProviderFactory.class) + .add(NoneGlobalLockProviderFactory.class) .add(MapEventStoreProviderFactory.class) .add(SingleUseObjectProviderFactory.class) .add(MapPublicKeyStorageProviderFactory.class) @@ -99,7 +100,7 @@ public class Map extends KeycloakModelParameters { .spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID) .spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID) .spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID) - .spi("dblock").defaultProvider(NoLockingDBLockProviderFactory.PROVIDER_ID) + .spi(GlobalLockProviderSpi.GLOBAL_LOCK).defaultProvider(NoneGlobalLockProviderFactory.PROVIDER_ID) .spi(EventStoreSpi.NAME).defaultProvider(MapEventStoreProviderFactory.PROVIDER_ID) .spi("publicKeyStorage").defaultProvider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) ; diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index f95fd4dbad..b7863a2e58 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -315,7 +315,7 @@ keycloak.profile.feature.map_storageenabled - keycloak.dblock.providernone + keycloak.globalLock.providernone keycloak.realm.providermap keycloak.client.providermap keycloak.clientScope.providermap 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 0b042e8ebd..e21397d2c1 100755 --- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json @@ -32,8 +32,8 @@ } }, - "dblock": { - "provider": "${keycloak.dblock.provider:jpa}" + "globalLock": { + "provider": "${keycloak.globalLock.provider:dblock}" }, "realm": {