/**
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.ut.biolab.medsavant.server.db;
import java.sql.*;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Based on JDC Connection Pool example.
*
* @author Andrew
*/
public class ConnectionPool {
private static final Log LOG = LogFactory.getLog(ConnectionPool.class);
private static final long TIMEOUT = 60000;
private final List<PooledConnection> connections;
private final String user, password;
private String dbName;
private ConnectionReaper reaper;
public ConnectionPool(String db, String user, String password) {
this.dbName = db;
this.user = user;
this.password = password;
connections = new ArrayList<PooledConnection>();
reaper = new ConnectionReaper();
reaper.start();
}
public synchronized void reapConnections() {
long stale = System.currentTimeMillis() - TIMEOUT;
for (PooledConnection conn: connections) {
if (conn.inUse() && stale > conn.getLastUse() && !conn.validate()) {
removeConnection(conn);
}
}
}
/**
* Closes all connections, but also stops the reaper thread.
*/
public synchronized void close() {
while (connections.size() > 0) {
removeConnection(connections.get(0));
}
reaper = null;
}
private synchronized void removeConnection(PooledConnection conn) {
connections.remove(conn);
}
public synchronized PooledConnection getConnection() throws SQLException {
for (PooledConnection conn: connections) {
if (conn.lease()) {
return conn;
}
}
// Create a new connection
LOG.info(connections.size() + " connections opened, all in use. Creating new connection to " + dbName + " for " + user);// + ":" + password);
Connection conn = DriverManager.getConnection(ConnectionController.getConnectionString(dbName), user, password);
PooledConnection pooledConn = new PooledConnection(conn, this);
pooledConn.lease();
connections.add(pooledConn);
return pooledConn;
}
public synchronized void returnConnection(PooledConnection conn) {
conn.expireLease();
}
public String getDBName() {
return dbName;
}
/**
* Change the database name. As a side-effect, this updates the URL and closes the connections.
* @param db the new database name
*/
public synchronized void setDBName(String db) {
if (!db.equals(dbName)) {
for (PooledConnection c: connections) {
returnConnection(c);
}
LOG.info("Returned connections for " + dbName + ", new database will be " + db);
dbName = db;
}
}
public String getUser() {
return user;
}
private class ConnectionReaper extends Thread {
private static final long DELAY = 30000;
@Override
public void run() {
while (reaper != null) {
try {
sleep(DELAY);
} catch (InterruptedException e) {
}
reapConnections();
}
}
}
}