summaryrefslogtreecommitdiffstats
path: root/cadi/core
diff options
context:
space:
mode:
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"));
}