com.github.pojomvcc.CacheElementRevisions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pojo-mvcc Show documentation
Show all versions of pojo-mvcc Show documentation
A simple in-memory POJO Multi Version Concurrency Control (MVCC) cache.
The newest version!
package com.github.pojomvcc;
import java.util.*;
/**
* Class that contains all revisions for an {@code V}.
*
* @author Aidan Morgan
*/
public class CacheElementRevisions {
private static final Comparator REVISION_COMPARATOR = new AscendingRevisionComparator();
/**
* The {@link K} this set of {@link CacheElementRevisions} is for.
*/
private K cacheKey;
/**
* The {@code java.util.SortedMap} of {@link CacheElementRevision}s that make up this revision
* history.
*/
private SortedMap> revisions;
/**
* A reference to the {@link RootObjectCache} for this revision history.
*/
private RootObjectCache rootObjectCache;
/**
* Constructor.
*
* @param rootObjectCache the {@link RootObjectCache} that will own this revision history.
* @param oid the {@link K} of the {@link V} that this
* class is keeping revision history for.
*/
public CacheElementRevisions(RootObjectCache rootObjectCache, K oid) {
this.rootObjectCache = rootObjectCache;
this.revisions = new TreeMap>(REVISION_COMPARATOR);
this.cacheKey = oid;
}
/**
* Returns the leading (latest) revision of the enclosed {@link V}.
*
* @return
*/
public V getLeading() {
Long lastRevision = revisions.lastKey();
return revisions.get(lastRevision).getElement();
}
/**
* Returns the {@link V} with the enclosed {@link K} at the provided revision.
*
* This works by moving through the recorded revision history and finding the {@link CacheElementRevision}
* in the internal store that has the highest revision that is less than the provided revision. This allows the storage
* of deltas-only whilst being able to retrieve the object state at a specific revision.
*
* @param revision
* @return
*/
public V get(long revision) {
if (revisions.isEmpty()) {
throw new ObjectCacheException("There are no revisions in this CacheElementRevisions. This is a bug, there should be at least one.");
}
CacheElementRevision rev = null;
// if the key is in the map just return it straight away.
if (revisions.containsKey(revision)) {
rev = revisions.get(revision);
} else {
// head map will return a SortedMap of all values that have a revision STRICTLY less than the
// value provided.
SortedMap> vals = revisions.headMap(revision);
if (null != vals && !vals.isEmpty()) {
rev = vals.get(vals.firstKey());
}
}
// If the revision element was not found in memory, then we need to go to the CacheExpirationHandler and beg for
// it (nicely of course).
if (null == rev) {
rev = rootObjectCache.getCacheExpiry().getHandler().retrieve(cacheKey, revision);
}
// if the revision is null at this point there is nothing we can do, it's not in the list.
if (null == rev) {
return null;
}
// if the revision has been deleted then we return null and pretend it never existed.
if (CacheRevisionType.DELETED == rev.getState()) {
return null;
}
return rev.getElement();
}
/**
* Returns the {@link K} for the enclosed {@link V}.
*
* @return
*/
public K getCacheKey() {
return cacheKey;
}
/**
* Adds the provided {@link V} to the revision history at the provided revision.
*
* Will record the modification as a {@link CacheRevisionType#ADDED}.
*
* @param l the revision of the {@link RootObjectCache} that the provided {@link V} is added at.
* @param key the {@link K} of the {@link V} to add.
* @param ele the {@link V} to add.
*/
public void addElement(long l, K key, V ele) {
if (null == ele) {
throw new ObjectCacheException("Element cannot be null.");
}
CacheElementRevision cr = new CacheElementRevision(key, ele, l, CacheRevisionType.ADDED);
revisions.put(l, cr);
}
/**
* Adds the provided {@link V} to the revision history at the provided revision.
*
* Will record the modification as a {@link CacheRevisionType#MODIFIED}.
*
* @param l
* @param ele
*/
public void addModification(long l, K key, V ele) {
if (null == ele) {
throw new ObjectCacheException("Element cannot be null.");
}
CacheElementRevision cr = new CacheElementRevision(key, ele, l, CacheRevisionType.MODIFIED);
revisions.put(l, cr);
}
/**
* Adds the provided {@link V} to the revision history at the provided revision.
*
* Will record the modification as a {@link CacheRevisionType#DELETED}.
*
* @param l
* @param ele
*/
public void removeElement(long l, K ele) {
if (ele == null) {
throw new ObjectCacheException("CacheKey for removal cannot be null.");
}
CacheElementRevision cr = new CacheElementRevision(ele, null, l, CacheRevisionType.DELETED);
revisions.put(l, cr);
}
/**
* Removes all of the provided {@link CacheElementRevision} from this history.
*
* @param remove
*/
public void removeRevisions(List> remove) {
for (CacheElementRevision rev : remove) {
revisions.remove(rev.getRevision());
}
}
/**
* Returns the {@code java.util.List} of {@link CacheElementRevision} that are the
* history of the enclosed {@link V}.
*
* @return
*/
public Iterator> getRevisions() {
return new Iterator>() {
Iterator>> itr = revisions.entrySet().iterator();
public boolean hasNext() {
return itr.hasNext();
}
public CacheElementRevision next() {
return itr.next().getValue();
}
public void remove() {
throw new UnsupportedOperationException("Removes are not allowed.");
}
};
}
/**
* Removes the revision history for the provided revision.
*
* @param revision
*/
public void removeRevision(long revision) {
if (!revisions.containsKey(revision)) {
return;
}
// if there is more than one revision in here
if (revisions.size() > 1) {
// and the revision we are wanting to remove is not the latest revision
if (revisions.get(revision).getRevision() > revision) {
revisions.remove(revision);
}
}
// we have only one revision (which is typically the minimum case)
else if (revisions.size() == 1) {
CacheElementRevision rev = revisions.get(revision);
if (null != rev) {
// if the revision was deleted then we are okay to evict the revision history
// and just pretend it never existed.
if (rev.getRevisionType() == CacheRevisionType.DELETED) {
revisions.remove(revision);
}
}
}
}
public boolean containsRevision(long revision) {
return get(revision) != null;
}
/**
* Returns the number of revisions that are being tracked by this {@link CacheElementRevision}.
*
* @return
*/
public int size() {
return revisions.size();
}
public boolean isDeleted() {
Long lastRevision = revisions.lastKey();
return revisions.get(lastRevision).getState() == CacheRevisionType.DELETED;
}
/**
* Implementation of the {@code java.util.Comparator} interface for comparing revision numbers.
*/
private static class AscendingRevisionComparator implements Comparator {
public int compare(Long o1, Long o2) {
return o1.compareTo(o2);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy