package org.dcache.srm.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.dcache.srm.request.Job; /** * * @author timur */ public class SharedMemoryCache { private static final Logger _log = LoggerFactory.getLogger(SharedMemoryCache.class); /** * This set is used to keep the Jobs that are in active * (Non final) state in memory * If the SRM application runs in the Terracotta clustered environment, * the Set is used as a root, so all jobs that are added to this * cache are shared between all instances of the SRM servers. * */ private final ReentrantReadWriteLock sharedMemoryReadWriteLock = new ReentrantReadWriteLock(); private final ReadLock sharedMemoryReadLock = sharedMemoryReadWriteLock.readLock(); private final WriteLock sharedMemoryWriteLock = sharedMemoryReadWriteLock.writeLock(); private final Map<Long,Job> sharedMemoryCache = new HashMap<>(); /** * Canonicalizes non-final jobs. * * @throws IllegalStateException if the canonical instance has a different type than {@code job} * @return a canonical instance of {@code job}, which may be job itself */ public <T extends Job> T canonicalize(T job) { sharedMemoryWriteLock.lock(); try { Job other = sharedMemoryCache.get(job.getId()); if (other != null) { if (!job.getClass().isInstance(other)) { throw new IllegalStateException("Conflicting types for request " + job.getId() + ": " + job.getClass() + " and " + other.getClass()); } job = (T) other; } else if (!job.getState().isFinal()) { sharedMemoryCache.put(job.getId(), job); } } finally { sharedMemoryWriteLock.unlock(); } return job; } /** * Updates the registration of job in the cache. * * A post-condition of this method is that job is in the cache if and only if * {@code job} is not final. * * @param job The canonical instance of a job * @throws IllegalArgumentException if {@code job} is not the canonical instance */ public <T extends Job> void update(T job) { sharedMemoryWriteLock.lock(); try { Job other = sharedMemoryCache.get(job.getId()); if (other != null) { if (other != job) { throw new IllegalArgumentException("Duplicate job #" + job.getId()); } if (job.getState().isFinal()) { sharedMemoryCache.remove(job.getId()); } } else { if (!job.getState().isFinal()) { sharedMemoryCache.put(job.getId(), job); } } } finally { sharedMemoryWriteLock.unlock(); } } public Job getJob(long jobId) { _log.debug("getJob ( "+jobId + " ) "); sharedMemoryReadLock.lock(); try { return sharedMemoryCache.get(jobId); } finally { sharedMemoryReadLock.unlock(); } } /** * removes all values from the cache */ public void clearCache() { sharedMemoryWriteLock.lock(); try { sharedMemoryCache.clear(); }finally{ sharedMemoryWriteLock.unlock(); } } public <T extends Job> Set<T> getJobs(Class<T> jobType) { sharedMemoryReadLock.lock(); try { Set<T> results = new HashSet<>(); for(Job job: sharedMemoryCache.values()) { if(job.getClass().equals(jobType)) { results.add((T)job); } } return results; } finally { sharedMemoryReadLock.unlock(); } } }