package uk.ac.imperial.lsds.seep.api.state.stateimpl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import uk.ac.imperial.lsds.seep.api.state.Checkpoint; import uk.ac.imperial.lsds.seep.api.state.DistributedMutableState; import uk.ac.imperial.lsds.seep.api.state.Mergeable; import uk.ac.imperial.lsds.seep.api.state.Partitionable; import uk.ac.imperial.lsds.seep.api.state.SeepState; import uk.ac.imperial.lsds.seep.api.state.Streamable; import uk.ac.imperial.lsds.seep.api.state.Versionable; import uk.ac.imperial.lsds.seep.errors.AttemptToReconcileStateNotInSnapshotModeException; import uk.ac.imperial.lsds.seep.errors.IllegalOperationOnStateException; import uk.ac.imperial.lsds.seep.errors.IncompatibleStateException; import uk.ac.imperial.lsds.seep.errors.NotImplementedException; public class SeepMap<K,V> implements Map<K,V>, Checkpoint, Partitionable, Mergeable, Streamable, Versionable, SeepState { // Main map private int owner; private DistributedMutableState dms = DistributedMutableState.PARTITIONED; private Map<K, V> map; // Support for dirtyState private Map<K, V> dirtyState; private Set<K> keysRemoved; private AtomicBoolean snapshotMode; private ReentrantLock lock; public SeepMap(){ this.map = new HashMap<>(); this.dirtyState = new HashMap<>(); this.keysRemoved = new HashSet<>(); this.snapshotMode = new AtomicBoolean(false); this.lock = new ReentrantLock(); } /** Implement SeepState interface **/ @Override public void setOwner(int owner){ this.owner = owner; } @Override public int getOwner(){ return owner; } @Override public DistributedMutableState getDMS() { return dms; } @Override public void setDMS(DistributedMutableState dms) { this.dms = dms; } /** Implement Map<K,V> interface **/ @Override public int size() { if(snapshotMode.get()){ lock.lock(); int size = map.size() - keysRemoved.size() + dirtyState.size(); lock.unlock(); return size; } return map.size(); } @Override public boolean isEmpty() { if(snapshotMode.get()){ lock.lock(); boolean empty = map.isEmpty() || dirtyState.isEmpty(); lock.unlock(); return empty; } return map.isEmpty(); } @Override public boolean containsKey(Object key) { if(snapshotMode.get()){ lock.lock(); boolean containsKey = (map.containsKey(key) && !keysRemoved.contains(key)) || dirtyState.containsKey(key); lock.unlock(); return containsKey; } return map.containsKey(key); } @Override public boolean containsValue(Object value) { // FIXME: note how broken is this implementation... who uses containsValue anyway... ainss... if(snapshotMode.get()){ lock.lock(); boolean containsValue = map.containsValue(value) || dirtyState.containsValue(value); lock.unlock(); return containsValue; } return map.containsValue(value); } @Override public V get(Object key) { if(snapshotMode.get()){ lock.lock(); if(dirtyState.containsKey(key)){ lock.unlock(); return dirtyState.get(key); } lock.unlock(); return map.get(key); } return map.get(key); } @Override public V put(K key, V value) { lock.lock(); V v = map.put(key, value); lock.unlock(); return v; } @Override public V remove(Object key) { lock.lock(); V v = map.remove(key); lock.unlock(); return v; } @Override public void putAll(Map<? extends K, ? extends V> m) { lock.lock(); map.putAll(m); lock.unlock(); } @Override public void clear() { lock.lock(); map.clear(); lock.unlock(); } @Override public Set<K> keySet() { return map.keySet(); } @Override public Collection<V> values() { return map.values(); } @Override public Set<Map.Entry<K, V>> entrySet() { return map.entrySet(); } /** Implement Versionable interface **/ @Override public void enterSnapshotMode() { this.snapshotMode.getAndSet(true); } @Override public void reconcile() { if(snapshotMode.get()){ lock.lock(); performReconcileOperation(); lock.unlock(); } else{ throw new AttemptToReconcileStateNotInSnapshotModeException("An attempt was made to reconcile state not in snapshot mode"); } } private void performReconcileOperation(){ // Additions and updates for(Map.Entry<K, V> dirtyEntry : dirtyState.entrySet()){ map.put(dirtyEntry.getKey(), dirtyEntry.getValue()); } // Removals for(K k : keysRemoved){ map.remove(k); } // Clean dirtyState dirtyState.clear(); keysRemoved.clear(); System.gc(); } /** Implement Streamable interface **/ @Override public Iterator<Map.Entry<K, V>> makeStream() { if(snapshotMode.get()) throw new IllegalOperationOnStateException("Attemp to stream state while in snapshot mode"); return map.entrySet().iterator(); } /** Implement Mergeable interface **/ @SuppressWarnings("unchecked") @Override public void merge(List<SeepState> state) { if(snapshotMode.get()) throw new IllegalOperationOnStateException("Attemp to stream state while in snapshot mode"); for(SeepState chunk : state){ if(!(chunk instanceof SeepMap)){ throw new IncompatibleStateException("Attempt to merge an incompatible state"); } for(Map.Entry<K, V> entry : ((SeepMap<K,V>)chunk).entrySet()){ map.put(entry.getKey(), entry.getValue()); } } } /** Implement Partitionable interface **/ @Override public List<SeepState> partition(){ List<SeepState> partitions = new ArrayList<>(); int partitioningKey = Integer.MAX_VALUE / 2; Map<K,V> partition1 = new SeepMap<>(); Map<K,V> partition2 = new SeepMap<>(); for(K key : map.keySet()){ if(key.hashCode() < partitioningKey){ partition1.put(key, map.get(key)); } else{ partition2.put(key, map.get(key)); } } partitions.add((SeepState)partition1); partitions.add((SeepState)partition2); return partitions; } @Override public List<SeepState> partition(int partitions) { throw new NotImplementedException("yet to implement n-partitioning..."); } /** Implement Checkpoint interface **/ @Override public byte[] checkpoint() { // TODO: Auto-generated method stub return null; } @Override public void recover(byte[] bytes) { // TODO Auto-generated method stub } }