package org.molgenis.data.cache.utils; import com.google.common.cache.Cache; import org.molgenis.data.Entity; import org.molgenis.data.EntityKey; import org.molgenis.data.meta.model.EntityType; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import static java.util.Objects.requireNonNull; import static java.util.Optional.empty; /** * Caches {@link org.molgenis.data.Entity}s from different repositories in dehydrated form in a single combined * Guava {@link Cache}. */ public class CombinedEntityCache { private final EntityHydration entityHydration; private final Cache<EntityKey, Optional<Map<String, Object>>> cache; /** * Creates a new {@link CombinedEntityCache} * * @param entityHydration {@link EntityHydration} used to hydrate and dehydrate the entities and generate cache keys * @param cache the {@link Cache} to store the entities in */ public CombinedEntityCache(EntityHydration entityHydration, Cache<EntityKey, Optional<Map<String, Object>>> cache) { this.entityHydration = requireNonNull(entityHydration); this.cache = requireNonNull(cache); } /** * Caches the deletion of an entity instance. * * @param entityKey the {@link EntityKey} of the deleted entity instance */ public void putDeletion(EntityKey entityKey) { cache.put(entityKey, empty()); } /** * Evicts all entries from the cache that belong to a certain entityName. * * @param entityName the name of the entity whose entries are to be evicted */ public void evictAll(String entityName) { cache.asMap().keySet().stream().filter(e -> e.getEntityName().equals(entityName)).forEach(cache::invalidate); } /** * Retrieves an entity from the cache if present. * * @param entityType EntityType of the entity to retrieve * @param id id value of the entity to retrieve * @return Optional {@link Entity} with the result from the cache, * or null if no record of the entity is present in the cache */ @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_OPTIONAL_RETURN_NULL", justification = "Intentional behavior") public Optional<Entity> getIfPresent(EntityType entityType, Object id) { Optional<Map<String, Object>> optionalDehydratedEntity = cache .getIfPresent(EntityKey.create(entityType, id)); if (optionalDehydratedEntity == null) { // no information present in cache return null; } return optionalDehydratedEntity .map(dehydratedEntity -> entityHydration.hydrate(dehydratedEntity, entityType)); } /** * Inserts an entity into the cache. * * @param entity the entity to insert into the cache */ public void put(Entity entity) { String entityName = entity.getEntityType().getName(); cache.put(EntityKey.create(entityName, entity.getIdValue()), Optional.of(entityHydration.dehydrate(entity))); } public void evict(Stream<EntityKey> entityKeys) { entityKeys.forEach(cache::invalidate); } }