/**
* Copyright 2010 The Apache Software Foundation
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.hbase.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* The <code>PoolMap</code> maps a key to a collection of values, the elements
* of which are managed by a pool. In effect, that collection acts as a shared
* pool of resources, access to which is closely controlled as per the semantics
* of the pool.
*
* <p>
* In case the size of the pool is set to a non-zero positive number, that is
* used to cap the number of resources that a pool may contain for any given
* key. A size of {@link Integer#MAX_VALUE} is interpreted as an unbounded pool.
* </p>
*
* @param <K>
* the type of the key to the resource
* @param <V>
* the type of the resource being pooled
*
* @author Karthick Sankarachary
*/
public class PoolMap<K, V> implements Map<K, V> {
private static final Log LOG =
LogFactory.getLog("org.apache.hadoop.hbase.util.PoolMap");
private PoolType poolType;
private int poolMaxSize;
private Map<K, Pool<V>> pools = Collections.synchronizedMap(new HashMap<K, Pool<V>>());
public PoolMap(PoolType poolType, int poolMaxSize) {
this.poolType = poolType;
this.poolMaxSize = poolMaxSize;
}
@Override
public V get(Object key) {
Pool<V> pool = pools.get(key);
return pool != null ? pool.get() : null;
}
@Override
public V put(K key, V value) {
Pool<V> pool = pools.get(key);
if (pool == null) {
synchronized (this) {
pool = pools.get(key);
if (pool == null) {
pools.put(key, pool = createPool());
}
}
}
return pool != null ? pool.put(value) : null;
}
@Override
public V remove(Object key) {
Pool<V> pool = pools.remove(key);
if (pool != null) {
pool.clear();
}
return null;
}
public boolean remove(K key, V value) {
Pool<V> pool = pools.get(key);
return pool != null ? pool.remove(value) : false;
}
@Override
public Collection<V> values() {
Collection<V> values = new ArrayList<V>();
for (Pool<V> pool : pools.values()) {
Collection<V> poolValues = pool.values();
if (poolValues != null) {
values.addAll(poolValues);
}
}
return values;
}
@Override
public boolean isEmpty() {
return pools.isEmpty();
}
@Override
public int size() {
return pools.size();
}
public int size(K key) {
Pool<V> pool = pools.get(key);
return pool != null ? pool.size() : 0;
}
@Override
public boolean containsKey(Object key) {
return pools.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
if (value == null) {
return false;
}
for (Pool<V> pool : pools.values()) {
if (value.equals(pool.get())) {
return true;
}
}
return false;
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public void clear() {
for (Pool<V> pool : pools.values()) {
pool.clear();
}
pools.clear();
}
@Override
public Set<K> keySet() {
return pools.keySet();
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> entries = new HashSet<Entry<K, V>>();
for (Map.Entry<K, Pool<V>> poolEntry : pools.entrySet()) {
final K poolKey = poolEntry.getKey();
final Pool<V> pool = poolEntry.getValue();
for (final V poolValue : pool.values()) {
if (pool != null) {
entries.add(new Map.Entry<K, V>() {
@Override
public K getKey() {
return poolKey;
}
@Override
public V getValue() {
return poolValue;
}
@Override
public V setValue(V value) {
return pool.put(value);
}
});
}
}
}
return entries;
}
protected interface Pool<R> {
public R get();
public R put(R resource);
public boolean remove(R resource);
public void clear();
public Collection<R> values();
public int size();
}
public enum PoolType {
Reusable("reusable"), ThreadLocal("threadlocal"), RoundRobin("roundrobin");
private String configName;
PoolType(String configName) {
this.configName = configName;
}
public String getConfigName() {
return configName;
}
public static PoolType valueOf(String poolTypeName, PoolType defaultPoolType, PoolType... otherPoolTypes) {
PoolType poolType = null;
if (poolTypeName != null)
poolType = PoolType.valueOf(poolTypeName);
if (poolType != null) {
boolean allowedType = false;
if (poolType.equals(defaultPoolType)) {
allowedType = true;
} else {
if (otherPoolTypes != null) {
for (PoolType allowedPoolType : otherPoolTypes) {
if (poolType.equals(allowedPoolType)) {
allowedType = true;
break;
}
}
}
}
if (!allowedType) {
poolType = null;
}
}
return (poolType != null) ? poolType : defaultPoolType;
}
}
protected Pool<V> createPool() {
switch (poolType) {
case Reusable:
return new ReusablePool<V>(poolMaxSize);
case RoundRobin:
return new RoundRobinPool<V>(poolMaxSize);
case ThreadLocal:
return new ThreadLocalPool<V>(poolMaxSize);
}
return null;
}
}