Clustering support
This commit is contained in:
1 parent
6075ef221f
commit
99c73a9cc7
67 files changed
+3250
-176
No files matched your search
Executable
+38
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.1.0-Alpha1-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-invalidation-cache-infinispan</artifactId>
|
||||
<name>Keycloak Invalidation Cache Infinispan</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-invalidation-cache-model</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-infinispan</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
Vendored
Executable
+42
@@ -0,0 +1,42 @@
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.CacheRealmProviderFactory;
|
||||
import org.keycloak.models.cache.DefaultCacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFactory {
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<String, String>();
|
||||
|
||||
@Override
|
||||
public CacheRealmProvider create(KeycloakSession session) {
|
||||
Cache<String, Object> cache = session.getProvider(InfinispanConnectionProvider.class).getCache("realms");
|
||||
RealmCache realmCache = new InfinispanRealmCache(cache, realmLookup);
|
||||
return new DefaultCacheRealmProvider(realmCache, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan";
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
Executable
+123
@@ -0,0 +1,123 @@
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.cache.CacheUserProvider;
|
||||
import org.keycloak.models.cache.CacheUserProviderFactory;
|
||||
import org.keycloak.models.cache.DefaultCacheUserProvider;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfinispanCacheUserProviderFactory implements CacheUserProviderFactory {
|
||||
|
||||
protected InfinispanUserCache userCache;
|
||||
|
||||
protected final RealmLookup usernameLookup = new RealmLookup();
|
||||
|
||||
protected final RealmLookup emailLookup = new RealmLookup();
|
||||
|
||||
@Override
|
||||
public CacheUserProvider create(KeycloakSession session) {
|
||||
lazyInit(session);
|
||||
return new DefaultCacheUserProvider(userCache, session);
|
||||
}
|
||||
|
||||
private void lazyInit(KeycloakSession session) {
|
||||
if (userCache == null) {
|
||||
synchronized (this) {
|
||||
if (userCache == null) {
|
||||
Cache<String, CachedUser> cache = session.getProvider(InfinispanConnectionProvider.class).getCache("users");
|
||||
cache.addListener(new CacheListener());
|
||||
userCache = new InfinispanUserCache(cache, usernameLookup, emailLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan";
|
||||
}
|
||||
|
||||
@Listener
|
||||
private class CacheListener {
|
||||
|
||||
@CacheEntryCreated
|
||||
public void userCreated(CacheEntryCreatedEvent<String, CachedUser> event) {
|
||||
if (!event.isPre() && event.getValue() != null) {
|
||||
CachedUser cachedUser = event.getValue();
|
||||
String realm = cachedUser.getRealm();
|
||||
usernameLookup.put(realm, cachedUser.getUsername(), cachedUser.getId());
|
||||
if (cachedUser.getEmail() != null) {
|
||||
emailLookup.put(realm, cachedUser.getEmail(), cachedUser.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryRemoved
|
||||
public void userRemoved(CacheEntryRemovedEvent<String, CachedUser> event) {
|
||||
if (event.isPre() && event.getValue() != null) {
|
||||
CachedUser cachedUser = event.getValue();
|
||||
String realm = cachedUser.getRealm();
|
||||
usernameLookup.remove(realm, cachedUser.getUsername());
|
||||
if (cachedUser.getEmail() != null) {
|
||||
emailLookup.remove(realm, cachedUser.getEmail());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class RealmLookup {
|
||||
|
||||
protected final ConcurrentHashMap<String, ConcurrentHashMap<String, String>> lookup = new ConcurrentHashMap<String, ConcurrentHashMap<String, String>>();
|
||||
|
||||
public void put(String realm, String key, String value) {
|
||||
ConcurrentHashMap<String, String> map = lookup.get(realm);
|
||||
if(map == null) {
|
||||
map = new ConcurrentHashMap<String, String>();
|
||||
ConcurrentHashMap<String, String> p = lookup.putIfAbsent(realm, map);
|
||||
if (p != null) {
|
||||
map = p;
|
||||
}
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public String get(String realm, String key) {
|
||||
ConcurrentHashMap<String, String> map = lookup.get(realm);
|
||||
return map != null ? map.get(key) : null;
|
||||
}
|
||||
|
||||
public void remove(String realm, String key) {
|
||||
ConcurrentHashMap<String, String> map = lookup.get(realm);
|
||||
if (map != null) {
|
||||
map.remove(key);
|
||||
if (map.isEmpty()) {
|
||||
lookup.remove(realm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
Executable
+166
@@ -0,0 +1,166 @@
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedApplication;
|
||||
import org.keycloak.models.cache.entities.CachedOAuthClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfinispanRealmCache implements RealmCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(InfinispanRealmCache.class);
|
||||
|
||||
protected final Cache<String, Object> cache;
|
||||
protected final ConcurrentHashMap<String, String> realmLookup;
|
||||
protected volatile boolean enabled = true;
|
||||
|
||||
public InfinispanRealmCache(Cache<String, Object> cache, ConcurrentHashMap<String, String> realmLookup) {
|
||||
this.cache = cache;
|
||||
this.realmLookup = realmLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
clear();
|
||||
this.enabled = enabled;
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getCachedRealm(String id) {
|
||||
if (!enabled) return null;
|
||||
return get(id, CachedRealm.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Invalidating realm {0}", realm.getId());
|
||||
cache.remove(realm.getId());
|
||||
realmLookup.remove(realm.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealmById(String id) {
|
||||
CachedRealm cached = (CachedRealm) cache.remove(id);
|
||||
if (cached != null) realmLookup.remove(cached.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRealm(CachedRealm realm) {
|
||||
if (!enabled) return;
|
||||
logger.tracev("Adding realm {0}", realm.getId());
|
||||
cache.put(realm.getId(), realm);
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getCachedRealmByName(String name) {
|
||||
if (!enabled) return null;
|
||||
String id = realmLookup.get(name);
|
||||
return id != null ? getCachedRealm(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedApplication getApplication(String id) {
|
||||
if (!enabled) return null;
|
||||
return get(id, CachedApplication.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateApplication(CachedApplication app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
cache.remove(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedApplication(CachedApplication app) {
|
||||
if (!enabled) return;
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
cache.put(app.getId(), app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedApplicationById(String id) {
|
||||
logger.tracev("Removing application {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedOAuthClient getOAuthClient(String id) {
|
||||
if (!enabled) return null;
|
||||
return get(id, CachedOAuthClient.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateOAuthClient(CachedOAuthClient client) {
|
||||
logger.tracev("Removing oauth client {0}", client.getId());
|
||||
cache.remove(client.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedOAuthClient(CachedOAuthClient client) {
|
||||
if (!enabled) return;
|
||||
logger.tracev("Adding oauth client {0}", client.getId());
|
||||
cache.put(client.getId(), client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedOAuthClientById(String id) {
|
||||
logger.tracev("Removing oauth client {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRole getRole(String id) {
|
||||
if (!enabled) return null;
|
||||
return get(id, CachedRole.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRole(CachedRole role) {
|
||||
logger.tracev("Removing role {0}", role.getId());
|
||||
cache.remove(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRole(CachedRole role) {
|
||||
if (!enabled) return;
|
||||
logger.tracev("Adding role {0}", role.getId());
|
||||
cache.put(role.getId(), role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
private <T> T get(String id, Class<T> type) {
|
||||
Object o = cache.get(id);
|
||||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
Executable
+95
@@ -0,0 +1,95 @@
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfinispanUserCache implements UserCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(InfinispanRealmCache.class);
|
||||
|
||||
protected volatile boolean enabled = true;
|
||||
|
||||
protected final Cache<String, CachedUser> cache;
|
||||
|
||||
protected final InfinispanCacheUserProviderFactory.RealmLookup usernameLookup;
|
||||
|
||||
protected final InfinispanCacheUserProviderFactory.RealmLookup emailLookup;
|
||||
|
||||
public InfinispanUserCache(Cache<String, CachedUser> cache, InfinispanCacheUserProviderFactory.RealmLookup usernameLookup, InfinispanCacheUserProviderFactory.RealmLookup emailLookup) {
|
||||
this.cache = cache;
|
||||
this.usernameLookup = usernameLookup;
|
||||
this.emailLookup = emailLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
clear();
|
||||
this.enabled = enabled;
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedUser getCachedUser(String realmId, String id) {
|
||||
if (realmId == null || id == null) return null;
|
||||
CachedUser user = cache.get(id);
|
||||
return user != null && realmId.equals(user.getRealm()) ? user : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedUser(String realmId, CachedUser user) {
|
||||
logger.tracev("Invalidating user {0}", user.getId());
|
||||
cache.remove(user.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedUserById(String realmId, String id) {
|
||||
logger.tracev("Invalidating user {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedUser(String realmId, CachedUser user) {
|
||||
logger.tracev("Adding user {0}", user.getId());
|
||||
cache.put(user.getId(), user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedUser getCachedUserByUsername(String realmId, String name) {
|
||||
String id = usernameLookup.get(realmId, name);
|
||||
return id != null ? getCachedUser(realmId, id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedUser getCachedUserByEmail(String realmId, String email) {
|
||||
String id = emailLookup.get(realmId, email);
|
||||
return id != null ? getCachedUser(realmId, id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRealmUsers(String realmId) {
|
||||
logger.tracev("Invalidating users for realm {0}", realmId);
|
||||
for (Map.Entry<String, CachedUser> u : cache.entrySet()) {
|
||||
if (u.getValue().getRealm().equals(realmId)) {
|
||||
cache.remove(u.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.models.cache.infinispan.InfinispanCacheUserProviderFactory
|
||||
+6
@@ -18,6 +18,7 @@ import java.util.Set;
|
||||
*/
|
||||
public class CachedUser {
|
||||
private String id;
|
||||
private String realm;
|
||||
private String username;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
@@ -34,6 +35,7 @@ public class CachedUser {
|
||||
|
||||
public CachedUser(RealmModel realm, UserModel user) {
|
||||
this.id = user.getId();
|
||||
this.realm = realm.getId();
|
||||
this.username = user.getUsername();
|
||||
this.firstName = user.getFirstName();
|
||||
this.lastName = user.getLastName();
|
||||
@@ -54,6 +56,10 @@ public class CachedUser {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@@ -26,5 +26,6 @@
|
||||
</build>
|
||||
<modules>
|
||||
<module>model-adapters</module>
|
||||
<module>infinispan</module>
|
||||
</modules>
|
||||
</project>
|
||||
Reference in new issue
Block a user