2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
27 package org.openecomp.appc.pool;
29 import java.io.Closeable;
30 import java.util.ArrayDeque;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.Deque;
34 import java.util.List;
35 import java.util.ListIterator;
36 import java.util.Properties;
37 import java.util.concurrent.atomic.AtomicBoolean;
38 import java.util.concurrent.locks.Lock;
39 import java.util.concurrent.locks.ReadWriteLock;
40 import java.util.concurrent.locks.ReentrantReadWriteLock;
43 * This class is used to manage a pool of things.
45 * The class is parameterized so that the type of objects maintained in the pool is definable by some provided type.
46 * This type must implement the <code>Comparable</code> interface so that it can be managed in the pool.
50 * The type of element being pooled
53 public class Pool<T extends Closeable> {
54 private Deque<T> free;
55 private List<T> allocated;
58 private Allocator<T> allocator;
59 private Destructor<T> destructor;
60 private ReadWriteLock lock;
61 private AtomicBoolean drained;
62 private Properties properties;
68 * The minimum size of the pool
70 * The maximum size of the pool, set to zero (0) for unbounded
71 * @throws PoolSpecificationException
72 * If the minimum size is less than 0, or if the max size is non-zero and less than the min size.
74 public Pool(int minPool, int maxPool) throws PoolSpecificationException {
77 throw new PoolSpecificationException(String.format("The minimum pool size must be a "
78 + "positive value or zero, %d is not valid.", minPool));
80 if (maxPool != 0 && maxPool < minPool) {
81 throw new PoolSpecificationException(String.format("The maximum pool size must be a "
82 + "positive value greater than the minimum size, or zero. %d is not valid.", maxPool));
85 this.minPool = minPool;
86 this.maxPool = maxPool;
88 properties = new Properties();
89 free = new ArrayDeque<T>();
90 allocated = new ArrayList<T>();
91 lock = new ReentrantReadWriteLock();
92 drained = new AtomicBoolean(false);
96 * Returns the amount of objects on the free collection
98 * @return The number of objects on the free collection
100 public int getFreeSize() {
101 Lock readLock = lock.readLock();
111 * Returns the value for a specified property of this pool, if defined.
114 * The key of the desired property
115 * @return The value of the property, or null if not defined
117 public String getProperty(String key) {
118 return properties.getProperty(key);
122 * Sets the value of the specified property or replaces it if it already exists
125 * The key of the property to be set
127 * The value to set the property to
129 public void setProperty(String key, String value) {
130 properties.setProperty(key, value);
134 * @return The properties object for the pool
136 public Properties getProperties() {
141 * Returns the number of objects that are currently allocated
143 * @return The allocate collection size
145 public int getAllocatedSize() {
146 Lock readLock = lock.readLock();
149 return allocated.size();
156 * @return the value of allocator
158 public Allocator<T> getAllocator() {
164 * the value for allocator
166 public void setAllocator(Allocator<T> allocator) {
167 this.allocator = allocator;
171 * @return the value of destructor
173 public Destructor<T> getDestructor() {
178 * @return the value of minPool
180 public int getMinPool() {
185 * @return the value of maxPool
187 public int getMaxPool() {
193 * the value for destructor
195 public void setDestructor(Destructor<T> destructor) {
196 this.destructor = destructor;
200 * Drains the pool, releasing and destroying all pooled objects, even if they are currently allocated.
202 public void drain() {
203 if (drained.compareAndSet(false, true)) {
204 Lock writeLock = lock.writeLock();
207 int size = getAllocatedSize();
209 * We can't use the "release" method call here because we are modifying the list we are iterating
211 ListIterator<T> it = allocated.listIterator();
212 while (it.hasNext()) {
217 size = getFreeSize();
226 * Returns an indication if the pool has been drained
228 * @return True indicates that the pool has been drained. Once a pool has been drained, it can no longer be used.
230 public boolean isDrained() {
231 return drained.get();
235 * Reserves an object of type T from the pool for the caller and returns it
237 * @return The object of type T to be used by the caller
238 * @throws PoolExtensionException
239 * If the pool cannot be extended
240 * @throws PoolDrainedException
241 * If the caller is trying to reserve an element from a drained pool
243 @SuppressWarnings("unchecked")
244 public T reserve() throws PoolExtensionException, PoolDrainedException {
246 throw new PoolDrainedException("The pool has been drained and cannot be used.");
250 Lock writeLock = lock.writeLock();
253 int freeSize = getFreeSize();
254 int allocatedSize = getAllocatedSize();
257 if (allocatedSize == 0) {
258 extend(minPool == 0 ? 1 : minPool);
259 } else if (allocatedSize >= maxPool && maxPool > 0) {
260 throw new PoolExtensionException(String.format("Unable to add "
261 + "more elements, pool is at maximum size of %d", maxPool));
267 obj = free.removeFirst();
274 * Now that we have the real object, lets wrap it in a dynamic proxy so that we can intercept the close call and
275 * just return the context to the free pool. obj.getClass().getInterfaces(). We need to find ALL interfaces that
276 * the object (and all superclasses) implement and have the proxy implement them too
278 Class<?> cls = obj.getClass();
280 List<Class<?>> interfaces = new ArrayList<Class<?>>();
281 while (!cls.equals(Object.class)) {
282 array = cls.getInterfaces();
283 for (Class<?> item : array) {
284 if (!interfaces.contains(item)) {
285 interfaces.add(item);
288 cls = cls.getSuperclass();
290 array = new Class<?>[interfaces.size()];
291 array = interfaces.toArray(array);
292 return CachedElement.newInstance(this, obj, array);
296 * releases the allocated object back to the free pool to be used by another request.
299 * The object to be returned to the pool
300 * @throws PoolDrainedException
301 * If the caller is trying to release an element to a drained pool
303 public void release(T obj) throws PoolDrainedException {
305 throw new PoolDrainedException("The pool has been drained and cannot be used.");
307 Lock writeLock = lock.writeLock();
310 if (allocated.remove(obj)) {
319 * Extend the free pool by some number of elements
322 * The number of elements to add to the pool
323 * @throws PoolExtensionException
324 * if the pool cannot be extended because no allocator has been specified.
326 private void extend(int count) throws PoolExtensionException {
327 if (allocator == null) {
328 throw new PoolExtensionException(String.format("Unable to extend pool "
329 + "because no allocator has been specified"));
331 Lock writeLock = lock.writeLock();
334 for (int index = 0; index < count; index++) {
335 T obj = allocator.allocate(this);
337 throw new PoolExtensionException(
338 "The allocator failed to allocate a new context to extend the pool.");
348 * Used to trim the free collection by some specified number of elements, or the free element count, whichever is
349 * less. The elements are removed from the end of the free element deque, thus trimming the oldest elements first.
352 * The number of elements to trim
354 private void trim(int count) {
355 Lock writeLock = lock.writeLock();
358 int trimCount = count;
359 if (getFreeSize() < count) {
360 trimCount = getFreeSize();
362 for (int i = 0; i < trimCount; i++) {
363 T obj = free.removeLast();
364 if (destructor != null) {
365 destructor.destroy(obj, this);