Improve Pool
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / util / Pool.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 /*
23  * Pool
24  *
25  * Author: Jonathan
26  * 5/27/2011
27  */
28 package org.onap.aaf.cadi.util;
29
30 import java.util.Iterator;
31 import java.util.LinkedList;
32
33 import org.onap.aaf.cadi.CadiException;
34
35 /**
36  * This Class pools on an As-Needed-Basis any particular kind of class, which is
37  * quite suitable for expensive operations.
38  *
39  * The user calls "get" on a Pool, and if a waiting resource (T) is available,
40  * it will be returned. Otherwise, one will be created with the "Creator" class
41  * (must be defined for (T)).
42  *
43  * You can Prime the instances to avoid huge startup costs
44  *
45  * The returned "Pooled" object simply has to call "done()" and the object is
46  * returned to the pool. If the developer does not return the object, a memory
47  * leak does not occur. There are no references to the object once "get" is
48  * called. However, the developer who does not return the object when done
49  * obviates the point of the pool, as new Objects are created in place of the
50  * Object not returned when another call to "get" is made.
51  *
52  * There is a cushion of extra objects, currently defaulted to MAX_RANGE. If the
53  * items returned become higher than the MAX_RANGE, the object is allowed to go
54  * out of scope, and be cleaned up. the default can be changed on a per-pool
55  * basis.
56  *
57  * Class revamped for CadiExceptions and Access logging 10/4/2017
58  *
59  * @author Jonathan
60  *
61  * @param <T>
62  */
63 public class Pool<T> {
64     /**
65      * This is a constant which specified the default maximum number of unused
66      * objects to be held at any given time.
67      */
68     public static final int MAX_RANGE = 6; // safety
69     
70     /**
71      * Maximum objects, in use or waiting
72      */
73     public static final int MAX_OBJECTS = 20; // assumption for thread
74
75     /**
76      * only Simple List needed.
77      *
78      * NOTE TO MAINTAINERS: THIS OBJECT DOES IT'S OWN SYNCHRONIZATION. All
79      * changes that touch list must account for correctly synchronizing list.
80      */
81     private LinkedList<Pooled<T>> list;
82
83     /**
84      * keep track of how many elements are currently available to use, to avoid asking list.
85      */
86     private int count;
87     
88     /**
89      * how many objects have been asked for, but not returned or tossed
90      */
91     private int used;
92     
93     /**
94      * Actual MAX number of spares allowed to hang around. Can be set to
95      * something besides the default MAX_RANGE.
96      */
97     private int max_range = MAX_RANGE;
98
99     /**
100      * Actual MAX number of Objects both in use, or waiting.
101      * This does not actually affect the Pool, because the objects, once they leave the pool, are not known until
102      * they are put back with done (offer).  It only affects the "overLimit()" function.
103      * 
104      * Important... this information is only valid if PooledObjects call "done()" or "toss()".
105      */
106     private int max_objects = MAX_OBJECTS;
107     
108     /**
109      * The Creator for this particular pool. It must work for type T.
110      */
111     private Creator<T> creator;
112
113     private Log logger;
114
115     /**
116      * Create a new Pool, given the implementation of Creator<T>, which must be
117      * able to create/destroy T objects at will.
118      *
119      * @param creator
120      */
121     public Pool(Creator<T> creator) {
122         count = used = 0;
123         this.creator = creator;
124         list = new LinkedList<>();
125         logger = Log.NULL;
126     }
127
128     /**
129      * Attach Pool Logging activities to any other Logging Mechanism.
130      * @param logger
131      */
132     public void setLogger(Log logger) {
133         this.logger = logger;
134         // Also reset existing Pooled objects
135         for(Pooled<?> p : list) {
136             if(p.content instanceof LogAware) {
137                 ((LogAware)p.content).setLog(logger);
138             } else {
139                 break;
140             }
141         }
142     }
143
144     public void log(Log.Type type, Object ...objects) {
145         logger.log(type,objects);
146     }
147
148     /**
149      * Preallocate a certain number of T Objects. Useful for services so that
150      * the first transactions don't get hit with all the Object creation costs
151      * 
152      * It is assumed that priming also means that it is the minimum desired available resources.  Therefore, 
153      * max_range is set to prime, if less than current max_range, if it is default.
154      * 
155      * @param lt
156      * @param prime
157      * @throws CadiException
158      */
159     public Pool<T> prime(int prime) throws CadiException  {
160         if(max_range == MAX_RANGE && prime<max_range) {
161                 max_range = prime;
162         }
163         for (int i = 0; i < prime; ++i) {
164             Pooled<T> pt = new Pooled<T>(creator.create(), this);
165             synchronized (list) {
166                 list.addFirst(pt);
167                 ++count;
168                 ++used;
169             }
170         }
171         return this;
172     }
173
174     /**
175      * Destroy and remove all remaining objects. This is valuable for closing
176      * down all Allocated objects cleanly for exiting. It is also a good method
177      * for removing objects when, for instance, all Objects are invalid because
178      * of broken connections, etc.
179      * 
180      * Use in conjunction with setMaxRange to no longer store objects, i.e.
181      *  
182      *  pool.setMaxRange(0).drain();
183      */
184     public synchronized void drain() {
185         while(list.size()>0) {
186             Pooled<T> pt = list.remove();
187             --used;
188             String name = pt.content.toString();
189             creator.destroy(pt.content);
190             logger.log(Log.Type.debug,"Pool destroyed", name);
191         }
192         count = 0;
193     }
194     
195     /**
196      * This is the essential function for Pool. Get an Object "T" inside a
197      * "Pooled<T>" object. If there is a spare Object, then use it. If not, then
198      * create and pass back.
199      *
200      * This one uses a Null LogTarget
201      *
202      * IMPORTANT: When the use of this object is done (and the object is still
203      * in a valid state), then "done()" should be called immediately to allow
204      * the object to be reused. That is the point of the Pool...
205      *
206      * If the Object is in an invalid state, then "toss()" should be used so the
207      * Pool doesn't pass on invalid objects to others.
208      *
209      * @param lt
210      * @return
211      * @throws CadiException
212      */
213     public Pooled<T> get() throws CadiException {
214         Pooled<T> pt;
215         synchronized (list) {
216                 pt = list.pollLast();
217         }
218         if (pt == null) {
219             pt = new Pooled<T>(creator.create(), this);
220             ++used;
221         } else {
222             --count;
223             creator.reuse(pt.content);
224         }
225         return pt;
226     }
227
228     /**
229      * This function will validate whether the Objects are still in a usable
230      * state. If not, they are tossed from the Pool. This is valuable to have
231      * when Remote Connections go down, and there is a question on whether the
232      * Pooled Objects are still functional.
233      *
234      * @return
235      */
236     public boolean validate() {
237         boolean rv = true;
238         synchronized (list) {
239             for (Iterator<Pooled<T>> iter = list.iterator(); iter.hasNext();) {
240                 Pooled<T> t = iter.next();
241                 if (!creator.isValid(t.content)) {
242                     rv = false;
243                     t.toss();
244                     iter.remove();
245                 }
246             }
247         }
248         return rv;
249     }
250
251     /**
252      * This is an internal method, used only by the Internal Pooled<T> class.
253      *
254      * The Pooled<T> class "offers" it's Object back after use. It is an
255      * "offer", because Pool will simply destroy and remove the object if it has
256      * more than enough spares.
257      *
258      * @param lt
259      * @param used
260      * @return
261      */
262     // Used only by Pooled<T>
263     private boolean offer(Pooled<T> usedP) {
264         if (count < max_range) {
265             synchronized (list) {
266                 list.addFirst(usedP);
267                 ++count;
268             }
269             logger.log(Log.Type.trace,"Pool recovered ", creator);
270         } else {
271                 destroy(usedP.content);
272         }
273         return false;
274     }
275     
276     /**
277      * Destroy, using Creator's specific semantics, the Object, and decrement "used"
278      * 
279      * @param t
280      */
281     private void destroy(T t) {
282         creator.destroy(t);
283         synchronized (list) {
284                 --used;
285         }
286         logger.log(Log.Type.debug,"Pool destroyed ", creator);
287     }
288
289     /**
290      * The Creator Interface give the Pool the ability to Create, Destroy and
291      * Validate the Objects it is maintaining. Thus, it is a specially written
292      * Implementation for each type.
293      *
294      * @author Jonathan
295      *
296      * @param <T>
297      */
298     public interface Creator<T> {
299         public T create() throws CadiException;
300
301         public void destroy(T t);
302
303         public boolean isValid(T t);
304
305         public void reuse(T t);
306     }
307
308     /**
309      * Pooled Classes can be "Log Aware", which means they can tie into the same
310      * Logging element that the Pool is using.  To do this, the Object must implement "LogAware"
311      * 
312      * @author Jonathan
313      *
314      */
315     public interface LogAware {
316         public void setLog(Log log);
317     }
318
319     /**
320      * The "Pooled<T>" class is the transient class that wraps the actual Object
321      * T for API use/ It gives the ability to return ("done()", or "toss()") the
322      * Object to the Pool when processing is finished.
323      *
324      * For Safety, i.e. to avoid memory leaks and invalid Object States, there
325      * is a "finalize" method. It is strictly for when coder forgets to return
326      * the object, or perhaps hasn't covered the case during Exceptions or
327      * Runtime Exceptions with finally (preferred). This should not be
328      * considered normal procedure, as finalize() is called at an undetermined
329      * time during garbage collection, and is thus rather useless for a Pool.
330      * However, we don't want Coding Mistakes to put the whole program in an
331      * invalid state, so if something happened such that "done()" or "toss()"
332      * were not called, the resource is still cleaned up as well as possible.
333      *
334      * @author Jonathan
335      *
336      * @param <T>
337      */
338     public static class Pooled<T> {
339         public final T content;
340         private Pool<T> pool;
341
342         /**
343          * Create the Wrapping Object Pooled<T>.
344          *
345          * @param t
346          * @param pool
347          * @param logTarget
348          */
349         public Pooled(T t, Pool<T> pool) {
350             content = t;
351             if(t instanceof LogAware) {
352                 ((LogAware)t).setLog(pool.logger);
353             }
354             this.pool = pool;
355         }
356
357         /**
358          * This is the key API for the Pool, as calling "done()" offers this
359          * object back to the Pool for reuse.
360          *
361          * Do not use the Pooled<T> object again after calling "done()".
362          */
363         public void done() {
364             if (pool != null) {
365                 pool.offer(this);
366             }
367         }
368
369         /**
370          * The user of the Object may discover that the Object t is no longer in
371          * a valid state. Don't put Garbage back in the Refrigerator... Toss it,
372          * if it's no longer valid.
373          *
374          * toss() is also used for draining the Pool, etc.
375          *
376          * toss() will attempt to destroy the Object by using the Creator
377          * Interface.
378          *
379          */
380         public void toss() {
381             if (pool != null) {
382                 pool.destroy(content);
383             }
384             // Don't allow finalize to put it back in.
385             pool = null;
386         }
387
388         /**
389          * Just in case someone neglected to offer back object... Do not rely on
390          * this, as there is no specific time when finalize is called, which
391          * rather defeats the purpose of a Pool.
392          */
393         @Override
394         protected void finalize() throws Throwable {
395             if (pool != null) {
396                 done();
397                 pool = null;
398             }
399         }
400         
401         @Override
402         public String toString() {
403                 return content.toString();
404         }
405     }
406
407     /**
408      * Set a Max Range for numbers of spare objects waiting to be used.
409      *
410      * No negative numbers are allowed
411      * 
412      * Use in conjunction with drain to no longer store objects, i.e.
413      *  
414      *  pool.setMaxRange(0).drain();
415      *
416      * @return
417      */
418     public Pool<T> setMaxRange(int max_range) {
419         // Do not allow negative numbers
420         this.max_range = Math.max(0, max_range);
421         return this;
422     }
423     
424     /**
425      * Set a Max Range for numbers of spare objects waiting to be used.
426      *
427      * No negative numbers are allowed
428      *
429      * @return
430      */
431     public Pool<T> setMaxObjects(int max_objects) {
432         // Do not allow negative numbers
433         this.max_objects = Math.max(0, max_objects);
434         return this;
435     }
436
437     /**
438      * return whether objects in use or waiting are beyond max allowed
439      * 
440      * Pool does not actually stop new creations, but allows this to be used by
441      * other entities to limit number of creations of expensive Objects, like 
442      * Thread Pooling
443      *
444      */
445     public boolean tooManyObjects() {
446         return used > max_objects;
447     }
448
449     public String toString() {
450         return String.format("Pool: count(%d), used(%d), max_range(%d), max_objects(%d)",
451                         count, used,max_range,max_objects);
452     }
453 }