package zeta.handler.database;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.LinkedList;
import zeta.util.DatabaseUtils;
public class ConnectionPool {
public ConnectionPool(String url, String user, String password, int poolsize, int totalConnections, String checkQuery) {
this.url = url;
this.user = user;
this.password = password;
this.poolsize = poolsize;
this.totalConnections = totalConnections;
this.checkQuery = checkQuery;
connections = new LinkedList();
reaper = new ConnectionReaper(this);
reaper.start();
}
public synchronized String getURL() {
return url;
}
public synchronized void setURL(String url) {
this.url = url;
closeConnections();
}
public synchronized void reapConnections() {
long currentTime = System.currentTimeMillis()-connectionTimeout;
Iterator i = connections.iterator();
while (i.hasNext()) {
PooledConnection con = (PooledConnection)i.next();
if (con.inUse()) {
if (con.getLastUse()+connectionTimeout < currentTime) {
removeConnection(con);
i = connections.iterator();
}
} else {
if (con.getLastUse()+unusedTimeout < currentTime) {
removeConnection(con);
i = connections.iterator();
}
}
}
}
public synchronized void closeConnections() {
while (!connections.isEmpty()) {
removeConnection((PooledConnection)connections.getFirst());
}
}
public synchronized Connection getConnection() throws SQLException {
Iterator i = connections.iterator();
while (i.hasNext()) {
PooledConnection con = (PooledConnection)i.next();
if (con.lease()) {
if (!con.isClosed()) {
return con;
}
removeConnection(con);
i = connections.iterator();
}
}
if (connections.size() < totalConnections) {
PooledConnection con = new PooledConnection(DriverManager.getConnection(url, user, password), this);
con.lease();
connections.add(con);
return con;
}
throw new SQLException("Too many connections");
}
public synchronized void returnConnection(PooledConnection con) {
con.expireLease();
try {
if (!connections.contains(con)) {
DatabaseUtils.close(con.getConnection());
} else if (con.isClosed() || con.getCreatedTimestamp()+agedTimeout < System.currentTimeMillis()) {
removeConnection(con);
}
} catch (SQLException se) {
removeConnection(con);
}
}
public synchronized int getPoolsize() {
return connections.size();
}
protected boolean checkConnection(Connection con) throws SQLException {
if (checkQuery != null && checkQuery.length() > 0) {
PreparedStatement pStmt = null;
try {
pStmt = con.prepareStatement(checkQuery);
} catch (SQLException se) {
return false;
} finally {
DatabaseUtils.close(pStmt);
}
}
return true;
}
protected void finalize() {
closeConnections();
}
private synchronized void removeConnection(PooledConnection con) {
connections.remove(con);
DatabaseUtils.close(con.getConnection());
}
private LinkedList connections;
private String url, user, password, checkQuery;
private int poolsize, totalConnections;
private ConnectionReaper reaper;
final private static long connectionTimeout = 1800*1000;
final private static long unusedTimeout = 1800*1000;
final private static long agedTimeout = 12*3600*1000;
}
class ConnectionReaper extends Thread {
ConnectionReaper(ConnectionPool pool) {
this.pool = pool;
}
public void run() {
while (true) {
try {
sleep(reapTime);
} catch (InterruptedException e) {
}
pool.reapConnections();
}
}
private ConnectionPool pool;
private final long reapTime = 180*1000;
}