All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.atomic.impl.AtomicHashMap Maven / Gradle / Ivy

package org.infinispan.atomic.impl;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.atomic.AtomicMapLookup;
import org.infinispan.atomic.CopyableDeltaAware;
import org.infinispan.atomic.Delta;
import org.infinispan.commons.marshall.AbstractExternalizer;
import org.infinispan.commons.util.FastCopyHashMap;
import org.infinispan.commons.util.Util;
import org.infinispan.marshall.core.Ids;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;

import net.jcip.annotations.NotThreadSafe;

/**
 * The default implementation of {@link AtomicMap}.  Note that this map cannot be constructed directly, and callers
 * should obtain references to AtomicHashMaps via the {@link AtomicMapLookup} helper.  This helper will ensure proper
 * concurrent construction and registration of AtomicMaps in Infinispan's data container.  E.g.:
 * 

* * AtomicMap<String, Integer> map = AtomicMapLookup.getAtomicMap(cache, "my_atomic_map_key"); * *

* Note that for replication to work properly, AtomicHashMap updates must always take place within the * scope of an ongoing JTA transaction or batch (see {@link Cache#startBatch()}). *

* * @author (various) * @param the type of keys maintained by this map * @param the type of mapped values * @see AtomicMap * @see AtomicMapLookup * @since 4.0 */ @NotThreadSafe public final class AtomicHashMap implements AtomicMap, CopyableDeltaAware, Cloneable { private static final Log log = LogFactory.getLog(AtomicHashMap.class); private static final boolean trace = log.isTraceEnabled(); protected final FastCopyHashMap delegate; private AtomicHashMapDelta delta = null; private volatile AtomicHashMapProxy proxy; volatile boolean copied = false; volatile boolean removed = false; private final ProxyMode proxyMode; /** * Construction only allowed through this factory method. This factory is intended for use internally by the * CacheDelegate. User code should use {@link AtomicMapLookup#getAtomicMap(Cache, Object)}. */ @SuppressWarnings("unchecked") public static AtomicHashMap newInstance(Cache cache, Object cacheKey, ProxyMode proxyMode) { AtomicHashMap value = new AtomicHashMap<>(proxyMode); Object oldValue = cache.putIfAbsent(cacheKey, value); if (oldValue != null) value = (AtomicHashMap) oldValue; return value; } //used in tests only public AtomicHashMap() { this(new FastCopyHashMap<>(), ProxyMode.COARSE); } public AtomicHashMap(ProxyMode proxyMode) { this(new FastCopyHashMap<>(), Objects.requireNonNull(proxyMode)); } private AtomicHashMap(FastCopyHashMap delegate, ProxyMode proxyMode) { this.delegate = delegate; this.proxyMode = proxyMode; } public AtomicHashMap(boolean isCopy, ProxyMode proxyMode) { this(new FastCopyHashMap<>(), proxyMode); this.copied = isCopy; } private AtomicHashMap(FastCopyHashMap newDelegate, AtomicHashMapProxy proxy, ProxyMode proxyMode) { this.delegate = newDelegate; this.proxy = proxy; this.copied = true; this.proxyMode = proxyMode; } @Override public void commit() { copied = false; delta = null; } @Override public int size() { return delegate.size(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } @Override public boolean containsKey(Object key) { return delegate.containsKey(key); } @Override public boolean containsValue(Object value) { return delegate.containsValue(value); } @Override public V get(Object key) { V v = delegate.get(key); if (trace) log.tracef("Atomic hash map get(key=%s) returns %s", key, v); return v; } @Override public Set keySet() { return delegate.keySet(); } @Override public Collection values() { return delegate.values(); } @Override public Set> entrySet() { return delegate.entrySet(); } @Override public V put(K key, V value) { V oldValue = delegate.put(key, value); PutOperation op = new PutOperation<>(key, oldValue, value); getDelta().addOperation(op); return oldValue; } @Override @SuppressWarnings("unchecked") public V remove(Object key) { V oldValue = delegate.remove(key); RemoveOperation op = new RemoveOperation<>((K) key, oldValue); getDelta().addOperation(op); return oldValue; } @Override public void putAll(Map t) { // this is crappy - need to do this more efficiently! for (Entry e : t.entrySet()) put(e.getKey(), e.getValue()); } @Override @SuppressWarnings("unchecked") public void clear() { FastCopyHashMap originalEntries = delegate.clone(); ClearOperation op = new ClearOperation<>(originalEntries); getDelta().addOperation(op); delegate.clear(); } /** * Builds a thread-safe proxy for this instance so that concurrent reads are isolated from writes. * @return an instance of AtomicHashMapProxy */ public AtomicHashMapProxy getProxy(AdvancedCache cache, Object mapKey) { // construct the proxy lazily if (proxy == null) // DCL is OK here since proxy is volatile (and we live in a post-JDK 5 world) { synchronized (this) { if (proxy == null) switch (proxyMode) { case FINE: proxy = new FineGrainedAtomicHashMapProxy<>(cache, mapKey); break; case COARSE: proxy = new AtomicHashMapProxy<>(cache, mapKey); break; default: throw new IllegalStateException("Unknown proxy mode: " + proxyMode); } } } return proxy; } public void markRemoved(boolean b) { removed = b; } @Override public Delta delta() { Delta toReturn = delta == null ? new AtomicHashMapDelta(proxyMode) : delta; delta = null; // reset return toReturn; } @SuppressWarnings("unchecked") public AtomicHashMap copy() { FastCopyHashMap newDelegate = delegate.clone(); return new AtomicHashMap(newDelegate, proxy, proxyMode); } @Override public String toString() { // Sanne: Avoid iterating on the delegate as that might lead to // exceptions from concurrent iterators: not nice to have during a toString! // // Galder: Sure, but we need a way to track the contents of the atomic // hash map somehow, so, we need to log each operation that affects its // contents, and when its state is restored. return "AtomicHashMap{size=" + size() + "}"; } /** * Initializes the delta instance to start recording changes. */ public void initForWriting() { delta = new AtomicHashMapDelta(proxyMode); } AtomicHashMapDelta getDelta() { if (delta == null) delta = new AtomicHashMapDelta(proxyMode); return delta; } public static class Externalizer extends AbstractExternalizer { @Override public void writeObject(ObjectOutput output, AtomicHashMap map) throws IOException { output.writeObject(map.delegate); output.writeByte(map.proxyMode.ordinal()); } @Override @SuppressWarnings("unchecked") public AtomicHashMap readObject(ObjectInput input) throws IOException, ClassNotFoundException { FastCopyHashMap delegate = (FastCopyHashMap) input.readObject(); ProxyMode proxyMode = ProxyMode.CACHED_VALUES[input.readByte()]; if (trace) log.tracef("Restore atomic hash map from %s", delegate); return new AtomicHashMap(delegate, proxyMode); } @Override public Integer getId() { return Ids.ATOMIC_HASH_MAP; } @Override @SuppressWarnings("unchecked") public Set> getTypeClasses() { return Util.>asSet(AtomicHashMap.class); } } public enum ProxyMode { FINE, COARSE; private static final ProxyMode[] CACHED_VALUES = values(); public static ProxyMode valueOf(int ordinal) { return CACHED_VALUES[ordinal]; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy