package com.trydone.core.database;
import net.jforum.util.preferences.ConfigKeys;
import net.jforum.util.preferences.SystemGlobals;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;
/**
* Every query and connection come from here.
* Before you can get some connection from the pool, you must
* call init() to initialize the connections. After that,
* to get a conneciton, simple use
* <p/>
* <blockquote><pre>
* Connection con = PooledConnection.getConnection();
* </pre></blockquote>
* <p/>
* The name of the query is associated with a Prepared Statement string, which is inside
* of a properties file, called [dbtype].sql, where the type is specified inside of
* database.properties.<br>
* <br>
* Also manages the connection to the database. The configuration is a config file which
* is read at the first <code>init()</code> call. You must init it before using the pool.<p>
* <p/>
* <code>PooledConnection</code> is for now a singleton.
*
* @author Paulo Silveira
* @author Rafael Steil
* @version $Id: PooledConnection.java,v 1.4 2006/01/05 07:48:16 pinke Exp $
*/
public class PooledConnection {
private int maxConnections;
private int pingDelay;
private String connectionString;
private String databaseUsername;
private String databasePassword;
private static final Logger logger = Logger.getLogger(PooledConnection.class);
protected boolean isDatabaseUp;
private static PooledConnection instance;
/**
* It is the connection pool
*/
private static LinkedList connections = new LinkedList();
/**
* It has all the connections, even the ones in use.
* This way, the garbage collector does not get them.
*/
private static LinkedList allConnections = new LinkedList();
// for sinalizing a release
private Object releaseSignal = new Object();
private static final boolean debug = true;
/**
* Private constructor that loads the driver and set the configuration from
* the properties file. It will also initialize the Database driver.
*
* @throws Exception Exception
*/
private PooledConnection() throws Exception {
init();
}
public static PooledConnection getInstance() throws Exception {
if (instance == null) {
instance = new PooledConnection();
logger.info("new dbconnect pool");
}
logger.info("All pools:" + allConnections.size() + ": free connections: " + connections.size());
return instance;
}
/**
* Inits ConnectionPool.
* If the pool was already initialized, this action will take no effect.
*
* @throws Exception Exception
*/
public void init() throws Exception {
String driver = SystemGlobals.getValue(ConfigKeys.DATABASE_CONNECTION_DRIVER);
int minConnections = SystemGlobals.getIntValue(ConfigKeys.DATABASE_POOL_MIN);
minConnections = minConnections == 0 ? 5 : minConnections;
this.maxConnections = SystemGlobals.getIntValue(ConfigKeys.DATABASE_POOL_MAX);
maxConnections = maxConnections == 0 ? 10 : maxConnections;
this.pingDelay = SystemGlobals.getIntValue(ConfigKeys.DATABASE_PING_DELAY);
this.connectionString = SystemGlobals.getValue(ConfigKeys.DATABASE_CONNECTION_STRING);
this.databasePassword = SystemGlobals.getValue(ConfigKeys.DATABASE_CONNECTION_PASSWORD);
this.databaseUsername = SystemGlobals.getValue(ConfigKeys.DATABASE_CONNECTION_USERNAME);
//(, "socity", "Socity111");
try {
Class.forName(driver);
if (logger.isDebugEnabled()) {
logger.info("*********************************************");
logger.info("******** STARTING CONNECTION POOL ***********");
logger.info("*********************************************");
logger.info("database.connection.driver = " + driver);
logger.info("minConnections = " + minConnections);
logger.info("maxConnections = " + this.maxConnections);
logger.info("pingDelay = " + this.pingDelay);
logger.info("*********************************************");
}
for (int i = 0; i < minConnections; i++) {
Connection conn = DriverManager.getConnection(this.connectionString, this.databaseUsername, this.databasePassword);
if (conn == null) {
throw new RuntimeException("Con't connect database to getConnection!");
}
connections.addLast(conn);
allConnections.add(conn);
if (logger.isDebugEnabled()) {
Date now = new Date();
logger.info(now.toString() + " opening connection " + (i + 1));
}
}
this.isDatabaseUp = true;
}
catch (ClassNotFoundException e) {
this.isDatabaseUp = false;
logger.error("Ouch... Cannot find database driver: " + driver);
throw new IOException("Ouch... Cannot find database driver: " + driver);
}
//ping
this.enableConnectionPinging();
}
/**
* Gets a connection to the database.<p>
* <p/>
* So you need to release it, after use. It will not be a huge problem if you do not
* release it, but this way you will get a better performance.<p>
* Thread safe.
*
* @return <code>Connection</code> object
* @throws SQLException java.sql.SQLException
*/
public synchronized Connection getConnection() throws SQLException {
Connection conn = null;
// if there is enought Connections
if (connections.size() > 0) {
synchronized (connections) {
conn = (Connection) connections.removeFirst();
}
// take a look if the connection has died!
try {
if (conn.isClosed()) {
synchronized (allConnections) {
allConnections.remove(conn);
conn = DriverManager.getConnection(this.connectionString, this.databaseUsername, this.databasePassword);
allConnections.add(conn);
}
}
} catch (SQLException e) {
//if (debug) {
logger.warn("Cannot reconnect a closed connection:" + this.connectionString + e);
//}
throw e;
}
return conn;
}
// Otherwise, create a new one if the Pool is now full
if (allConnections.size() < this.maxConnections) {
try {
conn = DriverManager.getConnection(this.connectionString, this.databaseUsername, this.databasePassword);
}
catch (SQLException e) {
//if (debug) {
logger.warn("Cannot stabilish a NEW connection to the database:" + this.connectionString + e);
//}
throw e;
}
// registering the new connection
synchronized (allConnections) {
allConnections.add(conn);
}
return conn;
}
/*
* Trying to get some Connections stuck inside some Queries.
* The Query.finalize method will release them.
* We need to wait sometime, so the GC will get the Connections for us
*/
System.gc();
synchronized (this.releaseSignal) {
/*
* Not inside a while, since we are giving it a maximum pingDelay,
* and this method is already SYNC, there is no way that we will loose
* the state if we receive a signal
*/
if (connections.size() == 0) {
try {
this.releaseSignal.wait(this.pingDelay);
}
catch (InterruptedException e) {
//if (debug)
logger.warn("Problems while waiting for connection. " + e);
}
}
if (connections.size() == 0) {
// TIMED OUT!!!!
// if (debug) {
logger.warn("Pool is empty, and th waiting for one timed out!"
+ "If this is happening too much, your code is probably not releasing the Connections."
+ "If you cant solve this, set your 'database.connection.pool.pingDelay' to a bigger number.");
// }
} else {
synchronized (connections) {
conn = (Connection) connections.removeFirst();
}
return conn;
}
}
return conn;
}
private boolean testConnection(Connection conn) {
try {
//TODO: execute a sql for test
return !conn.isClosed();
} catch (Exception e) {
return false;
}
}
private void pingConnections() {
try {
this.ReleaseAllConnections();
} catch (Exception e) {
e.printStackTrace();
}
try {
this.init();
}
catch (Exception e) {
e.printStackTrace();
}
}
public void enableConnectionPinging() {
new Timer(true).schedule(new TimerTask() {
public void run() {
pingConnections();
}
}, Long.parseLong(String.valueOf(pingDelay)));
}
public void ReleaseAllConnections() throws Exception {
if (allConnections == null || allConnections.size() == 0) {
return;
}
synchronized (allConnections) {
for (Iterator iter = allConnections.iterator(); iter.hasNext();) {
try {
Connection conn = (Connection) iter.next();
connections.remove(conn);
conn.close();
iter.remove();
logger.info("Real closing connection...");
}
catch (SQLException e) {
e.printStackTrace();
}
}
}
allConnections = new LinkedList();
connections = new LinkedList();
}
/**
* Releases a connection, making it available to the pool once more.
*
* @param conn <code>Connection</code> object to release
* @throws java.sql.SQLException
*/
public void releaseConnection(Connection conn) throws SQLException {
if (conn == null) {
if (debug) {
logger.warn("Cannot release a NULL connection!");
}
return;
}
// Sync because collection.contains() uses the fail fast iterator!
synchronized (allConnections) {
if (!allConnections.contains(conn) && debug) {
logger.warn("Cannot release a connection that is not from this pool!");
return;
}
try {
if (conn.isClosed()) {
allConnections.remove(conn);
return;
}
}
catch (SQLException e) {
//if (logger.isDebugEnabled()) {
logger.warn("Cannot get info about the conn: " + e);
//}
}
}
synchronized (this.releaseSignal) {
synchronized (connections) {
connections.addLast(conn);
}
this.releaseSignal.notify();
}
}
/**
* Returns the status
*
* @return The status
*/
public synchronized String getStatus() {
StringBuffer status = new StringBuffer();
int i = 0;
Iterator it = allConnections.iterator();
while (it.hasNext()) {
i++;
status.append("Connection ");
status.append(i);
status.append(": ");
Connection c = (Connection) it.next();
if (c != null) {
try {
status.append(c);
status.append(" closed: ");
status.append(c.isClosed());
}
catch (SQLException e) {
status.append(e);
}
} else {
status.append("NULL!!!");
}
status.append("\n");
}
status.append("\nPOOL:\n\n");
i = 0;
it = connections.iterator();
while (it.hasNext()) {
i++;
status.append("Connection ");
status.append(i);
status.append(": ");
Connection c = (Connection) it.next();
if (c != null) {
try {
status.append(c);
status.append(" closed: ");
status.append(c.isClosed());
}
catch (SQLException e) {
status.append(e);
}
} else
status.append("NULL!!!");
status.append("\n");
}
return status.toString();
}
public String toString() {
return "PooledConnection{" +
"maxConnections=" + maxConnections +
", pingDelay=" + pingDelay +
", connectionString='" + connectionString + '\'' +
", databaseUsername='" + databaseUsername + '\'' +
", databasePassword='" + databasePassword + '\'' +
", isDatabaseUp=" + isDatabaseUp +
", releaseSignal=" + releaseSignal +
'}';
}
public static void main(String[] args) {
try {
PooledConnection pc = new PooledConnection();
Connection conn = pc.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
}