StoreFactoryCacheSession.java
/*
* 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.cache.infinispan.authorization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.authorization.UserManagedPermissionUtil;
import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PermissionTicketStore;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.ModelException;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPermissionTicket;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedScope;
import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketResourceListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketScopeListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PolicyListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PolicyQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PolicyResourceListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.PolicyScopeListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ResourceListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ResourceQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ResourceScopeListQuery;
import org.keycloak.models.cache.infinispan.authorization.entities.ScopeListQuery;
import org.keycloak.models.cache.infinispan.authorization.events.PermissionTicketRemovedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.PermissionTicketUpdatedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.PolicyRemovedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.PolicyUpdatedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.ResourceRemovedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.ResourceServerRemovedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.ResourceServerUpdatedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.ResourceUpdatedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.ScopeRemovedEvent;
import org.keycloak.models.cache.infinispan.authorization.events.ScopeUpdatedEvent;
import org.keycloak.models.cache.infinispan.entities.NonExistentItem;
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.storage.StorageId;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
protected static final Logger logger = Logger.getLogger(StoreFactoryCacheSession.class);
protected StoreFactoryCacheManager cache;
protected boolean transactionActive;
protected boolean setRollbackOnly;
protected Map<String, ResourceServerAdapter> managedResourceServers = new HashMap<>();
protected Map<String, ScopeAdapter> managedScopes = new HashMap<>();
protected Map<String, ResourceAdapter> managedResources = new HashMap<>();
protected Map<String, PolicyAdapter> managedPolicies = new HashMap<>();
protected Map<String, PermissionTicketAdapter> managedPermissionTickets = new HashMap<>();
protected Set<String> invalidations = new HashSet<>();
protected Set<InvalidationEvent> invalidationEvents = new HashSet<>(); // Events to be sent across cluster
protected final long startupRevision;
protected StoreFactory delegate;
protected KeycloakSession session;
protected ResourceServerCache resourceServerCache;
protected ScopeCache scopeCache;
protected ResourceCache resourceCache;
protected PolicyCache policyCache;
protected PermissionTicketCache permissionTicketCache;
public StoreFactoryCacheSession(StoreFactoryCacheManager cache, KeycloakSession session) {
this.cache = cache;
this.startupRevision = cache.getCurrentCounter();
this.session = session;
this.resourceServerCache = new ResourceServerCache();
this.scopeCache = new ScopeCache();
this.resourceCache = new ResourceCache();
this.policyCache = new PolicyCache();
this.permissionTicketCache = new PermissionTicketCache();
session.getTransactionManager().enlistPrepare(getPrepareTransaction());
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
}
@Override
public ResourceServerStore getResourceServerStore() {
return resourceServerCache;
}
@Override
public ScopeStore getScopeStore() {
return scopeCache;
}
@Override
public ResourceStore getResourceStore() {
return resourceCache;
}
@Override
public PolicyStore getPolicyStore() {
return policyCache;
}
@Override
public PermissionTicketStore getPermissionTicketStore() {
return permissionTicketCache;
}
@Override
public void setReadOnly(boolean readOnly) {
getDelegate().setReadOnly(readOnly);
}
@Override
public boolean isReadOnly() {
return getDelegate().isReadOnly();
}
public void close() {
if (delegate != null) {
delegate.close();
}
}
private KeycloakTransaction getPrepareTransaction() {
return new KeycloakTransaction() {
@Override
public void begin() {
transactionActive = true;
}
@Override
public void commit() {
}
@Override
public void rollback() {
setRollbackOnly = true;
transactionActive = false;
}
@Override
public void setRollbackOnly() {
setRollbackOnly = true;
}
@Override
public boolean getRollbackOnly() {
return setRollbackOnly;
}
@Override
public boolean isActive() {
return transactionActive;
}
};
}
private KeycloakTransaction getAfterTransaction() {
return new KeycloakTransaction() {
@Override
public void begin() {
transactionActive = true;
}
@Override
public void commit() {
try {
runInvalidations();
transactionActive = false;
} finally {
cache.endRevisionBatch();
}
}
@Override
public void rollback() {
try {
setRollbackOnly = true;
runInvalidations();
transactionActive = false;
} finally {
cache.endRevisionBatch();
}
}
@Override
public void setRollbackOnly() {
setRollbackOnly = true;
}
@Override
public boolean getRollbackOnly() {
return setRollbackOnly;
}
@Override
public boolean isActive() {
return transactionActive;
}
};
}
protected void runInvalidations() {
for (String id : invalidations) {
cache.invalidateObject(id);
}
cache.sendInvalidationEvents(session, invalidationEvents, InfinispanCacheStoreFactoryProviderFactory.AUTHORIZATION_INVALIDATION_EVENTS);
}
public void registerResourceServerInvalidation(String id) {
cache.resourceServerUpdated(id, invalidations);
ResourceServerAdapter adapter = managedResourceServers.get(id);
if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(ResourceServerUpdatedEvent.create(id));
}
public void registerScopeInvalidation(String id, String name, String serverId) {
cache.scopeUpdated(id, name, serverId, invalidations);
ScopeAdapter adapter = managedScopes.get(id);
if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(ScopeUpdatedEvent.create(id, name, serverId));
}
public void registerResourceInvalidation(String id, String name, String type, Set<String> uris, Set<String> scopes, String serverId, String owner) {
cache.resourceUpdated(id, name, type, uris, scopes, serverId, owner, invalidations);
ResourceAdapter adapter = managedResources.get(id);
if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uris, owner, scopes, serverId));
}
public void registerPolicyInvalidation(String id, String name, Set<String> resources, Set<String> scopes, String defaultResourceType, String serverId) {
Set<String> resourceTypes = getResourceTypes(resources, serverId);
if (Objects.nonNull(defaultResourceType)) {
resourceTypes.add(defaultResourceType);
}
cache.policyUpdated(id, name, resources, resourceTypes, scopes, serverId, invalidations);
PolicyAdapter adapter = managedPolicies.get(id);
if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(PolicyUpdatedEvent.create(id, name, resources, resourceTypes, scopes, serverId));
}
public void registerPermissionTicketInvalidation(String id, String owner, String requester, String resource, String resourceName, String scope, String serverId) {
cache.permissionTicketUpdated(id, owner, requester, resource, resourceName, scope, serverId, invalidations);
PermissionTicketAdapter adapter = managedPermissionTickets.get(id);
if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(PermissionTicketUpdatedEvent.create(id, owner, requester, resource, resourceName, scope, serverId));
}
private Set<String> getResourceTypes(Set<String> resources, String serverId) {
if (resources == null) {
return Collections.emptySet();
}
ResourceServer resourceServer = getResourceServerStore().findById(serverId);
return resources.stream().map(resourceId -> {
Resource resource = getResourceStore().findById(resourceServer, resourceId);
String type = resource.getType();
if (type != null) {
return type;
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toSet());
}
public ResourceServerStore getResourceServerStoreDelegate() {
return getDelegate().getResourceServerStore();
}
public ScopeStore getScopeStoreDelegate() {
return getDelegate().getScopeStore();
}
public ResourceStore getResourceStoreDelegate() {
return getDelegate().getResourceStore();
}
public PolicyStore getPolicyStoreDelegate() {
return getDelegate().getPolicyStore();
}
public PermissionTicketStore getPermissionTicketStoreDelegate() {
return getDelegate().getPermissionTicketStore();
}
public static String getResourceServerByClientCacheKey(String clientId) {
return "resource.server.client.id." + clientId;
}
public static String getScopeByNameCacheKey(String name, String serverId) {
return "scope.name." + name + "." + serverId;
}
public static String getResourceByNameCacheKey(String name, String ownerId, String serverId) {
return "resource.name." + name + "." + ownerId + "." + serverId;
}
public static String getResourceByOwnerCacheKey(String owner, String serverId) {
return "resource.owner." + owner + "." + serverId;
}
public static String getResourceByTypeCacheKey(String type, String serverId) {
return "resource.type." + type + "." + serverId;
}
public static String getResourceByTypeCacheKey(String type, String owner, String serverId) {
return "resource.type." + type + ".owner." + owner + "." + serverId;
}
public static String getResourceByTypeInstanceCacheKey(String type, String serverId) {
return "resource.type.instance." + type + "." + serverId;
}
public static String getResourceByUriCacheKey(String uri, String serverId) {
return "resource.uri." + uri + "." + serverId;
}
public static String getResourceByScopeCacheKey(String scopeId, String serverId) {
return "resource.scope." + scopeId + "." + serverId;
}
public static String getPolicyByNameCacheKey(String name, String serverId) {
return "policy.name." + name + "." + serverId;
}
public static String getPolicyByResource(String resourceId, String serverId) {
return "policy.resource." + resourceId + "." + serverId;
}
public static String getPolicyByResourceType(String type, String serverId) {
return "policy.resource.type." + type + "." + serverId;
}
public static String getPolicyByScope(String scope, String serverId) {
return "policy.scope." + scope + "." + serverId;
}
public static String getPolicyByResourceScope(String scope, String resourceId, String serverId) {
return "policy.resource. " + resourceId + ".scope." + scope + "." + serverId;
}
public static String getPermissionTicketByResource(String resourceId, String serverId) {
return "permission.ticket.resource." + resourceId + "." + serverId;
}
public static String getPermissionTicketByScope(String scopeId, String serverId) {
return "permission.ticket.scope." + scopeId + "." + serverId;
}
public static String getPermissionTicketByGranted(String userId, String serverId) {
return "permission.ticket.granted." + userId + "." + serverId;
}
public static String getPermissionTicketByResourceNameAndGranted(String resourceName, String userId, String serverId) {
return "permission.ticket.granted." + resourceName + "." + userId + "." + serverId;
}
public static String getPermissionTicketByOwner(String owner, String serverId) {
return "permission.ticket.owner." + owner + "." + serverId;
}
public StoreFactory getDelegate() {
if (delegate != null) return delegate;
delegate = session.getProvider(StoreFactory.class);
return delegate;
}
private void setModelDoesNotExists(String id, Long loaded) {
if (! invalidations.contains(id)) {
cache.addRevisioned(new NonExistentItem(id, loaded), startupRevision);
}
}
boolean modelMightExist(String id) {
return invalidations.contains(id) || cache.get(id, NonExistentItem.class) == null;
}
protected class ResourceServerCache implements ResourceServerStore {
@Override
public ResourceServer create(ClientModel client) {
String clientId = client.getId();
if (!StorageId.isLocalStorage(clientId)) {
throw new ModelException("Creating resource server from federated ClientModel not supported");
}
ResourceServer server = getResourceServerStoreDelegate().create(client);
registerResourceServerInvalidation(server.getId());
return server;
}
@Override
public void delete(ClientModel client) {
String id = client.getId();
if (id == null) return;
ResourceServer server = findById(id);
if (server == null) return;
cache.invalidateObject(id);
invalidationEvents.add(ResourceServerRemovedEvent.create(id));
cache.resourceServerRemoval(id, invalidations);
getResourceServerStoreDelegate().delete(client);
}
@Override
public ResourceServer findById(String id) {
if (id == null) return null;
CachedResourceServer cached = cache.get(id, CachedResourceServer.class);
if (cached != null) {
logger.tracev("by id cache hit: {0}", cached.getId());
}
if (cached == null) {
Long loaded = cache.getCurrentRevision(id);
if (! modelMightExist(id)) return null;
ResourceServer model = getResourceServerStoreDelegate().findById(id);
if (model == null) {
setModelDoesNotExists(id, loaded);
return null;
}
if (invalidations.contains(id)) return model;
cached = new CachedResourceServer(loaded, model);
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
return getResourceServerStoreDelegate().findById(id);
} else if (managedResourceServers.containsKey(id)) {
return managedResourceServers.get(id);
}
ResourceServerAdapter adapter = new ResourceServerAdapter(cached, StoreFactoryCacheSession.this);
managedResourceServers.put(id, adapter);
return adapter;
}
@Override
public ResourceServer findByClient(ClientModel client) {
return findById(client.getId());
}
}
protected class ScopeCache implements ScopeStore {
@Override
public Scope create(ResourceServer resourceServer, String name) {
return create(resourceServer, null, name);
}
@Override
public Scope create(ResourceServer resourceServer, String id, String name) {
Scope scope = getScopeStoreDelegate().create(resourceServer, id, name);
registerScopeInvalidation(scope.getId(), scope.getName(), resourceServer.getId());
return scope;
}
@Override
public void delete(String id) {
if (id == null) return;
Scope scope = findById(null, id);
if (scope == null) return;
cache.invalidateObject(id);
invalidationEvents.add(ScopeRemovedEvent.create(id, scope.getName(), scope.getResourceServer().getId()));
cache.scopeRemoval(id, scope.getName(), scope.getResourceServer().getId(), invalidations);
getScopeStoreDelegate().delete(id);
}
@Override
public Scope findById(ResourceServer resourceServer, String id) {
if (id == null) return null;
CachedScope cached = cache.get(id, CachedScope.class);
if (cached != null) {
logger.tracev("by id cache hit: {0}", cached.getId());
}
if (cached == null) {
Long loaded = cache.getCurrentRevision(id);
if (! modelMightExist(id)) return null;
Scope model = getScopeStoreDelegate().findById(resourceServer, id);
if (model == null) {
setModelDoesNotExists(id, loaded);
return null;
}
if (invalidations.contains(id)) return model;
cached = new CachedScope(loaded, model);
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
return getScopeStoreDelegate().findById(resourceServer, id);
} else if (managedScopes.containsKey(id)) {
return managedScopes.get(id);
}
ScopeAdapter adapter = new ScopeAdapter(cached, StoreFactoryCacheSession.this);
managedScopes.put(id, adapter);
return adapter;
}
@Override
public Scope findByName(ResourceServer resourceServer, String name) {
if (name == null) return null;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getScopeByNameCacheKey(name, resourceServerId);
ScopeListQuery query = cache.get(cacheKey, ScopeListQuery.class);
if (query != null) {
logger.tracev("scope by name cache hit: {0}", name);
}
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
Scope model = getScopeStoreDelegate().findByName(resourceServer, name);
if (model == null) return null;
if (invalidations.contains(model.getId())) return model;
query = new ScopeListQuery(loaded, cacheKey, model.getId(), resourceServerId);
cache.addRevisioned(query, startupRevision);
return model;
} else if (invalidations.contains(cacheKey)) {
return getScopeStoreDelegate().findByName(resourceServer, name);
} else {
String id = query.getScopes().iterator().next();
if (invalidations.contains(id)) {
return getScopeStoreDelegate().findByName(resourceServer, name);
}
return findById(resourceServer, id);
}
}
@Override
public List<Scope> findByResourceServer(ResourceServer resourceServer) {
return getScopeStoreDelegate().findByResourceServer(resourceServer);
}
@Override
public List<Scope> findByResourceServer(ResourceServer resourceServer, Map<Scope.FilterOption, String[]> attributes, Integer firstResult, Integer maxResults) {
return getScopeStoreDelegate().findByResourceServer(resourceServer, attributes, firstResult, maxResults);
}
}
protected class ResourceCache implements ResourceStore {
@Override
public Resource create(ResourceServer resourceServer, String id, String name, String owner) {
Resource resource = getResourceStoreDelegate().create(resourceServer, id, name, owner);
Resource cached = findById(resourceServer, resource.getId());
registerResourceInvalidation(resource.getId(), resource.getName(), resource.getType(), resource.getUris(), resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()), resourceServer.getId(), resource.getOwner());
if (cached == null) {
cached = findById(resourceServer, resource.getId());
}
return cached;
}
@Override
public void delete(String id) {
if (id == null) return;
Resource resource = findById(null, id);
if (resource == null) return;
cache.invalidateObject(id);
invalidationEvents.add(ResourceRemovedEvent.create(id, resource.getName(), resource.getType(), resource.getUris(), resource.getOwner(), resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()), resource.getResourceServer().getId()));
cache.resourceRemoval(id, resource.getName(), resource.getType(), resource.getUris(), resource.getOwner(), resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()), resource.getResourceServer().getId(), invalidations);
getResourceStoreDelegate().delete(id);
}
@Override
public Resource findById(ResourceServer resourceServer, String id) {
if (id == null) return null;
CachedResource cached = cache.get(id, CachedResource.class);
if (cached != null) {
logger.tracev("by id cache hit: {0}", cached.getId());
}
if (cached == null) {
Long loaded = cache.getCurrentRevision(id);
if (! modelMightExist(id)) return null;
Resource model = getResourceStoreDelegate().findById(resourceServer, id);
if (model == null) {
setModelDoesNotExists(id, loaded);
return null;
}
if (invalidations.contains(id)) return model;
cached = new CachedResource(loaded, model);
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
return getResourceStoreDelegate().findById(resourceServer, id);
} else if (managedResources.containsKey(id)) {
return managedResources.get(id);
}
ResourceAdapter adapter = new ResourceAdapter(cached, StoreFactoryCacheSession.this);
managedResources.put(id, adapter);
return adapter;
}
@Override
public Resource findByName(ResourceServer resourceServer, String name, String ownerId) {
if (name == null) return null;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByNameCacheKey(name, ownerId, resourceServerId);
List<Resource> result = cacheQuery(cacheKey, ResourceListQuery.class, () -> {
Resource resource = getResourceStoreDelegate().findByName(resourceServer, name, ownerId);
if (resource == null) {
return Collections.emptyList();
}
return Arrays.asList(resource);
},
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
if (result.isEmpty()) {
return null;
}
return result.get(0);
}
@Override
public List<Resource> findByOwner(ResourceServer resourceServer, String ownerId) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByOwnerCacheKey(ownerId, resourceServerId);
return cacheQuery(cacheKey, ResourceListQuery.class, () -> getResourceStoreDelegate().findByOwner(resourceServer, ownerId),
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public void findByOwner(ResourceServer resourceServer, String ownerId, Consumer<Resource> consumer) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByOwnerCacheKey(ownerId, resourceServerId);
cacheQuery(cacheKey, ResourceListQuery.class, () -> {
List<Resource> resources = new ArrayList<>();
getResourceStoreDelegate().findByOwner(resourceServer, ownerId, new Consumer<Resource>() {
@Override
public void accept(Resource resource) {
consumer.andThen(resources::add)
.andThen(StoreFactoryCacheSession.this::cacheResource)
.accept(resource);
}
});
return resources;
},
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
@Override
public List<Resource> findByResourceServer(ResourceServer resourceServer) {
return getResourceStoreDelegate().findByResourceServer(resourceServer);
}
@Override
public List<Resource> find(ResourceServer resourceServer, Map<Resource.FilterOption, String[]> attributes, Integer firstResult, Integer maxResults) {
return getResourceStoreDelegate().find(resourceServer, attributes, firstResult, maxResults);
}
@Override
public List<Resource> findByScopes(ResourceServer resourceServer, Set<Scope> scopes) {
if (scopes == null) return null;
List<Resource> result = new ArrayList<>();
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
for (Scope scope : scopes) {
String cacheKey = getResourceByScopeCacheKey(scope.getId(), resourceServerId);
result.addAll(cacheQuery(cacheKey, ResourceScopeListQuery.class, () -> getResourceStoreDelegate().findByScopes(resourceServer, Collections.singleton(scope)), (revision, resources) -> new ResourceScopeListQuery(revision, cacheKey, scope.getId(), resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer));
}
return result;
}
@Override
public void findByScopes(ResourceServer resourceServer, Set<Scope> scopes, Consumer<Resource> consumer) {
if (scopes == null) return;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
for (Scope scope : scopes) {
String cacheKey = getResourceByScopeCacheKey(scope.getId(), resourceServerId);
cacheQuery(cacheKey, ResourceScopeListQuery.class, () -> {
List<Resource> resources = new ArrayList<>();
getResourceStoreDelegate().findByScopes(resourceServer, Collections.singleton(scope), new Consumer<Resource>() {
@Override
public void accept(Resource resource) {
consumer.andThen(resources::add)
.andThen(StoreFactoryCacheSession.this::cacheResource)
.accept(resource);
}
});
return resources;
}, (revision, resources) -> new ResourceScopeListQuery(revision, cacheKey, scope.getId(), resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
}
@Override
public List<Resource> findByType(ResourceServer resourceServer, String type) {
if (type == null) return Collections.emptyList();
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByTypeCacheKey(type, resourceServerId);
return cacheQuery(cacheKey, ResourceListQuery.class, () -> getResourceStoreDelegate().findByType(resourceServer, type),
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public void findByType(ResourceServer resourceServer, String type, Consumer<Resource> consumer) {
if (type == null) return;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByTypeCacheKey(type, resourceServerId);
cacheQuery(cacheKey, ResourceListQuery.class, () -> {
List<Resource> resources = new ArrayList<>();
getResourceStoreDelegate().findByType(resourceServer, type, new Consumer<Resource>() {
@Override
public void accept(Resource resource) {
consumer.andThen(resources::add)
.andThen(StoreFactoryCacheSession.this::cacheResource)
.accept(resource);
}
});
return resources;
},
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
@Override
public void findByType(ResourceServer resourceServer, String type, String owner, Consumer<Resource> consumer) {
if (type == null) return;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByTypeCacheKey(type, owner, resourceServerId);
cacheQuery(cacheKey, ResourceListQuery.class, () -> {
List<Resource> resources = new ArrayList<>();
getResourceStoreDelegate().findByType(resourceServer, type, owner, new Consumer<Resource>() {
@Override
public void accept(Resource resource) {
consumer.andThen(resources::add)
.andThen(StoreFactoryCacheSession.this::cacheResource)
.accept(resource);
}
});
return resources;
},
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
@Override
public void findByTypeInstance(ResourceServer resourceServer, String type, Consumer<Resource> consumer) {
if (type == null) return;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getResourceByTypeInstanceCacheKey(type, resourceServerId);
cacheQuery(cacheKey, ResourceListQuery.class, () -> {
List<Resource> resources = new ArrayList<>();
getResourceStoreDelegate().findByTypeInstance(resourceServer, type, new Consumer<Resource>() {
@Override
public void accept(Resource resource) {
consumer.andThen(resources::add)
.andThen(StoreFactoryCacheSession.this::cacheResource)
.accept(resource);
}
});
return resources;
},
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(Resource::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
private <R extends Resource, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer, Consumer<R> consumer) {
return cacheQuery(cacheKey, queryType, resultSupplier, querySupplier, resourceServer, consumer, false);
}
private <R extends Resource, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer) {
return cacheQuery(cacheKey, queryType, resultSupplier, querySupplier, resourceServer, null, true);
}
private <R extends Resource, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer, Consumer<R> consumer, boolean cacheResult) {
Q query = cache.get(cacheKey, queryType);
if (query != null) {
logger.tracev("cache hit for key: {0}", cacheKey);
}
List<R> model = Collections.emptyList();
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
model = resultSupplier.get();
if (model == null) return null;
if (!invalidations.contains(cacheKey)) {
query = querySupplier.apply(loaded, model);
cache.addRevisioned(query, startupRevision);
}
} else if (query.isInvalid(invalidations)) {
model = resultSupplier.get();
} else {
cacheResult = false;
Set<String> resources = query.getResources();
if (consumer != null) {
resources.stream().map(resourceId -> (R) findById(resourceServer, resourceId)).forEach(consumer);
} else {
model = resources.stream().map(resourceId -> (R) findById(resourceServer, resourceId)).collect(Collectors.toList());
}
}
if (cacheResult) {
model.forEach(StoreFactoryCacheSession.this::cacheResource);
}
return model;
}
}
protected class PolicyCache implements PolicyStore {
@Override
public Policy create(ResourceServer resourceServer, AbstractPolicyRepresentation representation) {
Policy policy = getPolicyStoreDelegate().create(resourceServer, representation);
Policy cached = findById(resourceServer, policy.getId());
registerPolicyInvalidation(policy.getId(), representation.getName(), representation.getResources(), representation.getScopes(), null, resourceServer.getId());
if (cached == null) {
cached = findById(resourceServer, policy.getId());
}
return cached;
}
@Override
public void delete(String id) {
if (id == null) return;
Policy policy = findById(null, id);
if (policy == null) return;
cache.invalidateObject(id);
Set<String> resources = policy.getResources().stream().map(Resource::getId).collect(Collectors.toSet());
ResourceServer resourceServer = policy.getResourceServer();
Set<String> resourceTypes = getResourceTypes(resources, resourceServer.getId());
String defaultResourceType = policy.getConfig().get("defaultResourceType");
if (Objects.nonNull(defaultResourceType)) {
resourceTypes.add(defaultResourceType);
}
Set<String> scopes = policy.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
invalidationEvents.add(PolicyRemovedEvent.create(id, policy.getName(), resources, resourceTypes, scopes, resourceServer.getId()));
cache.policyRemoval(id, policy.getName(), resources, resourceTypes, scopes, resourceServer.getId(), invalidations);
getPolicyStoreDelegate().delete(id);
}
@Override
public Policy findById(ResourceServer resourceServer, String id) {
if (id == null) return null;
CachedPolicy cached = cache.get(id, CachedPolicy.class);
if (cached != null) {
logger.tracev("by id cache hit: {0}", cached.getId());
}
if (cached == null) {
if (! modelMightExist(id)) return null;
Policy model = getPolicyStoreDelegate().findById(resourceServer, id);
Long loaded = cache.getCurrentRevision(id);
if (model == null) {
setModelDoesNotExists(id, loaded);
return null;
}
if (invalidations.contains(id)) return model;
cached = new CachedPolicy(loaded, model);
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
return getPolicyStoreDelegate().findById(resourceServer, id);
} else if (managedPolicies.containsKey(id)) {
return managedPolicies.get(id);
}
PolicyAdapter adapter = new PolicyAdapter(cached, StoreFactoryCacheSession.this);
managedPolicies.put(id, adapter);
return adapter;
}
@Override
public Policy findByName(ResourceServer resourceServer, String name) {
if (name == null) return null;
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPolicyByNameCacheKey(name, resourceServerId);
List<Policy> result = cacheQuery(cacheKey, PolicyListQuery.class, () -> {
Policy policy = getPolicyStoreDelegate().findByName(resourceServer, name);
if (policy == null) {
return Collections.emptyList();
}
return Arrays.asList(policy);
}, (revision, policies) -> new PolicyListQuery(revision, cacheKey, policies.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
if (result.isEmpty()) {
return null;
}
return result.get(0);
}
@Override
public List<Policy> findByResourceServer(ResourceServer resourceServer) {
return getPolicyStoreDelegate().findByResourceServer(resourceServer);
}
@Override
public List<Policy> find(ResourceServer resourceServer, Map<Policy.FilterOption, String[]> attributes, Integer firstResult, Integer maxResults) {
return getPolicyStoreDelegate().find(resourceServer, attributes, firstResult, maxResults);
}
@Override
public List<Policy> findByResource(ResourceServer resourceServer, Resource resource) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPolicyByResource(resource.getId(), resourceServerId);
return cacheQuery(cacheKey, PolicyResourceListQuery.class, () -> getPolicyStoreDelegate().findByResource(resourceServer, resource),
(revision, policies) -> new PolicyResourceListQuery(revision, cacheKey, resource.getId(), policies.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public void findByResource(ResourceServer resourceServer, Resource resource, Consumer<Policy> consumer) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPolicyByResource(resource.getId(), resourceServerId);
cacheQuery(cacheKey, PolicyResourceListQuery.class, () -> {
List<Policy> policies = new ArrayList<>();
getPolicyStoreDelegate().findByResource(resourceServer, resource, new Consumer<Policy>() {
@Override
public void accept(Policy policy) {
consumer.andThen(policies::add)
.andThen(StoreFactoryCacheSession.this::cachePolicy)
.accept(policy);
}
});
return policies;
},
(revision, policies) -> new PolicyResourceListQuery(revision, cacheKey, resource.getId(), policies.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
@Override
public List<Policy> findByResourceType(ResourceServer resourceServer, String resourceType) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPolicyByResourceType(resourceType, resourceServerId);
return cacheQuery(cacheKey, PolicyResourceListQuery.class, () -> getPolicyStoreDelegate().findByResourceType(resourceServer, resourceType),
(revision, policies) -> new PolicyResourceListQuery(revision, cacheKey, resourceType, policies.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public void findByResourceType(ResourceServer resourceServer, String resourceType, Consumer<Policy> consumer) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPolicyByResourceType(resourceType, resourceServerId);
cacheQuery(cacheKey, PolicyResourceListQuery.class, () -> {
List<Policy> policies = new ArrayList<>();
getPolicyStoreDelegate().findByResourceType(resourceServer, resourceType, new Consumer<Policy>() {
@Override
public void accept(Policy policy) {
consumer.andThen(policies::add)
.andThen(StoreFactoryCacheSession.this::cachePolicy)
.accept(policy);
}
});
return policies;
},
(revision, policies) -> new PolicyResourceListQuery(revision, cacheKey, resourceType, policies.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
@Override
public List<Policy> findByScopes(ResourceServer resourceServer, List<Scope> scopes) {
if (scopes == null) return null;
Set<Policy> result = new HashSet<>();
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
for (Scope scope : scopes) {
String cacheKey = getPolicyByScope(scope.getId(), resourceServerId);
result.addAll(cacheQuery(cacheKey, PolicyScopeListQuery.class, () -> getPolicyStoreDelegate().findByScopes(resourceServer, Collections.singletonList(scope)), (revision, resources) -> new PolicyScopeListQuery(revision, cacheKey, scope.getId(), resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServer));
}
return new ArrayList<>(result);
}
@Override
public List<Policy> findByScopes(ResourceServer resourceServer, Resource resource, List<Scope> scopes) {
if (scopes == null) return null;
Set<Policy> result = new HashSet<>();
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
for (Scope scope : scopes) {
String cacheKey = getPolicyByResourceScope(scope.getId(), resource == null ? null : resource.getId(), resourceServerId);
result.addAll(cacheQuery(cacheKey, PolicyScopeListQuery.class, () -> getPolicyStoreDelegate().findByScopes(resourceServer, resource, Collections.singletonList(scope)), (revision, resources) -> new PolicyScopeListQuery(revision, cacheKey, scope.getId(), resources.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer));
}
return new ArrayList<>(result);
}
@Override
public void findByScopes(ResourceServer resourceServer, Resource resource, List<Scope> scopes, Consumer<Policy> consumer) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String resourceId = resource == null ? null : resource.getId();
for (Scope scope : scopes) {
String cacheKey = getPolicyByResourceScope(scope.getId(), resourceId, resourceServerId);
cacheQuery(cacheKey, PolicyScopeListQuery.class, () -> {
List<Policy> policies = new ArrayList<>();
getPolicyStoreDelegate().findByScopes(resourceServer, resource, Collections.singletonList(scope),
policy -> {
consumer.andThen(policies::add)
.andThen(StoreFactoryCacheSession.this::cachePolicy)
.accept(policy);
});
return policies;
}, (revision, resources) -> new PolicyScopeListQuery(revision, cacheKey, scope.getId(), resources.stream().map(Policy::getId).collect(Collectors.toSet()), resourceServerId), resourceServer, consumer);
}
}
@Override
public List<Policy> findByType(ResourceServer resourceServer, String type) {
return getPolicyStoreDelegate().findByType(resourceServer, type);
}
@Override
public List<Policy> findDependentPolicies(ResourceServer resourceServer, String id) {
return getPolicyStoreDelegate().findDependentPolicies(resourceServer, id);
}
private <R extends Policy, Q extends PolicyQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer) {
return cacheQuery(cacheKey, queryType, resultSupplier, querySupplier, resourceServer, null, true);
}
private <R extends Policy, Q extends PolicyQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer, Consumer<R> consumer) {
return cacheQuery(cacheKey, queryType, resultSupplier, querySupplier, resourceServer, consumer, false);
}
private <R extends Policy, Q extends PolicyQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer, Consumer<R> consumer, boolean cacheResults) {
Q query = cache.get(cacheKey, queryType);
if (query != null) {
logger.tracev("cache hit for key: {0}", cacheKey);
}
List<R> model = Collections.emptyList();
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
model = resultSupplier.get();
if (model == null) return null;
if (!invalidations.contains(cacheKey)) {
query = querySupplier.apply(loaded, model);
cache.addRevisioned(query, startupRevision);
}
} else if (query.isInvalid(invalidations)) {
model = resultSupplier.get();
} else {
cacheResults = false;
Set<String> policies = query.getPolicies();
if (consumer != null) {
for (String id : policies) {
consumer.accept((R) findById(resourceServer, id));
}
} else {
model = policies.stream().map(resourceId -> (R) findById(resourceServer, resourceId))
.filter(Objects::nonNull).collect(Collectors.toList());
}
}
if (cacheResults) {
model.forEach(StoreFactoryCacheSession.this::cachePolicy);
}
return model;
}
}
protected class PermissionTicketCache implements PermissionTicketStore {
@Override
public long count(ResourceServer resourceServer, Map<PermissionTicket.FilterOption, String> attributes) {
return getPermissionTicketStoreDelegate().count(resourceServer, attributes);
}
@Override
public PermissionTicket create(ResourceServer resourceServer, Resource resource, Scope scope, String requester) {
PermissionTicket created = getPermissionTicketStoreDelegate().create(resourceServer, resource, scope, requester);
registerPermissionTicketInvalidation(created.getId(), created.getOwner(), created.getRequester(), created.getResource().getId(), created.getResource().getName(), scope == null ? null : scope.getId(), created.getResourceServer().getId());
return created;
}
@Override
public void delete(String id) {
if (id == null) return;
PermissionTicket permission = findById(null, id);
if (permission == null) return;
cache.invalidateObject(id);
String scopeId = null;
if (permission.getScope() != null) {
scopeId = permission.getScope().getId();
}
invalidationEvents.add(PermissionTicketRemovedEvent.create(id, permission.getOwner(), permission.getRequester(), permission.getResource().getId(), permission.getResource().getName(), scopeId, permission.getResourceServer().getId()));
cache.permissionTicketRemoval(id, permission.getOwner(), permission.getRequester(), permission.getResource().getId(), permission.getResource().getName(),scopeId, permission.getResourceServer().getId(), invalidations);
getPermissionTicketStoreDelegate().delete(id);
UserManagedPermissionUtil.removePolicy(permission, StoreFactoryCacheSession.this);
}
@Override
public PermissionTicket findById(ResourceServer resourceServer, String id) {
if (id == null) return null;
CachedPermissionTicket cached = cache.get(id, CachedPermissionTicket.class);
if (cached != null) {
logger.tracev("by id cache hit: {0}", cached.getId());
}
if (cached == null) {
Long loaded = cache.getCurrentRevision(id);
if (! modelMightExist(id)) return null;
PermissionTicket model = getPermissionTicketStoreDelegate().findById(resourceServer, id);
if (model == null) {
setModelDoesNotExists(id, loaded);
return null;
}
if (invalidations.contains(id)) return model;
cached = new CachedPermissionTicket(loaded, model);
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
return getPermissionTicketStoreDelegate().findById(resourceServer, id);
} else if (managedPermissionTickets.containsKey(id)) {
return managedPermissionTickets.get(id);
}
PermissionTicketAdapter adapter = new PermissionTicketAdapter(cached, StoreFactoryCacheSession.this);
managedPermissionTickets.put(id, adapter);
return adapter;
}
@Override
public List<PermissionTicket> findByResource(ResourceServer resourceServer, Resource resource) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPermissionTicketByResource(resource.getId(), resourceServerId);
return cacheQuery(cacheKey, PermissionTicketResourceListQuery.class, () -> getPermissionTicketStoreDelegate().findByResource(resourceServer, resource),
(revision, permissions) -> new PermissionTicketResourceListQuery(revision, cacheKey, resource.getId(), permissions.stream().map(PermissionTicket::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public List<PermissionTicket> findByScope(ResourceServer resourceServer, Scope scope) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPermissionTicketByScope(scope.getId(), resourceServerId);
return cacheQuery(cacheKey, PermissionTicketScopeListQuery.class, () -> getPermissionTicketStoreDelegate().findByScope(resourceServer, scope),
(revision, permissions) -> new PermissionTicketScopeListQuery(revision, cacheKey, scope.getId(), permissions.stream().map(PermissionTicket::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public List<PermissionTicket> find(ResourceServer resourceServer, Map<PermissionTicket.FilterOption, String> attributes, Integer firstResult, Integer maxResult) {
return getPermissionTicketStoreDelegate().find(resourceServer, attributes, firstResult, maxResult);
}
@Override
public List<PermissionTicket> findGranted(ResourceServer resourceServer, String userId) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPermissionTicketByGranted(userId, resourceServerId);
return cacheQuery(cacheKey, PermissionTicketListQuery.class, () -> getPermissionTicketStoreDelegate().findGranted(resourceServer, userId),
(revision, permissions) -> new PermissionTicketListQuery(revision, cacheKey, permissions.stream().map(PermissionTicket::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public List<PermissionTicket> findGranted(ResourceServer resourceServer, String resourceName, String userId) {
String resourceServerId = resourceServer == null ? null : resourceServer.getId();
String cacheKey = getPermissionTicketByResourceNameAndGranted(resourceName, userId, resourceServerId);
return cacheQuery(cacheKey, PermissionTicketListQuery.class, () -> getPermissionTicketStoreDelegate().findGranted(resourceServer, resourceName, userId),
(revision, permissions) -> new PermissionTicketResourceListQuery(revision, cacheKey, resourceName, permissions.stream().map(PermissionTicket::getId).collect(Collectors.toSet()), resourceServerId), resourceServer);
}
@Override
public List<Resource> findGrantedResources(String requester, String name, Integer first, Integer max) {
return getPermissionTicketStoreDelegate().findGrantedResources(requester, name, first, max);
}
@Override
public List<Resource> findGrantedOwnerResources(String owner, Integer firstResult, Integer maxResults) {
return getPermissionTicketStoreDelegate().findGrantedOwnerResources(owner, firstResult, maxResults);
}
private <R, Q extends PermissionTicketQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, ResourceServer resourceServer) {
Q query = cache.get(cacheKey, queryType);
if (query != null) {
logger.tracev("cache hit for key: {0}", cacheKey);
}
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
List<R> model = resultSupplier.get();
if (model == null) return null;
if (invalidations.contains(cacheKey)) return model;
query = querySupplier.apply(loaded, model);
cache.addRevisioned(query, startupRevision);
return model;
} else if (query.isInvalid(invalidations)) {
return resultSupplier.get();
} else {
return query.getPermissions().stream().map(resourceId -> (R) findById(resourceServer, resourceId)).collect(Collectors.toList());
}
}
}
void cachePolicy(Policy model) {
String id = model.getId();
if (cache.getCache().containsKey(id)) {
return;
}
if (!modelMightExist(id)) {
return;
}
if (invalidations.contains(id)) return;
cache.addRevisioned(createCachedPolicy(model, id), startupRevision);
}
CachedPolicy createCachedPolicy(Policy model, String id) {
Long loaded = cache.getCurrentRevision(id);
return new CachedPolicy(loaded, model);
}
void cacheResource(Resource model) {
String id = model.getId();
if (cache.getCache().containsKey(id)) {
return;
}
Long loaded = cache.getCurrentRevision(id);
if (!modelMightExist(id)) {
return;
}
if (invalidations.contains(id)) return;
cache.addRevisioned(new CachedResource(loaded, model), startupRevision);
}
void cacheScope(Scope model) {
String id = model.getId();
if (cache.getCache().containsKey(id)) {
return;
}
Long loaded = cache.getCurrentRevision(id);
if (!modelMightExist(id)) {
return;
}
if (invalidations.contains(id)) return;
cache.addRevisioned(new CachedScope(loaded, model), startupRevision);
}
}