#618 - Refactor - remove DataSourcePool implementation as separate dependency

This commit is contained in:
Robin Bygrave
2016-03-24 14:29:21 +13:00
parent 83d0d6320c
commit c296127bab
38 changed files with 69 additions and 6275 deletions
@@ -1,556 +0,0 @@
package com.avaje.ebean.config;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.util.StringHelper;
/**
* Used to config a DataSource when using the internal Ebean DataSource
* implementation.
* <p>
* If a DataSource instance is already defined via
* {@link ServerConfig#setDataSource(javax.sql.DataSource)} or defined as JNDI
* dataSource via {@link ServerConfig#setDataSourceJndiName(String)} then those
* will used and not this DataSourceConfig.
* </p>
*/
public class DataSourceConfig {
private String url;
private String username;
private String password;
private String driver;
private int minConnections = 2;
private int maxConnections = 20;
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
private int heartbeatTimeoutSeconds = 3;
private boolean captureStackTrace;
private int maxStackTraceSize = 5;
private int leakTimeMinutes = 30;
private int maxInactiveTimeSecs = 720;
private int maxAgeMinutes = 0;
private int trimPoolFreqSecs = 59;
private int pstmtCacheSize = 20;
private int cstmtCacheSize = 20;
private int waitTimeoutMillis = 1000;
private String poolListener;
private boolean offline;
protected Map<String, String> customProperties;
/**
* Return the connection URL.
*/
public String getUrl() {
return url;
}
/**
* Set the connection URL.
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Return the database username.
*/
public String getUsername() {
return username;
}
/**
* Set the database username.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Return the database password.
*/
public String getPassword() {
return password;
}
/**
* Set the database password.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Return the database driver.
*/
public String getDriver() {
return driver;
}
/**
* Set the database driver.
*/
public void setDriver(String driver) {
this.driver = driver;
}
/**
* Return the transaction isolation level.
*/
public int getIsolationLevel() {
return isolationLevel;
}
/**
* Set the transaction isolation level.
*/
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
/**
* Return autoCommit setting.
*/
public boolean isAutoCommit() {
return autoCommit;
}
/**
* Set to true to turn on autoCommit.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return the minimum number of connections the pool should maintain.
*/
public int getMinConnections() {
return minConnections;
}
/**
* Set the minimum number of connections the pool should maintain.
*/
public void setMinConnections(int minConnections) {
this.minConnections = minConnections;
}
/**
* Return the maximum number of connections the pool can reach.
*/
public int getMaxConnections() {
return maxConnections;
}
/**
* Set the maximum number of connections the pool can reach.
*/
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Return a SQL statement used to test the database is accessible.
* <p>
* Note that if this is not set then it can get defaulted from the
* DatabasePlatform.
* </p>
*/
public String getHeartbeatSql() {
return heartbeatSql;
}
/**
* Set a SQL statement used to test the database is accessible.
* <p>
* Note that if this is not set then it can get defaulted from the
* DatabasePlatform.
* </p>
*/
public void setHeartbeatSql(String heartbeatSql) {
this.heartbeatSql = heartbeatSql;
}
/**
* Return the heartbeat frequency in seconds.
* <p>
* This is the expected frequency in which the DataSource should be checked to
* make sure it is healthy and trim idle connections.
* </p>
*/
public int getHeartbeatFreqSecs() {
return heartbeatFreqSecs;
}
/**
* Set the expected heartbeat frequency in seconds.
*/
public void setHeartbeatFreqSecs(int heartbeatFreqSecs) {
this.heartbeatFreqSecs = heartbeatFreqSecs;
}
/**
* Return the heart beat timeout in seconds.
*/
public int getHeartbeatTimeoutSeconds() {
return heartbeatTimeoutSeconds;
}
/**
* Set the heart beat timeout in seconds.
*/
public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) {
this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds;
}
/**
* Return true if a stack trace should be captured when obtaining a connection
* from the pool.
* <p>
* This can be used to diagnose a suspected connection pool leak.
* </p>
* <p>
* Obviously this has a performance overhead.
* </p>
*/
public boolean isCaptureStackTrace() {
return captureStackTrace;
}
/**
* Set to true if a stack trace should be captured when obtaining a connection
* from the pool.
* <p>
* This can be used to diagnose a suspected connection pool leak.
* </p>
* <p>
* Obviously this has a performance overhead.
* </p>
*/
public void setCaptureStackTrace(boolean captureStackTrace) {
this.captureStackTrace = captureStackTrace;
}
/**
* Return the max size for reporting stack traces on busy connections.
*/
public int getMaxStackTraceSize() {
return maxStackTraceSize;
}
/**
* Set the max size for reporting stack traces on busy connections.
*/
public void setMaxStackTraceSize(int maxStackTraceSize) {
this.maxStackTraceSize = maxStackTraceSize;
}
/**
* Return the time in minutes after which a connection could be considered to
* have leaked.
*/
public int getLeakTimeMinutes() {
return leakTimeMinutes;
}
/**
* Set the time in minutes after which a connection could be considered to
* have leaked.
*/
public void setLeakTimeMinutes(int leakTimeMinutes) {
this.leakTimeMinutes = leakTimeMinutes;
}
/**
* Return the size of the PreparedStatement cache (per connection).
*/
public int getPstmtCacheSize() {
return pstmtCacheSize;
}
/**
* Set the size of the PreparedStatement cache (per connection).
*/
public void setPstmtCacheSize(int pstmtCacheSize) {
this.pstmtCacheSize = pstmtCacheSize;
}
/**
* Return the size of the CallableStatement cache (per connection).
*/
public int getCstmtCacheSize() {
return cstmtCacheSize;
}
/**
* Set the size of the CallableStatement cache (per connection).
*/
public void setCstmtCacheSize(int cstmtCacheSize) {
this.cstmtCacheSize = cstmtCacheSize;
}
/**
* Return the time in millis to wait for a connection before timing out once
* the pool has reached its maximum size.
*/
public int getWaitTimeoutMillis() {
return waitTimeoutMillis;
}
/**
* Set the time in millis to wait for a connection before timing out once the
* pool has reached its maximum size.
*/
public void setWaitTimeoutMillis(int waitTimeoutMillis) {
this.waitTimeoutMillis = waitTimeoutMillis;
}
/**
* Return the time in seconds a connection can be idle after which it can be
* trimmed from the pool.
* <p>
* This is so that the pool after a busy period can trend over time back
* towards the minimum connections.
* </p>
*/
public int getMaxInactiveTimeSecs() {
return maxInactiveTimeSecs;
}
/**
* Return the maximum age a connection is allowed to be before it is closed.
* <p>
* This can be used to close really old connections.
* </p>
*/
public int getMaxAgeMinutes() {
return maxAgeMinutes;
}
/**
* Set the maximum age a connection can be in minutes.
*/
public void setMaxAgeMinutes(int maxAgeMinutes) {
this.maxAgeMinutes = maxAgeMinutes;
}
/**
* Set the time in seconds a connection can be idle after which it can be
* trimmed from the pool.
* <p>
* This is so that the pool after a busy period can trend over time back
* towards the minimum connections.
* </p>
*/
public void setMaxInactiveTimeSecs(int maxInactiveTimeSecs) {
this.maxInactiveTimeSecs = maxInactiveTimeSecs;
}
/**
* Return the minimum time gap between pool trim checks.
* <p>
* This defaults to 59 seconds meaning that the pool trim check will run every
* minute assuming the heart beat check runs every 30 seconds.
* </p>
*/
public int getTrimPoolFreqSecs() {
return trimPoolFreqSecs;
}
/**
* Set the minimum trim gap between pool trim checks.
*/
public void setTrimPoolFreqSecs(int trimPoolFreqSecs) {
this.trimPoolFreqSecs = trimPoolFreqSecs;
}
/**
* Return the pool listener.
*/
public String getPoolListener() {
return poolListener;
}
/**
* Set a pool listener.
*/
public void setPoolListener(String poolListener) {
this.poolListener = poolListener;
}
/**
* Return true if the DataSource should be left offline.
* <p>
* This is to support DDL generation etc without having a real database.
* </p>
*/
public boolean isOffline() {
return offline;
}
/**
* Set to true if the DataSource should be left offline.
* <p>
* This is to support DDL generation etc without having a real database.
* </p>
* <p>
* Note that you MUST specify the database platform name (oracle, postgres,
* h2, mysql etc) using {@link ServerConfig#setDatabasePlatformName(String)}
* when you do this.
* </p>
*/
public void setOffline(boolean offline) {
this.offline = offline;
}
/**
* Return a map of custom properties for the jdbc driver connection.
*/
public Map<String, String> getCustomProperties() {
return customProperties;
}
/**
* Set custom properties for the jdbc driver connection.
*
* @param customProperties
*/
public void setCustomProperties(Map<String, String> customProperties) {
this.customProperties = customProperties;
}
/**
* Load the settings by reading the ebean.properties file.
*
* @param serverName name of the server
*/
public void loadSettings(String serverName) {
loadSettings(new PropertiesWrapper("datasource", serverName, PropertyMap.defaultProperties()));
}
/**
* Load the settings from the properties supplied.
* <p>
* You can use this when you have your own properties to use for configuration.
* </p>
*
* @param properties the properties to configure the datasource
* @param serverName the name of the specific datasource (optional)
*/
public void loadSettings(Properties properties, String serverName) {
PropertiesWrapper dbProps = new PropertiesWrapper("datasource", serverName, properties);
loadSettings(dbProps);
}
/**
* Load the settings from the PropertiesWrapper.
*/
public void loadSettings(PropertiesWrapper properties) {
username = properties.get("username", username);
password = properties.get("password", password);
driver = properties.get("driver", properties.get("databaseDriver", driver));
url = properties.get("url", properties.get("databaseUrl", url));
autoCommit = properties.getBoolean("autoCommit", autoCommit);
captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace);
maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize);
leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes);
maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs);
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
minConnections = properties.getInt("minConnections", minConnections);
maxConnections = properties.getInt("maxConnections", maxConnections);
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);
waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis);
heartbeatSql = properties.get("heartbeatSql", heartbeatSql);
heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds);
poolListener = properties.get("poolListener", poolListener);
offline = properties.getBoolean("offline", offline);
String isoLevel = properties.get("isolationlevel", getTransactionIsolationLevel(isolationLevel));
this.isolationLevel = getTransactionIsolationLevel(isoLevel);
String customProperties = properties.get("customProperties", null);
if (customProperties != null && customProperties.length() > 0) {
this.customProperties = StringHelper.delimitedToMap(customProperties, ";", "=");
}
}
/**
* Return the isolation level description from the associated Connection int value.
*/
public String getTransactionIsolationLevel(int level) {
switch (level) {
case Connection.TRANSACTION_NONE : return "NONE";
case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED";
case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED";
case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ";
case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE";
default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
/**
* Return the isolation level for a given string description.
*/
public int getTransactionIsolationLevel(String level) {
level = level.toUpperCase();
if (level.startsWith("TRANSACTION")) {
level = level.substring("TRANSACTION".length());
}
level = level.replace("_", "");
if ("NONE".equalsIgnoreCase(level)) {
return Connection.TRANSACTION_NONE;
}
if ("READCOMMITTED".equalsIgnoreCase(level)) {
return Connection.TRANSACTION_READ_COMMITTED;
}
if ("READUNCOMMITTED".equalsIgnoreCase(level)) {
return Connection.TRANSACTION_READ_UNCOMMITTED;
}
if ("REPEATABLEREAD".equalsIgnoreCase(level)) {
return Connection.TRANSACTION_REPEATABLE_READ;
}
if ("SERIALIZABLE".equalsIgnoreCase(level)) {
return Connection.TRANSACTION_SERIALIZABLE;
}
throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
@@ -22,6 +22,7 @@ import com.avaje.ebean.event.readaudit.ReadAuditLogger;
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
import com.avaje.ebean.meta.MetaInfoManager;
import com.fasterxml.jackson.core.JsonFactory;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
import java.util.ArrayList;
@@ -1391,8 +1392,6 @@ public class ServerConfig {
* <p>
* Values are oracle, h2, postgres, mysql, mssqlserver2005.
* </p>
*
* @see DataSourceConfig#setOffline(boolean)
*/
public void setDatabasePlatformName(String databasePlatformName) {
this.databasePlatformName = databasePlatformName;
@@ -2246,7 +2245,7 @@ public class ServerConfig {
* @param p - The defined property source passed to load settings
*/
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.withPrefix("datasource"));
dataSourceConfig.loadSettings(p.properties, name);
}
/**
@@ -1,254 +0,0 @@
package com.avaje.ebeaninternal.jdbc;
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.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class ConnectionDelegator implements Connection {
private final Connection delegate;
public ConnectionDelegator(Connection delegate) {
this.delegate = delegate;
}
@Override
public void setSchema(String schema) throws SQLException {
delegate.setSchema(schema);
}
@Override
public String getSchema() throws SQLException {
return delegate.getSchema();
}
@Override
public void abort(Executor executor) throws SQLException {
delegate.abort(executor);
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
delegate.setNetworkTimeout(executor, milliseconds);
}
@Override
public int getNetworkTimeout() throws SQLException {
return delegate.getNetworkTimeout();
}
public Statement createStatement() throws SQLException {
return delegate.createStatement();
}
public PreparedStatement prepareStatement(String sql) throws SQLException {
return delegate.prepareStatement(sql);
}
public CallableStatement prepareCall(String sql) throws SQLException {
return delegate.prepareCall(sql);
}
public String nativeSQL(String sql) throws SQLException {
return delegate.nativeSQL(sql);
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
delegate.setAutoCommit(autoCommit);
}
public boolean getAutoCommit() throws SQLException {
return delegate.getAutoCommit();
}
public void commit() throws SQLException {
delegate.commit();
}
public void rollback() throws SQLException {
delegate.rollback();
}
public void close() throws SQLException {
delegate.close();
}
public boolean isClosed() throws SQLException {
return delegate.isClosed();
}
public DatabaseMetaData getMetaData() throws SQLException {
return delegate.getMetaData();
}
public void setReadOnly(boolean readOnly) throws SQLException {
delegate.setReadOnly(readOnly);
}
public boolean isReadOnly() throws SQLException {
return delegate.isReadOnly();
}
public void setCatalog(String catalog) throws SQLException {
delegate.setCatalog(catalog);
}
public String getCatalog() throws SQLException {
return delegate.getCatalog();
}
public void setTransactionIsolation(int level) throws SQLException {
delegate.setTransactionIsolation(level);
}
public int getTransactionIsolation() throws SQLException {
return delegate.getTransactionIsolation();
}
public SQLWarning getWarnings() throws SQLException {
return delegate.getWarnings();
}
public void clearWarnings() throws SQLException {
delegate.clearWarnings();
}
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return delegate.createStatement(resultSetType, resultSetConcurrency);
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
}
public Map<String, Class<?>> getTypeMap() throws SQLException {
return delegate.getTypeMap();
}
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
delegate.setTypeMap(map);
}
public void setHoldability(int holdability) throws SQLException {
delegate.setHoldability(holdability);
}
public int getHoldability() throws SQLException {
return delegate.getHoldability();
}
public Savepoint setSavepoint() throws SQLException {
return delegate.setSavepoint();
}
public Savepoint setSavepoint(String name) throws SQLException {
return delegate.setSavepoint(name);
}
public void rollback(Savepoint savepoint) throws SQLException {
delegate.rollback(savepoint);
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
delegate.releaseSavepoint(savepoint);
}
public Statement createStatement(int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return delegate.prepareStatement(sql, autoGeneratedKeys);
}
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return delegate.prepareStatement(sql, columnIndexes);
}
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return delegate.prepareStatement(sql, columnNames);
}
public Clob createClob() throws SQLException {
return delegate.createClob();
}
public Blob createBlob() throws SQLException {
return delegate.createBlob();
}
public NClob createNClob() throws SQLException {
return delegate.createNClob();
}
public SQLXML createSQLXML() throws SQLException {
return delegate.createSQLXML();
}
public boolean isValid(int timeout) throws SQLException {
return delegate.isValid(timeout);
}
public void setClientInfo(String name, String value) throws SQLClientInfoException {
delegate.setClientInfo(name, value);
}
public void setClientInfo(Properties properties) throws SQLClientInfoException {
delegate.setClientInfo(properties);
}
public String getClientInfo(String name) throws SQLException {
return delegate.getClientInfo(name);
}
public Properties getClientInfo() throws SQLException {
return delegate.getClientInfo();
}
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return delegate.createArrayOf(typeName, elements);
}
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return delegate.createStruct(typeName, attributes);
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
}
}
@@ -1,435 +0,0 @@
package com.avaje.ebeaninternal.jdbc;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
public class PreparedStatementDelegator implements PreparedStatement {
private final PreparedStatement delegate;
public PreparedStatementDelegator(PreparedStatement delegate) {
this.delegate = delegate;
}
@Override
public void closeOnCompletion() throws SQLException {
delegate.closeOnCompletion();
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
return delegate.isCloseOnCompletion();
}
public ResultSet executeQuery() throws SQLException {
return delegate.executeQuery();
}
public int executeUpdate() throws SQLException {
return delegate.executeUpdate();
}
public void setNull(int parameterIndex, int sqlType) throws SQLException {
delegate.setNull(parameterIndex, sqlType);
}
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
delegate.setBoolean(parameterIndex, x);
}
public void setByte(int parameterIndex, byte x) throws SQLException {
delegate.setByte(parameterIndex, x);
}
public void setShort(int parameterIndex, short x) throws SQLException {
delegate.setShort(parameterIndex, x);
}
public void setInt(int parameterIndex, int x) throws SQLException {
delegate.setInt(parameterIndex, x);
}
public void setLong(int parameterIndex, long x) throws SQLException {
delegate.setLong(parameterIndex, x);
}
public void setFloat(int parameterIndex, float x) throws SQLException {
delegate.setFloat(parameterIndex, x);
}
public void setDouble(int parameterIndex, double x) throws SQLException {
delegate.setDouble(parameterIndex, x);
}
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
delegate.setBigDecimal(parameterIndex, x);
}
public void setString(int parameterIndex, String x) throws SQLException {
delegate.setString(parameterIndex, x);
}
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
delegate.setBytes(parameterIndex, x);
}
public void setDate(int parameterIndex, Date x) throws SQLException {
delegate.setDate(parameterIndex, x);
}
public void setTime(int parameterIndex, Time x) throws SQLException {
delegate.setTime(parameterIndex, x);
}
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
delegate.setTimestamp(parameterIndex, x);
}
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
delegate.setAsciiStream(parameterIndex, x, length);
}
@SuppressWarnings("deprecation")
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
delegate.setUnicodeStream(parameterIndex, x, length);
}
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
delegate.setBinaryStream(parameterIndex, x, length);
}
public void clearParameters() throws SQLException {
delegate.clearParameters();
}
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
delegate.setObject(parameterIndex, x, targetSqlType);
}
public void setObject(int parameterIndex, Object x) throws SQLException {
delegate.setObject(parameterIndex, x);
}
public boolean execute() throws SQLException {
return delegate.execute();
}
public void addBatch() throws SQLException {
delegate.addBatch();
}
public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
delegate.setCharacterStream(parameterIndex, reader, length);
}
public void setRef(int parameterIndex, Ref x) throws SQLException {
delegate.setRef(parameterIndex, x);
}
public void setBlob(int parameterIndex, Blob x) throws SQLException {
delegate.setBlob(parameterIndex, x);
}
public void setClob(int parameterIndex, Clob x) throws SQLException {
delegate.setClob(parameterIndex, x);
}
public void setArray(int parameterIndex, Array x) throws SQLException {
delegate.setArray(parameterIndex, x);
}
public ResultSetMetaData getMetaData() throws SQLException {
return delegate.getMetaData();
}
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
delegate.setDate(parameterIndex, x, cal);
}
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
delegate.setTime(parameterIndex, x, cal);
}
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
delegate.setTimestamp(parameterIndex, x, cal);
}
public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
delegate.setNull(parameterIndex, sqlType, typeName);
}
public void setURL(int parameterIndex, URL x) throws SQLException {
delegate.setURL(parameterIndex, x);
}
public ParameterMetaData getParameterMetaData() throws SQLException {
return delegate.getParameterMetaData();
}
public void setRowId(int parameterIndex, RowId x) throws SQLException {
delegate.setRowId(parameterIndex, x);
}
public void setNString(int parameterIndex, String value) throws SQLException {
delegate.setNString(parameterIndex, value);
}
public void setNCharacterStream(int parameterIndex, Reader value, long length)
throws SQLException {
delegate.setNCharacterStream(parameterIndex, value, length);
}
public void setNClob(int parameterIndex, NClob value) throws SQLException {
delegate.setNClob(parameterIndex, value);
}
public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
delegate.setClob(parameterIndex, reader, length);
}
public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
delegate.setBlob(parameterIndex, inputStream, length);
}
public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
delegate.setNClob(parameterIndex, reader, length);
}
public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
delegate.setSQLXML(parameterIndex, xmlObject);
}
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength)
throws SQLException {
delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
}
public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
delegate.setAsciiStream(parameterIndex, x, length);
}
public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
delegate.setBinaryStream(parameterIndex, x, length);
}
public void setCharacterStream(int parameterIndex, Reader reader, long length)
throws SQLException {
delegate.setCharacterStream(parameterIndex, reader, length);
}
public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
delegate.setAsciiStream(parameterIndex, x);
}
public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
delegate.setBinaryStream(parameterIndex, x);
}
public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
delegate.setCharacterStream(parameterIndex, reader);
}
public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
delegate.setNCharacterStream(parameterIndex, value);
}
public void setClob(int parameterIndex, Reader reader) throws SQLException {
delegate.setClob(parameterIndex, reader);
}
public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
delegate.setBlob(parameterIndex, inputStream);
}
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
delegate.setNClob(parameterIndex, reader);
}
public ResultSet executeQuery(String sql) throws SQLException {
return delegate.executeQuery(sql);
}
public int executeUpdate(String sql) throws SQLException {
return delegate.executeUpdate(sql);
}
public void close() throws SQLException {
delegate.close();
}
public int getMaxFieldSize() throws SQLException {
return delegate.getMaxFieldSize();
}
public void setMaxFieldSize(int max) throws SQLException {
delegate.setMaxFieldSize(max);
}
public int getMaxRows() throws SQLException {
return delegate.getMaxRows();
}
public void setMaxRows(int max) throws SQLException {
delegate.setMaxRows(max);
}
public void setEscapeProcessing(boolean enable) throws SQLException {
delegate.setEscapeProcessing(enable);
}
public int getQueryTimeout() throws SQLException {
return delegate.getQueryTimeout();
}
public void setQueryTimeout(int seconds) throws SQLException {
delegate.setQueryTimeout(seconds);
}
public void cancel() throws SQLException {
delegate.cancel();
}
public SQLWarning getWarnings() throws SQLException {
return delegate.getWarnings();
}
public void clearWarnings() throws SQLException {
delegate.clearWarnings();
}
public void setCursorName(String name) throws SQLException {
delegate.setCursorName(name);
}
public boolean execute(String sql) throws SQLException {
return delegate.execute(sql);
}
public ResultSet getResultSet() throws SQLException {
return delegate.getResultSet();
}
public int getUpdateCount() throws SQLException {
return delegate.getUpdateCount();
}
public boolean getMoreResults() throws SQLException {
return delegate.getMoreResults();
}
public void setFetchDirection(int direction) throws SQLException {
delegate.setFetchDirection(direction);
}
public int getFetchDirection() throws SQLException {
return delegate.getFetchDirection();
}
public void setFetchSize(int rows) throws SQLException {
delegate.setFetchSize(rows);
}
public int getFetchSize() throws SQLException {
return delegate.getFetchSize();
}
public int getResultSetConcurrency() throws SQLException {
return delegate.getResultSetConcurrency();
}
public int getResultSetType() throws SQLException {
return delegate.getResultSetType();
}
public void addBatch(String sql) throws SQLException {
delegate.addBatch(sql);
}
public void clearBatch() throws SQLException {
delegate.clearBatch();
}
public int[] executeBatch() throws SQLException {
return delegate.executeBatch();
}
public Connection getConnection() throws SQLException {
return delegate.getConnection();
}
public boolean getMoreResults(int current) throws SQLException {
return delegate.getMoreResults(current);
}
public ResultSet getGeneratedKeys() throws SQLException {
return delegate.getGeneratedKeys();
}
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return delegate.executeUpdate(sql, autoGeneratedKeys);
}
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
return delegate.executeUpdate(sql, columnIndexes);
}
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
return delegate.executeUpdate(sql, columnNames);
}
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
return delegate.execute(sql, autoGeneratedKeys);
}
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return delegate.execute(sql, columnIndexes);
}
public boolean execute(String sql, String[] columnNames) throws SQLException {
return delegate.execute(sql, columnNames);
}
public int getResultSetHoldability() throws SQLException {
return delegate.getResultSetHoldability();
}
public boolean isClosed() throws SQLException {
return delegate.isClosed();
}
public void setPoolable(boolean poolable) throws SQLException {
delegate.setPoolable(poolable);
}
public boolean isPoolable() throws SQLException {
return delegate.isPoolable();
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
}
}
@@ -6,7 +6,6 @@ import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.cache.ServerCacheOptions;
import com.avaje.ebean.common.SpiContainer;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.PropertyMap;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.UnderscoreNamingConvention;
@@ -18,10 +17,10 @@ import com.avaje.ebeaninternal.server.cache.DefaultServerCacheFactory;
import com.avaje.ebeaninternal.server.cache.DefaultServerCacheManager;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.lib.sql.DataSourceAlert;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePoolListener;
import com.avaje.ebeaninternal.server.lib.sql.SimpleDataSourceAlert;
import org.avaje.datasource.DataSourceAlertFactory;
import org.avaje.datasource.DataSourceConfig;
import org.avaje.datasource.DataSourceFactory;
import org.avaje.datasource.DataSourcePoolListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -295,18 +294,32 @@ public class DefaultContainer implements SpiContainer {
return null;
}
DataSourceAlert notify = new SimpleDataSourceAlert();
DataSourcePoolListener listener = createListener(config, dsConfig);
DataSourceFactory factory = config.service(DataSourceFactory.class);
if (factory == null) {
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
+ " Probably missing dependency to avaje-datasource?");
}
return new DataSourcePool(notify, config.getName(), dsConfig, listener);
DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class);
if (alertFactory != null) {
dsConfig.setAlert(alertFactory.createAlert());
}
attachListener(config, dsConfig);
return factory.createPool(config.getName(), dsConfig);
}
/**
* Create and return a DataSourcePoolListener if it has been specified.
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
*/
private DataSourcePoolListener createListener(ServerConfig config, DataSourceConfig dsConfig) {
String poolListener = dsConfig.getPoolListener();
return poolListener != null ? (DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener) : null;
private void attachListener(ServerConfig config, DataSourceConfig dsConfig) {
if (dsConfig.getListener() == null) {
String poolListener = dsConfig.getPoolListener();
if (poolListener != null) {
dsConfig.setListener((DataSourcePoolListener)config.getClassLoadConfig().newInstance(poolListener));
}
}
}
/**
@@ -29,7 +29,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import org.avaje.datasource.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -361,7 +361,7 @@ public class InternalConfiguration {
return true;
}
DataSource dataSource = serverConfig.getDataSource();
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).getAutoCommit();
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).isAutoCommit();
}
/**
@@ -1,199 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues;
/**
* A buffer especially designed for Busy PooledConnections.
* <p>
* All thread safety controlled externally (by PooledConnectionQueue).
* </p>
* <p>
* It has a set of 'slots' and PooledConnections know which slot they went into
* and this allows for fast addition and removal (by slotId without looping).
* The capacity will increase on demand by the 'growBy' amount.
* </p>
*
* @author rbygrave
*/
class BusyConnectionBuffer {
private static final Logger logger = LoggerFactory.getLogger(BusyConnectionBuffer.class);
private PooledConnection[] slots;
private final int growBy;
private int size;
private int pos = -1;
/**
* Create the buffer with an initial capacity and fixed growBy.
* We generally do not want the buffer to grow very often.
*
* @param capacity the initial capacity
* @param growBy the fixed amount to grow the buffer by.
*/
protected BusyConnectionBuffer(int capacity, int growBy) {
this.slots = new PooledConnection[capacity];
this.growBy = growBy;
}
/**
* We can only grow (not shrink) the capacity.
*/
protected void setCapacity(int newCapacity) {
if (newCapacity > slots.length) {
PooledConnection[] current = this.slots;
this.slots = new PooledConnection[newCapacity];
System.arraycopy(current, 0, this.slots, 0, current.length);
}
}
public String toString() {
return Arrays.toString(slots);
}
protected int getCapacity() {
return slots.length;
}
protected int size() {
return size;
}
protected boolean isEmpty() {
return size == 0;
}
protected int add(PooledConnection pc) {
if (size == slots.length) {
// grow the capacity
setCapacity(slots.length + growBy);
}
int slot = nextEmptySlot();
pc.setSlotId(slot);
slots[slot] = pc;
return ++size;
}
protected boolean remove(PooledConnection pc) {
int slotId = pc.getSlotId();
if (slots[slotId] != pc) {
PooledConnection heldBy = slots[slotId];
logger.warn("Failed to remove from slot[{}] PooledConnection[{}] - HeldBy[{}]", pc.getSlotId(), pc, heldBy);
return false;
}
slots[slotId] = null;
--size;
return true;
}
/**
* Collect the load statistics from all the busy connections.
*/
void collectStatistics(LoadValues values, boolean reset) {
for (int i = 0; i < slots.length; i++) {
if (slots[i] != null) {
values.plus(slots[i].getStatistics().getValues(reset));
}
}
}
/**
* Close connections that should be considered leaked.
*/
void closeBusyConnections(long leakTimeMinutes) {
long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes * 60000);
logger.debug("Closing busy connections using leakTimeMinutes {}", leakTimeMinutes);
for (int i = 0; i < slots.length; i++) {
if (slots[i] != null) {
//tmp.add(slots[i]);
PooledConnection pc = slots[i];
//noinspection StatementWithEmptyBody
if (pc.isLongRunning() || pc.getLastUsedTime() > olderThanTime) {
// PooledConnection has been used recently or
// expected to be longRunning so not closing...
} else {
slots[i] = null;
--size;
closeBusyConnection(pc);
}
}
}
}
private void closeBusyConnection(PooledConnection pc) {
try {
logger.warn("DataSourcePool closing busy connection? " + pc.getFullDescription());
System.out.println("CLOSING busy connection: " + pc.getFullDescription());
pc.closeConnectionFully(false);
} catch (Exception ex) {
// this should never actually happen
logger.error("Error when closing potentially leaked connection " + pc.getDescription(), ex);
}
}
/**
* Returns information describing connections that are currently being used.
*/
String getBusyConnectionInformation(boolean toLogger) {
if (toLogger) {
logger.info("Dumping [{}] busy connections: (Use datasource.xxx.capturestacktrace=true ... to get stackTraces)", size());
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < slots.length; i++) {
if (slots[i] != null) {
PooledConnection pc = slots[i];
if (toLogger) {
logger.info("Busy Connection - {}", pc.getFullDescription());
} else {
sb.append(pc.getFullDescription()).append("\r\n");
}
}
}
return sb.toString();
}
/**
* Return the position of the next empty slot.
*/
private int nextEmptySlot() {
// search forward
while (++pos < slots.length) {
if (slots[pos] == null) {
return pos;
}
}
// search from beginning
pos = -1;
while (++pos < slots.length) {
if (slots[pos] == null) {
return pos;
}
}
// not expecting this
throw new RuntimeException("No Empty Slot Found?");
}
}
@@ -1,27 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
/**
* Listener for notifications about the DataSource such as when the DataSource
* goes down, up or gets close to it's maximum size.
* <p>
* The intention is to send email notifications to an administrator (or similar)
* when these events occur on the DataSource.
* </p>
*/
public interface DataSourceAlert {
/**
* Send an alert to say the dataSource is back up.
*/
void dataSourceUp(String dataSourceName);
/**
* Send an alert to say the dataSource is down.
*/
void dataSourceDown(String dataSourceName);
/**
* Send an alert to say the dataSource is getting close to its max size.
*/
void dataSourceWarning(String subject, String msg);
}
File diff suppressed because it is too large Load Diff
@@ -1,34 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.Connection;
/**
* A {@link DataSourcePool} listener which allows you to hook on the
* borrow/return process of getting or returning connections from the pool.
* <p>
* In the configuration use the poolListener key to configure which listener to
* use.
* </p>
* <p>
* Example: datasource.ora10.poolListener=my.very.fancy.PoolListener
* </p>
* <p>
* <p>
* Notice: This listener only works if you are using the default Avaje
* {@link DataSourcePool}.
* </p>
*/
public interface DataSourcePoolListener {
/**
* Called after a connection has been retrieved from the connection pool
*/
void onAfterBorrowConnection(Connection c);
/**
* Called before a connection will be put back to the connection pool
*/
void onBeforeReturnConnection(Connection c);
}
@@ -1,86 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
/**
* Represents aggregated statistics collected from the DataSourcePool.
* <p>
* The goal is to present insight into the overload load of the DataSourcePool.
* These statistics can be collected and reported regularly to show load over
* time.
* </p>
* <p>
* Each pooled connection collects statistics. When a pooled connection is fully
* closed it can report it's statistics to the pool to be included as part of
* the collected statistics.
* </p>
*/
public class DataSourcePoolStatistics {
private final long collectionStart;
private final long count;
private final long errorCount;
private final long hwmMicros;
private final long totalMicros;
/**
* Construct with statistics collected.
*/
DataSourcePoolStatistics(long collectionStart, long count, long errorCount, long hwmMicros, long totalMicros) {
this.collectionStart = collectionStart;
this.count = count;
this.errorCount = errorCount;
this.hwmMicros = hwmMicros;
this.totalMicros = totalMicros;
}
public String toString() {
return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + totalMicros + "] hwmMicros[" + hwmMicros
+ "] avgMicros[" + getAvgMicros() + "]";
}
/**
* Return the start time this set of statistics was collected from.
*/
public long getCollectionStart() {
return collectionStart;
}
/**
* Return the total number of 'get connection' requests.
*/
public long getCount() {
return count;
}
/**
* Return the number of SQLExceptions reported.
*/
public long getErrorCount() {
return errorCount;
}
/**
* Return the high water mark for the duration a connection was busy/used.
*/
public long getHwmMicros() {
return hwmMicros;
}
/**
* Return the aggregate time connections were busy/used.
*/
public long getTotalMicros() {
return totalMicros;
}
/**
* Return the average time connections were busy/used.
*/
public long getAvgMicros() {
return (totalMicros == 0) ? 0 : totalMicros / count;
}
}
@@ -1,386 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
/**
* Extended PreparedStatement that supports caching.
* <p>
* Designed so that it can be cached by the PooledConnection. It additionally
* notes any Exceptions that occur and this is used to ensure bad connections
* are removed from the connection pool.
* </p>
*/
public class ExtendedPreparedStatement extends ExtendedStatement implements PreparedStatement {
/**
* The SQL used to create the underlying PreparedStatement.
*/
private final String sql;
/**
* The key used to cache this in the connection.
*/
private final String cacheKey;
/**
* Create a wrapped PreparedStatement that can be cached.
*/
public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, String sql, String cacheKey) {
super(pooledConnection, pstmt);
this.sql = sql;
this.cacheKey = cacheKey;
}
/**
* Return the key used to cache this on the Connection.
*/
String getCacheKey() {
return cacheKey;
}
/**
* Return the SQL used to create this PreparedStatement.
*/
public String getSql() {
return sql;
}
/**
* Fully close the underlying PreparedStatement. After this we can no longer
* reuse the PreparedStatement.
*/
void closeDestroy() throws SQLException {
pstmt.close();
}
/**
* Returns the PreparedStatement back into the cache. This doesn't fully
* close the underlying PreparedStatement.
*/
public void close() throws SQLException {
// return the connection back into the cache.
pooledConnection.returnPreparedStatement(this);
}
/**
* Add the last binding for batch execution.
*/
public void addBatch() throws SQLException {
try {
pstmt.addBatch();
} catch (SQLException e) {
// we got an error... need to check this
// connection before returning it
pooledConnection.markWithError();
throw e;
}
}
/**
* Clear parameters.
*/
public void clearParameters() throws SQLException {
try {
pstmt.clearParameters();
} catch (SQLException e) {
// we got an error... need to check
// this connection before returning it
pooledConnection.markWithError();
throw e;
}
}
/**
* execute the statement.
*/
public boolean execute() throws SQLException {
try {
return pstmt.execute();
} catch (SQLException e) {
// we got an error... need to check
// this connection before returning it
pooledConnection.markWithError();
throw e;
}
}
/**
* Execute teh query.
*/
public ResultSet executeQuery() throws SQLException {
try {
return pstmt.executeQuery();
} catch (SQLException e) {
// we got an error... need to check
// this connection before returning it
pooledConnection.markWithError();
throw e;
}
}
/**
* Execute the dml statement.
*/
public int executeUpdate() throws SQLException {
try {
return pstmt.executeUpdate();
} catch (SQLException e) {
// we got an error... need to check
// this connection before returning it
pooledConnection.markWithError();
throw e;
}
}
/**
* Return the MetaData for the query.
*/
public ResultSetMetaData getMetaData() throws SQLException {
try {
return pstmt.getMetaData();
} catch (SQLException e) {
// we got an error... need to check
// this connection before returning it
pooledConnection.markWithError();
throw e;
}
}
/**
* Standard PreparedStatement method execution.
*/
public ParameterMetaData getParameterMetaData() throws SQLException {
return pstmt.getParameterMetaData();
}
/**
* Standard PreparedStatement method execution.
*/
public void setArray(int i, Array x) throws SQLException {
pstmt.setArray(i, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
pstmt.setAsciiStream(parameterIndex, x, length);
}
/**
* Standard PreparedStatement method execution.
*/
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
pstmt.setBigDecimal(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
pstmt.setBinaryStream(parameterIndex, x, length);
}
/**
* Standard PreparedStatement method execution.
*/
public void setBlob(int i, Blob x) throws SQLException {
pstmt.setBlob(i, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
pstmt.setBoolean(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setByte(int parameterIndex, byte x) throws SQLException {
pstmt.setByte(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
pstmt.setBytes(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setCharacterStream(int parameterIndex, Reader reader, int length)
throws SQLException {
pstmt.setCharacterStream(parameterIndex, reader, length);
}
/**
* Standard PreparedStatement method execution.
*/
public void setClob(int i, Clob x) throws SQLException {
pstmt.setClob(i, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setDate(int parameterIndex, Date x) throws SQLException {
pstmt.setDate(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
pstmt.setDate(parameterIndex, x, cal);
}
/**
* Standard PreparedStatement method execution.
*/
public void setDouble(int parameterIndex, double x) throws SQLException {
pstmt.setDouble(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setFloat(int parameterIndex, float x) throws SQLException {
pstmt.setFloat(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setInt(int parameterIndex, int x) throws SQLException {
pstmt.setInt(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setLong(int parameterIndex, long x) throws SQLException {
pstmt.setLong(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setNull(int parameterIndex, int sqlType) throws SQLException {
pstmt.setNull(parameterIndex, sqlType);
}
/**
* Standard PreparedStatement method execution.
*/
public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException {
pstmt.setNull(paramIndex, sqlType, typeName);
}
/**
* Standard PreparedStatement method execution.
*/
public void setObject(int parameterIndex, Object x) throws SQLException {
pstmt.setObject(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
pstmt.setObject(parameterIndex, x, targetSqlType);
}
/**
* Standard PreparedStatement method execution.
*/
public void setObject(int parameterIndex, Object x, int targetSqlType, int scale)
throws SQLException {
pstmt.setObject(parameterIndex, x, targetSqlType, scale);
}
/**
* Standard PreparedStatement method execution.
*/
public void setRef(int i, Ref x) throws SQLException {
pstmt.setRef(i, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setShort(int parameterIndex, short x) throws SQLException {
pstmt.setShort(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setString(int parameterIndex, String x) throws SQLException {
pstmt.setString(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setTime(int parameterIndex, Time x) throws SQLException {
pstmt.setTime(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
pstmt.setTime(parameterIndex, x, cal);
}
/**
* Standard PreparedStatement method execution.
*/
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
pstmt.setTimestamp(parameterIndex, x);
}
/**
* Standard PreparedStatement method execution.
*/
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
pstmt.setTimestamp(parameterIndex, x, cal);
}
/**
* Standard PreparedStatement method execution.
*
* @deprecated
*/
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
pstmt.setUnicodeStream(parameterIndex, x, length);
}
/**
* Standard PreparedStatement method execution.
*/
public void setURL(int parameterIndex, URL x) throws SQLException {
pstmt.setURL(parameterIndex, x);
}
}
@@ -1,327 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import com.avaje.ebeaninternal.jdbc.PreparedStatementDelegator;
/**
* Implements the Statement methods for ExtendedPreparedStatement.
* <p>
* PreparedStatements should always be used and the intention is that there
* should be no use of Statement at all. The implementation here is generally
* for the case where someone uses the Statement api on an ExtendedPreparedStatement.
* </p>
*/
abstract class ExtendedStatement extends PreparedStatementDelegator {
/**
* The pooled connection this Statement belongs to.
*/
final PooledConnection pooledConnection;
/**
* The underlying Statement that this object wraps.
*/
protected final PreparedStatement pstmt;
/**
* Create the ExtendedStatement for a given pooledConnection.
*/
ExtendedStatement(PooledConnection pooledConnection, PreparedStatement pstmt) {
super(pstmt);
this.pooledConnection = pooledConnection;
this.pstmt = pstmt;
}
/**
* Put the statement back into the statement cache.
*/
public abstract void close() throws SQLException;
/**
* Return the underlying connection.
*/
public Connection getConnection() throws SQLException {
try {
return pstmt.getConnection();
} catch (SQLException e) {
pooledConnection.markWithError();
throw e;
}
}
/**
* Add the sql for batch execution.
*/
public void addBatch(String sql) throws SQLException {
try {
pooledConnection.setLastStatement(sql);
pstmt.addBatch(sql);
} catch (SQLException e) {
pooledConnection.markWithError();
throw e;
}
}
/**
* Execute the sql.
*/
public boolean execute(String sql) throws SQLException {
try {
pooledConnection.setLastStatement(sql);
return pstmt.execute(sql);
} catch (SQLException e) {
pooledConnection.markWithError();
throw e;
}
}
/**
* Execute the query.
*/
public ResultSet executeQuery(String sql) throws SQLException {
try {
pooledConnection.setLastStatement(sql);
return pstmt.executeQuery(sql);
} catch (SQLException e) {
pooledConnection.markWithError();
throw e;
}
}
/**
* Execute the dml sql.
*/
public int executeUpdate(String sql) throws SQLException {
try {
pooledConnection.setLastStatement(sql);
return pstmt.executeUpdate(sql);
} catch (SQLException e) {
pooledConnection.markWithError();
throw e;
}
}
/**
* Standard Statement method call.
*/
public int[] executeBatch() throws SQLException {
return pstmt.executeBatch();
}
/**
* Standard Statement method call.
*/
public void cancel() throws SQLException {
pstmt.cancel();
}
/**
* Standard Statement method call.
*/
public void clearBatch() throws SQLException {
pstmt.clearBatch();
}
/**
* Standard Statement method call.
*/
public void clearWarnings() throws SQLException {
pstmt.clearWarnings();
}
/**
* Standard Statement method call.
*/
public int getFetchDirection() throws SQLException {
return pstmt.getFetchDirection();
}
/**
* Standard Statement method call.
*/
public int getFetchSize() throws SQLException {
return pstmt.getFetchSize();
}
/**
* Standard Statement method call.
*/
public int getMaxFieldSize() throws SQLException {
return pstmt.getMaxFieldSize();
}
/**
* Standard Statement method call.
*/
public int getMaxRows() throws SQLException {
return pstmt.getMaxRows();
}
/**
* Standard Statement method call.
*/
public boolean getMoreResults() throws SQLException {
return pstmt.getMoreResults();
}
/**
* Standard Statement method call.
*/
public int getQueryTimeout() throws SQLException {
return pstmt.getQueryTimeout();
}
/**
* Standard Statement method call.
*/
public ResultSet getResultSet() throws SQLException {
return pstmt.getResultSet();
}
/**
* Standard Statement method call.
*/
public int getResultSetConcurrency() throws SQLException {
return pstmt.getResultSetConcurrency();
}
/**
* Standard Statement method call.
*/
public int getResultSetType() throws SQLException {
return pstmt.getResultSetType();
}
/**
* Standard Statement method call.
*/
public int getUpdateCount() throws SQLException {
return pstmt.getUpdateCount();
}
/**
* Standard Statement method call.
*/
public SQLWarning getWarnings() throws SQLException {
return pstmt.getWarnings();
}
/**
* Standard Statement method call.
*/
public void setCursorName(String name) throws SQLException {
pstmt.setCursorName(name);
}
/**
* Standard Statement method call.
*/
public void setEscapeProcessing(boolean enable) throws SQLException {
pstmt.setEscapeProcessing(enable);
}
/**
* Standard Statement method call.
*/
public void setFetchDirection(int direction) throws SQLException {
pstmt.setFetchDirection(direction);
}
/**
* Standard Statement method call.
*/
public void setFetchSize(int rows) throws SQLException {
pstmt.setFetchSize(rows);
}
/**
* Standard Statement method call.
*/
public void setMaxFieldSize(int max) throws SQLException {
pstmt.setMaxFieldSize(max);
}
/**
* Standard Statement method call.
*/
public void setMaxRows(int max) throws SQLException {
pstmt.setMaxRows(max);
}
/**
* Standard Statement method call.
*/
public void setQueryTimeout(int seconds) throws SQLException {
pstmt.setQueryTimeout(seconds);
}
/**
* Standard Statement method call.
*/
public boolean getMoreResults(int i) throws SQLException {
return pstmt.getMoreResults(i);
}
/**
* Standard Statement method call.
*/
public ResultSet getGeneratedKeys() throws SQLException {
return pstmt.getGeneratedKeys();
}
/**
* Standard Statement method call.
*/
public int executeUpdate(String s, int i) throws SQLException {
return pstmt.executeUpdate(s, i);
}
/**
* Standard Statement method call.
*/
public int executeUpdate(String s, int[] i) throws SQLException {
return pstmt.executeUpdate(s, i);
}
/**
* Standard Statement method call.
*/
public int executeUpdate(String s, String[] i) throws SQLException {
return pstmt.executeUpdate(s, i);
}
/**
* Standard Statement method call.
*/
public boolean execute(String s, int i) throws SQLException {
return pstmt.execute(s, i);
}
/**
* Standard Statement method call.
*/
public boolean execute(String s, int[] i) throws SQLException {
return pstmt.execute(s, i);
}
/**
* Standard Statement method call.
*/
public boolean execute(String s, String[] i) throws SQLException {
return pstmt.execute(s, i);
}
/**
* Standard Statement method call.
*/
public int getResultSetHoldability() throws SQLException {
return pstmt.getResultSetHoldability();
}
}
@@ -1,106 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues;
/**
* A buffer designed especially to hold free pooled connections.
* <p>
* All thread safety controlled externally (by PooledConnectionQueue).
* </p>
*/
class FreeConnectionBuffer {
private static final Logger logger = LoggerFactory.getLogger(FreeConnectionBuffer.class);
/**
* Buffer oriented for add and remove.
*/
private final LinkedList<PooledConnection> freeBuffer = new LinkedList<PooledConnection>();
FreeConnectionBuffer() {
}
protected int size() {
return freeBuffer.size();
}
protected boolean isEmpty() {
return freeBuffer.isEmpty();
}
/**
* Add connection to the free list.
*/
protected void add(PooledConnection pc) {
freeBuffer.addLast(pc);
}
/**
* Remove a connection from the free list.
*/
protected PooledConnection remove() {
return freeBuffer.removeFirst();
}
/**
* Close all connections in this buffer.
*/
void closeAll(boolean logErrors) {
// create a temporary list
List<PooledConnection> tempList = new ArrayList<PooledConnection>(freeBuffer.size());
// add all the connections into it
for (PooledConnection c : freeBuffer) {
tempList.add(c);
}
// clear the buffer (in case it takes some time to close these connections).
freeBuffer.clear();
logger.debug("... closing all {} connections from the free list with logErrors: {}", tempList.size(), logErrors);
for (int i = 0; i < tempList.size(); i++) {
PooledConnection pooledConnection = tempList.get(i);
logger.debug("... closing {} of {} connections from the free list", i, tempList.size());
pooledConnection.closeConnectionFully(logErrors);
}
}
/**
* Trim any inactive connections that have not been used since usedSince.
*/
protected int trim(long usedSince, long createdSince) {
int trimCount = 0;
Iterator<PooledConnection> iterator = freeBuffer.iterator();
while (iterator.hasNext()) {
PooledConnection pooledConnection = iterator.next();
if (pooledConnection.shouldTrim(usedSince, createdSince)) {
iterator.remove();
pooledConnection.closeConnectionFully(true);
trimCount++;
}
}
return trimCount;
}
/**
* Collect the load statistics from all the free connections.
*/
void collectStatistics(LoadValues values, boolean reset) {
for (PooledConnection c : freeBuffer) {
values.plus(c.getStatistics().getValues(reset));
}
}
}
@@ -1,970 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.jdbc.ConnectionDelegator;
/**
* Is a connection that belongs to a DataSourcePool.
* <p/>
* <p>
* It is designed to be part of DataSourcePool. Closing the connection puts it
* back into the pool.
* </p>
* <p/>
* <p>
* It defaults autoCommit and Transaction Isolation to the defaults of the
* DataSourcePool.
* </p>
* <p/>
* <p>
* It has caching of Statements and PreparedStatements. Remembers the last
* statement that was executed. Keeps statistics on how long it is in use.
* </p>
*/
public class PooledConnection extends ConnectionDelegator {
private static final Logger logger = LoggerFactory.getLogger(PooledConnection.class);
private static final String IDLE_CONNECTION_ACCESSED_ERROR = "Pooled Connection has been accessed whilst idle in the pool, via method: ";
/**
* Marker for when connection is closed due to exceeding the max allowed age.
*/
private static final String REASON_MAXAGE = "maxAge";
/**
* Marker for when connection is closed due to exceeding the max inactive time.
*/
private static final String REASON_IDLE = "idleTime";
/**
* Marker for when the connection is closed due to a reset.
*/
private static final String REASON_RESET = "reset";
/**
* Set when connection is idle in the pool. In general when in the pool the
* connection should not be modified.
*/
private static final int STATUS_IDLE = 88;
/**
* Set when connection given to client.
*/
private static final int STATUS_ACTIVE = 89;
/**
* Set when commit() or rollback() called.
*/
private static final int STATUS_ENDED = 87;
/**
* Name used to identify the PooledConnection for logging.
*/
private final String name;
/**
* The pool this connection belongs to.
*/
private final DataSourcePool pool;
/**
* The underlying connection.
*/
private final Connection connection;
/**
* The time this connection was created.
*/
private final long creationTime;
/**
* Cache of the PreparedStatements
*/
private final PstmtCache pstmtCache;
private final Object pstmtMonitor = new Object();
/**
* Helper for statistics collection.
*/
private final PooledConnectionStatistics stats = new PooledConnectionStatistics();
/**
* The status of the connection. IDLE, ACTIVE or ENDED.
*/
private int status = STATUS_IDLE;
/**
* The reason for a connection closing.
*/
private String closeReason;
/**
* Set this to true if the connection will be busy for a long time.
* <p>
* This means it should skip the suspected connection pool leak checking.
* </p>
*/
private boolean longRunning;
/**
* Flag to indicate that this connection had errors and should be checked to
* make sure it is okay.
*/
private boolean hadErrors;
/**
* The last start time. When the connection was given to a thread.
*/
private long startUseTime;
/**
* The last end time of this connection. This is to calculate the usage
* time.
*/
private long lastUseTime;
private long exeStartNanos;
/**
* The last statement executed by this connection.
*/
private String lastStatement;
/**
* The non avaje method that created the connection.
*/
private String createdByMethod;
/**
* Used to find connection pool leaks.
*/
private StackTraceElement[] stackTrace;
private final int maxStackTrace;
/**
* Slot position in the BusyConnectionBuffer.
*/
private int slotId;
private boolean resetIsolationReadOnlyRequired;
/**
* Construct the connection that can refer back to the pool it belongs to.
* <p>
* close() will return the connection back to the pool , while
* closeDestroy() will close() the underlining connection properly.
* </p>
*/
public PooledConnection(DataSourcePool pool, int uniqueId, Connection connection) {
super(connection);
this.pool = pool;
this.connection = connection;
this.name = pool.getName() + "." + uniqueId;
this.pstmtCache = new PstmtCache(pool.getPstmtCacheSize());
this.maxStackTrace = pool.getMaxStackTraceSize();
this.creationTime = System.currentTimeMillis();
this.lastUseTime = creationTime;
}
/**
* For testing the pool without real connections.
*/
protected PooledConnection(String name) {
super(null);
this.name = name;
this.pool = null;
this.connection = null;
this.pstmtCache = null;
this.maxStackTrace = 0;
this.creationTime = System.currentTimeMillis();
this.lastUseTime = creationTime;
}
/**
* Return the slot position in the busy buffer.
*/
int getSlotId() {
return slotId;
}
/**
* Set the slot position in the busy buffer.
*/
void setSlotId(int slotId) {
this.slotId = slotId;
}
/**
* Return a string to identify the connection.
*/
public String getName() {
return name;
}
private String getNameSlot() {
return name + ":" + slotId;
}
public String toString() {
return getDescription();
}
private long getBusySeconds() {
return (System.currentTimeMillis() - startUseTime) / 1000;
}
public String getDescription() {
return "name[" + name + "] slot[" + slotId + "] startTime[" + getStartUseTime() + "] busySeconds[" + getBusySeconds() + "] createdBy[" + getCreatedByMethod() + "] stmt[" + getLastStatement() + "]";
}
String getFullDescription() {
return "name[" + name + "] slot[" + slotId + "] startTime[" + getStartUseTime() + "] busySeconds[" + getBusySeconds() + "] stackTrace[" + getStackTraceAsString() + "] stmt[" + getLastStatement() + "]";
}
public PooledConnectionStatistics getStatistics() {
return stats;
}
/**
* Return true if the connection should be treated as long running (skip connection pool leak check).
*/
boolean isLongRunning() {
return longRunning;
}
/**
* Set this to true if the connection is a long running connection and should skip the
* 'suspected connection pool leak' checking.
*/
public void setLongRunning(boolean longRunning) {
this.longRunning = longRunning;
}
/**
* Close the connection fully NOT putting in back into the pool.
* <p>
* The logErrors parameter exists so that expected errors are not logged
* such as when the database is known to be down.
* </p>
*
* @param logErrors if false then don't log errors when closing
*/
void closeConnectionFully(boolean logErrors) {
if (pool != null) {
// allow collection of load statistics
pool.reportClosingConnection(this);
}
if (logger.isDebugEnabled()) {
logger.debug("Closing Connection[{}] slot[{}] reason[{}] stats: {} , pstmtStats: {} ", name, slotId, closeReason, stats.getValues(false), pstmtCache.getDescription());
}
try {
if (connection.isClosed()) {
// Typically the JDBC Driver has its own JVM shutdown hook and already
// closed the connections in our DataSource pool so making this DEBUG level
logger.debug("Closing Connection[{}] that is already closed?", name);
return;
}
} catch (SQLException ex) {
if (logErrors) {
logger.error("Error checking if connection [" + getNameSlot() + "] is closed", ex);
}
}
try {
for (ExtendedPreparedStatement ps : pstmtCache.values()) {
ps.closeDestroy();
}
} catch (SQLException ex) {
if (logErrors) {
logger.warn("Error when closing connection Statements", ex);
}
}
try {
connection.close();
} catch (SQLException ex) {
if (logErrors || logger.isDebugEnabled()) {
logger.error("Error when fully closing connection [" + getFullDescription() + "]", ex);
}
}
}
/**
* Creates a wrapper ExtendedStatement so that I can get the executed sql. I
* want to do this so that I can get the slowest query statments etc, and
* log that information.
*/
public Statement createStatement() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()");
}
try {
return connection.createStatement();
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public Statement createStatement(int resultSetType, int resultSetConcurreny) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()");
}
try {
return connection.createStatement(resultSetType, resultSetConcurreny);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
/**
* Return a PreparedStatement back into the cache.
*/
void returnPreparedStatement(ExtendedPreparedStatement pstmt) {
synchronized (pstmtMonitor) {
if (!pstmtCache.returnStatement(pstmt)) {
try {
// Already an entry in the cache with the exact same SQL...
pstmt.closeDestroy();
} catch (SQLException e) {
logger.error("Error closing Pstmt", e);
}
}
}
}
/**
* This will try to use a cache of PreparedStatements.
*/
public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws SQLException {
String cacheKey = sql + returnKeysFlag;
return prepareStatement(sql, true, returnKeysFlag, cacheKey);
}
/**
* This will try to use a cache of PreparedStatements.
*/
public PreparedStatement prepareStatement(String sql) throws SQLException {
return prepareStatement(sql, false, 0, sql);
}
/**
* This will try to use a cache of PreparedStatements.
*/
private PreparedStatement prepareStatement(String sql, boolean useFlag, int flag, String cacheKey) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()");
}
try {
synchronized (pstmtMonitor) {
lastStatement = sql;
// try to get a matching cached PStmt from the cache.
ExtendedPreparedStatement pstmt = pstmtCache.remove(cacheKey);
if (pstmt != null) {
return pstmt;
}
// create a new PreparedStatement
PreparedStatement actualPstmt;
if (useFlag) {
actualPstmt = connection.prepareStatement(sql, flag);
} else {
actualPstmt = connection.prepareStatement(sql);
}
return new ExtendedPreparedStatement(this, actualPstmt, sql, cacheKey);
}
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurreny) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()");
}
try {
// no caching when creating PreparedStatements this way
lastStatement = sql;
return connection.prepareStatement(sql, resultSetType, resultSetConcurreny);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
/**
* Reset the connection for returning to the client. Resets the status,
* startUseTime and hadErrors.
*/
void resetForUse() {
this.status = STATUS_ACTIVE;
this.startUseTime = System.currentTimeMillis();
this.exeStartNanos = System.nanoTime();
this.createdByMethod = null;
this.lastStatement = null;
this.hadErrors = false;
this.longRunning = false;
}
/**
* When an error occurs during use add it the connection.
* <p>
* Any PooledConnection that has an error is checked to make sure it works
* before it is placed back into the connection pool.
* </p>
*/
void markWithError() {
hadErrors = true;
}
/**
* close the connection putting it back into the connection pool.
* <p>
* Note that to ensure that the next transaction starts at the correct time
* a commit() or rollback() should be called. If neither has occured at this
* time then a rollback() is used (to end the transaction).
* </p>
* <p>
* To close the connection fully use closeConnectionFully().
* </p>
*/
public void close() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "close()");
}
long durationNanos = System.nanoTime() - exeStartNanos;
stats.add(durationNanos, hadErrors);
if (hadErrors) {
if (!pool.validateConnection(this)) {
// the connection is BAD, remove it, close it and test the pool
pool.returnConnectionForceClose(this);
return;
}
}
try {
// reset the autoCommit back if client code changed it
if (connection.getAutoCommit() != pool.getAutoCommit()) {
connection.setAutoCommit(pool.getAutoCommit());
}
// Generally resetting Isolation level seems expensive.
// Hence using resetIsolationReadOnlyRequired flag
// performance reasons.
if (resetIsolationReadOnlyRequired) {
resetIsolationReadOnly();
resetIsolationReadOnlyRequired = false;
}
// the connection is assumed GOOD so put it back in the pool
lastUseTime = System.currentTimeMillis();
// connection.clearWarnings();
status = STATUS_IDLE;
pool.returnConnection(this);
} catch (Exception ex) {
// the connection is BAD, remove it, close it and test the pool
logger.warn("Error when trying to return connection to pool, closing fully.", ex);
pool.returnConnectionForceClose(this);
}
}
private void resetIsolationReadOnly() throws SQLException {
// reset the transaction isolation if the client code changed it
//noinspection MagicConstant
if (connection.getTransactionIsolation() != pool.getTransactionIsolation()) {
//noinspection MagicConstant
connection.setTransactionIsolation(pool.getTransactionIsolation());
}
// reset readonly to false
if (connection.isReadOnly()) {
connection.setReadOnly(false);
}
}
protected void finalize() throws Throwable {
try {
if (connection != null && !connection.isClosed()) {
// connect leak?
logger.warn("Closing Connection on finalize() - {}", getFullDescription());
closeConnectionFully(false);
}
} catch (Exception e) {
logger.error("Error when finalize is closing a connection? (unexpected)", e);
}
super.finalize();
}
/**
* Return true if the connection is too old.
*/
private boolean exceedsMaxAge(long maxAgeMillis) {
if (maxAgeMillis > 0 && (creationTime < (System.currentTimeMillis() - maxAgeMillis))) {
this.closeReason = REASON_MAXAGE;
return true;
}
return false;
}
boolean shouldTrimOnReturn(long lastResetTime, long maxAgeMillis) {
if (creationTime <= lastResetTime) {
this.closeReason = REASON_RESET;
return true;
}
return exceedsMaxAge(maxAgeMillis);
}
/**
* Return true if the connection has been idle for too long or is too old.
*/
boolean shouldTrim(long usedSince, long createdSince) {
if (lastUseTime < usedSince) {
// been idle for too long so trim it
this.closeReason = REASON_IDLE;
return true;
}
if (createdSince > 0 && createdSince > creationTime) {
// exceeds max age so trim it
this.closeReason = REASON_MAXAGE;
return true;
}
return false;
}
/**
* Return the time the connection was passed to the client code.
* <p>
* Used to detect busy connections that could be leaks.
* </p>
*/
private long getStartUseTime() {
return startUseTime;
}
/**
* Returns the time the connection was last used.
* <p>
* Used to close connections that have been idle for some time. Typically 5
* minutes.
* </p>
*/
long getLastUsedTime() {
return lastUseTime;
}
/**
* Returns the last sql statement executed.
*/
private String getLastStatement() {
return lastStatement;
}
/**
* Called by ExtendedStatement to trace the sql being executed.
* <p>
* Note with addBatch() this will not really work.
* </p>
*/
void setLastStatement(String lastStatement) {
this.lastStatement = lastStatement;
if (logger.isTraceEnabled()) {
logger.trace(".setLastStatement[" + lastStatement + "]");
}
}
/**
* Also note the read only status needs to be reset when put back into the
* pool.
*/
public void setReadOnly(boolean readOnly) throws SQLException {
// A bit loose not checking for STATUS_IDLE
// if (status == STATUS_IDLE) {
// throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR +
// "setReadOnly()");
// }
resetIsolationReadOnlyRequired = true;
connection.setReadOnly(readOnly);
}
/**
* Also note the Isolation level needs to be reset when put back into the
* pool.
*/
public void setTransactionIsolation(int level) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTransactionIsolation()");
}
try {
resetIsolationReadOnlyRequired = true;
connection.setTransactionIsolation(level);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
//
//
// Simple wrapper methods which pass a method call onto the acutal
// connection object. These methods are safe-guarded to prevent use of
// the methods whilst the connection is in the connection pool.
//
//
public void clearWarnings() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()");
}
connection.clearWarnings();
}
public void commit() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()");
}
try {
status = STATUS_ENDED;
connection.commit();
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public boolean getAutoCommit() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()");
}
return connection.getAutoCommit();
}
public String getCatalog() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()");
}
return connection.getCatalog();
}
public DatabaseMetaData getMetaData() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()");
}
return connection.getMetaData();
}
public int getTransactionIsolation() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTransactionIsolation()");
}
return connection.getTransactionIsolation();
}
public Map<String, Class<?>> getTypeMap() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTypeMap()");
}
return connection.getTypeMap();
}
public SQLWarning getWarnings() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getWarnings()");
}
return connection.getWarnings();
}
public boolean isClosed() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isClosed()");
}
return connection.isClosed();
}
public boolean isReadOnly() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isReadOnly()");
}
return connection.isReadOnly();
}
public String nativeSQL(String sql) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "nativeSQL()");
}
lastStatement = sql;
return connection.nativeSQL(sql);
}
public CallableStatement prepareCall(String sql) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()");
}
lastStatement = sql;
return connection.prepareCall(sql);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurreny) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()");
}
lastStatement = sql;
return connection.prepareCall(sql, resultSetType, resultSetConcurreny);
}
public void rollback() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "rollback()");
}
try {
status = STATUS_ENDED;
connection.rollback();
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setAutoCommit()");
}
try {
connection.setAutoCommit(autoCommit);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public void setCatalog(String catalog) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()");
}
connection.setCatalog(catalog);
}
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTypeMap()");
}
connection.setTypeMap(map);
}
public Savepoint setSavepoint() throws SQLException {
try {
return connection.setSavepoint();
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public Savepoint setSavepoint(String savepointName) throws SQLException {
try {
return connection.setSavepoint(savepointName);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public void rollback(Savepoint sp) throws SQLException {
try {
connection.rollback(sp);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public void releaseSavepoint(Savepoint sp) throws SQLException {
try {
connection.releaseSavepoint(sp);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public void setHoldability(int i) throws SQLException {
try {
connection.setHoldability(i);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public int getHoldability() throws SQLException {
try {
return connection.getHoldability();
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public Statement createStatement(int i, int x, int y) throws SQLException {
try {
return connection.createStatement(i, x, y);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public PreparedStatement prepareStatement(String s, int i, int x, int y) throws SQLException {
try {
return connection.prepareStatement(s, i, x, y);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public PreparedStatement prepareStatement(String s, int[] i) throws SQLException {
try {
return connection.prepareStatement(s, i);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public PreparedStatement prepareStatement(String s, String[] s2) throws SQLException {
try {
return connection.prepareStatement(s, s2);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
public CallableStatement prepareCall(String s, int i, int x, int y) throws SQLException {
try {
return connection.prepareCall(s, i, x, y);
} catch (SQLException ex) {
markWithError();
throw ex;
}
}
/**
* Returns the method that created the connection.
* <p>
* Used to help finding connection pool leaks.
* </p>
*/
private String getCreatedByMethod() {
if (createdByMethod != null) {
return createdByMethod;
}
if (stackTrace == null) {
return null;
}
for (int j = 0; j < stackTrace.length; j++) {
String methodLine = stackTrace[j].toString();
if (!skipElement(methodLine)) {
createdByMethod = methodLine;
return createdByMethod;
}
}
return null;
}
private boolean skipElement(String methodLine) {
if (methodLine.startsWith("java.lang.")) {
return true;
} else if (methodLine.startsWith("java.util.")) {
return true;
} else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.CallableQuery.<init>")) {
// creating connection on future...
return true;
} else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.Callable")) {
// it is a future task being executed...
return false;
} else {
return methodLine.startsWith("com.avaje.ebeaninternal");
}
}
/**
* Set the stack trace to help find connection pool leaks.
*/
protected void setStackTrace(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
}
/**
* Return the stackTrace as a String for logging purposes.
*/
private String getStackTraceAsString() {
StackTraceElement[] stackTrace = getStackTrace();
if (stackTrace == null) {
return "";
}
return Arrays.toString(stackTrace);
}
/**
* Return the full stack trace that got the connection from the pool. You
* could use this if getCreatedByMethod() doesn't work for you.
*/
public StackTraceElement[] getStackTrace() {
if (stackTrace == null) {
return null;
}
// filter off the top of the stack that we are not interested in
ArrayList<StackTraceElement> filteredList = new ArrayList<StackTraceElement>();
boolean include = false;
for (int i = 0; i < stackTrace.length; i++) {
if (!include && !skipElement(stackTrace[i].toString())) {
include = true;
}
if (include && filteredList.size() < maxStackTrace) {
filteredList.add(stackTrace[i]);
}
}
return filteredList.toArray(new StackTraceElement[filteredList.size()]);
}
}
@@ -1,532 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool.Status;
import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues;
public class PooledConnectionQueue {
private static final Logger logger = LoggerFactory.getLogger(PooledConnectionQueue.class);
private static final TimeUnit MILLIS_TIME_UNIT = TimeUnit.MILLISECONDS;
private final String name;
private final DataSourcePool pool;
/**
* A 'circular' buffer designed specifically for free connections.
*/
private final FreeConnectionBuffer freeList;
/**
* A 'slots' buffer designed specifically for busy connections.
* Fast add remove based on slot id.
*/
private final BusyConnectionBuffer busyList;
/**
* Load statistics collected off connections that have closed fully (left the pool).
*/
private final PooledConnectionStatistics collectedStats = new PooledConnectionStatistics();
/**
* Currently accumulated load statistics.
*/
private LoadValues accumulatedValues = new LoadValues();
/**
* Main lock guarding all access
*/
private final ReentrantLock lock;
/**
* Condition for threads waiting to take a connection
*/
private final Condition notEmpty;
private int connectionId;
private final long waitTimeoutMillis;
private final long leakTimeMinutes;
private final long maxAgeMillis;
private int warningSize;
private int maxSize;
private int minSize;
/**
* Number of threads in the wait queue.
*/
private int waitingThreads;
/**
* Number of times a thread had to wait.
*/
private int waitCount;
/**
* Number of times a connection was got from this queue.
*/
private int hitCount;
/**
* The high water mark for the queue size.
*/
private int highWaterMark;
/**
* Last time the pool was reset. Used to close busy connections as they are
* returned to the pool that where created prior to the lastResetTime.
*/
private long lastResetTime;
private boolean doingShutdown;
public PooledConnectionQueue(DataSourcePool pool) {
this.pool = pool;
this.name = pool.getName();
this.minSize = pool.getMinSize();
this.maxSize = pool.getMaxSize();
this.warningSize = pool.getWarningSize();
this.waitTimeoutMillis = pool.getWaitTimeoutMillis();
this.leakTimeMinutes = pool.getLeakTimeMinutes();
this.maxAgeMillis = pool.getMaxAgeMillis();
this.busyList = new BusyConnectionBuffer(maxSize, 20);
this.freeList = new FreeConnectionBuffer();
this.lock = new ReentrantLock(false);
this.notEmpty = lock.newCondition();
}
private Status createStatus() {
return new Status(name, minSize, maxSize, freeList.size(), busyList.size(), waitingThreads, highWaterMark, waitCount, hitCount);
}
public String toString() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return createStatus().toString();
} finally {
lock.unlock();
}
}
/**
* Collect statistics of a connection that is fully closing
*/
void reportClosingConnection(PooledConnection pooledConnection) {
collectedStats.add(pooledConnection.getStatistics());
}
public DataSourcePoolStatistics getStatistics(boolean reset) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
LoadValues aggregate = collectedStats.getValues(reset);
freeList.collectStatistics(aggregate, reset);
busyList.collectStatistics(aggregate, reset);
aggregate.plus(accumulatedValues);
this.accumulatedValues = (reset) ? new LoadValues() : aggregate;
return new DataSourcePoolStatistics(aggregate.getCollectionStart(), aggregate.getCount(), aggregate.getErrorCount(), aggregate.getHwmMicros(), aggregate.getTotalMicros());
} finally {
lock.unlock();
}
}
public Status getStatus(boolean reset) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Status s = createStatus();
if (reset) {
highWaterMark = busyList.size();
hitCount = 0;
waitCount = 0;
}
return s;
} finally {
lock.unlock();
}
}
void setMinSize(int minSize) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (minSize > this.maxSize) {
throw new IllegalArgumentException("minSize " + minSize + " > maxSize " + this.maxSize);
}
this.minSize = minSize;
} finally {
lock.unlock();
}
}
void setMaxSize(int maxSize) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (maxSize < this.minSize) {
throw new IllegalArgumentException("maxSize " + maxSize + " < minSize " + this.minSize);
}
this.busyList.setCapacity(maxSize);
this.maxSize = maxSize;
} finally {
lock.unlock();
}
}
void setWarningSize(int warningSize) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (warningSize > this.maxSize) {
throw new IllegalArgumentException("warningSize " + warningSize + " > maxSize " + this.maxSize);
}
this.warningSize = warningSize;
} finally {
lock.unlock();
}
}
private int totalConnections() {
return freeList.size() + busyList.size();
}
void ensureMinimumConnections() throws SQLException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
int add = minSize - totalConnections();
if (add > 0) {
for (int i = 0; i < add; i++) {
PooledConnection c = pool.createConnectionForQueue(connectionId++);
freeList.add(c);
}
notEmpty.signal();
}
} finally {
lock.unlock();
}
}
/**
* Return a PooledConnection.
*/
void returnPooledConnection(PooledConnection c, boolean forceClose) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (!busyList.remove(c)) {
logger.error("Connection [{}] not found in BusyList? ", c);
}
if (forceClose || c.shouldTrimOnReturn(lastResetTime, maxAgeMillis)) {
c.closeConnectionFully(false);
} else {
freeList.add(c);
notEmpty.signal();
}
} finally {
lock.unlock();
}
}
private PooledConnection extractFromFreeList() {
PooledConnection c = freeList.remove();
registerBusyConnection(c);
return c;
}
PooledConnection getPooledConnection() throws SQLException {
try {
PooledConnection pc = _getPooledConnection();
pc.resetForUse();
return pc;
} catch (InterruptedException e) {
String msg = "Interrupted getting connection from pool " + e;
throw new SQLException(msg);
}
}
/**
* Register the PooledConnection with the busyList.
*/
private int registerBusyConnection(PooledConnection c) {
int busySize = busyList.add(c);
if (busySize > highWaterMark) {
highWaterMark = busySize;
}
return busySize;
}
private PooledConnection _getPooledConnection() throws InterruptedException, SQLException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
if (doingShutdown) {
throw new SQLException("Trying to access the Connection Pool when it is shutting down");
}
// this includes attempts that fail with InterruptedException
// or SQLException but that is ok as its only an indicator
hitCount++;
// are other threads already waiting? (they get priority)
if (waitingThreads == 0) {
if (!freeList.isEmpty()) {
// we have a free connection to return
return extractFromFreeList();
}
if (busyList.size() < maxSize) {
// grow the connection pool
PooledConnection c = pool.createConnectionForQueue(connectionId++);
int busySize = registerBusyConnection(c);
if (logger.isDebugEnabled()) {
logger.debug("DataSourcePool [{}] grow; id[{}] busy[{}] max[{}]", name, c.getName(), busySize, maxSize);
}
checkForWarningSize();
return c;
}
}
try {
// The pool is at maximum size. We are going to go into
// a wait loop until connections are returned into the pool.
waitCount++;
waitingThreads++;
return _getPooledConnectionWaitLoop();
} finally {
waitingThreads--;
}
} finally {
lock.unlock();
}
}
/**
* Got into a loop waiting for connections to be returned to the pool.
*/
private PooledConnection _getPooledConnectionWaitLoop() throws SQLException, InterruptedException {
long nanos = MILLIS_TIME_UNIT.toNanos(waitTimeoutMillis);
for (; ; ) {
if (nanos <= 0) {
String msg = "Unsuccessfully waited [" + waitTimeoutMillis + "] millis for a connection to be returned."
+ " No connections are free. You need to Increase the max connections of [" + maxSize + "]"
+ " or look for a connection pool leak using datasource.xxx.capturestacktrace=true";
if (pool.isCaptureStackTrace()) {
dumpBusyConnectionInformation();
}
throw new SQLException(msg);
}
try {
nanos = notEmpty.awaitNanos(nanos);
if (!freeList.isEmpty()) {
// successfully waited
return extractFromFreeList();
}
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to non-interrupted thread
throw ie;
}
}
}
public void shutdown() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
doingShutdown = true;
Status status = createStatus();
DataSourcePoolStatistics statistics = pool.getStatistics(false);
logger.debug("DataSourcePool [{}] shutdown {} - Statistics {}", name, status, statistics);
closeFreeConnections(true);
if (!busyList.isEmpty()) {
logger.warn("Closing busy connections on shutdown size: " + busyList.size());
dumpBusyConnectionInformation();
closeBusyConnections(0);
}
} finally {
lock.unlock();
}
}
/**
* Close all the connections in the pool and any current busy connections
* when they are returned. New connections will be then created on demand.
* <p>
* This is typically done when a database down event occurs.
* </p>
*/
public void reset(long leakTimeMinutes) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Status status = createStatus();
logger.info("Reseting DataSourcePool [{}] {}", name, status);
lastResetTime = System.currentTimeMillis();
closeFreeConnections(false);
closeBusyConnections(leakTimeMinutes);
logger.info("Busy Connections:\n" + getBusyConnectionInformation());
} finally {
lock.unlock();
}
}
public void trim(long maxInactiveMillis, long maxAgeMillis) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis) > 0) {
try {
ensureMinimumConnections();
} catch (SQLException e) {
logger.error("Error trying to ensure minimum connections", e);
}
}
} finally {
lock.unlock();
}
}
/**
* Trim connections that have been not used for some time.
*/
private int trimInactiveConnections(long maxInactiveMillis, long maxAgeMillis) {
long usedSince = System.currentTimeMillis() - maxInactiveMillis;
long createdSince = (maxAgeMillis == 0) ? 0 : System.currentTimeMillis() - maxAgeMillis;
int trimedCount = freeList.trim(usedSince, createdSince);
if (trimedCount > 0) {
logger.debug("DataSourcePool [{}] trimmed [{}] inactive connections. New size[{}]", name, trimedCount, totalConnections());
}
return trimedCount;
}
/**
* Close all the connections that are in the free list.
*/
private void closeFreeConnections(boolean logErrors) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
freeList.closeAll(logErrors);
} finally {
lock.unlock();
}
}
/**
* Close any busy connections that have not been used for some time.
* <p>
* These connections are considered to have leaked from the connection pool.
* </p>
* <p>
* Connection leaks occur when code doesn't ensure that connections are
* closed() after they have been finished with. There should be an
* appropriate try catch finally block to ensure connections are always
* closed and put back into the pool.
* </p>
*/
void closeBusyConnections(long leakTimeMinutes) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
busyList.closeBusyConnections(leakTimeMinutes);
} finally {
lock.unlock();
}
}
/**
* As the pool grows it gets closer to the maxConnections limit. We can send
* an Alert (or warning) as we get close to this limit and hence an
* Administrator could increase the pool size if desired.
* <p>
* This is called whenever the pool grows in size (towards the max limit).
* </p>
*/
private void checkForWarningSize() {
// the the total number of connections that we can add
// to the pool before it hits the maximum
int availableGrowth = (maxSize - totalConnections());
if (availableGrowth < warningSize) {
closeBusyConnections(leakTimeMinutes);
String msg = "DataSourcePool [" + name + "] is [" + availableGrowth + "] connections from its maximum size.";
pool.notifyWarning(msg);
}
}
String getBusyConnectionInformation() {
return getBusyConnectionInformation(false);
}
void dumpBusyConnectionInformation() {
getBusyConnectionInformation(true);
}
/**
* Returns information describing connections that are currently being used.
*/
private String getBusyConnectionInformation(boolean toLogger) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return busyList.getBusyConnectionInformation(toLogger);
} finally {
lock.unlock();
}
}
}
@@ -1,160 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* Collects load statistics for a PooledConnection.
*/
class PooledConnectionStatistics {
private final AtomicLong count = new AtomicLong();
private final AtomicLong errorCount = new AtomicLong();
private final AtomicLong hwmNanos = new AtomicLong();
private final AtomicLong totalNanos = new AtomicLong();
private final AtomicLong collectionStart;
PooledConnectionStatistics() {
this.collectionStart = new AtomicLong(System.currentTimeMillis());
}
/**
* Add statistics from another collector.
*/
public void add(PooledConnectionStatistics other) {
errorCount.addAndGet(other.getErrorCount());
totalNanos.addAndGet(other.totalNanos.get());
count.addAndGet(other.getCount());
final long otherHwm = other.hwmNanos.get();
if (otherHwm > hwmNanos.get()) {
hwmNanos.set(otherHwm);
}
}
/**
* Add some time duration to the statistics.
*/
public void add(long durationNanos, boolean hasError) {
// This will be done in pretty much single threaded fashion
// as the Connections generally are not shared across threads
if (hasError) {
errorCount.incrementAndGet();
}
count.incrementAndGet();
totalNanos.addAndGet(durationNanos);
if (durationNanos > hwmNanos.get()) {
hwmNanos.set(durationNanos);
}
}
public String toString() {
return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + getTotalMicros() + "] hwmMicros[" + getHwmMicros() + "]";
}
public long getCollectionStart() {
return collectionStart.get();
}
public long getCount() {
return count.get();
}
private long getErrorCount() {
return errorCount.get();
}
private long getTotalMicros() {
return TimeUnit.MICROSECONDS.convert(totalNanos.get(), TimeUnit.NANOSECONDS);
}
private long getHwmMicros() {
return TimeUnit.MICROSECONDS.convert(hwmNanos.get(), TimeUnit.NANOSECONDS);
}
/**
* Get the current values and reset the statistics if necessary.
*/
LoadValues getValues(boolean reset) {
LoadValues value = new LoadValues(collectionStart.get(), count.get(), errorCount.get(), getHwmMicros(), getTotalMicros());
if (reset) {
count.set(0);
errorCount.set(0);
hwmNanos.set(0);
totalNanos.set(0);
collectionStart.set(System.currentTimeMillis());
}
return value;
}
/**
* Values representing the load or activity of a PooledConnection.
* <p>
* These are aggregated up to get a total for the DataSourcePool.
* </p>
*/
static class LoadValues {
private long collectionStart;
private long count;
private long errorCount;
private long hwmMicros;
private long totalMicros;
LoadValues() {
}
LoadValues(long collectionStart, long count, long errorCount, long hwmMicros, long totalMicros) {
this.collectionStart = collectionStart;
this.count = count;
this.errorCount = errorCount;
this.hwmMicros = hwmMicros;
this.totalMicros = totalMicros;
}
public void plus(LoadValues additional) {
collectionStart = (collectionStart == 0) ? additional.collectionStart : Math.min(collectionStart, additional.collectionStart);
count += additional.count;
errorCount += additional.errorCount;
hwmMicros = Math.max(hwmMicros, additional.hwmMicros);
totalMicros += additional.totalMicros;
}
public String toString() {
return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + totalMicros + "] hwmMicros[" + hwmMicros + "] avgMicros[" + getAvgMicros() + "]";
}
long getCollectionStart() {
return collectionStart;
}
public long getCount() {
return count;
}
long getErrorCount() {
return errorCount;
}
long getHwmMicros() {
return hwmMicros;
}
long getTotalMicros() {
return totalMicros;
}
long getAvgMicros() {
return (count == 0) ? 0 : totalMicros / count;
}
}
}
@@ -1,176 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* A LRU based cache for PreparedStatements.
*/
class PstmtCache extends LinkedHashMap<String, ExtendedPreparedStatement> {
private static final Logger logger = LoggerFactory.getLogger(PstmtCache.class);
static final long serialVersionUID = -3096406924865550697L;
/**
* The maximum size of the cache. When this is exceeded the oldest entry is removed.
*/
private final int maxSize;
/**
* The total number of entries removed from this cache.
*/
private int removeCounter;
/**
* The number of get hits.
*/
private int hitCounter;
/**
* The number of get() misses.
*/
private int missCounter;
/**
* The number of puts into this cache.
*/
private int putCounter;
PstmtCache(int maxCacheSize) {
// note = access ordered list. This is what gives it the LRU order
super(maxCacheSize * 3, 0.75f, true);
this.maxSize = maxCacheSize;
}
/**
* Return a summary description of this cache.
*/
public String getDescription() {
return "size[" + size() + "] max[" + maxSize + "] hits[" + hitCounter + "] miss[" + missCounter + "] hitRatio[" + getHitRatio() + "] removes[" + removeCounter + "]";
}
/**
* returns the current maximum size of the cache.
*/
public int getMaxSize() {
return maxSize;
}
/**
* Gets the hit ratio. A number between 0 and 100 indicating the number of
* hits to misses. A number approaching 100 is desirable.
*/
private int getHitRatio() {
if (hitCounter == 0) {
return 0;
} else {
return hitCounter * 100 / (hitCounter + missCounter);
}
}
/**
* The total number of hits against this cache.
*/
public int getHitCounter() {
return hitCounter;
}
/**
* The total number of misses against this cache.
*/
public int getMissCounter() {
return missCounter;
}
/**
* The total number of puts against this cache.
*/
public int getPutCounter() {
return putCounter;
}
/**
* Try to add the returning statement to the cache. If there is already a
* matching ExtendedPreparedStatement in the cache return false else add
* the statement to the cache and return true.
*/
boolean returnStatement(ExtendedPreparedStatement pstmt) {
ExtendedPreparedStatement alreadyInCache = super.get(pstmt.getCacheKey());
if (alreadyInCache != null) {
return false;
}
// add the returning prepared statement to the cache.
// Note that the LRUCache will automatically close fully old unused
// PStmts when the cache has hit its maximum size.
put(pstmt.getCacheKey(), pstmt);
return true;
}
/**
* additionally maintains hit and miss statistics.
*/
public ExtendedPreparedStatement get(Object key) {
ExtendedPreparedStatement o = super.get(key);
if (o == null) {
missCounter++;
} else {
hitCounter++;
}
return o;
}
/**
* additionally maintains hit and miss statistics.
*/
public ExtendedPreparedStatement remove(Object key) {
ExtendedPreparedStatement o = super.remove(key);
if (o == null) {
missCounter++;
} else {
hitCounter++;
}
return o;
}
/**
* additionally maintains put counter statistics.
*/
public ExtendedPreparedStatement put(String key, ExtendedPreparedStatement value) {
putCounter++;
return super.put(key, value);
}
/**
* will check to see if we need to remove entries and
* if so call the cacheCleanup.cleanupEldestLRUCacheEntry() if
* one has been set.
*/
protected boolean removeEldestEntry(Map.Entry<String, ExtendedPreparedStatement> eldest) {
if (size() < maxSize) {
return false;
}
removeCounter++;
try {
ExtendedPreparedStatement pstmt = eldest.getValue();
pstmt.closeDestroy();
} catch (SQLException e) {
logger.error("Error closing ExtendedPreparedStatement", e);
}
return true;
}
}
@@ -1,106 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import com.avaje.ebeaninternal.server.lib.util.MailEvent;
import com.avaje.ebeaninternal.server.lib.util.MailListener;
import com.avaje.ebeaninternal.server.lib.util.MailMessage;
import com.avaje.ebeaninternal.server.lib.util.MailSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple smtp email alert that sends a email message on dataSourceDown and
* dataSourceUp etc.
* <ul>
* <li>alert.fromuser = the from user name
* <li>alert.fromemail = the from email account
* <li>alert.toemail = comma delimited list of email accounts to email
* <li>alert.mailserver = the smpt server name
* </ul>
*/
public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
private static final Logger logger = LoggerFactory.getLogger(SimpleDataSourceAlert.class);
private static final String alertMailServerName = System.getProperty("ebean.datasource.alert.mailserver");
private static final String fromUser = System.getProperty("ebean.datasource.alert.fromUser");
private static final String fromEmail = System.getProperty("ebean.datasource.alert.fromEmail");
private static final String toEmail = System.getProperty("ebean.datasource.alert.toEmail");
/**
* Create a SimpleAlerter.
*/
public SimpleDataSourceAlert() {
}
/**
* If the email failed then log the error.
*/
public void handleEvent(MailEvent event) {
Throwable e = event.getError();
if (e != null) {
logger.error(null, e);
}
}
/**
* Send the dataSource down alert.
*/
@Override
public void dataSourceDown(String dataSourceName) {
String msg = getSubject(true, dataSourceName);
sendMessage(msg, msg);
}
/**
* Send the dataSource up alert.
*/
@Override
public void dataSourceUp(String dataSourceName) {
String msg = getSubject(false, dataSourceName);
sendMessage(msg, msg);
}
/**
* Send the warning message.
*/
@Override
public void dataSourceWarning(String subject, String msg) {
sendMessage(subject, msg);
}
private String getSubject(boolean isDown, String dsName) {
String msg = "The DataSource " + dsName;
if (isDown) {
msg += " is DOWN!!";
} else {
msg += " is UP.";
}
return msg;
}
private void sendMessage(String subject, String msg) {
if (alertMailServerName == null) {
return;
}
MailMessage data = new MailMessage();
data.setSender(fromUser, fromEmail);
data.addBodyLine(msg);
data.setSubject(subject);
String[] toList = toEmail.split(",");
if (toList.length == 0) {
logger.error("alert.toemail has not been set?");
} else {
for (int i = 0; i < toList.length; i++) {
data.addRecipient(null, toList[i].trim());
}
MailSender sender = new MailSender(alertMailServerName);
sender.setMailListener(this);
sender.sendInBackground(data);
}
}
}
@@ -1,38 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.Connection;
/**
* Helper object that can convert between transaction isolation descriptions and values.
*/
class TransactionIsolation {
/**
* Return the string description of the transaction isolation level specified.
* <p>Returned value is one of NONE, READ_COMMITTED,READ_UNCOMMITTED,
* REPEATABLE_READ or SERIALIZABLE.</p>
*
* @param level the transaction isolation level as per java.sql.Connection
* @return the level description as a string.
*/
static String getLevelDescription(int level) {
switch (level) {
case Connection.TRANSACTION_NONE:
return "NONE";
case Connection.TRANSACTION_READ_COMMITTED:
return "READ_COMMITTED";
case Connection.TRANSACTION_READ_UNCOMMITTED:
return "READ_UNCOMMITTED";
case Connection.TRANSACTION_REPEATABLE_READ:
return "REPEATABLE_READ";
case Connection.TRANSACTION_SERIALIZABLE:
return "SERIALIZABLE";
case -1:
return "NotSet";
default:
throw new RuntimeException("Transaction Isolaction level [" + level + "] is not defined.");
}
}
}
@@ -1,20 +0,0 @@
FATAL_ERROR=ERROR: A fatal error has occured. Check the error log. {0}
DATASOURCE_OK=DataSource {0} working ok.
SHUTTING_DOWN=DataSource shutdown all datasources in [{0}]
SHUT_DOWN_FINISHED=DataSource shutdown has finished.
CANT_FIND_PROPS=ERROR: Can't find the datasource props file.
ERROR_CREATING_FACTORY=ERROR: An error occured when creating the DataSourceFactory {0}.
CANT_SHUTDOWN_TYPE=WARN: Can't shutdown DataSource objects of this type {0}.
DB_DRIVER_NOTFOUND=ERROR: The JDBC Driver {0} can't be found.
POOL_IN_SHUTDOWN=ERROR: Trying to use the pool while it is shutting down.
WAIT_TIME_EXCEEDED=ERROR: Wait time {0} for connection exceeded. {1}.
SHUTDOWN_START=DataSource [{0}] Shutting down.
SHUTDOWN_LEAK=A Connection leak has been detected on shutdown {0}.
SHUTDOWN_END=DataSource [{0}] Shutdown ended.
METHOD_NOT_SUPPORTED=ERROR: this method is not supported.
SET_ALERT=WARN: Alert {0} has been set.
MISSING_PARAMETER=ERROR: A parameter {0} is missing from the props file.
IDLE_CONNECTION_ACCESSED=Pooled Connection has been accessed whilst idle in the pool, via method:
DEFAULT_DS_NOT_SPECIFIED=ERROR: No default dataSource has been specified.
@@ -1,12 +0,0 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
<TITLE>AvajeLib</TITLE>
</HEAD>
<Body BGCOLOR="#ffffff">
Enhanced JDBC objects and connection pool.
<P>Provides Database meta data objects, Robust connection pooling, PreparedStatement Caching.
</P>
</Body>
</HTML>
@@ -1,43 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* An Email address with an associated alias.
*/
public class MailAddress {
final String alias;
final String emailAddress;
/**
* Create an address with an optional alias.
*/
public MailAddress(String alias, String emailAddress) {
this.alias = alias;
this.emailAddress = emailAddress;
}
/**
* Return the alias.
* If the alias is null this returns an empty string.
*/
public String getAlias() {
if (alias == null) {
return "";
}
return alias;
}
/**
* Return the email address.
*/
public String getEmailAddress() {
return emailAddress;
}
public String toString() {
return getAlias() + " " + "<" + getEmailAddress() + ">";
}
}
@@ -1,49 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* Represents the success or failure of a mail send.
*/
public class MailEvent {
/**
* The error indicating a send failure.
*/
final Throwable error;
/**
* The message that was sent.
*/
final MailMessage message;
/**
* The message send failed with an error.
*/
public MailEvent(MailMessage message, Throwable error) {
this.message = message;
this.error = error;
}
/**
* The message that we attempted to send.
*/
public MailMessage getMailMessage() {
return message;
}
/**
* Returns true if the message was sent successfully.
*/
public boolean wasSuccessful() {
return (error == null);
}
/**
* The error indicating the send failed.
*/
public Throwable getError() {
return error;
}
}
@@ -1,13 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* Listens to see if the message was successfully sent.
*/
public interface MailListener {
/**
* Handle the message event.
*/
void handleEvent(MailEvent event);
}
@@ -1,153 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/**
* A simple test message that can be sent via smtp.
*/
public class MailMessage {
/**
* The body content.
*/
final ArrayList<String> bodylines;
/**
* The sender email address.
*/
MailAddress senderAddress;
/**
* The headers.
*/
final HashMap<String, String> header = new HashMap<String, String>();
/**
* the recipient of the email.
*/
MailAddress currentRecipient;
/**
* The list of recipients.
*/
final ArrayList<MailAddress> recipientList = new ArrayList<MailAddress>();
/**
* Create the message.
*/
public MailMessage() {
bodylines = new ArrayList<String>();
}
/**
* Set the current recipient.
*/
public void setCurrentRecipient(MailAddress currentRecipient) {
this.currentRecipient = currentRecipient;
}
/**
* Return the current recipient.
*/
public MailAddress getCurrentRecipient() {
return currentRecipient;
}
/**
* Add a recipient.
*/
public void addRecipient(String alias, String emailAddress) {
recipientList.add(new MailAddress(alias, emailAddress));
}
/**
* Set the sender details.
*/
public void setSender(String alias, String senderEmail) {
this.senderAddress = new MailAddress(alias, senderEmail);
}
/**
* Return the sender address.
*/
public MailAddress getSender() {
return senderAddress;
}
/**
* Return the recipient list.
*/
public List<MailAddress> getRecipientList() {
return recipientList;
}
/**
* add a header to the message.
*/
public void addHeader(String key, String val) {
header.put(key, val);
}
/**
* Set the subject text.
*/
public void setSubject(String subject) {
addHeader("Subject", subject);
}
/**
* Return the subject text.
*/
public String getSubject() {
return getHeader("Subject");
}
/**
* Add text to the body.
*/
public void addBodyLine(String line) {
bodylines.add(line);
}
/**
* Return the body text.
*/
public List<String> getBodyLines() {
return bodylines;
}
/**
* Return the headers.
*/
public Collection<String> getHeaderFields() {
return header.keySet();
}
/**
* Return a given header.
*/
public String getHeader(String key) {
return header.get(key);
}
public String toString() {
StringBuilder sb = new StringBuilder(100);
sb.append("Sender: ").append(senderAddress).append("\tRecipient: ").append(recipientList).append("\n");
for (String key : header.keySet()) {
String hline = key + ": " + header.get(key) + "\n";
sb.append(hline);
}
sb.append("\n");
for (String line : bodylines) {
sb.append(line).append("\n");
}
return sb.toString();
}
}
@@ -1,206 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* Sends simple MailMessages via smtp.
*/
public class MailSender implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(MailSender.class);
int traceLevel = 0;
Socket sserver;
final String server;
BufferedReader in;
OutputStreamWriter out;
MailMessage message;
MailListener listener = null;
private static final int SMTP_PORT = 25;
/**
* Create for a given mail server.
*/
public MailSender(String server) {
this.server = server;
}
/**
* Set the listener to handle MessageEvents.
*/
public void setMailListener(MailListener listener) {
this.listener = listener;
}
/**
* Send the message.
*/
public void run() {
send(message);
}
/**
* Send the message in a background thread.
*/
public void sendInBackground(MailMessage message) {
this.message = message;
Thread thread = new Thread(this);
thread.start();
}
/**
* Send the message in the current thread.
*/
public void send(MailMessage message) {
try {
for (MailAddress recipientAddress : message.getRecipientList()) {
sserver = new Socket(server, SMTP_PORT);
send(message, sserver, recipientAddress);
sserver.close();
if (listener != null) {
MailEvent event = new MailEvent(message, null);
listener.handleEvent(event);
}
}
} catch (Exception ex) {
if (listener != null) {
MailEvent event = new MailEvent(message, ex);
listener.handleEvent(event);
} else {
logger.error(null, ex);
}
}
}
private void send(MailMessage message, Socket sserver, MailAddress recipientAddress) throws IOException {
// A bit convoluted, but doesn't depend on DNS in any way...
InetAddress localhost = sserver.getLocalAddress();
String localaddress = localhost.getHostAddress();
MailAddress sender = message.getSender();
message.setCurrentRecipient(recipientAddress);
// Mandatory header fields, Date and From
if (message.getHeader("Date") == null) {
message.addHeader("Date", new java.util.Date().toString());
}
if (message.getHeader("From") == null) {
message.addHeader("From", sender.getAlias() + " <" + sender.getEmailAddress() + ">");
}
// if (message.getHeader("From") == null){
message.addHeader("To", recipientAddress.getAlias() + " <" + recipientAddress.getEmailAddress() + ">");
// }
out = new OutputStreamWriter(sserver.getOutputStream());
in = new BufferedReader(new InputStreamReader(sserver.getInputStream()));
String sintro = readln();
if (!sintro.startsWith("220")) { // 220
logger.debug("SmtpSender: intro==" + sintro);
return;
}
writeln("EHLO " + localaddress);
if (!expect250()) {
return;
}
writeln("MAIL FROM:<" + sender.getEmailAddress() + ">");
if (!expect250()) {
return;
}
writeln("RCPT TO:<" + recipientAddress.getEmailAddress() + ">");
if (!expect250()) {
return;
}
writeln("DATA");
while (true) { // may be multiple 250 replies pending from server
String line = readln();
if (line.startsWith("3"))
break; // ready to send
if (!line.startsWith("2")) {
logger.debug("SmtpSender.send reponse to DATA: " + line);
return;
}
}
for (String key : message.getHeaderFields()) {
writeln(key + ": " + message.getHeader(key));
}
writeln(""); // end of header;
for (String bline : message.getBodyLines()) {
if (bline.startsWith(".")) {
bline = "." + bline;
}
writeln(bline);
}
writeln(".");
expect250();
writeln("QUIT");
}
private boolean expect250() throws IOException {
String line = readln();
if (!line.startsWith("2")) {
logger.info("SmtpSender.expect250: " + line);
return false;
}
return true;
}
private void writeln(String s) throws IOException {
if (traceLevel > 2) {
logger.debug("From client: " + s);
}
out.write(s + "\r\n");
out.flush();
}
private String readln() throws IOException {
String line = in.readLine();
if (traceLevel > 1) {
logger.debug("From server: " + line);
}
return line;
}
/**
* Set the trace level.
*/
public void setTraceLevel(int traceLevel) {
this.traceLevel = traceLevel;
}
/**
* Return the hostname of the local machine.
*/
public String getLocalHostName() {
try {
InetAddress ipaddress = InetAddress.getLocalHost();
String localHost = ipaddress.getHostName();
if (localHost == null) {
return "localhost";
} else {
return localHost;
}
} catch (UnknownHostException e) {
return "localhost";
}
}
}
@@ -16,7 +16,7 @@ import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import org.avaje.datasource.DataSourcePool;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import org.slf4j.Logger;
@@ -27,7 +27,6 @@ import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -129,15 +128,6 @@ public class TransactionManager {
this.externalTransPrefix = "e";
this.onQueryOnly = initOnQueryOnly(config.getDatabasePlatform().getOnQueryOnly(), dataSource);
initialiseHeartbeat();
}
private void initialiseHeartbeat() {
if (dataSource instanceof DataSourcePool) {
DataSourcePool ds = (DataSourcePool) dataSource;
backgroundExecutor.executePeriodically(ds.getHeartbeatRunnable(), ds.getHeartbeatFreqSecs(), TimeUnit.SECONDS);
}
}
public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) {