diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java new file mode 100755 index 0000000000..ee6676180c --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/CacheManager.java @@ -0,0 +1,215 @@ +package org.keycloak.models.cache.infinispan; + +import org.infinispan.Cache; +import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated; +import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent; +import org.jboss.logging.Logger; +import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; +import org.keycloak.models.cache.infinispan.entities.Revisioned; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public abstract class CacheManager { + protected static final Logger logger = Logger.getLogger(CacheManager.class); + protected final Cache revisions; + protected final Cache cache; + + public CacheManager(Cache cache, Cache revisions) { + this.cache = cache; + this.revisions = revisions; + } + + public Cache getCache() { + return cache; + } + + public Cache getRevisions() { + return revisions; + } + + public Long getCurrentRevision(String id) { + Long revision = revisions.get(id); + if (revision == null) { + revision = UpdateCounter.current(); + } + // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event + // so, we do this to force this. + String invalidationKey = "invalidation.key" + id; + cache.putForExternalRead(invalidationKey, new AbstractRevisioned(-1L, invalidationKey)); + return revision; + } + + public void endRevisionBatch() { + try { + revisions.endBatch(true); + } catch (Exception e) { + } + + } + + public T get(String id, Class type) { + Revisioned o = (Revisioned)cache.get(id); + if (o == null) { + return null; + } + Long rev = revisions.get(id); + if (rev == null) { + RealmCacheManager.logger.tracev("get() missing rev"); + return null; + } + long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue(); + if (rev > oRev) { + RealmCacheManager.logger.tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev); + return null; + } + return o != null && type.isInstance(o) ? type.cast(o) : null; + } + + public Object invalidateObject(String id) { + Revisioned removed = (Revisioned)cache.remove(id); + // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event + // so, we do this to force the event. + cache.remove("invalidation.key" + id); + bumpVersion(id); + return removed; + } + + protected void bumpVersion(String id) { + long next = UpdateCounter.next(); + Object rev = revisions.put(id, next); + } + + public void addRevisioned(Revisioned object, long startupRevision) { + //startRevisionBatch(); + String id = object.getId(); + try { + //revisions.getAdvancedCache().lock(id); + Long rev = revisions.get(id); + if (rev == null) { + if (id.endsWith("realm.clients")) RealmCacheManager.logger.trace("addRevisioned rev == null realm.clients"); + rev = UpdateCounter.current(); + revisions.put(id, rev); + } + revisions.startBatch(); + if (!revisions.getAdvancedCache().lock(id)) { + RealmCacheManager.logger.trace("Could not obtain version lock"); + return; + } + rev = revisions.get(id); + if (rev == null) { + if (id.endsWith("realm.clients")) RealmCacheManager.logger.trace("addRevisioned rev2 == null realm.clients"); + return; + } + if (rev > startupRevision) { // revision is ahead transaction start. Other transaction updated in the meantime. Don't cache + if (RealmCacheManager.logger.isTraceEnabled()) { + RealmCacheManager.logger.tracev("Skipped cache. Current revision {0}, Transaction start revision {1}", object.getRevision(), startupRevision); + } + return; + } + if (rev.equals(object.getRevision())) { + if (id.endsWith("realm.clients")) RealmCacheManager.logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev); + cache.putForExternalRead(id, object); + return; + } + if (rev > object.getRevision()) { // revision is ahead, don't cache + if (id.endsWith("realm.clients")) RealmCacheManager.logger.trace("addRevisioned revision is ahead realm.clients"); + return; + } + // revisions cache has a lower value than the object.revision, so update revision and add it to cache + if (id.endsWith("realm.clients")) RealmCacheManager.logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev); + revisions.put(id, object.getRevision()); + cache.putForExternalRead(id, object); + } finally { + endRevisionBatch(); + } + + } + + public void clear() { + cache.clear(); + } + + public void addInvalidations(Predicate> predicate, Set invalidations) { + Iterator> it = getEntryIterator(predicate); + while (it.hasNext()) { + invalidations.add(it.next().getKey()); + } + } + + private Iterator> getEntryIterator(Predicate> predicate) { + return cache + .entrySet() + .stream() + .filter(predicate).iterator(); + } + + @CacheEntryInvalidated + public void cacheInvalidated(CacheEntryInvalidatedEvent event) { + if (event.isPre()) { + String key = event.getKey(); + if (key.startsWith("invalidation.key")) { + // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event + // so, we do this to force this. + String bump = key.substring("invalidation.key".length()); + RealmCacheManager.logger.tracev("bumping invalidation key {0}", bump); + bumpVersion(bump); + return; + } + + } else { + //if (!event.isPre()) { + String key = event.getKey(); + if (key.startsWith("invalidation.key")) { + // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event + // so, we do this to force this. + String bump = key.substring("invalidation.key".length()); + bumpVersion(bump); + RealmCacheManager.logger.tracev("bumping invalidation key {0}", bump); + return; + } + bumpVersion(key); + Object object = event.getValue(); + if (object != null) { + bumpVersion(key); + Predicate> predicate = getInvalidationPredicate(object); + if (predicate != null) runEvictions(predicate); + RealmCacheManager.logger.tracev("invalidating: {0}" + object.getClass().getName()); + } + } + } + + @CacheEntriesEvicted + public void cacheEvicted(CacheEntriesEvictedEvent event) { + if (!event.isPre()) + for (Map.Entry entry : event.getEntries().entrySet()) { + Object object = entry.getValue(); + bumpVersion(entry.getKey()); + if (object == null) continue; + RealmCacheManager.logger.tracev("evicting: {0}" + object.getClass().getName()); + Predicate> predicate = getInvalidationPredicate(object); + if (predicate != null) runEvictions(predicate); + } + } + + public void runEvictions(Predicate> current) { + Set evictions = new HashSet<>(); + addInvalidations(current, evictions); + RealmCacheManager.logger.tracev("running evictions size: {0}", evictions.size()); + for (String key : evictions) { + cache.evict(key); + bumpVersion(key); + } + } + + protected abstract Predicate> getInvalidationPredicate(Object object); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java index fb9946b28e..f32ac69369 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java @@ -35,12 +35,12 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa private static final Logger log = Logger.getLogger(InfinispanCacheRealmProviderFactory.class); - protected volatile StreamRealmCache realmCache; + protected volatile RealmCacheManager realmCache; @Override public CacheRealmProvider create(KeycloakSession session) { lazyInit(session); - return new StreamCacheRealmProvider(realmCache, session); + return new RealmCacheSession(realmCache, session); } private void lazyInit(KeycloakSession session) { @@ -49,7 +49,7 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa if (realmCache == null) { Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); Cache revisions = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.VERSION_CACHE_NAME); - realmCache = new StreamRealmCache(cache, revisions); + realmCache = new RealmCacheManager(cache, revisions); } } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheManager.java new file mode 100755 index 0000000000..9923edbc10 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheManager.java @@ -0,0 +1,163 @@ +/* + * Copyright 2016 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.cache.infinispan; + +import org.infinispan.Cache; +import org.infinispan.notifications.Listener; +import org.jboss.logging.Logger; +import org.keycloak.models.cache.infinispan.entities.CachedClient; +import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate; +import org.keycloak.models.cache.infinispan.entities.CachedGroup; +import org.keycloak.models.cache.infinispan.entities.CachedRealm; +import org.keycloak.models.cache.infinispan.entities.CachedRole; +import org.keycloak.models.cache.infinispan.entities.Revisioned; +import org.keycloak.models.cache.infinispan.stream.ClientQueryPredicate; +import org.keycloak.models.cache.infinispan.stream.ClientTemplateQueryPredicate; +import org.keycloak.models.cache.infinispan.stream.GroupQueryPredicate; +import org.keycloak.models.cache.infinispan.stream.HasRolePredicate; +import org.keycloak.models.cache.infinispan.stream.InClientPredicate; +import org.keycloak.models.cache.infinispan.stream.InRealmPredicate; +import org.keycloak.models.cache.infinispan.stream.RealmQueryPredicate; + +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +/** + * @author Stian Thorgersen + */ +@Listener +public class RealmCacheManager extends CacheManager { + + protected static final Logger logger = Logger.getLogger(RealmCacheManager.class); + + public RealmCacheManager(Cache cache, Cache revisions) { + super(cache, revisions); + this.cache.addListener(this); + } + + + public void realmInvalidation(String id, Set invalidations) { + Predicate> predicate = getRealmInvalidationPredicate(id); + addInvalidations(predicate, invalidations); + } + + public Predicate> getRealmInvalidationPredicate(String id) { + return RealmQueryPredicate.create().realm(id); + } + + public void clientInvalidation(String id, Set invalidations) { + addInvalidations(getClientInvalidationPredicate(id), invalidations); + } + + public Predicate> getClientInvalidationPredicate(String id) { + return ClientQueryPredicate.create().client(id); + } + + public void roleInvalidation(String id, Set invalidations) { + addInvalidations(getRoleInvalidationPredicate(id), invalidations); + + } + + public Predicate> getRoleInvalidationPredicate(String id) { + return HasRolePredicate.create().role(id); + } + + public void groupInvalidation(String id, Set invalidations) { + addInvalidations(getGroupInvalidationPredicate(id), invalidations); + + } + + public Predicate> getGroupInvalidationPredicate(String id) { + return GroupQueryPredicate.create().group(id); + } + + public void clientTemplateInvalidation(String id, Set invalidations) { + addInvalidations(getClientTemplateInvalidationPredicate(id), invalidations); + + } + + public Predicate> getClientTemplateInvalidationPredicate(String id) { + return ClientTemplateQueryPredicate.create().template(id); + } + + public void realmRemoval(String id, Set invalidations) { + Predicate> predicate = getRealmRemovalPredicate(id); + addInvalidations(predicate, invalidations); + } + + public Predicate> getRealmRemovalPredicate(String id) { + Predicate> predicate = null; + predicate = RealmQueryPredicate.create().realm(id) + .or(InRealmPredicate.create().realm(id)); + return predicate; + } + + public void clientAdded(String realmId, String id, Set invalidations) { + addInvalidations(getClientAddedPredicate(realmId), invalidations); + } + + public Predicate> getClientAddedPredicate(String realmId) { + return ClientQueryPredicate.create().inRealm(realmId); + } + + public void clientRemoval(String realmId, String id, Set invalidations) { + Predicate> predicate = null; + predicate = getClientRemovalPredicate(realmId, id); + addInvalidations(predicate, invalidations); + } + + public Predicate> getClientRemovalPredicate(String realmId, String id) { + Predicate> predicate; + predicate = ClientQueryPredicate.create().inRealm(realmId) + .or(ClientQueryPredicate.create().client(id)) + .or(InClientPredicate.create().client(id)); + return predicate; + } + + public void roleRemoval(String id, Set invalidations) { + addInvalidations(getRoleRemovalPredicate(id), invalidations); + + } + + public Predicate> getRoleRemovalPredicate(String id) { + return getRoleInvalidationPredicate(id); + } + + @Override + protected Predicate> getInvalidationPredicate(Object object) { + if (object instanceof CachedRealm) { + CachedRealm cached = (CachedRealm)object; + return getRealmRemovalPredicate(cached.getId()); + } else if (object instanceof CachedClient) { + CachedClient cached = (CachedClient)object; + Predicate> predicate = getClientRemovalPredicate(cached.getRealm(), cached.getId()); + return predicate; + } else if (object instanceof CachedRole) { + CachedRole cached = (CachedRole)object; + return getRoleRemovalPredicate(cached.getId()); + } else if (object instanceof CachedGroup) { + CachedGroup cached = (CachedGroup)object; + return getGroupInvalidationPredicate(cached.getId()); + } else if (object instanceof CachedClientTemplate) { + CachedClientTemplate cached = (CachedClientTemplate)object; + return getClientTemplateInvalidationPredicate(cached.getId()); + } + return null; + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java similarity index 99% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java index b33a0e81eb..af8fbb1742 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java @@ -40,7 +40,6 @@ import org.keycloak.models.cache.infinispan.entities.RealmListQuery; import org.keycloak.models.cache.infinispan.entities.RoleListQuery; import org.keycloak.models.utils.KeycloakModelUtils; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -107,12 +106,12 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class StreamCacheRealmProvider implements CacheRealmProvider { - protected static final Logger logger = Logger.getLogger(StreamCacheRealmProvider.class); +public class RealmCacheSession implements CacheRealmProvider { + protected static final Logger logger = Logger.getLogger(RealmCacheSession.class); public static final String REALM_CLIENTS_QUERY_SUFFIX = ".realm.clients"; public static final String ROLES_QUERY_SUFFIX = ".roles"; public static final String ROLE_BY_NAME_QUERY_SUFFIX = ".role.by-name"; - protected StreamRealmCache cache; + protected RealmCacheManager cache; protected KeycloakSession session; protected RealmProvider delegate; protected boolean transactionActive; @@ -129,7 +128,7 @@ public class StreamCacheRealmProvider implements CacheRealmProvider { protected boolean clearAll; protected final long startupRevision; - public StreamCacheRealmProvider(StreamRealmCache cache, KeycloakSession session) { + public RealmCacheSession(RealmCacheManager cache, KeycloakSession session) { this.cache = cache; this.session = session; this.startupRevision = UpdateCounter.current(); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java deleted file mode 100755 index 119db6fc32..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2016 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.cache.infinispan; - -import org.infinispan.Cache; -import org.infinispan.notifications.Listener; -import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; -import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated; -import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; -import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent; -import org.jboss.logging.Logger; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned; -import org.keycloak.models.cache.infinispan.entities.CachedClient; -import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate; -import org.keycloak.models.cache.infinispan.entities.CachedGroup; -import org.keycloak.models.cache.infinispan.entities.CachedRealm; -import org.keycloak.models.cache.infinispan.entities.CachedRole; -import org.keycloak.models.cache.infinispan.entities.Revisioned; -import org.keycloak.models.cache.infinispan.stream.ClientQueryPredicate; -import org.keycloak.models.cache.infinispan.stream.ClientTemplateQueryPredicate; -import org.keycloak.models.cache.infinispan.stream.GroupQueryPredicate; -import org.keycloak.models.cache.infinispan.stream.HasRolePredicate; -import org.keycloak.models.cache.infinispan.stream.InClientPredicate; -import org.keycloak.models.cache.infinispan.stream.InRealmPredicate; -import org.keycloak.models.cache.infinispan.stream.RealmQueryPredicate; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; - -/** - * @author Stian Thorgersen - */ -@Listener -public class StreamRealmCache { - - protected static final Logger logger = Logger.getLogger(StreamRealmCache.class); - - protected final Cache revisions; - protected final Cache cache; - - public StreamRealmCache(Cache cache, Cache revisions) { - this.cache = cache; - this.cache.addListener(this); - this.revisions = revisions; - } - - public Cache getCache() { - return cache; - } - - public Cache getRevisions() { - return revisions; - } - - public Long getCurrentRevision(String id) { - Long revision = revisions.get(id); - if (revision == null) { - revision = UpdateCounter.current(); - } - // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event - // so, we do this to force this. - String invalidationKey = "invalidation.key" + id; - cache.putForExternalRead(invalidationKey, new AbstractRevisioned(-1L, invalidationKey)); - return revision; - } - - public void endRevisionBatch() { - try { - revisions.endBatch(true); - } catch (Exception e) { - } - - } - - public T get(String id, Class type) { - Revisioned o = (Revisioned)cache.get(id); - if (o == null) { - return null; - } - Long rev = revisions.get(id); - if (rev == null) { - logger.tracev("get() missing rev"); - return null; - } - long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue(); - if (rev > oRev) { - logger.tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev); - return null; - } - return o != null && type.isInstance(o) ? type.cast(o) : null; - } - - public Object invalidateObject(String id) { - Revisioned removed = (Revisioned)cache.remove(id); - // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event - // so, we do this to force the event. - cache.remove("invalidation.key" + id); - bumpVersion(id); - return removed; - } - - protected void bumpVersion(String id) { - long next = UpdateCounter.next(); - Object rev = revisions.put(id, next); - } - - public void addRevisioned(Revisioned object, long startupRevision) { - //startRevisionBatch(); - String id = object.getId(); - try { - //revisions.getAdvancedCache().lock(id); - Long rev = revisions.get(id); - if (rev == null) { - if (id.endsWith("realm.clients")) logger.trace("addRevisioned rev == null realm.clients"); - rev = UpdateCounter.current(); - revisions.put(id, rev); - } - revisions.startBatch(); - if (!revisions.getAdvancedCache().lock(id)) { - logger.trace("Could not obtain version lock"); - return; - } - rev = revisions.get(id); - if (rev == null) { - if (id.endsWith("realm.clients")) logger.trace("addRevisioned rev2 == null realm.clients"); - return; - } - if (rev > startupRevision) { // revision is ahead transaction start. Other transaction updated in the meantime. Don't cache - if (logger.isTraceEnabled()) { - logger.tracev("Skipped cache. Current revision {0}, Transaction start revision {1}", object.getRevision(), startupRevision); - } - return; - } - if (rev.equals(object.getRevision())) { - if (id.endsWith("realm.clients")) logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev); - cache.putForExternalRead(id, object); - return; - } - if (rev > object.getRevision()) { // revision is ahead, don't cache - if (id.endsWith("realm.clients")) logger.trace("addRevisioned revision is ahead realm.clients"); - return; - } - // revisions cache has a lower value than the object.revision, so update revision and add it to cache - if (id.endsWith("realm.clients")) logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev); - revisions.put(id, object.getRevision()); - cache.putForExternalRead(id, object); - } finally { - endRevisionBatch(); - } - - } - - - - public void clear() { - cache.clear(); - } - - public void realmInvalidation(String id, Set invalidations) { - Predicate> predicate = getRealmInvalidationPredicate(id); - addInvalidations(predicate, invalidations); - } - - public Predicate> getRealmInvalidationPredicate(String id) { - return RealmQueryPredicate.create().realm(id); - } - - public void clientInvalidation(String id, Set invalidations) { - addInvalidations(getClientInvalidationPredicate(id), invalidations); - } - - public Predicate> getClientInvalidationPredicate(String id) { - return ClientQueryPredicate.create().client(id); - } - - public void roleInvalidation(String id, Set invalidations) { - addInvalidations(getRoleInvalidationPredicate(id), invalidations); - - } - - public Predicate> getRoleInvalidationPredicate(String id) { - return HasRolePredicate.create().role(id); - } - - public void groupInvalidation(String id, Set invalidations) { - addInvalidations(getGroupInvalidationPredicate(id), invalidations); - - } - - public Predicate> getGroupInvalidationPredicate(String id) { - return GroupQueryPredicate.create().group(id); - } - - public void clientTemplateInvalidation(String id, Set invalidations) { - addInvalidations(getClientTemplateInvalidationPredicate(id), invalidations); - - } - - public Predicate> getClientTemplateInvalidationPredicate(String id) { - return ClientTemplateQueryPredicate.create().template(id); - } - - public void realmRemoval(String id, Set invalidations) { - Predicate> predicate = getRealmRemovalPredicate(id); - addInvalidations(predicate, invalidations); - } - - public Predicate> getRealmRemovalPredicate(String id) { - Predicate> predicate = null; - predicate = RealmQueryPredicate.create().realm(id) - .or(InRealmPredicate.create().realm(id)); - return predicate; - } - - public void clientAdded(String realmId, String id, Set invalidations) { - addInvalidations(getClientAddedPredicate(realmId), invalidations); - } - - public Predicate> getClientAddedPredicate(String realmId) { - return ClientQueryPredicate.create().inRealm(realmId); - } - - public void clientRemoval(String realmId, String id, Set invalidations) { - Predicate> predicate = null; - predicate = getClientRemovalPredicate(realmId, id); - addInvalidations(predicate, invalidations); - } - - public Predicate> getClientRemovalPredicate(String realmId, String id) { - Predicate> predicate; - predicate = ClientQueryPredicate.create().inRealm(realmId) - .or(ClientQueryPredicate.create().client(id)) - .or(InClientPredicate.create().client(id)); - return predicate; - } - - public void roleRemoval(String id, Set invalidations) { - addInvalidations(getRoleRemovalPredicate(id), invalidations); - - } - - public Predicate> getRoleRemovalPredicate(String id) { - return getRoleInvalidationPredicate(id); - } - - public void addInvalidations(Predicate> predicate, Set invalidations) { - Iterator> it = getEntryIterator(predicate); - while (it.hasNext()) { - invalidations.add(it.next().getKey()); - } - } - - private Iterator> getEntryIterator(Predicate> predicate) { - return cache - .entrySet() - .stream() - .filter(predicate).iterator(); - } - - @CacheEntryInvalidated - public void cacheInvalidated(CacheEntryInvalidatedEvent event) { - if (event.isPre()) { - String key = event.getKey(); - if (key.startsWith("invalidation.key")) { - // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event - // so, we do this to force this. - String bump = key.substring("invalidation.key".length()); - logger.tracev("bumping invalidation key {0}", bump); - bumpVersion(bump); - return; - } - - } else { - //if (!event.isPre()) { - String key = event.getKey(); - if (key.startsWith("invalidation.key")) { - // if you do cache.remove() on node 1 and the entry doesn't exist on node 2, node 2 never receives a invalidation event - // so, we do this to force this. - String bump = key.substring("invalidation.key".length()); - bumpVersion(bump); - logger.tracev("bumping invalidation key {0}", bump); - return; - } - bumpVersion(key); - Object object = event.getValue(); - if (object != null) { - bumpVersion(key); - Predicate> predicate = getInvalidationPredicate(object); - if (predicate != null) runEvictions(predicate); - logger.tracev("invalidating: {0}" + object.getClass().getName()); - } - } - } - - @CacheEntriesEvicted - public void cacheEvicted(CacheEntriesEvictedEvent event) { - if (!event.isPre()) - for (Map.Entry entry : event.getEntries().entrySet()) { - Object object = entry.getValue(); - bumpVersion(entry.getKey()); - if (object == null) continue; - logger.tracev("evicting: {0}" + object.getClass().getName()); - Predicate> predicate = getInvalidationPredicate(object); - if (predicate != null) runEvictions(predicate); - } - } - - public void runEvictions(Predicate> current) { - Set evictions = new HashSet<>(); - addInvalidations(current, evictions); - logger.tracev("running evictions size: {0}", evictions.size()); - for (String key : evictions) { - cache.evict(key); - bumpVersion(key); - } - } - - protected Predicate> getInvalidationPredicate(Object object) { - if (object instanceof CachedRealm) { - CachedRealm cached = (CachedRealm)object; - return getRealmRemovalPredicate(cached.getId()); - } else if (object instanceof CachedClient) { - CachedClient cached = (CachedClient)object; - Predicate> predicate = getClientRemovalPredicate(cached.getRealm(), cached.getId()); - return predicate; - } else if (object instanceof CachedRole) { - CachedRole cached = (CachedRole)object; - return getRoleRemovalPredicate(cached.getId()); - } else if (object instanceof CachedGroup) { - CachedGroup cached = (CachedGroup)object; - return getGroupInvalidationPredicate(cached.getId()); - } else if (object instanceof CachedClientTemplate) { - CachedClientTemplate cached = (CachedClientTemplate)object; - return getClientTemplateInvalidationPredicate(cached.getId()); - } - return null; - } -}