summaryrefslogtreecommitdiffstats
path: root/cadi/core
diff options
context:
space:
mode:
authorInstrumental <jgonap@stl.gathman.org>2020-05-15 10:24:47 -0500
committerInstrumental <jgonap@stl.gathman.org>2020-05-15 10:24:56 -0500
commitea095eb9cdbb451f2310a262f3877c79f527cc8f (patch)
tree14f2e5d7b026afab4214a6c0b82a166393197586 /cadi/core
parent9f2d11a8c9a8952bfc97e64418b21f26be4e51a6 (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/.gitignore1
-rw-r--r--cadi/core/src/main/java/org/onap/aaf/cadi/util/Log.java37
-rw-r--r--cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java183
-rw-r--r--cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java188
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"));
}