package org.molgenis.data.listeners; import com.google.common.collect.HashMultimap; import com.google.common.collect.SetMultimap; import org.molgenis.data.Entity; import org.molgenis.data.MolgenisDataException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Stream; import static java.util.Objects.requireNonNull; @Service public final class EntityListenersService { private final Logger LOG = LoggerFactory.getLogger(EntityListenersService.class); private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Map<String, SetMultimap<Object, EntityListener>> entityListeners = new HashMap<>(); /** * Register a repository to the entity listeners service once */ void register(String repoFullName) { lock.writeLock().lock(); try { if (!entityListeners.containsKey(requireNonNull(repoFullName))) { entityListeners.put(repoFullName, HashMultimap.create()); } } finally { lock.writeLock().unlock(); } } /** * Update all registered listeners of the entities * * @return Stream<Entity> */ Stream<Entity> updateEntities(String repoFullName, Stream<Entity> entities) { lock.readLock().lock(); try { verifyRepoRegistered(repoFullName); SetMultimap<Object, EntityListener> entityListeners = this.entityListeners.get(repoFullName); return entities.filter(entity -> { Set<EntityListener> entityEntityListeners = entityListeners.get(entity.getIdValue()); entityEntityListeners.forEach(entityListener -> entityListener.postUpdate(entity)); return true; }); } finally { lock.readLock().unlock(); } } /** * Update all registered listeners of an entity */ void updateEntity(String repoFullName, Entity entity) { lock.readLock().lock(); try { verifyRepoRegistered(repoFullName); SetMultimap<Object, EntityListener> entityListeners = this.entityListeners.get(repoFullName); Set<EntityListener> entityEntityListeners = entityListeners.get(entity.getIdValue()); entityEntityListeners.forEach(entityListener -> entityListener.postUpdate(entity)); } finally { lock.readLock().unlock(); } } /** * Adds an entity listener for a entity of the given class that listens to entity changes * * @param entityListener entity listener for a entity */ public void addEntityListener(String repoFullName, EntityListener entityListener) { lock.writeLock().lock(); try { verifyRepoRegistered(repoFullName); SetMultimap<Object, EntityListener> entityListeners = this.entityListeners.get(repoFullName); entityListeners.put(entityListener.getEntityId(), entityListener); } finally { lock.writeLock().unlock(); } } /** * Removes an entity listener for a entity of the given class * * @param entityListener entity listener for a entity * @return boolean */ public boolean removeEntityListener(String repoFullName, EntityListener entityListener) { lock.writeLock().lock(); try { verifyRepoRegistered(repoFullName); SetMultimap<Object, EntityListener> entityListeners = this.entityListeners.get(repoFullName); if (entityListeners.containsKey(entityListener.getEntityId())) { entityListeners.remove(entityListener.getEntityId(), entityListener); return true; } return false; } finally { lock.writeLock().unlock(); } } /** * Check if a repository has no listeners * Repository must be registered * * @return boolean */ boolean isEmpty(String repoFullName) { lock.readLock().lock(); try { verifyRepoRegistered(repoFullName); return entityListeners.get(repoFullName).isEmpty(); } finally { lock.readLock().unlock(); } } /** * Verify that the repository is registered */ private void verifyRepoRegistered(String repoFullName) { lock.readLock().lock(); try { if (!entityListeners.containsKey(requireNonNull(repoFullName))) { LOG.error("Repository [" + repoFullName + "] is not registered in the entity listeners service"); throw new MolgenisDataException( "Repository [" + repoFullName + "] is not registered, please contact your administrator"); } } finally { lock.readLock().unlock(); } } }