Clustering support

This commit is contained in:
Stian Thorgersen committed 2014-09-30 09:40:20 +02:00
1 parent 6075ef221f
commit 99c73a9cc7
67 files changed
+3250 -176

No files matched your search

+38
View File
@@ -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>
@@ -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";
}
}
@@ -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);
}
}
}
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -0,0 +1 @@
org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory
@@ -0,0 +1 @@
org.keycloak.models.cache.infinispan.InfinispanCacheUserProviderFactory
@@ -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;
}
+1
View File
@@ -26,5 +26,6 @@
</build>
<modules>
<module>model-adapters</module>
<module>infinispan</module>
</modules>
</project>