Deprecate DBLockProvider and replace it with new GlobalLockProvider
Closes #9388
This commit is contained in:
parent
5e891951f5
commit
6d683824a4
31 changed files with 486 additions and 88 deletions
|
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -18,6 +18,7 @@
|
||||||
org.keycloak.models.cache.CacheUserProviderSpi
|
org.keycloak.models.cache.CacheUserProviderSpi
|
||||||
org.keycloak.models.cache.CacheRealmProviderSpi
|
org.keycloak.models.cache.CacheRealmProviderSpi
|
||||||
org.keycloak.models.cache.CachePublicKeyProviderSpi
|
org.keycloak.models.cache.CachePublicKeyProviderSpi
|
||||||
|
org.keycloak.models.dblock.DBLockSpi
|
||||||
org.keycloak.storage.client.ClientStorageProviderSpi
|
org.keycloak.storage.client.ClientStorageProviderSpi
|
||||||
org.keycloak.storage.group.GroupStorageProviderSpi
|
org.keycloak.storage.group.GroupStorageProviderSpi
|
||||||
org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi
|
org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
@ -64,7 +65,9 @@ import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserLoginFailureModel;
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
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.MapProtocolMapperEntity;
|
||||||
import org.keycloak.models.map.client.MapProtocolMapperEntityImpl;
|
import org.keycloak.models.map.client.MapProtocolMapperEntityImpl;
|
||||||
import org.keycloak.models.map.common.DeepCloner;
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
|
@ -433,13 +436,11 @@ public class JpaMapStorageProviderFactory implements
|
||||||
|
|
||||||
private void update(Class<?> modelType, Connection connection, KeycloakSession session) {
|
private void update(Class<?> modelType, Connection connection, KeycloakSession session) {
|
||||||
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession lockSession) -> {
|
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession lockSession) -> {
|
||||||
// TODO locking tables based on modelType: https://github.com/keycloak/keycloak/issues/9388
|
GlobalLockProvider globalLock = session.getProvider(GlobalLockProvider.class);
|
||||||
DBLockProvider dbLock = session.getProvider(DBLockProvider.class);
|
try (GlobalLock l = globalLock.acquireLock(modelType.getName())) {
|
||||||
dbLock.waitForLock(DBLockProvider.Namespace.DATABASE);
|
|
||||||
try {
|
|
||||||
session.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, config.get("schema"));
|
session.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, config.get("schema"));
|
||||||
} finally {
|
} catch (LockAcquiringTimeoutException e) {
|
||||||
dbLock.releaseLock();
|
throw new RuntimeException("Acquiring " + modelType.getName() + " failed.", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,7 +217,7 @@ public class StorageOptions {
|
||||||
.buildTime(true)
|
.buildTime(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static final Option<String> STORAGE_DBLOCK = new OptionBuilder<>("storage-dblock", String.class)
|
public static final Option<String> STORAGE_GLOBAL_LOCK_PROVIDER = new OptionBuilder<>("storage-global-lock-provider", String.class)
|
||||||
.category(OptionCategory.STORAGE)
|
.category(OptionCategory.STORAGE)
|
||||||
.hidden()
|
.hidden()
|
||||||
.buildTime(true)
|
.buildTime(true)
|
||||||
|
|
|
@ -195,10 +195,10 @@ final class StoragePropertyMappers {
|
||||||
.transformer(StoragePropertyMappers::resolveMapStorageProvider)
|
.transformer(StoragePropertyMappers::resolveMapStorageProvider)
|
||||||
.paramLabel("type")
|
.paramLabel("type")
|
||||||
.build(),
|
.build(),
|
||||||
fromOption(StorageOptions.STORAGE_DBLOCK)
|
fromOption(StorageOptions.STORAGE_GLOBAL_LOCK_PROVIDER)
|
||||||
.to("kc.spi-dblock-provider")
|
.to("kc.spi-global-lock-provider")
|
||||||
.mapFrom("storage")
|
.mapFrom("storage")
|
||||||
.transformer(StoragePropertyMappers::getDbLockProvider)
|
.transformer(StoragePropertyMappers::getGlobalLockProvider)
|
||||||
.paramLabel("type")
|
.paramLabel("type")
|
||||||
.build(),
|
.build(),
|
||||||
fromOption(StorageOptions.STORAGE_CACHE_REALM_ENABLED)
|
fromOption(StorageOptions.STORAGE_CACHE_REALM_ENABLED)
|
||||||
|
@ -308,8 +308,8 @@ final class StoragePropertyMappers {
|
||||||
return of(storage.isEmpty() ? "infinispan" : "map");
|
return of(storage.isEmpty() ? "infinispan" : "map");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optional<String> getDbLockProvider(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
private static Optional<String> getGlobalLockProvider(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||||
return of(storage.isEmpty() ? "jpa" : "none");
|
return of(storage.isEmpty() ? "dblock" : "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optional<String> getUserSessionPersisterStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
private static Optional<String> getUserSessionPersisterStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.sql.Statement;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javax.enterprise.inject.Instance;
|
import javax.enterprise.inject.Instance;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
@ -50,8 +51,10 @@ import org.keycloak.migration.MigrationModelManager;
|
||||||
import org.keycloak.migration.ModelVersion;
|
import org.keycloak.migration.ModelVersion;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.dblock.DBLockManager;
|
import org.keycloak.models.dblock.DBLockGlobalLockProvider;
|
||||||
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.provider.EnvironmentDependentProviderFactory;
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
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) {
|
private void update(Connection connection, String schema, KeycloakSession session, JpaUpdaterProvider updater) {
|
||||||
DBLockManager dbLockManager = new DBLockManager(session);
|
GlobalLockProvider globalLock = session.getProvider(GlobalLockProvider.class);
|
||||||
DBLockProvider dbLock2 = dbLockManager.getDBLock();
|
try (GlobalLock l = globalLock.acquireLock(DBLockGlobalLockProvider.DATABASE)) {
|
||||||
dbLock2.waitForLock(DBLockProvider.Namespace.DATABASE);
|
|
||||||
try {
|
|
||||||
updater.update(connection, schema);
|
updater.update(connection, schema);
|
||||||
} finally {
|
} catch (LockAcquiringTimeoutException e) {
|
||||||
dbLock2.releaseLock();
|
throw new RuntimeException("Acquiring database failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void export(Connection connection, String schema, File databaseUpdateFile, KeycloakSession session,
|
private void export(Connection connection, String schema, File databaseUpdateFile, KeycloakSession session,
|
||||||
JpaUpdaterProvider updater) {
|
JpaUpdaterProvider updater) {
|
||||||
DBLockManager dbLockManager = new DBLockManager(session);
|
GlobalLockProvider globalLock = session.getProvider(GlobalLockProvider.class);
|
||||||
DBLockProvider dbLock2 = dbLockManager.getDBLock();
|
try (GlobalLock l = globalLock.acquireLock(DBLockGlobalLockProvider.DATABASE)) {
|
||||||
dbLock2.waitForLock(DBLockProvider.Namespace.DATABASE);
|
|
||||||
try {
|
|
||||||
updater.export(connection, schema, databaseUpdateFile);
|
updater.export(connection, schema, databaseUpdateFile);
|
||||||
} finally {
|
} catch (LockAcquiringTimeoutException e) {
|
||||||
dbLock2.releaseLock();
|
throw new RuntimeException("Acquiring database failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)}
|
||||||
|
* <p />
|
||||||
|
* 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.
|
||||||
|
* <p />
|
||||||
|
* The lock is guaranteed to be kept until the returned {@link GlobalLock} is closed
|
||||||
|
* using the {@link GlobalLock#close} method.
|
||||||
|
* <p />
|
||||||
|
* 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.
|
||||||
|
* <p />
|
||||||
|
* This method is intended to be used in a {@code try}-with-resources block.
|
||||||
|
* <p />
|
||||||
|
* 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}
|
||||||
|
* <p />
|
||||||
|
* 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.
|
||||||
|
* <p />
|
||||||
|
* This method must unlock all existing locks acquired by this provider regardless of the thread
|
||||||
|
* or Keycloak instance that originally acquired them.
|
||||||
|
*/
|
||||||
|
void forceReleaseAllLocks();
|
||||||
|
}
|
|
@ -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<GlobalLockProvider> {
|
||||||
|
}
|
|
@ -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<? extends Provider> getProviderClass() {
|
||||||
|
return GlobalLockProvider.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||||
|
return GlobalLockProviderFactory.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
* and other contributors as indicated by the @author tags.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.models.dblock;
|
package org.keycloak.models.locking;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
@ -23,29 +23,30 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
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";
|
public static final String PROVIDER_ID = "none";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTimeouts(long lockRecheckTimeMillis, long lockWaitTimeoutMillis) {
|
public GlobalLockProvider create(KeycloakSession session) {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DBLockProvider create(KeycloakSession session) {
|
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -58,32 +59,20 @@ public class NoLockingDBLockProviderFactory implements DBLockProviderFactory, En
|
||||||
return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
|
return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final DBLockProvider INSTANCE = new DBLockProvider() {
|
private static final GlobalLockProvider INSTANCE = new GlobalLockProvider() {
|
||||||
@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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlobalLock acquire(String lockName, Duration timeToWaitForLock) {
|
||||||
|
return () -> {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forceReleaseAllLocks() {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,4 +15,4 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
org.keycloak.models.dblock.NoLockingDBLockProviderFactory
|
org.keycloak.models.locking.NoneGlobalLockProviderFactory
|
|
@ -28,7 +28,7 @@ org.keycloak.models.SingleUseObjectSpi
|
||||||
org.keycloak.models.UserSessionSpi
|
org.keycloak.models.UserSessionSpi
|
||||||
org.keycloak.models.UserLoginFailureSpi
|
org.keycloak.models.UserLoginFailureSpi
|
||||||
org.keycloak.models.UserSpi
|
org.keycloak.models.UserSpi
|
||||||
org.keycloak.models.dblock.DBLockSpi
|
org.keycloak.models.locking.GlobalLockProviderSpi
|
||||||
org.keycloak.migration.MigrationSpi
|
org.keycloak.migration.MigrationSpi
|
||||||
org.keycloak.events.EventListenerSpi
|
org.keycloak.events.EventListenerSpi
|
||||||
org.keycloak.events.EventStoreSpi
|
org.keycloak.events.EventStoreSpi
|
||||||
|
|
|
@ -32,8 +32,9 @@ import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.dblock.DBLockManager;
|
import org.keycloak.models.locking.GlobalLock;
|
||||||
import org.keycloak.models.dblock.DBLockProvider;
|
import org.keycloak.models.locking.GlobalLockProvider;
|
||||||
|
import org.keycloak.models.locking.LockAcquiringTimeoutException;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.PostMigrationEvent;
|
import org.keycloak.models.utils.PostMigrationEvent;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
@ -70,6 +71,7 @@ import java.util.Optional;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -131,17 +133,27 @@ public class KeycloakApplication extends Application {
|
||||||
|
|
||||||
ExportImportManager[] exportImportManager = new ExportImportManager[1];
|
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() {
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run(KeycloakSession session) {
|
public void run(KeycloakSession session) {
|
||||||
DBLockManager dbLockManager = new DBLockManager(session);
|
GlobalLockProvider locks = session.getProvider(GlobalLockProvider.class);
|
||||||
dbLockManager.checkForcedUnlock();
|
try (GlobalLock l = locks.acquireLock(GlobalLock.Constants.KEYCLOAK_BOOT)) {
|
||||||
DBLockProvider dbLock = dbLockManager.getDBLock();
|
|
||||||
dbLock.waitForLock(DBLockProvider.Namespace.KEYCLOAK_BOOT);
|
|
||||||
try {
|
|
||||||
exportImportManager[0] = bootstrap();
|
exportImportManager[0] = bootstrap();
|
||||||
} finally {
|
} catch (LockAcquiringTimeoutException e) {
|
||||||
dbLock.releaseLock();
|
throw new RuntimeException("Acquiring keycloak-boot lock failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -784,7 +784,7 @@
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
||||||
<keycloak.mapStorage.provider>concurrenthashmap</keycloak.mapStorage.provider>
|
<keycloak.mapStorage.provider>concurrenthashmap</keycloak.mapStorage.provider>
|
||||||
<keycloak.dblock.provider>none</keycloak.dblock.provider>
|
<keycloak.globalLock.provider>none</keycloak.globalLock.provider>
|
||||||
<keycloak.realm.provider>map</keycloak.realm.provider>
|
<keycloak.realm.provider>map</keycloak.realm.provider>
|
||||||
<keycloak.client.provider>map</keycloak.client.provider>
|
<keycloak.client.provider>map</keycloak.client.provider>
|
||||||
<keycloak.clientScope.provider>map</keycloak.clientScope.provider>
|
<keycloak.clientScope.provider>map</keycloak.clientScope.provider>
|
||||||
|
|
|
@ -51,8 +51,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"dblock": {
|
"globalLock": {
|
||||||
"provider": "${keycloak.dblock.provider:jpa}"
|
"provider": "${keycloak.globalLock.provider:dblock}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"realm": {
|
"realm": {
|
||||||
|
|
|
@ -316,7 +316,7 @@
|
||||||
<id>map</id>
|
<id>map</id>
|
||||||
<properties>
|
<properties>
|
||||||
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
||||||
<keycloak.model.parameters>LegacyJpa,Map,ConcurrentHashMapStorage</keycloak.model.parameters>
|
<keycloak.model.parameters>Map,ConcurrentHashMapStorage</keycloak.model.parameters>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@
|
||||||
<id>hot-rod</id>
|
<id>hot-rod</id>
|
||||||
<properties>
|
<properties>
|
||||||
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
||||||
<keycloak.model.parameters>LegacyJpa,Map,HotRodMapStorage</keycloak.model.parameters>
|
<keycloak.model.parameters>Map,HotRodMapStorage</keycloak.model.parameters>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@
|
||||||
<id>map-ldap</id>
|
<id>map-ldap</id>
|
||||||
<properties>
|
<properties>
|
||||||
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
|
||||||
<keycloak.model.parameters>Jpa,Map,LdapMapStorage</keycloak.model.parameters>
|
<keycloak.model.parameters>Map,LdapMapStorage</keycloak.model.parameters>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.keycloak.models.DeploymentStateSpi;
|
||||||
import org.keycloak.models.UserLoginFailureSpi;
|
import org.keycloak.models.UserLoginFailureSpi;
|
||||||
import org.keycloak.models.UserSessionSpi;
|
import org.keycloak.models.UserSessionSpi;
|
||||||
import org.keycloak.models.UserSpi;
|
import org.keycloak.models.UserSpi;
|
||||||
|
import org.keycloak.models.locking.GlobalLockProviderSpi;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.PostMigrationEvent;
|
import org.keycloak.models.utils.PostMigrationEvent;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
@ -102,7 +103,6 @@ import org.junit.rules.TestWatcher;
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
import org.junit.runners.model.Statement;
|
import org.junit.runners.model.Statement;
|
||||||
import org.keycloak.models.DeploymentStateProviderFactory;
|
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
|
* 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(ClientSpi.class)
|
||||||
.add(ComponentFactorySpi.class)
|
.add(ComponentFactorySpi.class)
|
||||||
.add(ClusterSpi.class)
|
.add(ClusterSpi.class)
|
||||||
.add(DBLockSpi.class)
|
.add(GlobalLockProviderSpi.class)
|
||||||
.add(EventStoreSpi.class)
|
.add(EventStoreSpi.class)
|
||||||
.add(ExecutorsSpi.class)
|
.add(ExecutorsSpi.class)
|
||||||
.add(GroupSpi.class)
|
.add(GroupSpi.class)
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.keycloak.models.DeploymentStateSpi;
|
||||||
import org.keycloak.models.SingleUseObjectSpi;
|
import org.keycloak.models.SingleUseObjectSpi;
|
||||||
import org.keycloak.models.UserLoginFailureSpi;
|
import org.keycloak.models.UserLoginFailureSpi;
|
||||||
import org.keycloak.models.UserSessionSpi;
|
import org.keycloak.models.UserSessionSpi;
|
||||||
import org.keycloak.models.dblock.NoLockingDBLockProviderFactory;
|
|
||||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
||||||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||||
import org.keycloak.models.map.client.MapClientProviderFactory;
|
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("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(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(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)
|
.spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-admin-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.config("storage-auth-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID);
|
.config("storage-auth-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.keycloak.models.DeploymentStateSpi;
|
||||||
import org.keycloak.models.SingleUseObjectSpi;
|
import org.keycloak.models.SingleUseObjectSpi;
|
||||||
import org.keycloak.models.UserLoginFailureSpi;
|
import org.keycloak.models.UserLoginFailureSpi;
|
||||||
import org.keycloak.models.UserSessionSpi;
|
import org.keycloak.models.UserSessionSpi;
|
||||||
import org.keycloak.models.dblock.NoLockingDBLockProviderFactory;
|
|
||||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
||||||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||||
import org.keycloak.models.map.client.MapClientProviderFactory;
|
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(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("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(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(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("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||||
|
|
|
@ -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.conn.LiquibaseConnectionSpi;
|
||||||
import org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProviderFactory;
|
import org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProviderFactory;
|
||||||
import org.keycloak.events.jpa.JpaEventStoreProviderFactory;
|
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.jpa.session.JpaUserSessionPersisterProviderFactory;
|
||||||
|
import org.keycloak.models.locking.GlobalLockProviderSpi;
|
||||||
import org.keycloak.models.session.UserSessionPersisterSpi;
|
import org.keycloak.models.session.UserSessionPersisterSpi;
|
||||||
import org.keycloak.migration.MigrationProviderFactory;
|
import org.keycloak.migration.MigrationProviderFactory;
|
||||||
import org.keycloak.migration.MigrationSpi;
|
import org.keycloak.migration.MigrationSpi;
|
||||||
|
@ -65,6 +68,8 @@ public class LegacyJpa extends KeycloakModelParameters {
|
||||||
.add(MigrationSpi.class)
|
.add(MigrationSpi.class)
|
||||||
.add(LoginProtocolSpi.class)
|
.add(LoginProtocolSpi.class)
|
||||||
|
|
||||||
|
.add(DBLockSpi.class)
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
||||||
|
@ -83,6 +88,7 @@ public class LegacyJpa extends KeycloakModelParameters {
|
||||||
.add(JpaUserProviderFactory.class)
|
.add(JpaUserProviderFactory.class)
|
||||||
.add(LiquibaseConnectionProviderFactory.class)
|
.add(LiquibaseConnectionProviderFactory.class)
|
||||||
.add(LiquibaseDBLockProviderFactory.class)
|
.add(LiquibaseDBLockProviderFactory.class)
|
||||||
|
.add(DBLockGlobalLockProviderFactory.class)
|
||||||
.add(JpaUserSessionPersisterProviderFactory.class)
|
.add(JpaUserSessionPersisterProviderFactory.class)
|
||||||
|
|
||||||
//required for migrateModel
|
//required for migrateModel
|
||||||
|
@ -110,6 +116,7 @@ public class LegacyJpa extends KeycloakModelParameters {
|
||||||
.spi("realm").defaultProvider("jpa")
|
.spi("realm").defaultProvider("jpa")
|
||||||
.spi("deploymentState").defaultProvider("jpa")
|
.spi("deploymentState").defaultProvider("jpa")
|
||||||
.spi("dblock").defaultProvider("jpa")
|
.spi("dblock").defaultProvider("jpa")
|
||||||
|
.spi(GlobalLockProviderSpi.GLOBAL_LOCK).defaultProvider(DBLockGlobalLockProviderFactory.PROVIDER_ID)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ import org.keycloak.models.SingleUseObjectProviderFactory;
|
||||||
import org.keycloak.models.SingleUseObjectSpi;
|
import org.keycloak.models.SingleUseObjectSpi;
|
||||||
import org.keycloak.models.UserLoginFailureSpi;
|
import org.keycloak.models.UserLoginFailureSpi;
|
||||||
import org.keycloak.models.UserSessionSpi;
|
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.authSession.MapRootAuthenticationSessionProviderFactory;
|
||||||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||||
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
||||||
|
@ -75,7 +76,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.add(MapUserProviderFactory.class)
|
.add(MapUserProviderFactory.class)
|
||||||
.add(MapUserSessionProviderFactory.class)
|
.add(MapUserSessionProviderFactory.class)
|
||||||
.add(MapUserLoginFailureProviderFactory.class)
|
.add(MapUserLoginFailureProviderFactory.class)
|
||||||
.add(NoLockingDBLockProviderFactory.class)
|
.add(NoneGlobalLockProviderFactory.class)
|
||||||
.add(MapEventStoreProviderFactory.class)
|
.add(MapEventStoreProviderFactory.class)
|
||||||
.add(SingleUseObjectProviderFactory.class)
|
.add(SingleUseObjectProviderFactory.class)
|
||||||
.add(MapPublicKeyStorageProviderFactory.class)
|
.add(MapPublicKeyStorageProviderFactory.class)
|
||||||
|
@ -99,7 +100,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
|
.spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
|
||||||
.spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID)
|
.spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID)
|
||||||
.spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.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(EventStoreSpi.NAME).defaultProvider(MapEventStoreProviderFactory.PROVIDER_ID)
|
||||||
.spi("publicKeyStorage").defaultProvider(MapPublicKeyStorageProviderFactory.PROVIDER_ID)
|
.spi("publicKeyStorage").defaultProvider(MapPublicKeyStorageProviderFactory.PROVIDER_ID)
|
||||||
;
|
;
|
||||||
|
|
|
@ -315,7 +315,7 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<systemProperties>
|
<systemProperties>
|
||||||
<systemProperty><key>keycloak.profile.feature.map_storage</key><value>enabled</value></systemProperty>
|
<systemProperty><key>keycloak.profile.feature.map_storage</key><value>enabled</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.dblock.provider</key><value>none</value></systemProperty>
|
<systemProperty><key>keycloak.globalLock.provider</key><value>none</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.realm.provider</key><value>map</value></systemProperty>
|
<systemProperty><key>keycloak.realm.provider</key><value>map</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.client.provider</key><value>map</value></systemProperty>
|
<systemProperty><key>keycloak.client.provider</key><value>map</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.clientScope.provider</key><value>map</value></systemProperty>
|
<systemProperty><key>keycloak.clientScope.provider</key><value>map</value></systemProperty>
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"dblock": {
|
"globalLock": {
|
||||||
"provider": "${keycloak.dblock.provider:jpa}"
|
"provider": "${keycloak.globalLock.provider:dblock}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"realm": {
|
"realm": {
|
||||||
|
|
Loading…
Reference in a new issue