diff options
author | Instrumental <jgonap@stl.gathman.org> | 2020-05-15 10:24:47 -0500 |
---|---|---|
committer | Instrumental <jgonap@stl.gathman.org> | 2020-05-15 10:24:56 -0500 |
commit | ea095eb9cdbb451f2310a262f3877c79f527cc8f (patch) | |
tree | 14f2e5d7b026afab4214a6c0b82a166393197586 /cadi/core | |
parent | 9f2d11a8c9a8952bfc97e64418b21f26be4e51a6 (diff) |
Improve Pool
Move interface Log out, make JUPool test the functionality better, add "tooManyObjects"
to help control memory.
Minor BasicEnv for standalone usability.
included ".checkstyle" files, generated by Eclipse/Maven to .gitignore. They shouldn't be in repo.
Issue-ID: AAF-1146
Signed-off-by: Instrumental <jgonap@stl.gathman.org>
Change-Id: I72c22fa455029950e3d73d2e9addbccc1a990189
Diffstat (limited to 'cadi/core')
-rw-r--r-- | cadi/core/.gitignore | 1 | ||||
-rw-r--r-- | cadi/core/src/main/java/org/onap/aaf/cadi/util/Log.java | 37 | ||||
-rw-r--r-- | cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java | 183 | ||||
-rw-r--r-- | cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java | 188 |
4 files changed, 298 insertions, 111 deletions
diff --git a/cadi/core/.gitignore b/cadi/core/.gitignore index 6028f0a5..112dedb3 100644 --- a/cadi/core/.gitignore +++ b/cadi/core/.gitignore @@ -2,3 +2,4 @@ /.settings/ /target/ /.project +/.checkstyle diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Log.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Log.java new file mode 100644 index 00000000..af334e9a --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Log.java @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START==================================================== + * Log + * =========================================================================== + * Copyright (c) May 11, 2020 Gathman Systems. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + */ +package org.onap.aaf.cadi.util; + +/** + * A basic log interface used to Facade into Log Libraries used locally. + * + * @author Jonathan + * + */ +public interface Log { + enum Type {debug,info,warn,error,trace}; + public void log(Log.Type type, Object ... o); + + public final static Log NULL = new Log() { + @Override + public void log(Log.Type type, Object ... o) { + } + }; +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java index 72d09bfe..6980e0aa 100644 --- a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java @@ -65,7 +65,12 @@ public class Pool<T> { * This is a constant which specified the default maximum number of unused * objects to be held at any given time. */ - private static final int MAX_RANGE = 6; // safety + public static final int MAX_RANGE = 6; // safety + + /** + * Maximum objects, in use or waiting + */ + public static final int MAX_OBJECTS = 20; // assumption for thread /** * only Simple List needed. @@ -76,15 +81,15 @@ public class Pool<T> { private LinkedList<Pooled<T>> list; /** - * keep track of how many elements exist, to avoid asking list. + * keep track of how many elements are currently available to use, to avoid asking list. */ private int count; - + /** - * Spares are those Object that are primed and ready to go. + * how many objects have been asked for, but not returned or tossed */ - private int spares; - + private int used; + /** * Actual MAX number of spares allowed to hang around. Can be set to * something besides the default MAX_RANGE. @@ -92,6 +97,15 @@ public class Pool<T> { private int max_range = MAX_RANGE; /** + * Actual MAX number of Objects both in use, or waiting. + * This does not actually affect the Pool, because the objects, once they leave the pool, are not known until + * they are put back with done (offer). It only affects the "overLimit()" function. + * + * Important... this information is only valid if PooledObjects call "done()" or "toss()". + */ + private int max_objects = MAX_OBJECTS; + + /** * The Creator for this particular pool. It must work for type T. */ private Creator<T> creator; @@ -105,7 +119,7 @@ public class Pool<T> { * @param creator */ public Pool(Creator<T> creator) { - count = spares = 0; + count = used = 0; this.creator = creator; list = new LinkedList<>(); logger = Log.NULL; @@ -117,29 +131,44 @@ public class Pool<T> { */ public void setLogger(Log logger) { this.logger = logger; + // Also reset existing Pooled objects + for(Pooled<?> p : list) { + if(p.content instanceof LogAware) { + ((LogAware)p.content).setLog(logger); + } else { + break; + } + } } - public void log(Object ...objects) { - logger.log(objects); + public void log(Log.Type type, Object ...objects) { + logger.log(type,objects); } /** * Preallocate a certain number of T Objects. Useful for services so that * the first transactions don't get hit with all the Object creation costs - * + * + * It is assumed that priming also means that it is the minimum desired available resources. Therefore, + * max_range is set to prime, if less than current max_range, if it is default. + * * @param lt * @param prime * @throws CadiException */ - public void prime(int prime) throws CadiException { + public Pool<T> prime(int prime) throws CadiException { + if(max_range == MAX_RANGE && prime<max_range) { + max_range = prime; + } for (int i = 0; i < prime; ++i) { Pooled<T> pt = new Pooled<T>(creator.create(), this); synchronized (list) { list.addFirst(pt); ++count; + ++used; } } - + return this; } /** @@ -147,19 +176,22 @@ public class Pool<T> { * down all Allocated objects cleanly for exiting. It is also a good method * for removing objects when, for instance, all Objects are invalid because * of broken connections, etc. + * + * Use in conjunction with setMaxRange to no longer store objects, i.e. + * + * pool.setMaxRange(0).drain(); */ - public void drain() { - synchronized (list) { - for (int i = 0; i < list.size(); ++i) { - Pooled<T> pt = list.remove(); - creator.destroy(pt.content); - logger.log("Pool drained ", creator.toString()); - } - count = spares = 0; - } - + public synchronized void drain() { + while(list.size()>0) { + Pooled<T> pt = list.remove(); + --used; + String name = pt.content.toString(); + creator.destroy(pt.content); + logger.log(Log.Type.debug,"Pool destroyed", name); + } + count = 0; } - + /** * This is the essential function for Pool. Get an Object "T" inside a * "Pooled<T>" object. If there is a spare Object, then use it. If not, then @@ -181,21 +213,14 @@ public class Pool<T> { public Pooled<T> get() throws CadiException { Pooled<T> pt; synchronized (list) { - if (list.isEmpty()) { - pt = null; - } else { - pt = list.removeLast(); - --count; - creator.reuse(pt.content); - } + pt = list.pollLast(); } if (pt == null) { - if (spares < max_range) - ++spares; pt = new Pooled<T>(creator.create(), this); + ++used; } else { - if (spares > 1) - --spares; + --count; + creator.reuse(pt.content); } return pt; } @@ -235,19 +260,31 @@ public class Pool<T> { * @return */ // Used only by Pooled<T> - private boolean offer(Pooled<T> used) { - if (count < spares) { + private boolean offer(Pooled<T> usedP) { + if (count < max_range) { synchronized (list) { - list.addFirst(used); + list.addFirst(usedP); ++count; } - logger.log("Pool recovered ", creator); + logger.log(Log.Type.trace,"Pool recovered ", creator); } else { - logger.log("Pool destroyed ", creator); - creator.destroy(used.content); + destroy(usedP.content); } return false; } + + /** + * Destroy, using Creator's specific semantics, the Object, and decrement "used" + * + * @param t + */ + private void destroy(T t) { + creator.destroy(t); + synchronized (list) { + --used; + } + logger.log(Log.Type.debug,"Pool destroyed ", creator); + } /** * The Creator Interface give the Pool the ability to Create, Destroy and @@ -268,15 +305,17 @@ public class Pool<T> { public void reuse(T t); } - public interface Log { - public void log(Object ... o); - - public final static Log NULL = new Log() { - @Override - public void log(Object ... o) { - } - }; + /** + * Pooled Classes can be "Log Aware", which means they can tie into the same + * Logging element that the Pool is using. To do this, the Object must implement "LogAware" + * + * @author Jonathan + * + */ + public interface LogAware { + public void setLog(Log log); } + /** * The "Pooled<T>" class is the transient class that wraps the actual Object * T for API use/ It gives the ability to return ("done()", or "toss()") the @@ -309,8 +348,10 @@ public class Pool<T> { */ public Pooled(T t, Pool<T> pool) { content = t; + if(t instanceof LogAware) { + ((LogAware)t).setLog(pool.logger); + } this.pool = pool; - } /** @@ -338,7 +379,7 @@ public class Pool<T> { */ public void toss() { if (pool != null) { - pool.creator.destroy(content); + pool.destroy(content); } // Don't allow finalize to put it back in. pool = null; @@ -356,17 +397,30 @@ public class Pool<T> { pool = null; } } + + @Override + public String toString() { + return content.toString(); + } } /** - * Get the maximum number of spare objects allowed at any moment + * Set a Max Range for numbers of spare objects waiting to be used. + * + * No negative numbers are allowed + * + * Use in conjunction with drain to no longer store objects, i.e. + * + * pool.setMaxRange(0).drain(); * * @return */ - public int getMaxRange() { - return max_range; + public Pool<T> setMaxRange(int max_range) { + // Do not allow negative numbers + this.max_range = Math.max(0, max_range); + return this; } - + /** * Set a Max Range for numbers of spare objects waiting to be used. * @@ -374,9 +428,26 @@ public class Pool<T> { * * @return */ - public void setMaxRange(int max_range) { + public Pool<T> setMaxObjects(int max_objects) { // Do not allow negative numbers - this.max_range = Math.max(0, max_range); + this.max_objects = Math.max(0, max_objects); + return this; } + /** + * return whether objects in use or waiting are beyond max allowed + * + * Pool does not actually stop new creations, but allows this to be used by + * other entities to limit number of creations of expensive Objects, like + * Thread Pooling + * + */ + public boolean tooManyObjects() { + return used > max_objects; + } + + public String toString() { + return String.format("Pool: count(%d), used(%d), max_range(%d), max_objects(%d)", + count, used,max_range,max_objects); + } } diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java index 230c6b3b..b38a7914 100644 --- a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java @@ -29,13 +29,12 @@ import java.util.List; import static org.hamcrest.CoreMatchers.*; import org.junit.*; import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.util.Log; import org.onap.aaf.cadi.util.Pool; import org.onap.aaf.cadi.util.Pool.*; public class JU_Pool { - private StringBuilder sb = new StringBuilder(); - private class IntegerCreator implements Creator<Integer> { private int current = 0; @@ -59,59 +58,147 @@ public class JU_Pool { } } + // Used for CustomLogger Testing + private StringBuilder sb = new StringBuilder(); + private class CustomLogger implements Log { @Override - public void log(Object... o) { + public void log(Log.Type type, Object... o) { for (Object item : o) { sb.append(item.toString()); } } } + /** + * Enter variable amount in this order + * + * count, used, max_range, max_objects + * @param intPool + * @param ints + */ + private void check(Pool<Integer> intPool, int ... ints) { + String rpt = intPool.toString(); + // Fallthrough on purpose, to process only the ints entered, but in the right order. + switch(ints.length) { + case 4: + assertTrue(rpt.contains(String.format("max_objects(%d)", ints[3]))); + case 3: + assertTrue(rpt.contains(String.format("max_range(%d)", ints[2]))); + case 2: + assertTrue(rpt.contains(String.format("used(%d)", ints[1]))); + case 1: + assertTrue(rpt.contains(String.format("count(%d)", ints[0]))); + } + } + + @Test + public void settings() throws CadiException { + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + check(intPool,0,0,Pool.MAX_RANGE,Pool.MAX_OBJECTS); + + // Check MaxObjects, min is 0 + intPool.setMaxObjects(-10); + check(intPool,0,0,Pool.MAX_RANGE,0); + + intPool.setMaxObjects(10); + check(intPool,0,0,Pool.MAX_RANGE,10); + + // Check MaxRange, min is 0 + intPool.setMaxRange(-10); + check(intPool,0,0,0,10); + + intPool.setMaxRange(2); + check(intPool,0,0,2,10); + + // Validate Priming + intPool.prime(3); + check(intPool,3,3,2,10); + + // Drain + intPool.drain(); + check(intPool,0,0,2,10); + } + @Test - public void getTest() throws CadiException { - Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); - - List<Pooled<Integer>> gotten = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - gotten.add(intPool.get()); - assertThat(gotten.get(i).content, is(i)); - } - - gotten.get(9).done(); - gotten.set(9, intPool.get()); - assertThat(gotten.get(9).content, is(9)); - - for (int i = 0; i < 10; i++) { - gotten.get(i).done(); - } - - for (int i = 0; i < 10; i++) { - gotten.set(i, intPool.get()); - if (i < 5) { - assertThat(gotten.get(i).content, is(i)); - } else { - assertThat(gotten.get(i).content, is(i + 5)); - } - } - - for (int i = 0; i < 10; i++) { - gotten.get(i).toss(); - // Coverage calls - gotten.get(i).toss(); - gotten.get(i).done(); - - // only set some objects to null -> this is for the finalize coverage test - if (i < 5) { - gotten.set(i, null); - } - } - - // Coverage of finalize() - System.gc(); + public void range() throws CadiException { + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + intPool.setMaxRange(2); + check(intPool,0,0,2); + + // Prime + intPool.prime(3); + check(intPool,3,3,2); + + // Using 3 leaves count (in Pool) and Used (by System) 3 + List<Pooled<Integer>> using = new ArrayList<>(); + for(int i=0;i<3;++i) { + using.add(intPool.get()); + } + check(intPool,0,3,2); + + // Using 3 more creates more Objects, and uses immediately + for(int i=0;i<3;++i) { + using.add(intPool.get()); + } + check(intPool,0,6,2); + + // Clean out all Objects in possession, but there are 6 Objects not returned yet. + intPool.drain(); + check(intPool,0,6,2); + + // Returning Objects + for(Pooled<Integer> i : using) { + i.done(); + } + + // Since Range is 2, keep only 2, and destroy the rest + check(intPool,2,2,2); + + // Shutdown (helpful for stopping Services) involves turning off range + intPool.setMaxRange(0).drain(); + check(intPool,0,0,0); } - + @Test + public void tooManyObjects() throws CadiException { + /* + * It should be noted that "tooManyObjects" isn't enforced by the Pool, because Objects are not + * tracked (other than used) once they leave the pool. + * + * It is information that using entities, like Thread Pools, can use to limit creations of expensive objects + */ + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + intPool.setMaxObjects(10).setMaxRange(2); + check(intPool,0,0,2,10); + + assertFalse(intPool.tooManyObjects()); + + // Obtain up to maxium Objects + List<Pooled<Integer>> using = new ArrayList<>(); + for(int i=0;i<10;++i) { + using.add(intPool.get()); + } + + check(intPool,0,10,2,10); + assertFalse(intPool.tooManyObjects()); + + using.add(intPool.get()); + check(intPool,0,11,2,10); + assertTrue(intPool.tooManyObjects()); + + // Returning Objects + for(Pooled<Integer> i : using) { + i.done(); + } + + // Returning Objects puts Pool back in range + check(intPool,2,2,2,10); + assertFalse(intPool.tooManyObjects()); + + } + + @Test public void bulkTest() throws CadiException { Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); @@ -136,23 +223,14 @@ public class JU_Pool { } @Test - public void setMaxTest() { - Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); - intPool.setMaxRange(10); - assertThat(intPool.getMaxRange(), is(10)); - intPool.setMaxRange(-10); - assertThat(intPool.getMaxRange(), is(0)); - } - - @Test public void loggingTest() { Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); // Log to Log.NULL for coverage - intPool.log("Test log output"); + intPool.log(Log.Type.info,"Test log output"); intPool.setLogger(new CustomLogger()); - intPool.log("Test log output"); + intPool.log(Log.Type.info,"Test log output"); assertThat(sb.toString(), is("Test log output")); } |