package com.tesora.dve.cas.impl; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import com.tesora.dve.cas.AtomicState; import com.tesora.dve.cas.ConcurrentReferenceMap; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; //A special purpose concurrent map that uses identity comparisions rather than equals(), so that concurrent map calls like putIfAbsent() and replace() behave more like AtomicReference.compareAndSet(). public class ConcurrentReferenceMapImpl<K,V> implements ConcurrentReferenceMap<K,V> { ConcurrentMap<K,IdentityValue<V>> backingMap = new ConcurrentHashMap<K, IdentityValue<V>>(); @Override public AtomicState<V> binding(K key){ return new BoundAtomicState<K,V>(this,key); } @Override public V putIfAbsent(K key, V value) { IdentityValue<V> wrappedValue = new IdentityValue<V>(value); IdentityValue<V> existing = backingMap.putIfAbsent(key,wrappedValue); if (existing == null) return null; else return existing.value; } @Override public boolean remove(Object key, Object value) { IdentityValue<Object> wrappedValue = new IdentityValue<Object>(value); return backingMap.remove(key,wrappedValue); } @Override public boolean replace(K key, V oldValue, V newValue) { IdentityValue<V> expectedValue = new IdentityValue<V>(oldValue); IdentityValue<V> wrappedValue = new IdentityValue<V>(newValue); return backingMap.replace(key,expectedValue,wrappedValue); } @Override public V replace(K key, V value) { IdentityValue<V> wrappedValue = new IdentityValue<V>(value); IdentityValue<V> existing = backingMap.replace(key,wrappedValue); if (existing == null) return null; else return existing.value; } @Override public int size() { return backingMap.size(); } @Override public boolean isEmpty() { return backingMap.isEmpty(); } @Override public boolean containsKey(Object key) { return backingMap.containsKey(key); } @Override public boolean containsValue(Object value) { IdentityValue<Object> wrappedValue = new IdentityValue<Object>(value); return backingMap.containsValue(wrappedValue); } @Override public V get(Object key) { IdentityValue<V> existing = backingMap.get(key); if (existing == null) return null; else return existing.value; } @Override public V put(K key, V value) { IdentityValue<V> wrappedValue = new IdentityValue<V>(value); IdentityValue<V> existing = backingMap.put(key,wrappedValue); if (existing == null) return null; else return existing.value; } @Override public V remove(Object key) { IdentityValue<V> existing = backingMap.remove(key); if (existing == null) return null; else return existing.value; } @Override public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) this.put(e.getKey(), e.getValue()); } @Override public void clear() { backingMap.clear(); } @Override public Set<K> keySet() { return backingMap.keySet(); } @Override public Collection<V> values() { ArrayList<V> unwrapped = new ArrayList<V>(); Collection<IdentityValue<V>> wrappedValues = backingMap.values(); for (IdentityValue<V> entry : wrappedValues) unwrapped.add(entry.value); return unwrapped; } @Override public Set<Entry<K, V>> entrySet() { HashSet< Entry<K,V> > entries = new HashSet<Entry<K, V>>(); for (Map.Entry<K,IdentityValue<V>> entry : backingMap.entrySet()) entries.add( new AbstractMap.SimpleImmutableEntry<K,V>(entry.getKey(),entry.getValue().value)); return entries; } static class IdentityValue<X> { X value; IdentityValue(X value) { this.value = value; } @Override public boolean equals(Object other) { if (!(other instanceof IdentityValue)) return false; IdentityValue otherValue = (IdentityValue)other; return this.value == otherValue.value; } @Override public int hashCode() { return Objects.hashCode(this.value); } } static class BoundAtomicState<X,Y> implements AtomicState<Y> { ConcurrentReferenceMapImpl<X,Y> backingMap; X boundKey; BoundAtomicState(ConcurrentReferenceMapImpl<X, Y> backingMap, X boundKey) { this.backingMap = backingMap; this.boundKey = boundKey; } @Override public Y get() { return this.backingMap.get(boundKey); } @Override public boolean compareAndSet(Y expected, Y value) { if (expected == null){ Y existing = backingMap.putIfAbsent(boundKey,value); return (existing == null); } else { return backingMap.replace(boundKey,expected,value); } } } }