mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#618 - Refactor - remove DataSourcePool implementation as separate dependency
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user