package org.enhydra.jdbc.pool;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import org.enhydra.jdbc.core.JdbcThreadFactory;
import org.enhydra.jdbc.util.Logger;
public class GenericPool {
private long lifeTime;
private Hashtable locked, unlocked;
private Vector hitList;
private JdbcThreadFactory threadFactory;
private int minSize;
private int maxSize;
private PoolHelper poolHelper;
private int count;
private boolean gc;
private boolean debug;
private long deadLockMaxWait;
private long deadLockRetryWait;
private Logger log;
private int checkLevelObject;
protected Thread keeper;
protected PoolKeeper poolKeeper;
private long sleepTime;
protected int generation = 1;
public static final long DEFAULT_EXPIRATION = 600000;
public static final long DEFAULT_SLEEPTIME = 300000;
public static final int DEFAULT_MINSIZE = 2;
public static final int DEFAULT_MAXSIZE = 50;
public static final int DEFAULT_DEADLOCKMAXWAIT = 300000;
public static final int DEFAULT_DEADLOCKRETRYWAIT = 10000;
public GenericPool(PoolHelper helper) {
this(
helper,
DEFAULT_MINSIZE,
DEFAULT_MAXSIZE,
DEFAULT_EXPIRATION,
DEFAULT_SLEEPTIME);
}
public GenericPool(PoolHelper helper, int initSize) {
this(
helper,
DEFAULT_MINSIZE,
initSize,
DEFAULT_EXPIRATION,
DEFAULT_SLEEPTIME);
}
public GenericPool(
PoolHelper helper,
int minSize,
int maxSize,
long lifeTime,
long sleepTime) {
this.threadFactory = null;
this.lifeTime = lifeTime;
this.minSize = minSize;
this.maxSize = maxSize;
this.poolHelper = helper;
this.sleepTime = sleepTime;
this.checkLevelObject = 0;
this.deadLockMaxWait = DEFAULT_DEADLOCKMAXWAIT;
this.deadLockRetryWait = DEFAULT_DEADLOCKRETRYWAIT;
}
public synchronized void start() {
locked = new Hashtable();
unlocked = new Hashtable();
hitList = new Vector();
count = 0;
gc = false;
long now = System.currentTimeMillis();
for (int i = 0;
i < minSize;
i++) {
try {
GenerationObject genObject = poolHelper.create();
unlocked.put(genObject, new Long(now));
} catch (Exception e) {
log.error("Error Exception in GenericPool:start " + e);
}
++count;
}
if (threadFactory != null) {
try {
this.poolKeeper = new PoolKeeper(sleepTime, this);
this.keeper = threadFactory.getThread(poolKeeper);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
} else {
this.poolKeeper = new PoolKeeper(sleepTime, this);
this.keeper = new Thread(poolKeeper);
}
keeper.start();
log.debug("GenericPool:start pool started");
}
private Object getFromPool(String user, String password)
throws Exception {
long now = System.currentTimeMillis();
if (getUnlockedObjectCount() > 0) {
GenerationObject o = null;
Object realObject = null;
Long life = null;
Enumeration e = unlocked.keys();
while (e.hasMoreElements()) {
synchronized (this) {
if (!e.hasMoreElements()) break;
o = (GenerationObject) e.nextElement();
life = (Long) unlocked.get(o);
unlocked.remove(o);
realObject = o.getObj();
}
if ((now - life.longValue()) > lifeTime) {
log.debug("GenericPool:getFromPool an object has expired");
removeUnlockedObject(o);
} else {
log.debug(
"GenericPool:getFromPool check the owner of the connection");
if (checkOwner(o, user, password)) {
log.debug("GenericPool:getFromPool owner is verified");
if ((checkLevelObject == 0)
|| ((checkLevelObject == 1)
&& poolHelper.checkThisObject(realObject))
|| ((checkLevelObject == 2)
&& poolHelper.testThisObject(realObject))) {
locked.put(o, new Long(now));
log.debug(
"GenericPool:getFromPool return an object (after verification if needed)");
return (o.getObj());
} else {
log.debug(
"GenericPool:getFromPool kill an object from the pool");
removeUnlockedObject(o);
}
} else
log.debug("GenericPool:getFromPool owner is FALSE");
}
}
}
boolean create = false;
synchronized (this) {
if (count < maxSize) {
create = true;
count++;
}
}
if (create) {
log.debug(
"GenericPool:getFromPool no objects available, create a new one");
try {
GenerationObject genObject = poolHelper.create(user, password);
locked.put(genObject, new Long(now));
return (genObject.getObj());
} catch (Exception excp) {
synchronized (this) {
count--;
}
log.error(
"GenericPool:getFromPool Error Exception in GenericPool:getFromPool");
throw excp;
}
}
return null;
}
public synchronized boolean checkOwner(
GenerationObject genObject,
String user,
String password) {
return equals(user, genObject.getUser())
&& equals(password, genObject.getPassword());
}
JdbcThreadFactory getThreadFactory() {
return threadFactory;
}
void setThreadFactory(JdbcThreadFactory tf) {
threadFactory = tf;
}
private boolean equals(String a, String b) {
if (a == null)
return (b == null);
if (b == null)
return (a == null);
return a.equals(b);
}
public Object checkOut(String user, String password)
throws Exception {
log.debug("GenericPool:checkOut an object");
long now = System.currentTimeMillis();
GenerationObject o;
Enumeration e;
Object realObject;
log.debug(
"GenericPool:checkOut UnlockedObjectCount="
+ getUnlockedObjectCount());
log.debug(
"GenericPool:checkOut LockedObjectCount=" + getLockedObjectCount());
log.debug(
"GenericPool:checkOut count=" + count + " maxSize=" + maxSize);
if (getUnlockedObjectCount() > 0) {
if ((checkLevelObject == 3)
|| (checkLevelObject == 4)) {
e = unlocked.keys();
while (e.hasMoreElements()) {
o = (GenerationObject) e.nextElement();
realObject = o.getObj();
if ((now - ((Long) unlocked.get(o)).longValue()) > lifeTime) {
log.debug("GenericPool:checkOut an object has expired");
removeUnlockedObject(o);
} else {
log.debug(
"GenericPool:checkOut check the owner of the connection");
if (checkOwner(o, user, password)) {
if (((checkLevelObject == 3)
&& !poolHelper.checkThisObject(realObject))
|| ((checkLevelObject == 4)
&& !poolHelper.testThisObject(realObject))) {
log.debug(
"GenericPool:checkOut remove object checkLevelObject="
+ checkLevelObject);
removeUnlockedObject(o);
}
}
}
}
}
}
int currentWait = 0;
Object obj = getFromPool(user, password);
while ((obj == null) && (currentWait < getDeadLockMaxWait())) {
log.info("GenericPool:checkOut waiting for an object :"+this.poolHelper.toString());
try {
synchronized (this) {
wait(getDeadLockRetryWait());
}
} catch (InterruptedException excp) {
log.error(
"GenericPool:checkOut ERROR Failed while waiting for an object: "
+ excp);
}
currentWait += getDeadLockRetryWait();
obj = getFromPool(user, password);
}
if (obj == null)
throw new Exception("GenericPool:checkOut ERROR impossible to obtain a new object from the pool");
return obj;
}
synchronized public void minimumObject() {
minimumObject(null, null);
}
synchronized public void minimumObject(String user, String password) {
log.debug(
"GenericPool:minimumObject create object if there are less than minSize objects in the pool count ="
+ count);
if ((count < minSize)
&& (unlocked != null)) {
long now = System.currentTimeMillis();
for (int i = count;
i < minSize;
i++) {
try {
GenerationObject genObject;
if ((user != null) && (password != null))
genObject = poolHelper.create();
else
genObject = poolHelper.create(user, password);
unlocked.put(genObject, new Long(now));
} catch (Exception e) {
log.error(
"GenericPool:minimumObject Error Exception in GenericPool:minimumObject");
}
}
log.debug(
"GenericPool:minimumObject count="
+ count
+ " Unlocked="
+ this.getUnlockedObjectCount()
+ " locked="
+ this.getLockedObjectCount());
count = minSize;
}
}
public synchronized void checkIn(Object o) {
log.debug("GenericPool:checkIn return an object to the pool");
for (Enumeration enum = locked.keys();
enum.hasMoreElements();
) {
GenerationObject obj = (GenerationObject) enum.nextElement();
if (obj.getObj().equals(o)) {
locked.remove(obj);
unlocked.put(obj, new Long(System.currentTimeMillis()));
int genObj = obj.getGeneration();
if (generation > genObj) {
if (!poolHelper.checkThisObject(obj.getObj()))
removeUnlockedObject(obj);
}
notifyAll();
}
}
if (count > maxSize) {
log.info(
"GenericPool:checkIn more than maxSize object in the pool");
Enumeration enum = unlocked.keys();
for (int i = maxSize;
i < count;
i++) {
if (getUnlockedObjectCount() > 0) {
GenerationObject obj =
(GenerationObject) enum.nextElement();
removeUnlockedObject(obj);
}
}
count = getUnlockedObjectCount() + getLockedObjectCount();
if (count > maxSize)
log.warn(
"GenericPool:checkIn Be careful, the maximum size of the pool does not correspond"
+ " to your data. When objects will be check in, the pool "
+ "will decrease");
}
}
synchronized private void removeUnlockedObject(GenerationObject obj) {
--count;
notifyAll();
unlocked.remove(obj);
hitList.add(obj);
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public boolean isDebug() {
return debug;
}
public synchronized void setMinSize(int min) throws Exception {
if (min < 0)
throw new Exception("GenericPool:setMinSize Minimum size of the pool can't be lesser than 0");
else if (min > maxSize)
throw new Exception(
"GenericPool:setMinSize Minimum size of the pool can't be greater than the maxSize ("
+ maxSize
+ ")");
else {
this.minSize = min;
}
}
public synchronized void setMaxSize(int max) throws Exception {
if (max < 0)
throw new Exception("GenericPool:setMaxSize Maximum size of the pool can't be lesser than 0");
else if (max < minSize)
throw new Exception(
"GenericPool:setMaxSize Maximum size of the pool can't be lesser than the minSize ("
+ minSize
+ ")");
else {
this.maxSize = max;
if (count > max) {
log.info(
"GenericPool:setMaxSize pool has more than max element");
Enumeration enum = unlocked.keys();
for (int i = max; i < count; i++) {
if (getUnlockedObjectCount() > 0) {
GenerationObject o =
(GenerationObject) enum.nextElement();
removeUnlockedObject(o);
}
}
count = getUnlockedObjectCount() + getLockedObjectCount();
if (count > max)
log.warn(
"GenericPool:setMaxSize Be careful, the maximum size of "
+ "the pool does not correspond to your data. When objects "
+ "will be check in, the pool will decrease");
}
}
}
public void setLifeTime(long lifeTime) {
this.lifeTime = lifeTime;
}
public void setSleepTime(long sleepTime) {
this.sleepTime = sleepTime;
}
public void setGeneration(int generation) {
this.generation = generation;
log.debug(
"GenericPool:setGeneration Be careful, it is very dangerous to change "
+ "the generation number, many objects could be destroyed");
}
public void setGC(boolean gc) {
this.gc = gc;
}
public void setCheckLevelObject(int level) {
if ((level > 0) && (level <= 4))
this.checkLevelObject = level;
}
public void setDeadLockMaxWait(long deadLock) {
this.deadLockMaxWait = deadLock;
}
public void setDeadLockRetryWait(long deadLockRetryWait) {
this.deadLockRetryWait = deadLockRetryWait;
}
public int getMinSize() {
return minSize;
}
public int getMaxSize() {
return maxSize;
}
public long getLifeTime() {
return lifeTime;
}
public boolean isGC() {
return gc;
}
public int getCount() {
return count;
}
public long getSleepTime() {
return sleepTime;
}
public int getGeneration() {
return generation;
}
public int getCheckLevelObject() {
return checkLevelObject;
}
public void stop() {
log.debug("GenericPool:stop start to stop the pool");
if ((getLockedObjectCount() != 0) || (getUnlockedObjectCount() != 0)) {
expireAll();
if (poolKeeper != null)
poolKeeper.stop();
keeper.interrupt();
locked.clear();
unlocked.clear();
locked = null;
unlocked = null;
count = 0;
}
log.debug("GenericPool:stop pool stopped");
}
public int getLockedObjectCount() {
if (locked != null)
return locked.size();
else
return 0;
}
public int getUnlockedObjectCount() {
if (unlocked != null)
return unlocked.size();
else
return 0;
}
public long getDeadLockMaxWait() {
return this.deadLockMaxWait;
}
public long getDeadLockRetryWait() {
return this.deadLockRetryWait;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("GenericPool:\n");
sb.append(" num of element =<"+ count+">\n");
sb.append(" minSize =<"+ minSize+">\n");
sb.append(" maxSize =<"+ maxSize+">\n");
sb.append(" lifeTime =<"+ lifeTime+">\n");
sb.append(" ngeneration =<"+ generation+">\n");
sb.append(" getLockedObjectCount() =<"+ getLockedObjectCount()+">\n");
sb.append(" getUnlockedObjectCount() =<"+ getUnlockedObjectCount()+">\n");
sb.append(" getDeadLockMaxWait() =<"+ getDeadLockMaxWait()+">\n");
sb.append(" getDeadLockRetryWait() =<"+ getDeadLockRetryWait()+">\n");
if (unlocked != null) {
sb.append("Unlocked pool:\n");
Enumeration e = unlocked.keys();
while (e.hasMoreElements()) {
GenerationObject o = (GenerationObject) e.nextElement();
sb.append(o.getObj().toString());
}
}
if (locked != null) {
sb.append("Locked pool:\n");
Enumeration e = unlocked.keys();
while (e.hasMoreElements()) {
GenerationObject o = (GenerationObject) e.nextElement();
sb.append(o.getObj().toString());
}
}
return sb.toString();
}
protected void cleanUp() {
synchronized (this) {
if (unlocked == null)
return;
}
long now = System.currentTimeMillis();
synchronized (this) {
for (Enumeration enum = unlocked.keys();
enum.hasMoreElements();
) {
GenerationObject o = (GenerationObject) enum.nextElement();
long lasttouch = ((Long) unlocked.get(o)).longValue();
if ((now - lasttouch) > lifeTime) {
log.debug("GenericPool:cleanUp clean up the pool");
removeUnlockedObject(o);
}
}
}
while (hitList.size() > 0) {
GenerationObject obj = (GenerationObject) hitList.remove(0);
log.debug("GenericPool:cleanUp killing an object");
poolHelper.expire(obj.getObj());
obj.killObject();
}
if (isGC())
System.gc();
boolean resize = false;
synchronized (this) {
resize = count < minSize;
}
if (resize) {
log.info(
"GenericPool:cleanUp less than minSize objects in the pool "
+ "min="
+ minSize
+ " max="
+ maxSize
+ " count="
+ count);
while (true) {
try {
GenerationObject genObject = poolHelper.create();
synchronized (this) {
unlocked.put(genObject, new Long(now));
++count;
notifyAll();
if (count >= minSize) break;
}
} catch (Exception e) {
log.error(
"GenericPool:cleanUp Error Exception in GenericPool:cleanUp");
}
synchronized (this) {
notifyAll();
}
}
log.info(
"GenericPool:cleanUp done "
+ "min="
+ minSize
+ " max="
+ maxSize
+ " count="
+ count);
}
}
void expireAll() {
log.debug(
"GenericPool:expireAll close all object in the unlocked and locked structures");
for (Enumeration enum = unlocked.keys();
enum.hasMoreElements();
) {
GenerationObject o = (GenerationObject) enum.nextElement();
poolHelper.expire(o.getObj());
o.killObject();
o = null;
}
for (Enumeration enum = locked.keys();
enum.hasMoreElements();
) {
GenerationObject o = (GenerationObject) enum.nextElement();
poolHelper.expire(o.getObj());
o.killObject();
o = null;
}
}
public void nextGeneration(Object obj) {
log.debug("GenericPool:nextGeneration");
int genObj = 0;
for (Enumeration enum = locked.keys();
enum.hasMoreElements();
) {
GenerationObject o = (GenerationObject) enum.nextElement();
if (o.getObj().equals(obj))
genObj = o.getGeneration();
}
for (Enumeration enum = unlocked.keys();
enum.hasMoreElements();
) {
GenerationObject o = (GenerationObject) enum.nextElement();
if (o.getGeneration() <= genObj) {
if (!poolHelper.checkThisObject(o.getObj()))
removeUnlockedObject(o);
}
}
++this.generation;
}
synchronized public void removeLockedObject(Object obj) {
log.debug("GenericPool:removeObject remove an object");
for (Enumeration enum = locked.keys();
enum.hasMoreElements();
) {
GenerationObject o = (GenerationObject) enum.nextElement();
if (o.getObj().equals(obj)) {
locked.remove(o);
--count;
o.killObject();
o = null;
}
}
}
public void setLogger(Logger alog) {
log = alog;
}
public Hashtable getLockedObject() {
return locked;
}
}