CLI options to disable encryption and authentication to external Infinispan
Closes #28750 Signed-off-by: Pedro Ruivo <pruivo@redhat.com>
This commit is contained in:
parent
6977d58d27
commit
3de5357091
8 changed files with 114 additions and 128 deletions
|
@ -151,6 +151,16 @@ The configuration file is relative to the `conf/` directory.
|
|||
For configuration of {project_name} server for high availability and multi-node clustered setup there was introduced following CLI options `cache-remote-host`, `cache-remote-port`, `cache-remote-username` and `cache-remote-password` simplifying configuration within the XML file.
|
||||
Once any of declared CLI parameters are present, it is expected there is no configuration related to remote store present in the XML file.
|
||||
|
||||
==== Connecting to an insecure Infinispan server
|
||||
|
||||
WARNING: Disabling security is not recommended in production!
|
||||
|
||||
In development or test environment, it is easier to start an unsecured Infinispan server.
|
||||
For these use case, the CLI options `cache-remote-tls-enabled` disables the encryption (SSL) between {project_name} and Infinispan.
|
||||
{project_name} will fail to start if the Infinispan server is configured to accept only encrypted connections.
|
||||
|
||||
The CLI options `cache-remote-username` and `cache-remote-password` are optional and, if not set, {project_name} will connect to the Infinispan server without presenting any credentials.
|
||||
If the Infinispan server has authentication enabled, {project_name} will fail to start.
|
||||
|
||||
== Transport stacks
|
||||
Transport stacks ensure that distributed cache nodes in a cluster communicate in a reliable fashion.
|
||||
|
|
|
@ -186,7 +186,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
|
|||
throw new RuntimeException("Multiple " + org.keycloak.cluster.ManagedCacheManagerProvider.class + " providers found.");
|
||||
}
|
||||
|
||||
managedCacheManager = provider.getCacheManager(config);
|
||||
managedCacheManager = provider.getEmbeddedCacheManager(config);
|
||||
}
|
||||
|
||||
// store it in a locale variable first, so it is not visible to the outside, yet
|
||||
|
|
|
@ -21,6 +21,7 @@ public class CachingOptions {
|
|||
public static final String CACHE_REMOTE_PORT_PROPERTY = CACHE_REMOTE_PREFIX + "-port";
|
||||
public static final String CACHE_REMOTE_USERNAME_PROPERTY = CACHE_REMOTE_PREFIX + "-username";
|
||||
public static final String CACHE_REMOTE_PASSWORD_PROPERTY = CACHE_REMOTE_PREFIX + "-password";
|
||||
public static final String CACHE_REMOTE_TLS_ENABLED_PROPERTY = CACHE_REMOTE_PREFIX + "-tls-enabled";
|
||||
|
||||
private static final String CACHE_METRICS_PREFIX = "cache-metrics";
|
||||
public static final String CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY = CACHE_METRICS_PREFIX + "-histograms-enabled";
|
||||
|
@ -44,7 +45,7 @@ public class CachingOptions {
|
|||
kubernetes,
|
||||
ec2,
|
||||
azure,
|
||||
google;
|
||||
google
|
||||
}
|
||||
|
||||
public static final Option<Stack> CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class)
|
||||
|
@ -126,4 +127,9 @@ public class CachingOptions {
|
|||
.description("Enable histograms for metrics for the embedded caches.")
|
||||
.build();
|
||||
|
||||
public static final Option<Boolean> CACHE_REMOTE_TLS_ENABLED = new OptionBuilder<>(CACHE_REMOTE_TLS_ENABLED_PROPERTY, Boolean.class)
|
||||
.category(OptionCategory.CACHE)
|
||||
.description("Enable SSL support to communication with a secure remote Infinispan server. It is not recommended to disable in production!")
|
||||
.defaultValue(Boolean.TRUE)
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -36,37 +36,32 @@ import io.quarkus.agroal.DataSource;
|
|||
import io.quarkus.arc.Arc;
|
||||
import io.quarkus.arc.InstanceHandle;
|
||||
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener;
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
import io.quarkus.runtime.ShutdownContext;
|
||||
import io.quarkus.runtime.annotations.Recorder;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import liquibase.Scope;
|
||||
|
||||
import liquibase.servicelocator.ServiceLocator;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.infinispan.commons.util.FileLookupFactory;
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.crypto.CryptoProvider;
|
||||
import org.keycloak.common.crypto.FipsMode;
|
||||
import org.keycloak.config.MetricsOptions;
|
||||
import org.keycloak.config.TruststoreOptions;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||
import org.keycloak.quarkus.runtime.storage.database.liquibase.FastServiceLocator;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.theme.ClasspathThemeProviderFactory;
|
||||
import org.keycloak.truststore.TruststoreBuilder;
|
||||
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
import io.quarkus.runtime.ShutdownContext;
|
||||
import io.quarkus.runtime.annotations.Recorder;
|
||||
import liquibase.servicelocator.ServiceLocator;
|
||||
import org.keycloak.userprofile.DeclarativeUserProfileProviderFactory;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getKcConfigValue;
|
||||
|
@ -123,17 +118,8 @@ public class KeycloakRecorder {
|
|||
|
||||
public RuntimeValue<CacheManagerFactory> createCacheInitializer(ShutdownContext shutdownContext) {
|
||||
try {
|
||||
boolean isMetricsEnabled = Configuration.isTrue(MetricsOptions.METRICS_ENABLED);
|
||||
CacheManagerFactory cacheManagerFactory = new CacheManagerFactory(getInfinispanConfigFile(), isMetricsEnabled);
|
||||
|
||||
shutdownContext.addShutdownTask(() -> {
|
||||
DefaultCacheManager cacheManager = cacheManagerFactory.getOrCreate();
|
||||
|
||||
if (cacheManager != null) {
|
||||
cacheManager.stop();
|
||||
}
|
||||
});
|
||||
|
||||
CacheManagerFactory cacheManagerFactory = new CacheManagerFactory(getInfinispanConfigFile());
|
||||
shutdownContext.addShutdownTask(cacheManagerFactory::shutdown);
|
||||
return new RuntimeValue<>(cacheManagerFactory);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
|
|
@ -137,13 +137,7 @@ public final class Configuration {
|
|||
}
|
||||
|
||||
public static Optional<Boolean> getOptionalBooleanKcValue(String propertyName) {
|
||||
Optional<String> value = getOptionalValue(NS_KEYCLOAK_PREFIX.concat(propertyName));
|
||||
|
||||
if (value.isPresent()) {
|
||||
return value.map(Boolean::parseBoolean);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
return getOptionalValue(NS_KEYCLOAK_PREFIX.concat(propertyName)).map(Boolean::parseBoolean);
|
||||
}
|
||||
|
||||
public static Optional<Boolean> getOptionalBooleanValue(String name) {
|
||||
|
|
|
@ -19,13 +19,15 @@ package org.keycloak.quarkus.runtime.storage.legacy.infinispan;
|
|||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
|
||||
import org.infinispan.commons.api.Lifecycle;
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.infinispan.configuration.cache.PersistenceConfigurationBuilder;
|
||||
import org.infinispan.configuration.global.GlobalConfiguration;
|
||||
|
@ -43,16 +45,16 @@ import org.jgroups.protocols.UDP;
|
|||
import org.jgroups.util.TLS;
|
||||
import org.jgroups.util.TLSClientAuth;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.config.CachingOptions;
|
||||
import org.keycloak.config.MetricsOptions;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_HOST_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_PASSWORD_PROPERTY;
|
||||
import static org.keycloak.config.CachingOptions.CACHE_REMOTE_PORT_PROPERTY;
|
||||
|
@ -68,44 +70,41 @@ public class CacheManagerFactory {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(CacheManagerFactory.class);
|
||||
|
||||
private String config;
|
||||
private final boolean metricsEnabled;
|
||||
private DefaultCacheManager cacheManager;
|
||||
private Future<DefaultCacheManager> cacheManagerFuture;
|
||||
private ExecutorService executor;
|
||||
private boolean initialized;
|
||||
private final String config;
|
||||
private final CompletableFuture<DefaultCacheManager> cacheManagerFuture;
|
||||
|
||||
public CacheManagerFactory(String config, boolean metricsEnabled) {
|
||||
public CacheManagerFactory(String config) {
|
||||
this.config = config;
|
||||
this.metricsEnabled = metricsEnabled;
|
||||
this.executor = createThreadPool();
|
||||
this.cacheManagerFuture = executor.submit(this::startCacheManager);
|
||||
this.cacheManagerFuture = CompletableFuture.supplyAsync(this::startEmbeddedCacheManager);
|
||||
}
|
||||
|
||||
public DefaultCacheManager getOrCreate() {
|
||||
if (cacheManager == null) {
|
||||
if (initialized) {
|
||||
return null;
|
||||
public DefaultCacheManager getOrCreateEmbeddedCacheManager() {
|
||||
return join(cacheManagerFuture);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
logger.debug("Shutdown embedded cache manager");
|
||||
cacheManagerFuture.thenAccept(CacheManagerFactory::close);
|
||||
}
|
||||
|
||||
private static <T> T join(Future<T> future) {
|
||||
try {
|
||||
// for now, we don't have any explicit property for setting the cache start timeout
|
||||
return cacheManager = cacheManagerFuture.get(getStartTimeout(), TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to start caches", e);
|
||||
} finally {
|
||||
shutdownThreadPool();
|
||||
return future.get(getStartTimeout(), TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
throw new RuntimeException("Failed to start embedded or remote cache manager", e);
|
||||
}
|
||||
}
|
||||
|
||||
return cacheManager;
|
||||
private static void close(Lifecycle lifecycle) {
|
||||
if (lifecycle != null) {
|
||||
lifecycle.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutorService createThreadPool() {
|
||||
return Executors.newSingleThreadExecutor(r -> new Thread(r, "keycloak-cache-init"));
|
||||
}
|
||||
|
||||
private DefaultCacheManager startCacheManager() {
|
||||
private DefaultCacheManager startEmbeddedCacheManager() {
|
||||
ConfigurationBuilderHolder builder = new ParserRegistry().parse(config);
|
||||
|
||||
if (builder.getNamedConfigurationBuilders().entrySet().stream().anyMatch(c -> c.getValue().clustering().cacheMode().isClustered())) {
|
||||
|
@ -124,12 +123,12 @@ public class CacheManagerFactory {
|
|||
}
|
||||
});
|
||||
|
||||
if (metricsEnabled) {
|
||||
if (Configuration.isTrue(MetricsOptions.METRICS_ENABLED)) {
|
||||
builder.getGlobalConfigurationBuilder().addModule(MicrometerMeterRegisterConfigurationBuilder.class);
|
||||
builder.getGlobalConfigurationBuilder().module(MicrometerMeterRegisterConfigurationBuilder.class).meterRegistry(Metrics.globalRegistry);
|
||||
builder.getGlobalConfigurationBuilder().cacheContainer().statistics(true);
|
||||
builder.getGlobalConfigurationBuilder().metrics().namesAsTags(true);
|
||||
if (booleanProperty(CACHE_METRICS_HISTOGRAMS_ENABLED_PROPERTY)) {
|
||||
if (Configuration.isTrue(CachingOptions.CACHE_METRICS_HISTOGRAMS_ENABLED)) {
|
||||
builder.getGlobalConfigurationBuilder().metrics().histograms(true);
|
||||
}
|
||||
builder.getNamedConfigurationBuilders().forEach((s, configurationBuilder) -> configurationBuilder.statistics().enabled(true));
|
||||
|
@ -143,36 +142,35 @@ public class CacheManagerFactory {
|
|||
return new DefaultCacheManager(builder, isStartEagerly());
|
||||
}
|
||||
|
||||
private boolean isStartEagerly() {
|
||||
private static boolean isRemoteTLSEnabled() {
|
||||
return Configuration.isTrue(CachingOptions.CACHE_REMOTE_TLS_ENABLED);
|
||||
}
|
||||
|
||||
private static boolean isRemoteAuthenticationEnabled() {
|
||||
return Configuration.getOptionalValue(CACHE_REMOTE_USERNAME_PROPERTY).isPresent() ||
|
||||
Configuration.getOptionalValue(CACHE_REMOTE_PASSWORD_PROPERTY).isEmpty();
|
||||
}
|
||||
|
||||
private static SSLContext createSSLContext() {
|
||||
try {
|
||||
// uses the default Java Runtime TrustStore, or the one generated by Keycloak (see org.keycloak.truststore.TruststoreBuilder)
|
||||
var sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, null, null);
|
||||
return sslContext;
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isStartEagerly() {
|
||||
// eagerly starts caches by default
|
||||
return Boolean.parseBoolean(System.getProperty("kc.cache-ispn-start-eagerly", Boolean.TRUE.toString()));
|
||||
}
|
||||
|
||||
private Integer getStartTimeout() {
|
||||
private static int getStartTimeout() {
|
||||
return Integer.getInteger("kc.cache-ispn-start-timeout", 120);
|
||||
}
|
||||
|
||||
private void shutdownThreadPool() {
|
||||
if (executor != null) {
|
||||
executor.shutdown();
|
||||
try {
|
||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow();
|
||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
Logger.getLogger(CacheManagerFactory.class).warn("Cache init thread pool not terminated");
|
||||
}
|
||||
}
|
||||
} catch (Exception cause) {
|
||||
executor.shutdownNow();
|
||||
} finally {
|
||||
executor = null;
|
||||
cacheManagerFuture = null;
|
||||
config = null;
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void configureTransportStack(ConfigurationBuilderHolder builder) {
|
||||
String transportStack = Configuration.getRawValue("kc.cache-stack");
|
||||
|
||||
|
@ -181,7 +179,7 @@ public class CacheManagerFactory {
|
|||
transportConfig.defaultTransport().stack(transportStack);
|
||||
}
|
||||
|
||||
if (booleanProperty(CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY)) {
|
||||
if (Configuration.isTrue(CachingOptions.CACHE_REMOTE_TLS_ENABLED)) {
|
||||
validateTlsAvailable(transportConfig.build());
|
||||
var tls = new TLS()
|
||||
.enabled(true)
|
||||
|
@ -218,25 +216,14 @@ public class CacheManagerFactory {
|
|||
|
||||
private void configureRemoteStores(ConfigurationBuilderHolder builder) {
|
||||
//if one of remote store command line parameters is defined, some other are required, otherwise assume it'd configured via xml only
|
||||
if (Configuration.getOptionalKcValue(CACHE_REMOTE_HOST_PROPERTY).isPresent() ||
|
||||
Configuration.getOptionalKcValue(CACHE_REMOTE_USERNAME_PROPERTY).isPresent() ||
|
||||
Configuration.getOptionalKcValue(CACHE_REMOTE_PASSWORD_PROPERTY).isPresent()) {
|
||||
if (Configuration.getOptionalKcValue(CACHE_REMOTE_HOST_PROPERTY).isPresent()) {
|
||||
|
||||
String cacheRemoteHost = requiredStringProperty(CACHE_REMOTE_HOST_PROPERTY);
|
||||
Integer cacheRemotePort = Configuration.getOptionalKcValue(CACHE_REMOTE_PORT_PROPERTY)
|
||||
.map(Integer::parseInt)
|
||||
.orElse(ConfigurationProperties.DEFAULT_HOTROD_PORT);
|
||||
String cacheRemoteUsername = requiredStringProperty(CACHE_REMOTE_USERNAME_PROPERTY);
|
||||
String cacheRemotePassword = requiredStringProperty(CACHE_REMOTE_PASSWORD_PROPERTY);
|
||||
|
||||
SSLContext sslContext;
|
||||
try {
|
||||
// uses the default Java Runtime TrustStore, or the one generated by Keycloak (see org.keycloak.truststore.TruststoreBuilder)
|
||||
sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, null, null);
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
SSLContext sslContext = createSSLContext();
|
||||
|
||||
DISTRIBUTED_REPLICATED_CACHE_NAMES.forEach(cacheName -> {
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS_NO_CACHE) &&
|
||||
|
@ -251,7 +238,8 @@ public class CacheManagerFactory {
|
|||
throw new RuntimeException(String.format("Remote store for cache '%s' is already configured via CLI parameters. It should not be present in the XML file.", cacheName));
|
||||
}
|
||||
|
||||
persistenceCB.addStore(RemoteStoreConfigurationBuilder.class)
|
||||
var storeBuilder = persistenceCB.addStore(RemoteStoreConfigurationBuilder.class);
|
||||
storeBuilder
|
||||
.rawValues(true)
|
||||
.shared(true)
|
||||
.segmented(false)
|
||||
|
@ -259,26 +247,29 @@ public class CacheManagerFactory {
|
|||
.connectionPool()
|
||||
.maxActive(16)
|
||||
.exhaustedAction(ExhaustedAction.CREATE_NEW)
|
||||
.remoteSecurity()
|
||||
.ssl()
|
||||
.enable()
|
||||
.sslContext(sslContext)
|
||||
.sniHostName(cacheRemoteHost)
|
||||
.authentication()
|
||||
.enable()
|
||||
.username(cacheRemoteUsername)
|
||||
.password(cacheRemotePassword)
|
||||
.realm("default")
|
||||
.saslMechanism(SCRAM_SHA_512)
|
||||
.addServer()
|
||||
.host(cacheRemoteHost)
|
||||
.port(cacheRemotePort);
|
||||
});
|
||||
}
|
||||
|
||||
if (isRemoteTLSEnabled()) {
|
||||
storeBuilder.remoteSecurity()
|
||||
.ssl()
|
||||
.enable()
|
||||
.sslContext(sslContext)
|
||||
.sniHostName(cacheRemoteHost);
|
||||
}
|
||||
|
||||
private static boolean booleanProperty(String propertyName) {
|
||||
return Configuration.getOptionalKcValue(propertyName).map(Boolean::parseBoolean).orElse(Boolean.FALSE);
|
||||
if (isRemoteAuthenticationEnabled()) {
|
||||
storeBuilder.remoteSecurity()
|
||||
.authentication()
|
||||
.enable()
|
||||
.username(requiredStringProperty(CACHE_REMOTE_USERNAME_PROPERTY))
|
||||
.password(requiredStringProperty(CACHE_REMOTE_PASSWORD_PROPERTY))
|
||||
.realm("default")
|
||||
.saslMechanism(SCRAM_SHA_512);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static String requiredStringProperty(String propertyName) {
|
||||
|
|
|
@ -17,10 +17,9 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.storage.legacy.infinispan;
|
||||
|
||||
import org.keycloak.cluster.ManagedCacheManagerProvider;
|
||||
import org.keycloak.Config;
|
||||
|
||||
import io.quarkus.arc.Arc;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.cluster.ManagedCacheManagerProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -28,7 +27,7 @@ import io.quarkus.arc.Arc;
|
|||
public final class QuarkusCacheManagerProvider implements ManagedCacheManagerProvider {
|
||||
|
||||
@Override
|
||||
public <C> C getCacheManager(Config.Scope config) {
|
||||
return (C) Arc.container().instance(CacheManagerFactory.class).get().getOrCreate();
|
||||
public <C> C getEmbeddedCacheManager(Config.Scope config) {
|
||||
return (C) Arc.container().instance(CacheManagerFactory.class).get().getOrCreateEmbeddedCacheManager();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@ package org.keycloak.cluster;
|
|||
import org.keycloak.Config;
|
||||
|
||||
/**
|
||||
* A Service Provider Interface (SPI) that allows to plug-in a cache manager instance.
|
||||
* A Service Provider Interface (SPI) that allows to plug-in an embedded cache manager instance.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public interface ManagedCacheManagerProvider {
|
||||
|
||||
<C> C getCacheManager(Config.Scope config);
|
||||
<C> C getEmbeddedCacheManager(Config.Scope config);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue