io.atlassian.util.concurrent.AbstractCopyOnWriteMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlassian-util-concurrent Show documentation
Show all versions of atlassian-util-concurrent Show documentation
This project contains utility classes that are used by
various products and projects inside Atlassian and may have some
utility to the world at large.
/**
* Copyright 2008 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.atlassian.util.concurrent;
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
/**
* Abstract base class for COW {@link Map} implementations that delegate to an
* internal map.
*
* @param The key type
* @param The value type
* @param the internal {@link Map} or extension for things like sorted and
* navigable maps.
*/
@ThreadSafe abstract class AbstractCopyOnWriteMap> implements ConcurrentMap, Serializable {
private static final long serialVersionUID = 4508989182041753878L;
@GuardedBy("lock") private volatile M delegate;
private final transient Lock lock = new ReentrantLock();
private final View view;
/**
* Create a new {@link io.atlassian.util.concurrent.CopyOnWriteMap} with the
* supplied {@link java.util.Map} to initialize the values.
*
* @param map the initial map to initialize with
* @param viewType for writable or read-only key, value and entrySet views
* @param original map type.
*/
protected > AbstractCopyOnWriteMap(final N map, final View.Type viewType) {
this.delegate = requireNonNull(copy(requireNonNull(map, "map")), "delegate");
this.view = requireNonNull(viewType, "viewType").get(this);
}
/**
* Copy function, implemented by sub-classes.
*
* @param the map to copy and return.
* @param map the initial values of the newly created map.
* @return a new map. Will never be modified after construction.
*/
@GuardedBy("lock") abstract > M copy(N map);
//
// mutable operations
//
/**
* Return a new copy containing no elements
*/
public final void clear() {
lock.lock();
try {
set(copy(Collections. emptyMap()));
} finally {
lock.unlock();
}
}
/** {@inheritDoc} */
public final V remove(final Object key) {
lock.lock();
try {
// short circuit if key doesn't exist
if (!delegate.containsKey(key)) {
return null;
}
final M map = copy();
try {
return map.remove(key);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
/** {@inheritDoc} */
public final boolean remove(final Object key, final Object value) {
lock.lock();
try {
if (delegate.containsKey(key) && equals(value, delegate.get(key))) {
final M map = copy();
map.remove(key);
set(map);
return true;
} else {
return false;
}
} finally {
lock.unlock();
}
}
/**
* Attempt to replace the value of the key with newValues so long as the key
* is still associated with old value
*
* @param key a K key to to define a new association for.
* @param oldValue if this value is still present replace it with newValue.
* @param newValue a V to add to the map.
* @return a boolean if the replacement is successful
*/
public final boolean replace(final K key, final V oldValue, final V newValue) {
lock.lock();
try {
if (!delegate.containsKey(key) || !equals(oldValue, delegate.get(key))) {
return false;
}
final M map = copy();
map.put(key, newValue);
set(map);
return true;
} finally {
lock.unlock();
}
}
/**
* Replace the value of the key if the key is present returning the value.
*
* @param key a K key.
* @param value a V value.
* @return a V if the key is present or null if it is not.
*/
public final V replace(final K key, final V value) {
lock.lock();
try {
if (!delegate.containsKey(key)) {
return null;
}
final M map = copy();
try {
return map.put(key, value);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
/**
* Add this key and its value to the map
*
* @param key a K key.
* @param value a V value.
* @return a V value added.
*/
public final V put(final K key, final V value) {
lock.lock();
try {
final M map = copy();
try {
return map.put(key, value);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
/**
* If the key is not currently contained in the map add it. Returns the
* current value associated with the key whether or not the put succeeds.
*
* @param key a K key.
* @param value a V value.
* @return a V associated with the key, may or may not be the input value to
* this method.
*/
public final V putIfAbsent(final K key, final V value) {
lock.lock();
try {
if (!delegate.containsKey(key)) {
final M map = copy();
try {
return map.put(key, value);
} finally {
set(map);
}
}
return delegate.get(key);
} finally {
lock.unlock();
}
}
/** {@inheritDoc} */
public final void putAll(final Map extends K, ? extends V> t) {
lock.lock();
try {
final M map = copy();
map.putAll(t);
set(map);
} finally {
lock.unlock();
}
}
/**
* Create a copy of the underlying map.
*
* @return a M map.
*/
protected M copy() {
lock.lock();
try {
return copy(delegate);
} finally {
lock.unlock();
}
}
/**
* Set the contained map
*
* @param map a M contained by this class.
*/
@GuardedBy("lock") protected final void set(final M map) {
delegate = map;
}
//
// Collection views
//
/** {@inheritDoc} */
public final Set> entrySet() {
return view.entrySet();
}
/** {@inheritDoc} */
public final Set keySet() {
return view.keySet();
}
/** {@inheritDoc} */
public final Collection values() {
return view.values();
}
//
// delegate operations
//
/** {@inheritDoc} */
public final boolean containsKey(final Object key) {
return delegate.containsKey(key);
}
/** {@inheritDoc} */
public final boolean containsValue(final Object value) {
return delegate.containsValue(value);
}
/** {@inheritDoc} */
public final V get(final Object key) {
return delegate.get(key);
}
/** {@inheritDoc} */
public final boolean isEmpty() {
return delegate.isEmpty();
}
/** {@inheritDoc} */
public final int size() {
return delegate.size();
}
/** {@inheritDoc} */
@Override public final boolean equals(final Object o) {
return delegate.equals(o);
}
/** {@inheritDoc} */
@Override public final int hashCode() {
return delegate.hashCode();
}
/**
* Return the internal delegate map.
*
* @return a M map.
*/
protected final M getDelegate() {
return delegate;
}
/** {@inheritDoc} */
@Override public String toString() {
return delegate.toString();
}
//
// inner classes
//
private class KeySet extends CollectionView implements Set {
@Override Collection getDelegate() {
return delegate.keySet();
}
//
// mutable operations
//
public void clear() {
lock.lock();
try {
final M map = copy();
map.keySet().clear();
set(map);
} finally {
lock.unlock();
}
}
public boolean remove(final Object o) {
return AbstractCopyOnWriteMap.this.remove(o) != null;
}
public boolean removeAll(final Collection> c) {
lock.lock();
try {
final M map = copy();
try {
return map.keySet().removeAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean retainAll(final Collection> c) {
lock.lock();
try {
final M map = copy();
try {
return map.keySet().retainAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
}
private final class Values extends CollectionView {
@Override Collection getDelegate() {
return delegate.values();
}
public void clear() {
lock.lock();
try {
final M map = copy();
map.values().clear();
set(map);
} finally {
lock.unlock();
}
}
public boolean remove(final Object o) {
lock.lock();
try {
if (!contains(o)) {
return false;
}
final M map = copy();
try {
return map.values().remove(o);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean removeAll(final Collection> c) {
lock.lock();
try {
final M map = copy();
try {
return map.values().removeAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean retainAll(final Collection> c) {
lock.lock();
try {
final M map = copy();
try {
return map.values().retainAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
}
private class EntrySet extends CollectionView> implements Set> {
@Override Collection> getDelegate() {
return delegate.entrySet();
}
public void clear() {
lock.lock();
try {
final M map = copy();
map.entrySet().clear();
set(map);
} finally {
lock.unlock();
}
}
public boolean remove(final Object o) {
lock.lock();
try {
if (!contains(o)) {
return false;
}
final M map = copy();
try {
return map.entrySet().remove(o);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean removeAll(final Collection> c) {
lock.lock();
try {
final M map = copy();
try {
return map.entrySet().removeAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean retainAll(final Collection> c) {
lock.lock();
try {
final M map = copy();
try {
return map.entrySet().retainAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
}
private static class UnmodifiableIterator implements Iterator {
private final Iterator delegate;
public UnmodifiableIterator(final Iterator delegate) {
this.delegate = delegate;
}
public boolean hasNext() {
return delegate.hasNext();
}
public T next() {
return delegate.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
protected static abstract class CollectionView implements Collection {
abstract Collection getDelegate();
//
// delegate operations
//
public final boolean contains(final Object o) {
return getDelegate().contains(o);
}
public final boolean containsAll(final Collection> c) {
return getDelegate().containsAll(c);
}
public final Iterator iterator() {
return new UnmodifiableIterator(getDelegate().iterator());
}
public final boolean isEmpty() {
return getDelegate().isEmpty();
}
public final int size() {
return getDelegate().size();
}
public final Object[] toArray() {
return getDelegate().toArray();
}
public final T[] toArray(final T[] a) {
return getDelegate().toArray(a);
}
@Override public int hashCode() {
return getDelegate().hashCode();
}
@Override public boolean equals(final Object obj) {
return getDelegate().equals(obj);
}
@Override public String toString() {
return getDelegate().toString();
}
//
// unsupported operations
//
public final boolean add(final E o) {
throw new UnsupportedOperationException();
}
public final boolean addAll(final Collection extends E> c) {
throw new UnsupportedOperationException();
}
}
private boolean equals(final Object o1, final Object o2) {
if (o1 == null) {
return o2 == null;
}
return o1.equals(o2);
}
/**
* Provides access to the views of the underlying key, value and entry
* collections.
*/
public static abstract class View {
View() {}
abstract Set keySet();
abstract Set> entrySet();
abstract Collection values();
/**
* The different types of {@link View} available
*/
public enum Type {
STABLE {
@Override > View get(final AbstractCopyOnWriteMap host) {
return host.new Immutable();
}
},
LIVE {
@Override > View get(final AbstractCopyOnWriteMap host) {
return host.new Mutable();
}
};
abstract > View get(AbstractCopyOnWriteMap host);
}
}
final class Immutable extends View implements Serializable {
private static final long serialVersionUID = -4158727180429303818L;
@Override public Set keySet() {
return unmodifiableSet(delegate.keySet());
}
@Override public Set> entrySet() {
return unmodifiableSet(delegate.entrySet());
}
@Override public Collection values() {
return unmodifiableCollection(delegate.values());
}
}
final class Mutable extends View implements Serializable {
private static final long serialVersionUID = 1624520291194797634L;
private final transient KeySet keySet = new KeySet();
private final transient EntrySet entrySet = new EntrySet();
private final transient Values values = new Values();
@Override public Set keySet() {
return keySet;
}
@Override public Set> entrySet() {
return entrySet;
}
@Override public Collection values() {
return values;
}
}
}