/*
* Copyright (C) 2013-2017 NTT DATA Corporation
*
* 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 org.terasoluna.gfw.common.codelist;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* {@link Map} implementation class which enables thread-safe operations on the map of key-value pairs contained in it.
* @param <K> Key
* @param <V> Value
*/
public class ReadWriteLockMapWrapper<K, V> implements Map<K, V> {
/**
* backing map to be locked
*/
private final Map<K, V> map;
/**
* Instance of {@link ReadWriteLock} implementation
*/
private final ReadWriteLock lock;
/**
* Constructor with a single {@link Map} parameter.
* @param map target map of read or write lock
*/
public ReadWriteLockMapWrapper(Map<K, V> map) {
this.map = map;
this.lock = new ReentrantReadWriteLock();
}
/**
* A contract to represent a method call of enclosing {@link Map} instance.
* <p>
* Implementation of this interface must first acquire read or write lock using implementation of <br>
* {@link ReadWriteLock} before calling the method of enclosing {@link Map}. <br>
* </p>
* @param <T> The return type of the method represented by the instance of implementation of this interface.
*/
public interface LockedCallback<T> {
/**
* Implementation must first acquire read or write lock before calling the method represented by this contract <br>
* @return value returned by the method call
*/
T apply();
}
/**
* Provides read locked call to a method of {@link Map}. {@link Map} is not locked exclusively. <br>
* <p>
* {@link Map} is the shared resource which is wrapped in this class. Access to this shared resource is regulated using
* {@link ReadWriteLock} implementation. A read lock can be acquired by as any number of threads. A read lock does not block
* other read locks.
* </p>
* <p>
* A {@link LockedCallback} instance passed as argument to this method. It represents a method call of the {@link Map}.<br>
* A read lock is first acquired over the {@code Map} and then using {@code callback}, method represented by
* {@code callback} <br>
* is executed.
* </p>
* @param <T> a return value type of callback method within read lock
* @param callback An instance of {@link LockedCallback} which represents a method call of {@link Map}
* @return the return value of the method represented by {@code callback}
*/
public <T> T withReadLock(LockedCallback<T> callback) {
Lock readLock = this.lock.readLock();
T result = null;
try {
readLock.lock();
result = callback.apply();
} finally {
readLock.unlock();
}
return result;
}
/**
* Provides write locked call to a method of {@link Map}. {@link Map} is exclusively locked for write operation. <br>
* <p>
* {@link Map} is the shared resource which is wrapped in this class. Access to this shared resource is regulated <br>
* using {@link ReadWriteLock} implementation. A write lock can be acquired by only a single thread. No read lock can be <br>
* acquired while the resource is write locked. A write lock blocks other write locks as well as read locks. <br>
* </p>
* <p>
* A {@link LockedCallback} instance passed as argument to this method. It represents a method call of the {@link Map}.<br>
* A read lock is first acquired over the {@code Map} and then using {@code callback}, method represented by
* {@code callback} <br>
* is executed.<br>
* </p>
* @param <T> a return value type of callback method within write lock
* @param callback An instance of {@link LockedCallback} which represents a method call of {@link Map}
* @return the return value of the method represented by {@code callback}
*/
public <T> T withWriteLock(LockedCallback<T> callback) {
Lock writeLock = this.lock.writeLock();
T result = null;
try {
writeLock.lock();
result = callback.apply();
} finally {
writeLock.unlock();
}
return result;
}
/**
* A read locked call to {@code size()} method of {@link Map}
* @see java.util.Map#size()
*/
@Override
public int size() {
return withReadLock(new LockedCallback<Integer>() {
@Override
public Integer apply() {
return map.size();
}
});
}
/**
* A read locked call to {@code isEmpty()} method of {@link Map}
* @see java.util.Map#isEmpty()
*/
@Override
public boolean isEmpty() {
return withReadLock(new LockedCallback<Boolean>() {
@Override
public Boolean apply() {
return map.isEmpty();
}
});
}
/**
* A read locked call to {@code containsKey()} method of {@link Map}
* @see java.util.Map#containsKey(java.lang.Object)
*/
@Override
public boolean containsKey(final Object key) {
return withReadLock(new LockedCallback<Boolean>() {
@Override
public Boolean apply() {
return map.containsKey(key);
}
});
}
/**
* A read locked call to {@code containsValue()} method of {@link Map}
* @see java.util.Map#containsValue(java.lang.Object)
*/
@Override
public boolean containsValue(final Object value) {
return withReadLock(new LockedCallback<Boolean>() {
@Override
public Boolean apply() {
return map.containsValue(value);
}
});
}
/**
* A read locked call to {@code get()} method of {@link Map}
* @see java.util.Map#get(java.lang.Object)
*/
@Override
public V get(final Object key) {
return withReadLock(new LockedCallback<V>() {
@Override
public V apply() {
return map.get(key);
}
});
}
/**
* A write locked call to {@code put()} method of {@link Map}
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
@Override
public V put(final K key, final V value) {
return withWriteLock(new LockedCallback<V>() {
@Override
public V apply() {
return map.put(key, value);
}
});
}
/**
* A write locked call to {@code remove()} method of {@link Map}
* @see java.util.Map#remove(java.lang.Object)
*/
@Override
public V remove(final Object key) {
return withWriteLock(new LockedCallback<V>() {
@Override
public V apply() {
return map.remove(key);
}
});
}
/**
* A write locked call to {@code putAll()} method of {@link Map}
* @see java.util.Map#putAll(java.util.Map)
*/
@Override
public void putAll(final Map<? extends K, ? extends V> m) {
withWriteLock(new LockedCallback<Void>() {
@Override
public Void apply() {
map.putAll(m);
return null;
}
});
}
/**
* A write locked call to {@code clear()} method of {@link Map}
* @see java.util.Map#clear()
*/
@Override
public void clear() {
withWriteLock(new LockedCallback<Void>() {
@Override
public Void apply() {
map.clear();
return null;
}
});
}
/**
* A write locked call to {@code clear()} and {@link #putAll(java.util.Map)} methods of {@link Map}
* <p>
* Clears the {@link Map} which is encapsulated in this class and loads it with new values of<br>
* the {@link Map} received as argument.
* @param m {@link Map} with new values
*/
public void clearAndPutAll(final Map<? extends K, ? extends V> m) {
withWriteLock(new LockedCallback<Void>() {
@Override
public Void apply() {
map.clear();
map.putAll(m);
return null;
}
});
}
/**
* A read locked call to {@code keySet()} method of {@link Map}
* @see java.util.Map#keySet()
*/
@Override
public Set<K> keySet() {
return withReadLock(new LockedCallback<Set<K>>() {
@Override
public Set<K> apply() {
return map.keySet();
}
});
}
/**
* A read locked call to {@code values()} method of {@link Map}
* @see java.util.Map#values()
*/
@Override
public Collection<V> values() {
return withReadLock(new LockedCallback<Collection<V>>() {
@Override
public Collection<V> apply() {
return map.values();
}
});
}
/**
* A read locked call to {@code entrySet()} method of {@link Map}
* @see java.util.Map#entrySet()
*/
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return withReadLock(new LockedCallback<Set<java.util.Map.Entry<K, V>>>() {
@Override
public Set<java.util.Map.Entry<K, V>> apply() {
return map.entrySet();
}
});
}
/**
* A read locked call to {@code equals()} method of {@link Map}
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object o) {
return withReadLock(new LockedCallback<Boolean>() {
@Override
public Boolean apply() {
return map.equals(o);
}
});
}
/**
* A read locked call to {@code hashCode()} method of {@link Map}
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return withReadLock(new LockedCallback<Integer>() {
@Override
public Integer apply() {
return map.hashCode();
}
});
}
}