3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 package org
.apache
.hadoop
.hbase
.util
;
21 import java
.util
.ArrayList
;
22 import java
.util
.Collection
;
23 import java
.util
.HashMap
;
24 import java
.util
.HashSet
;
25 import java
.util
.List
;
26 import java
.util
.Locale
;
29 import java
.util
.concurrent
.ConcurrentHashMap
;
30 import java
.util
.concurrent
.ConcurrentLinkedQueue
;
31 import java
.util
.concurrent
.CopyOnWriteArrayList
;
32 import java
.util
.concurrent
.atomic
.AtomicInteger
;
34 import org
.apache
.yetus
.audience
.InterfaceAudience
;
38 * The <code>PoolMap</code> maps a key to a collection of values, the elements
39 * of which are managed by a pool. In effect, that collection acts as a shared
40 * pool of resources, access to which is closely controlled as per the semantics
44 * In case the size of the pool is set to a non-zero positive number, that is
45 * used to cap the number of resources that a pool may contain for any given
46 * key. A size of {@link Integer#MAX_VALUE} is interpreted as an unbounded pool.
50 * the type of the key to the resource
52 * the type of the resource being pooled
54 @InterfaceAudience.Private
55 public class PoolMap
<K
, V
> implements Map
<K
, V
> {
56 private PoolType poolType
;
58 private int poolMaxSize
;
60 private Map
<K
, Pool
<V
>> pools
= new ConcurrentHashMap
<>();
62 public PoolMap(PoolType poolType
) {
63 this.poolType
= poolType
;
66 public PoolMap(PoolType poolType
, int poolMaxSize
) {
67 this.poolType
= poolType
;
68 this.poolMaxSize
= poolMaxSize
;
72 public V
get(Object key
) {
73 Pool
<V
> pool
= pools
.get(key
);
74 return pool
!= null ? pool
.get() : null;
78 public V
put(K key
, V value
) {
79 Pool
<V
> pool
= pools
.get(key
);
81 pools
.put(key
, pool
= createPool());
83 return pool
!= null ? pool
.put(value
) : null;
86 @SuppressWarnings("unchecked")
88 public V
remove(Object key
) {
89 Pool
<V
> pool
= pools
.remove(key
);
91 removeValue((K
) key
, pool
.get());
96 public boolean removeValue(K key
, V value
) {
97 Pool
<V
> pool
= pools
.get(key
);
100 res
= pool
.remove(value
);
101 if (res
&& pool
.size() == 0) {
109 public Collection
<V
> values() {
110 Collection
<V
> values
= new ArrayList
<>();
111 for (Pool
<V
> pool
: pools
.values()) {
112 Collection
<V
> poolValues
= pool
.values();
113 if (poolValues
!= null) {
114 values
.addAll(poolValues
);
120 public Collection
<V
> values(K key
) {
121 Collection
<V
> values
= new ArrayList
<>();
122 Pool
<V
> pool
= pools
.get(key
);
124 Collection
<V
> poolValues
= pool
.values();
125 if (poolValues
!= null) {
126 values
.addAll(poolValues
);
134 public boolean isEmpty() {
135 return pools
.isEmpty();
143 public int size(K key
) {
144 Pool
<V
> pool
= pools
.get(key
);
145 return pool
!= null ? pool
.size() : 0;
149 public boolean containsKey(Object key
) {
150 return pools
.containsKey(key
);
154 public boolean containsValue(Object value
) {
158 for (Pool
<V
> pool
: pools
.values()) {
159 if (value
.equals(pool
.get())) {
167 public void putAll(Map
<?
extends K
, ?
extends V
> map
) {
168 for (Map
.Entry
<?
extends K
, ?
extends V
> entry
: map
.entrySet()) {
169 put(entry
.getKey(), entry
.getValue());
174 public void clear() {
175 for (Pool
<V
> pool
: pools
.values()) {
182 public Set
<K
> keySet() {
183 return pools
.keySet();
187 public Set
<Map
.Entry
<K
, V
>> entrySet() {
188 Set
<Map
.Entry
<K
, V
>> entries
= new HashSet
<>();
189 for (Map
.Entry
<K
, Pool
<V
>> poolEntry
: pools
.entrySet()) {
190 final K poolKey
= poolEntry
.getKey();
191 final Pool
<V
> pool
= poolEntry
.getValue();
193 for (final V poolValue
: pool
.values()) {
194 entries
.add(new Map
.Entry
<K
, V
>() {
201 public V
getValue() {
206 public V
setValue(V value
) {
207 return pool
.put(value
);
216 protected interface Pool
<R
> {
221 boolean remove(R resource
);
225 Collection
<R
> values();
230 public enum PoolType
{
231 Reusable
, ThreadLocal
, RoundRobin
;
233 public static PoolType
valueOf(String poolTypeName
,
234 PoolType defaultPoolType
, PoolType
... allowedPoolTypes
) {
235 PoolType poolType
= PoolType
.fuzzyMatch(poolTypeName
);
236 if (poolType
!= null) {
237 boolean allowedType
= false;
238 if (poolType
.equals(defaultPoolType
)) {
241 if (allowedPoolTypes
!= null) {
242 for (PoolType allowedPoolType
: allowedPoolTypes
) {
243 if (poolType
.equals(allowedPoolType
)) {
254 return (poolType
!= null) ? poolType
: defaultPoolType
;
257 public static String
fuzzyNormalize(String name
) {
258 return name
!= null ? name
.replaceAll("-", "").trim().toLowerCase(Locale
.ROOT
) : "";
261 public static PoolType
fuzzyMatch(String name
) {
262 for (PoolType poolType
: values()) {
263 if (fuzzyNormalize(name
).equals(fuzzyNormalize(poolType
.name()))) {
271 protected Pool
<V
> createPool() {
274 return new ReusablePool
<>(poolMaxSize
);
276 return new RoundRobinPool
<>(poolMaxSize
);
278 return new ThreadLocalPool
<>();
284 * The <code>ReusablePool</code> represents a {@link PoolMap.Pool} that builds
285 * on the {@link java.util.LinkedList} class. It essentially allows resources to be
286 * checked out, at which point it is removed from this pool. When the resource
287 * is no longer required, it should be returned to the pool in order to be
291 * If {@link #maxSize} is set to {@link Integer#MAX_VALUE}, then the size of
292 * the pool is unbounded. Otherwise, it caps the number of consumers that can
293 * check out a resource from this pool to the (non-zero positive) value
294 * specified in {@link #maxSize}.
298 * the type of the resource
300 @SuppressWarnings("serial")
301 public static class ReusablePool
<R
> extends ConcurrentLinkedQueue
<R
> implements Pool
<R
> {
304 public ReusablePool(int maxSize
) {
305 this.maxSize
= maxSize
;
315 public R
put(R resource
) {
316 if (super.size() < maxSize
) {
323 public Collection
<R
> values() {
329 * The <code>RoundRobinPool</code> represents a {@link PoolMap.Pool}, which
330 * stores its resources in an {@link ArrayList}. It load-balances access to
331 * its resources by returning a different resource every time a given key is
335 * If {@link #maxSize} is set to {@link Integer#MAX_VALUE}, then the size of
336 * the pool is unbounded. Otherwise, it caps the number of resources in this
337 * pool to the (non-zero positive) value specified in {@link #maxSize}.
341 * the type of the resource
344 @SuppressWarnings("serial")
345 static class RoundRobinPool
<R
> extends CopyOnWriteArrayList
<R
> implements Pool
<R
> {
347 private int nextResource
= 0;
349 public RoundRobinPool(int maxSize
) {
350 this.maxSize
= maxSize
;
354 public R
put(R resource
) {
355 if (super.size() < maxSize
) {
363 if (super.size() < maxSize
) {
366 nextResource
%= super.size();
367 R resource
= get(nextResource
++);
372 public Collection
<R
> values() {
379 * The <code>ThreadLocalPool</code> represents a {@link PoolMap.Pool} that
380 * builds on the {@link ThreadLocal} class. It essentially binds the resource
381 * to the thread from which it is accessed.
384 * Note that the size of the pool is essentially bounded by the number of threads
385 * that add resources to this pool.
389 * the type of the resource
391 static class ThreadLocalPool
<R
> extends ThreadLocal
<R
> implements Pool
<R
> {
392 private static final Map
<ThreadLocalPool
<?
>, AtomicInteger
> poolSizes
= new HashMap
<>();
394 public ThreadLocalPool() {
398 public R
put(R resource
) {
399 R previousResource
= get();
400 if (previousResource
== null) {
401 AtomicInteger poolSize
= poolSizes
.get(this);
402 if (poolSize
== null) {
403 poolSizes
.put(this, poolSize
= new AtomicInteger(0));
405 poolSize
.incrementAndGet();
408 return previousResource
;
412 public void remove() {
414 AtomicInteger poolSize
= poolSizes
.get(this);
415 if (poolSize
!= null) {
416 poolSize
.decrementAndGet();
422 AtomicInteger poolSize
= poolSizes
.get(this);
423 return poolSize
!= null ? poolSize
.get() : 0;
427 public boolean remove(R resource
) {
428 R previousResource
= super.get();
429 if (resource
!= null && resource
.equals(previousResource
)) {
438 public void clear() {
443 public Collection
<R
> values() {
444 List
<R
> values
= new ArrayList
<>();