![JAR search and dependency download from the Maven repository](/logo.png)
xdev.db.ConnectionPool Maven / Gradle / Ivy
/*
* XDEV Application Framework - XDEV Application Framework
* Copyright © 2003 XDEV Software (https://xdev.software)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 program. If not, see .
*/
package xdev.db;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.concurrent.Executor;
import xdev.io.IOUtils;
import xdev.util.logging.LoggerFactory;
import xdev.util.logging.XdevLogger;
import xdev.util.systemproperty.XdevSystemPropertyKeys;
import xdev.util.systemproperty.XdevSystemPropertyUtils;
import com.xdev.jadoth.sqlengine.dbms.DbmsAdaptor;
import com.xdev.jadoth.sqlengine.interfaces.ConnectionProvider;
/**
* A pool including one or more PooledConnection
objects. The
* ConnectionPool
provides methods to handle the
* PooledConnection
.
*
*
*
* Use {@link XdevSystemPropertyKeys} to enable "tracing who is calling" with
* XDEV_DB_TRACEWHOISCALLING, connection timeout with XDEV_DB_CONNECTIONTIMEOUT
* and "stacktrace timeout delay" with XDEV_DB_STACKTRACETIMEOUTDELAY
*
* @author XDEV Software (MP)
*/
public class ConnectionPool implements ConnectionProvider
{
// default values
private static final long DEFAULT_CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5min
private static final long DEFAULT_STACKTRACE_TIMEOUT_DELAY = 10 * 1000; // 10s
private static final boolean DEFAULT_CONNECTION_DEBUG_MODE = false;
/**
* Logger instance for this class.
*/
private static final XdevLogger LOGGER = LoggerFactory
.getLogger(ConnectionPool.class);
private final int size;
private final boolean keepConnectionsAlive;
private final DbmsAdaptor adaptor;
private final ConnectionInformation info;
private final Vector pool;
private final Timer stacktraceTimer = new Timer();
private int usedConnections = 0;
// over system properties configurable variables
/**
* Stacktrace timeout in ms. After this time the connection will be closed
* and traceWhoIsCalling == true
the stacktrace is set to
* leaseStack
*/
private boolean isConnectionDebugMode;
private long stackTraceTimeOutDelay;
private long connectionTimeout;
private final int maxStatementsPerConnection;
/**
* Construct a new {@link ConnectionPool} that is initialize with the given
* parameter.
*
* @param size
* the limit of concurrent connections, a size
<= 0
* means no limit
*
* @param keepConnectionsAlive
* true
to close the connection definitely or
* false
if the connection is no longer used
*
* @param adaptor
* the {@link DbmsAdaptor} for this {@link ConnectionPool}
*
* @param info
* the connection information
*
*/
public ConnectionPool(int size, boolean keepConnectionsAlive, DbmsAdaptor adaptor,
ConnectionInformation info, int maxStmtPerCon)
{
this.size = size;
this.keepConnectionsAlive = keepConnectionsAlive;
this.adaptor = adaptor;
this.info = info;
this.pool = new Vector();
this.maxStatementsPerConnection = maxStmtPerCon;
this.initConfigurableVariables();
}
private void initConfigurableVariables()
{
this.isConnectionDebugMode = XdevSystemPropertyUtils.getBooleanValue(
XdevSystemPropertyKeys.CONNECTION_DEBUG_MODE,DEFAULT_CONNECTION_DEBUG_MODE);
this.stackTraceTimeOutDelay = XdevSystemPropertyUtils.getLongValue(
XdevSystemPropertyKeys.STACKTRACE_TIMEOUT_DELAY,DEFAULT_STACKTRACE_TIMEOUT_DELAY);
this.connectionTimeout = XdevSystemPropertyUtils.getLongValue(
XdevSystemPropertyKeys.CONNECTION_TIMEOUT,DEFAULT_CONNECTION_TIMEOUT);
}
private synchronized void reapConnections()
{
final long stale = System.currentTimeMillis() - this.connectionTimeout;
final Enumeration connlist = this.pool.elements();
while(connlist.hasMoreElements())
{
final PooledConnection conn = connlist.nextElement();
if(conn.inuse && stale > conn.initTimestamp && !conn.validate())
{
this.usedConnections--;
this.pool.removeElement(conn);
conn.closeDefinite();
}
}
}
// /**
// * Returns the first {@link Connection} of this {@link ConnectionPool}
// that
// * is not in use. If no {@link Connection} is available, a new
// * {@link PooledConnection} is created and the method (
// * {@link #getConnection()}) is called again.
// *
// * @return the {@link Connection}
// */
/**
* {@inheritDoc}
*/
@Override
public synchronized Connection getConnection()
{
for(final PooledConnection connection : this.pool)
{
if(connection.lease() && connection.validate())
{
this.usedConnections++;
if(LOGGER.isDebugEnabled())
{
LOGGER.debug("Took connection from pool: " + connection.toString() + " "
+ this.getPoolConnectionStats());
}
return connection;
}
}
this.reapConnections();
if(this.size <= 0 || this.pool.size() < this.size)
{
final PooledConnection connection = new PooledConnection(this.info.createConnection(), this.maxStatementsPerConnection);
connection.lease();
this.pool.add(connection);
this.usedConnections++;
if(LOGGER.isDebugEnabled())
{
LOGGER.debug("Took connection from pool: " + connection.toString() + " "
+ this.getPoolConnectionStats());
}
return connection;
}
try
{
Thread.sleep(1000);
}
catch(final InterruptedException e)
{
}
return this.getConnection();
}
private String getPoolConnectionStats()
{
return "Poolsize / used: " + this.pool.size() + " / " + this.usedConnections;
}
/**
* {@inheritDoc}
*/
@Override
public DbmsAdaptor getDbmsAdaptor()
{
return this.adaptor;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void closeConnection(Connection connection)
{
try
{
connection.close();
}
catch(final Exception e)
{
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void close()
{
for(final PooledConnection connection : this.pool)
{
connection.closeDefinite();
}
this.pool.clear();
this.usedConnections = 0;
}
/**
* @since 3.1
*/
public void dumpActiveConnectionStacks()
{
for(final PooledConnection connection : this.pool)
{
if(connection.inuse)
{
LOGGER.info(connection.toString());
LOGGER.info(connection.leaseStack);
}
}
}
private class PooledConnection implements ConnectionWrapper
{
private static final int UNLIMITED_STATEMENTS_PER_CONNECTION = 0;
Connection connection;
boolean inuse;
long initTimestamp;
String leaseStack;
StacktraceTimeOut stacktraceTimeOut;
int maxStatementsPerConnection = UNLIMITED_STATEMENTS_PER_CONNECTION;
int statementCount = 0;
PooledConnection(Connection connection, int maxStatementsPerCon)
{
this.init(connection);
this.maxStatementsPerConnection = maxStatementsPerCon;
}
private void init(final Connection connection)
{
this.connection = connection;
this.inuse = false;
this.initTimestamp = 0;
this.leaseStack = "set system property " + XdevSystemPropertyKeys.CONNECTION_DEBUG_MODE
+ " to get lease stack";
}
@Override
public String toString()
{
return super.toString() + " [in use = " + this.inuse + "]";
}
@Override
public Connection getActualConnection()
{
return this.connection;
}
/**
* {@inheritDoc}
*/
@Override
public Statement createStatement() throws SQLException
{
this.statementCount++;
return this.connection.createStatement();
}
/**
* {@inheritDoc}
*/
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException
{
this.statementCount++;
return this.connection.createStatement(resultSetType,resultSetConcurrency);
}
/**
* {@inheritDoc}
*/
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException
{
this.statementCount++;
return this.connection.createStatement(resultSetType,resultSetConcurrency,
resultSetHoldability);
}
/**
* {@inheritDoc}
*/
@Override
public CallableStatement prepareCall(String sql) throws SQLException
{
this.statementCount++;
return this.connection.prepareCall(sql);
}
/**
* {@inheritDoc}
*/
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException
{
this.statementCount++;
return this.connection.prepareCall(sql,resultSetType,resultSetConcurrency);
}
/**
* {@inheritDoc}
*/
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
this.statementCount++;
return this.connection.prepareCall(sql,resultSetType,resultSetConcurrency,
resultSetHoldability);
}
/**
* {@inheritDoc}
*/
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException
{
this.statementCount++;
return this.connection.prepareStatement(sql);
}
/**
* {@inheritDoc}
*/
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException
{
this.statementCount++;
return this.connection.prepareStatement(sql,autoGeneratedKeys);
}
/**
* {@inheritDoc}
*/
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException
{
this.statementCount++;
return this.connection.prepareStatement(sql,columnIndexes);
}
/**
* {@inheritDoc}
*/
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException
{
this.statementCount++;
return this.connection.prepareStatement(sql,columnNames);
}
/**
* {@inheritDoc}
*/
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException
{
this.statementCount++;
return this.connection.prepareStatement(sql,resultSetType,resultSetConcurrency);
}
/**
* {@inheritDoc}
*/
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
this.statementCount++;
return this.connection.prepareStatement(sql,resultSetType,resultSetConcurrency,
resultSetHoldability);
}
synchronized boolean lease()
{
if(this.inuse)
{
return false;
}
else
{
this.inuse = true;
this.initTimestamp = System.currentTimeMillis();
if(ConnectionPool.this.isConnectionDebugMode)
{
final StringBuilder sb = new StringBuilder();
for(final StackTraceElement elem : new Throwable().getStackTrace())
{
if(sb.length() > 0)
{
sb.append(IOUtils.LINE_SEPARATOR);
}
sb.append(elem.toString());
}
this.leaseStack = sb.toString();
this.stacktraceTimeOut = new StacktraceTimeOut(LOGGER,this);
ConnectionPool.this.stacktraceTimer.schedule(this.stacktraceTimeOut,ConnectionPool.this.stackTraceTimeOutDelay);
}
return true;
}
}
boolean validate()
{
if(this.maxStatementsPerConnection > UNLIMITED_STATEMENTS_PER_CONNECTION && this.statementCount > this.maxStatementsPerConnection) {
return false;
} else {
return ConnectionPool.this.info.isConnectionValid(this.connection);
}
}
@Override
public void close()
{
this.inuse = false;
if(this.stacktraceTimeOut != null)
{
this.stacktraceTimeOut.cancel();
}
ConnectionPool.this.usedConnections--;
if(!ConnectionPool.this.keepConnectionsAlive)
{
this.closeDefinite();
}
}
void closeDefinite()
{
this.inuse = false;
try
{
this.connection.close();
}
catch(final Exception e)
{
}
}
/**
* {@inheritDoc}
*/
@Override
public void clearWarnings() throws SQLException
{
this.connection.clearWarnings();
}
/**
* {@inheritDoc}
*/
@Override
public void commit() throws SQLException
{
this.connection.commit();
}
/**
* {@inheritDoc}
*/
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException
{
return this.connection.createArrayOf(typeName,elements);
}
/**
* {@inheritDoc}
*/
@Override
public Blob createBlob() throws SQLException
{
return this.connection.createBlob();
}
/**
* {@inheritDoc}
*/
@Override
public Clob createClob() throws SQLException
{
return this.connection.createClob();
}
/**
* {@inheritDoc}
*/
@Override
public NClob createNClob() throws SQLException
{
return this.connection.createNClob();
}
/**
* {@inheritDoc}
*/
@Override
public SQLXML createSQLXML() throws SQLException
{
return this.connection.createSQLXML();
}
/**
* {@inheritDoc}
*/
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException
{
return this.connection.createStruct(typeName,attributes);
}
/**
* {@inheritDoc}
*/
@Override
public boolean getAutoCommit() throws SQLException
{
return this.connection.getAutoCommit();
}
/**
* {@inheritDoc}
*/
@Override
public String getCatalog() throws SQLException
{
return this.connection.getCatalog();
}
/**
* {@inheritDoc}
*/
@Override
public Properties getClientInfo() throws SQLException
{
return this.connection.getClientInfo();
}
/**
* {@inheritDoc}
*/
@Override
public String getClientInfo(String name) throws SQLException
{
return this.connection.getClientInfo(name);
}
/**
* {@inheritDoc}
*/
@Override
public int getHoldability() throws SQLException
{
return this.connection.getHoldability();
}
/**
* {@inheritDoc}
*/
@Override
public DatabaseMetaData getMetaData() throws SQLException
{
return this.connection.getMetaData();
}
/**
* {@inheritDoc}
*/
@Override
public int getTransactionIsolation() throws SQLException
{
return this.connection.getTransactionIsolation();
}
/**
* {@inheritDoc}
*/
@Override
public Map> getTypeMap() throws SQLException
{
return this.connection.getTypeMap();
}
/**
* {@inheritDoc}
*/
@Override
public SQLWarning getWarnings() throws SQLException
{
return this.connection.getWarnings();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isClosed() throws SQLException
{
return this.connection.isClosed();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isReadOnly() throws SQLException
{
return this.connection.isReadOnly();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValid(int timeout) throws SQLException
{
return this.connection.isValid(timeout);
}
/**
* {@inheritDoc}
*/
@Override
public String nativeSQL(String sql) throws SQLException
{
return this.connection.nativeSQL(sql);
}
/**
* {@inheritDoc}
*/
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException
{
this.connection.releaseSavepoint(savepoint);
}
/**
* {@inheritDoc}
*/
@Override
public void rollback() throws SQLException
{
this.connection.rollback();
}
/**
* {@inheritDoc}
*/
@Override
public void rollback(Savepoint savepoint) throws SQLException
{
this.connection.rollback(savepoint);
}
/**
* {@inheritDoc}
*/
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException
{
this.connection.setAutoCommit(autoCommit);
}
/**
* {@inheritDoc}
*/
@Override
public void setCatalog(String catalog) throws SQLException
{
this.connection.setCatalog(catalog);
}
/**
* {@inheritDoc}
*/
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException
{
this.connection.setClientInfo(properties);
}
/**
* {@inheritDoc}
*/
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException
{
this.connection.setClientInfo(name,value);
}
/**
* {@inheritDoc}
*/
@Override
public void setHoldability(int holdability) throws SQLException
{
this.connection.setHoldability(holdability);
}
/**
* {@inheritDoc}
*/
@Override
public void setReadOnly(boolean readOnly) throws SQLException
{
this.connection.setReadOnly(readOnly);
}
/**
* {@inheritDoc}
*/
@Override
public Savepoint setSavepoint() throws SQLException
{
return this.connection.setSavepoint();
}
/**
* {@inheritDoc}
*/
@Override
public Savepoint setSavepoint(String name) throws SQLException
{
return this.connection.setSavepoint(name);
}
/**
* {@inheritDoc}
*/
@Override
public void setTransactionIsolation(int level) throws SQLException
{
this.connection.setTransactionIsolation(level);
}
/**
* {@inheritDoc}
*/
@Override
public void setTypeMap(Map> map) throws SQLException
{
this.connection.setTypeMap(map);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isWrapperFor(Class> iface) throws SQLException
{
return this.connection.isWrapperFor(iface);
}
/**
* {@inheritDoc}
*/
@Override
public T unwrap(Class iface) throws SQLException
{
return this.connection.unwrap(iface);
}
// Java 7 changes
public void setSchema(String schema) throws SQLException
{
try
{
this.connection.getClass().getMethod("setSchema",String.class).invoke(this.connection,schema);
}
catch(final Exception e)
{
throw new SQLException(e);
}
// connection.setSchema(schema);
}
public String getSchema() throws SQLException
{
try
{
return (String)this.connection.getClass().getMethod("getSchema").invoke(this.connection);
}
catch(final Exception e)
{
throw new SQLException(e);
}
// return connection.getSchema();
}
public void abort(Executor executor) throws SQLException
{
try
{
this.connection.getClass().getMethod("abort",Executor.class).invoke(this.connection,executor);
}
catch(final Exception e)
{
throw new SQLException(e);
}
// connection.abort(executor);
}
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException
{
try
{
this.connection.getClass().getMethod("setNetworkTimeout",Executor.class,int.class)
.invoke(this.connection,executor,milliseconds);
}
catch(final Exception e)
{
throw new SQLException(e);
}
// connection.setNetworkTimeout(executor,milliseconds);
}
public int getNetworkTimeout() throws SQLException
{
try
{
return (Integer)this.connection.getClass().getMethod("getNetworkTimeout")
.invoke(this.connection);
}
catch(final Exception e)
{
throw new SQLException(e);
}
// return connection.getNetworkTimeout();
}
}
private class StacktraceTimeOut extends TimerTask
{
final XdevLogger logger;
final PooledConnection connection;
public StacktraceTimeOut(final XdevLogger logger, final PooledConnection connection)
{
this.logger = logger;
this.connection = connection;
}
@Override
public void run()
{
this.logger.debug(this.connection.leaseStack);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy