All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibatis.common.jdbc.SimpleDataSource Maven / Gradle / Ivy

/*
 * Copyright 2004-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ibatis.common.jdbc;

import com.ibatis.common.beans.ClassInfo;
import com.ibatis.common.logging.Log;
import com.ibatis.common.logging.LogFactory;
import com.ibatis.common.resources.Resources;

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.*;
import java.util.logging.Logger;

import javax.sql.DataSource;

/**
 * This is a simple, synchronous, thread-safe database connection pool.
 * 

* REQUIRED PROPERTIES ------------------- JDBC.Driver JDBC.ConnectionURL JDBC.Username JDBC.Password *

* Pool.MaximumActiveConnections Pool.MaximumIdleConnections Pool.MaximumCheckoutTime Pool.TimeToWait Pool.PingQuery * Pool.PingEnabled Pool.PingConnectionsOlderThan Pool.PingConnectionsNotUsedFor Pool.QuietMode */ public class SimpleDataSource implements DataSource { /** The Constant log. */ private static final Log log = LogFactory.getLog(SimpleDataSource.class); /** The Constant PROP_JDBC_DRIVER. */ // Required Properties private static final String PROP_JDBC_DRIVER = "JDBC.Driver"; /** The Constant PROP_JDBC_URL. */ private static final String PROP_JDBC_URL = "JDBC.ConnectionURL"; /** The Constant PROP_JDBC_USERNAME. */ private static final String PROP_JDBC_USERNAME = "JDBC.Username"; /** The Constant PROP_JDBC_PASSWORD. */ private static final String PROP_JDBC_PASSWORD = "JDBC.Password"; /** The Constant PROP_JDBC_DEFAULT_AUTOCOMMIT. */ private static final String PROP_JDBC_DEFAULT_AUTOCOMMIT = "JDBC.DefaultAutoCommit"; /** The Constant PROP_POOL_MAX_ACTIVE_CONN. */ // Optional Properties private static final String PROP_POOL_MAX_ACTIVE_CONN = "Pool.MaximumActiveConnections"; /** The Constant PROP_POOL_MAX_IDLE_CONN. */ private static final String PROP_POOL_MAX_IDLE_CONN = "Pool.MaximumIdleConnections"; /** The Constant PROP_POOL_MAX_CHECKOUT_TIME. */ private static final String PROP_POOL_MAX_CHECKOUT_TIME = "Pool.MaximumCheckoutTime"; /** The Constant PROP_POOL_TIME_TO_WAIT. */ private static final String PROP_POOL_TIME_TO_WAIT = "Pool.TimeToWait"; /** The Constant PROP_POOL_PING_QUERY. */ private static final String PROP_POOL_PING_QUERY = "Pool.PingQuery"; /** The Constant PROP_POOL_PING_CONN_OLDER_THAN. */ private static final String PROP_POOL_PING_CONN_OLDER_THAN = "Pool.PingConnectionsOlderThan"; /** The Constant PROP_POOL_PING_ENABLED. */ private static final String PROP_POOL_PING_ENABLED = "Pool.PingEnabled"; /** The Constant PROP_POOL_PING_CONN_NOT_USED_FOR. */ private static final String PROP_POOL_PING_CONN_NOT_USED_FOR = "Pool.PingConnectionsNotUsedFor"; /** The expected connection type code. */ private int expectedConnectionTypeCode; /** The Constant ADD_DRIVER_PROPS_PREFIX. */ // Additional Driver Properties prefix private static final String ADD_DRIVER_PROPS_PREFIX = "Driver."; /** The Constant ADD_DRIVER_PROPS_PREFIX_LENGTH. */ private static final int ADD_DRIVER_PROPS_PREFIX_LENGTH = ADD_DRIVER_PROPS_PREFIX.length(); /** The pool lock. */ // ----- BEGIN: FIELDS LOCKED BY POOL_LOCK ----- private final Object POOL_LOCK = new Object(); /** The idle connections. */ private List idleConnections = new ArrayList(); /** The active connections. */ private List activeConnections = new ArrayList(); /** The request count. */ private long requestCount = 0; /** The accumulated request time. */ private long accumulatedRequestTime = 0; /** The accumulated checkout time. */ private long accumulatedCheckoutTime = 0; /** The claimed overdue connection count. */ private long claimedOverdueConnectionCount = 0; /** The accumulated checkout time of overdue connections. */ private long accumulatedCheckoutTimeOfOverdueConnections = 0; /** The accumulated wait time. */ private long accumulatedWaitTime = 0; /** The had to wait count. */ private long hadToWaitCount = 0; /** The bad connection count. */ private long badConnectionCount = 0; // ----- END: FIELDS LOCKED BY POOL_LOCK ----- /** The jdbc driver. */ // ----- BEGIN: PROPERTY FIELDS FOR CONFIGURATION ----- private String jdbcDriver; /** The jdbc url. */ private String jdbcUrl; /** The jdbc username. */ private String jdbcUsername; /** The jdbc password. */ private String jdbcPassword; /** The jdbc default auto commit. */ private boolean jdbcDefaultAutoCommit; /** The driver props. */ private Properties driverProps; /** The use driver props. */ private boolean useDriverProps; /** The pool maximum active connections. */ private int poolMaximumActiveConnections; /** The pool maximum idle connections. */ private int poolMaximumIdleConnections; /** The pool maximum checkout time. */ private int poolMaximumCheckoutTime; /** The pool time to wait. */ private int poolTimeToWait; /** The pool ping query. */ private String poolPingQuery; /** The pool ping enabled. */ private boolean poolPingEnabled; /** The pool ping connections older than. */ private int poolPingConnectionsOlderThan; /** The pool ping connections not used for. */ private int poolPingConnectionsNotUsedFor; // ----- END: PROPERTY FIELDS FOR CONFIGURATION ----- /** * Constructor to allow passing in a map of properties for configuration. * * @param props * - the configuration parameters */ public SimpleDataSource(Map props) { initialize(props); } /** * Initialize. * * @param props * the props */ private void initialize(Map props) { try { String prop_pool_ping_query = null; if (props == null) { throw new RuntimeException("SimpleDataSource: The properties map passed to the initializer was null."); } if (!(props.containsKey(PROP_JDBC_DRIVER) && props.containsKey(PROP_JDBC_URL) && props.containsKey(PROP_JDBC_USERNAME) && props.containsKey(PROP_JDBC_PASSWORD))) { throw new RuntimeException("SimpleDataSource: Some properties were not set."); } else { jdbcDriver = (String) props.get(PROP_JDBC_DRIVER); jdbcUrl = (String) props.get(PROP_JDBC_URL); jdbcUsername = (String) props.get(PROP_JDBC_USERNAME); jdbcPassword = (String) props.get(PROP_JDBC_PASSWORD); poolMaximumActiveConnections = props.containsKey(PROP_POOL_MAX_ACTIVE_CONN) ? Integer.parseInt((String) props.get(PROP_POOL_MAX_ACTIVE_CONN)) : 10; poolMaximumIdleConnections = props.containsKey(PROP_POOL_MAX_IDLE_CONN) ? Integer.parseInt((String) props.get(PROP_POOL_MAX_IDLE_CONN)) : 5; poolMaximumCheckoutTime = props.containsKey(PROP_POOL_MAX_CHECKOUT_TIME) ? Integer.parseInt((String) props.get(PROP_POOL_MAX_CHECKOUT_TIME)) : 20000; poolTimeToWait = props.containsKey(PROP_POOL_TIME_TO_WAIT) ? Integer.parseInt((String) props.get(PROP_POOL_TIME_TO_WAIT)) : 20000; poolPingEnabled = props.containsKey(PROP_POOL_PING_ENABLED) && Boolean.valueOf((String) props.get(PROP_POOL_PING_ENABLED)).booleanValue(); prop_pool_ping_query = (String) props.get(PROP_POOL_PING_QUERY); poolPingQuery = props.containsKey(PROP_POOL_PING_QUERY) ? prop_pool_ping_query : "NO PING QUERY SET"; poolPingConnectionsOlderThan = props.containsKey(PROP_POOL_PING_CONN_OLDER_THAN) ? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_OLDER_THAN)) : 0; poolPingConnectionsNotUsedFor = props.containsKey(PROP_POOL_PING_CONN_NOT_USED_FOR) ? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_NOT_USED_FOR)) : 0; jdbcDefaultAutoCommit = props.containsKey(PROP_JDBC_DEFAULT_AUTOCOMMIT) && Boolean.valueOf((String) props.get(PROP_JDBC_DEFAULT_AUTOCOMMIT)).booleanValue(); useDriverProps = false; Iterator propIter = props.keySet().iterator(); driverProps = new Properties(); driverProps.put("user", jdbcUsername); driverProps.put("password", jdbcPassword); while (propIter.hasNext()) { String name = (String) propIter.next(); String value = (String) props.get(name); if (name.startsWith(ADD_DRIVER_PROPS_PREFIX)) { driverProps.put(name.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), value); useDriverProps = true; } } expectedConnectionTypeCode = assembleConnectionTypeCode(jdbcUrl, jdbcUsername, jdbcPassword); Resources.instantiate(jdbcDriver); if (poolPingEnabled && (!props.containsKey(PROP_POOL_PING_QUERY) || prop_pool_ping_query.trim().length() == 0)) { throw new RuntimeException("SimpleDataSource: property '" + PROP_POOL_PING_ENABLED + "' is true, but property '" + PROP_POOL_PING_QUERY + "' is not set correctly."); } } } catch (Exception e) { log.error("SimpleDataSource: Error while loading properties. Cause: " + e.toString(), e); throw new RuntimeException("SimpleDataSource: Error while loading properties. Cause: " + e, e); } } /** * Assemble connection type code. * * @param url * the url * @param username * the username * @param password * the password * * @return the int */ private int assembleConnectionTypeCode(String url, String username, String password) { return ("" + url + username + password).hashCode(); } /** * @see javax.sql.DataSource#getConnection() */ public Connection getConnection() throws SQLException { return popConnection(jdbcUsername, jdbcPassword).getProxyConnection(); } /** * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String) */ public Connection getConnection(String username, String password) throws SQLException { return popConnection(username, password).getProxyConnection(); } /** * @see javax.sql.DataSource#setLoginTimeout(int) */ public void setLoginTimeout(int loginTimeout) throws SQLException { DriverManager.setLoginTimeout(loginTimeout); } /** * @see javax.sql.DataSource#getLoginTimeout() */ public int getLoginTimeout() throws SQLException { return DriverManager.getLoginTimeout(); } /** * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter) */ public void setLogWriter(PrintWriter logWriter) throws SQLException { DriverManager.setLogWriter(logWriter); } /** * @see javax.sql.DataSource#getLogWriter() */ public PrintWriter getLogWriter() throws SQLException { return DriverManager.getLogWriter(); } /** * If a connection has not been used in this many milliseconds, ping the database to make sure the connection is still * good. * * @return the number of milliseconds of inactivity that will trigger a ping */ public int getPoolPingConnectionsNotUsedFor() { return poolPingConnectionsNotUsedFor; } /** * Getter for the name of the JDBC driver class used. * * @return The name of the class */ public String getJdbcDriver() { return jdbcDriver; } /** * Getter of the JDBC URL used. * * @return The JDBC URL */ public String getJdbcUrl() { return jdbcUrl; } /** * Getter for the JDBC user name used. * * @return The user name */ public String getJdbcUsername() { return jdbcUsername; } /** * Getter for the JDBC password used. * * @return The password */ public String getJdbcPassword() { return jdbcPassword; } /** * Getter for the maximum number of active connections. * * @return The maximum number of active connections */ public int getPoolMaximumActiveConnections() { return poolMaximumActiveConnections; } /** * Getter for the maximum number of idle connections. * * @return The maximum number of idle connections */ public int getPoolMaximumIdleConnections() { return poolMaximumIdleConnections; } /** * Getter for the maximum time a connection can be used before it *may* be given away again. * * @return The maximum time */ public int getPoolMaximumCheckoutTime() { return poolMaximumCheckoutTime; } /** * Getter for the time to wait before retrying to get a connection. * * @return The time to wait */ public int getPoolTimeToWait() { return poolTimeToWait; } /** * Getter for the query to be used to check a connection. * * @return The query */ public String getPoolPingQuery() { return poolPingQuery; } /** * Getter to tell if we should use the ping query. * * @return True if we need to check a connection before using it */ public boolean isPoolPingEnabled() { return poolPingEnabled; } /** * Getter for the age of connections that should be pinged before using. * * @return The age */ public int getPoolPingConnectionsOlderThan() { return poolPingConnectionsOlderThan; } /** * Gets the expected connection type code. * * @return the expected connection type code */ private int getExpectedConnectionTypeCode() { return expectedConnectionTypeCode; } /** * Getter for the number of connection requests made. * * @return The number of connection requests made */ public long getRequestCount() { synchronized (POOL_LOCK) { return requestCount; } } /** * Getter for the average time required to get a connection to the database. * * @return The average time */ public long getAverageRequestTime() { synchronized (POOL_LOCK) { return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount; } } /** * Getter for the average time spent waiting for connections that were in use. * * @return The average time */ public long getAverageWaitTime() { synchronized (POOL_LOCK) { return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount; } } /** * Getter for the number of requests that had to wait for connections that were in use. * * @return The number of requests that had to wait */ public long getHadToWaitCount() { synchronized (POOL_LOCK) { return hadToWaitCount; } } /** * Getter for the number of invalid connections that were found in the pool. * * @return The number of invalid connections */ public long getBadConnectionCount() { synchronized (POOL_LOCK) { return badConnectionCount; } } /** * Getter for the number of connections that were claimed before they were returned. * * @return The number of connections */ public long getClaimedOverdueConnectionCount() { synchronized (POOL_LOCK) { return claimedOverdueConnectionCount; } } /** * Getter for the average age of overdue connections. * * @return The average age */ public long getAverageOverdueCheckoutTime() { synchronized (POOL_LOCK) { return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount; } } /** * Getter for the average age of a connection checkout. * * @return The average age */ public long getAverageCheckoutTime() { synchronized (POOL_LOCK) { return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount; } } /** * Returns the status of the connection pool. * * @return The status */ public String getStatus() { StringBuilder builder = new StringBuilder(); builder.append("\n==============================================================="); builder.append("\n jdbcDriver ").append(jdbcDriver); builder.append("\n jdbcUrl ").append(jdbcUrl); builder.append("\n jdbcUsername ").append(jdbcUsername); builder.append("\n jdbcPassword ").append((jdbcPassword == null ? "NULL" : "************")); builder.append("\n poolMaxActiveConnections ").append(poolMaximumActiveConnections); builder.append("\n poolMaxIdleConnections ").append(poolMaximumIdleConnections); builder.append("\n poolMaxCheckoutTime " + poolMaximumCheckoutTime); builder.append("\n poolTimeToWait " + poolTimeToWait); builder.append("\n poolPingEnabled " + poolPingEnabled); builder.append("\n poolPingQuery " + poolPingQuery); builder.append("\n poolPingConnectionsOlderThan " + poolPingConnectionsOlderThan); builder.append("\n poolPingConnectionsNotUsedFor " + poolPingConnectionsNotUsedFor); builder.append("\n --------------------------------------------------------------"); builder.append("\n activeConnections " + activeConnections.size()); builder.append("\n idleConnections " + idleConnections.size()); builder.append("\n requestCount " + getRequestCount()); builder.append("\n averageRequestTime " + getAverageRequestTime()); builder.append("\n averageCheckoutTime " + getAverageCheckoutTime()); builder.append("\n claimedOverdue " + getClaimedOverdueConnectionCount()); builder.append("\n averageOverdueCheckoutTime " + getAverageOverdueCheckoutTime()); builder.append("\n hadToWait " + getHadToWaitCount()); builder.append("\n averageWaitTime " + getAverageWaitTime()); builder.append("\n badConnectionCount " + getBadConnectionCount()); builder.append("\n==============================================================="); return builder.toString(); } /** * Closes all of the connections in the pool. */ public void forceCloseAll() { synchronized (POOL_LOCK) { for (int i = activeConnections.size(); i > 0; i--) { try { SimplePooledConnection conn = (SimplePooledConnection) activeConnections.remove(i - 1); conn.invalidate(); Connection realConn = conn.getRealConnection(); if (!realConn.getAutoCommit()) { realConn.rollback(); } realConn.close(); } catch (Exception e) { // ignore } } for (int i = idleConnections.size(); i > 0; i--) { try { SimplePooledConnection conn = (SimplePooledConnection) idleConnections.remove(i - 1); conn.invalidate(); Connection realConn = conn.getRealConnection(); if (!realConn.getAutoCommit()) { realConn.rollback(); } realConn.close(); } catch (Exception e) { // ignore } } } if (log.isDebugEnabled()) { log.debug("SimpleDataSource forcefully closed/removed all connections."); } } /** * Push connection. * * @param conn * the conn * * @throws SQLException * the SQL exception */ private void pushConnection(SimplePooledConnection conn) throws SQLException { synchronized (POOL_LOCK) { activeConnections.remove(conn); if (conn.isValid()) { if (idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == getExpectedConnectionTypeCode()) { accumulatedCheckoutTime += conn.getCheckoutTime(); if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } SimplePooledConnection newConn = new SimplePooledConnection(conn.getRealConnection(), this); idleConnections.add(newConn); newConn.setCreatedTimestamp(conn.getCreatedTimestamp()); newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp()); conn.invalidate(); if (log.isDebugEnabled()) { log.debug("Returned connection " + newConn.getRealHashCode() + " to pool."); } POOL_LOCK.notifyAll(); } else { accumulatedCheckoutTime += conn.getCheckoutTime(); if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } conn.getRealConnection().close(); if (log.isDebugEnabled()) { log.debug("Closed connection " + conn.getRealHashCode() + "."); } conn.invalidate(); } } else { if (log.isDebugEnabled()) { log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection."); } badConnectionCount++; } } } /** * Pop connection. * * @param username * the username * @param password * the password * * @return the simple pooled connection * * @throws SQLException * the SQL exception */ private SimplePooledConnection popConnection(String username, String password) throws SQLException { boolean countedWait = false; SimplePooledConnection conn = null; long t = System.currentTimeMillis(); int localBadConnectionCount = 0; while (conn == null) { synchronized (POOL_LOCK) { if (idleConnections.size() > 0) { // Pool has available connection conn = (SimplePooledConnection) idleConnections.remove(0); if (log.isDebugEnabled()) { log.debug("Checked out connection " + conn.getRealHashCode() + " from pool."); } } else { // Pool does not have available connection if (activeConnections.size() < poolMaximumActiveConnections) { // Can create new connection if (useDriverProps) { conn = new SimplePooledConnection(DriverManager.getConnection(jdbcUrl, driverProps), this); } else { conn = new SimplePooledConnection(DriverManager.getConnection(jdbcUrl, jdbcUsername, jdbcPassword), this); } Connection realConn = conn.getRealConnection(); if (realConn.getAutoCommit() != jdbcDefaultAutoCommit) { realConn.setAutoCommit(jdbcDefaultAutoCommit); } if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else { // Cannot create new connection SimplePooledConnection oldestActiveConnection = (SimplePooledConnection) activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > poolMaximumCheckoutTime) { // Can claim overdue connection claimedOverdueConnectionCount++; accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime; accumulatedCheckoutTime += longestCheckoutTime; activeConnections.remove(oldestActiveConnection); if (!oldestActiveConnection.getRealConnection().getAutoCommit()) { oldestActiveConnection.getRealConnection().rollback(); } conn = new SimplePooledConnection(oldestActiveConnection.getRealConnection(), this); oldestActiveConnection.invalidate(); if (log.isDebugEnabled()) { log.debug("Claimed overdue connection " + conn.getRealHashCode() + "."); } } else { // Must wait try { if (!countedWait) { hadToWaitCount++; countedWait = true; } if (log.isDebugEnabled()) { log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection."); } long wt = System.currentTimeMillis(); POOL_LOCK.wait(poolTimeToWait); accumulatedWaitTime += System.currentTimeMillis() - wt; } catch (InterruptedException e) { break; } } } } if (conn != null) { if (conn.isValid()) { if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } conn.setConnectionTypeCode(assembleConnectionTypeCode(jdbcUrl, username, password)); conn.setCheckoutTimestamp(System.currentTimeMillis()); conn.setLastUsedTimestamp(System.currentTimeMillis()); activeConnections.add(conn); requestCount++; accumulatedRequestTime += System.currentTimeMillis() - t; } else { if (log.isDebugEnabled()) { log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection."); } badConnectionCount++; localBadConnectionCount++; conn = null; if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) { if (log.isDebugEnabled()) { log.debug("SimpleDataSource: Could not get a good connection to the database."); } throw new SQLException("SimpleDataSource: Could not get a good connection to the database."); } } } } } if (conn == null) { if (log.isDebugEnabled()) { log.debug("SimpleDataSource: Unknown severe error condition. The connection pool returned a null connection."); } throw new SQLException( "SimpleDataSource: Unknown severe error condition. The connection pool returned a null connection."); } return conn; } /** * Method to check to see if a connection is still usable. * * @param conn * - the connection to check * * @return True if the connection is still usable */ private boolean pingConnection(SimplePooledConnection conn) { boolean result = true; try { result = !conn.getRealConnection().isClosed(); } catch (SQLException e) { if (log.isDebugEnabled()) { log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage()); } result = false; } if (result) { if (poolPingEnabled) { if ((poolPingConnectionsOlderThan > 0 && conn.getAge() > poolPingConnectionsOlderThan) || (poolPingConnectionsNotUsedFor > 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor)) { try { if (log.isDebugEnabled()) { log.debug("Testing connection " + conn.getRealHashCode() + " ..."); } Connection realConn = conn.getRealConnection(); Statement statement = realConn.createStatement(); ResultSet rs = statement.executeQuery(poolPingQuery); rs.close(); statement.close(); if (!realConn.getAutoCommit()) { realConn.rollback(); } result = true; if (log.isDebugEnabled()) { log.debug("Connection " + conn.getRealHashCode() + " is GOOD!"); } } catch (Exception e) { log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage()); try { conn.getRealConnection().close(); } catch (Exception e2) { // ignore } result = false; if (log.isDebugEnabled()) { log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage()); } } } } } return result; } /** * Unwraps a pooled connection to get to the 'real' connection. * * @param conn * - the pooled connection to unwrap * * @return The 'real' connection */ public static Connection unwrapConnection(Connection conn) { if (conn instanceof SimplePooledConnection) { return ((SimplePooledConnection) conn).getRealConnection(); } else { return conn; } } protected void finalize() throws Throwable { forceCloseAll(); } /** * --------------------------------------------------------------------------------------- SimplePooledConnection * ---------------------------------------------------------------------------------------. */ public static class SimplePooledConnection implements InvocationHandler { /** The Constant CLOSE. */ private static final String CLOSE = "close"; /** The Constant IFACES. */ private static final Class[] IFACES = new Class[] { Connection.class }; /** The hash code. */ private int hashCode = 0; /** The data source. */ private SimpleDataSource dataSource; /** The real connection. */ private Connection realConnection; /** The proxy connection. */ private Connection proxyConnection; /** The checkout timestamp. */ private long checkoutTimestamp; /** The created timestamp. */ private long createdTimestamp; /** The last used timestamp. */ private long lastUsedTimestamp; /** The connection type code. */ private int connectionTypeCode; /** The valid. */ private boolean valid; /** * Constructor for SimplePooledConnection that uses the Connection and SimpleDataSource passed in. * * @param connection * - the connection that is to be presented as a pooled connection * @param dataSource * - the dataSource that the connection is from */ public SimplePooledConnection(Connection connection, SimpleDataSource dataSource) { this.hashCode = connection.hashCode(); this.realConnection = connection; this.dataSource = dataSource; this.createdTimestamp = System.currentTimeMillis(); this.lastUsedTimestamp = System.currentTimeMillis(); this.valid = true; proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this); } /** * Invalidates the connection. */ public void invalidate() { valid = false; } /** * Method to see if the connection is usable. * * @return True if the connection is usable */ public boolean isValid() { return valid && realConnection != null && dataSource.pingConnection(this); } /** * Getter for the *real* connection that this wraps. * * @return The connection */ public Connection getRealConnection() { return realConnection; } /** * Getter for the proxy for the connection. * * @return The proxy */ public Connection getProxyConnection() { return proxyConnection; } /** * Gets the hashcode of the real connection (or 0 if it is null). * * @return The hashcode of the real connection (or 0 if it is null) */ public int getRealHashCode() { if (realConnection == null) { return 0; } else { return realConnection.hashCode(); } } /** * Getter for the connection type (based on url + user + password). * * @return The connection type */ public int getConnectionTypeCode() { return connectionTypeCode; } /** * Setter for the connection type. * * @param connectionTypeCode * - the connection type */ public void setConnectionTypeCode(int connectionTypeCode) { this.connectionTypeCode = connectionTypeCode; } /** * Getter for the time that the connection was created. * * @return The creation timestamp */ public long getCreatedTimestamp() { return createdTimestamp; } /** * Setter for the time that the connection was created. * * @param createdTimestamp * - the timestamp */ public void setCreatedTimestamp(long createdTimestamp) { this.createdTimestamp = createdTimestamp; } /** * Getter for the time that the connection was last used. * * @return - the timestamp */ public long getLastUsedTimestamp() { return lastUsedTimestamp; } /** * Setter for the time that the connection was last used. * * @param lastUsedTimestamp * - the timestamp */ public void setLastUsedTimestamp(long lastUsedTimestamp) { this.lastUsedTimestamp = lastUsedTimestamp; } /** * Getter for the time since this connection was last used. * * @return - the time since the last use */ public long getTimeElapsedSinceLastUse() { return System.currentTimeMillis() - lastUsedTimestamp; } /** * Getter for the age of the connection. * * @return the age */ public long getAge() { return System.currentTimeMillis() - createdTimestamp; } /** * Getter for the timestamp that this connection was checked out. * * @return the timestamp */ public long getCheckoutTimestamp() { return checkoutTimestamp; } /** * Setter for the timestamp that this connection was checked out. * * @param timestamp * the timestamp */ public void setCheckoutTimestamp(long timestamp) { this.checkoutTimestamp = timestamp; } /** * Getter for the time that this connection has been checked out. * * @return the time */ public long getCheckoutTime() { return System.currentTimeMillis() - checkoutTimestamp; } /** * Gets the valid connection. * * @return the valid connection */ private Connection getValidConnection() { if (!valid) { throw new RuntimeException("Error accessing SimplePooledConnection. Connection is invalid."); } return realConnection; } public int hashCode() { return hashCode; } /** * Allows comparing this connection to another * * @param obj * - the other connection to test for equality * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { if (obj instanceof SimplePooledConnection) { return realConnection.hashCode() == (((SimplePooledConnection) obj).realConnection.hashCode()); } else if (obj instanceof Connection) { return hashCode == obj.hashCode(); } else { return false; } } // ********************************** // Implemented Connection Methods -- Now handled by proxy // ********************************** /** * Required for InvocationHandler implementation. * * @param proxy * - not used * @param method * - the method to be executed * @param args * - the parameters to be passed to the method * * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { dataSource.pushConnection(this); return null; } else { try { return method.invoke(getValidConnection(), args); } catch (Throwable t) { throw ClassInfo.unwrapThrowable(t); } } } /** * Creates the statement. * * @return the statement * * @throws SQLException * the SQL exception */ public Statement createStatement() throws SQLException { return getValidConnection().createStatement(); } /** * Prepare statement. * * @param sql * the sql * * @return the prepared statement * * @throws SQLException * the SQL exception */ public PreparedStatement prepareStatement(String sql) throws SQLException { return getValidConnection().prepareStatement(sql); } /** * Prepare call. * * @param sql * the sql * * @return the callable statement * * @throws SQLException * the SQL exception */ public CallableStatement prepareCall(String sql) throws SQLException { return getValidConnection().prepareCall(sql); } /** * Native SQL. * * @param sql * the sql * * @return the string * * @throws SQLException * the SQL exception */ public String nativeSQL(String sql) throws SQLException { return getValidConnection().nativeSQL(sql); } /** * Sets the auto commit. * * @param autoCommit * the new auto commit * * @throws SQLException * the SQL exception */ public void setAutoCommit(boolean autoCommit) throws SQLException { getValidConnection().setAutoCommit(autoCommit); } /** * Gets the auto commit. * * @return the auto commit * * @throws SQLException * the SQL exception */ public boolean getAutoCommit() throws SQLException { return getValidConnection().getAutoCommit(); } /** * Commit. * * @throws SQLException * the SQL exception */ public void commit() throws SQLException { getValidConnection().commit(); } /** * Rollback. * * @throws SQLException * the SQL exception */ public void rollback() throws SQLException { getValidConnection().rollback(); } /** * Close. * * @throws SQLException * the SQL exception */ public void close() throws SQLException { dataSource.pushConnection(this); } /** * Checks if is closed. * * @return true, if is closed * * @throws SQLException * the SQL exception */ public boolean isClosed() throws SQLException { return getValidConnection().isClosed(); } /** * Gets the meta data. * * @return the meta data * * @throws SQLException * the SQL exception */ public DatabaseMetaData getMetaData() throws SQLException { return getValidConnection().getMetaData(); } /** * Sets the read only. * * @param readOnly * the new read only * * @throws SQLException * the SQL exception */ public void setReadOnly(boolean readOnly) throws SQLException { getValidConnection().setReadOnly(readOnly); } /** * Checks if is read only. * * @return true, if is read only * * @throws SQLException * the SQL exception */ public boolean isReadOnly() throws SQLException { return getValidConnection().isReadOnly(); } /** * Sets the catalog. * * @param catalog * the new catalog * * @throws SQLException * the SQL exception */ public void setCatalog(String catalog) throws SQLException { getValidConnection().setCatalog(catalog); } /** * Gets the catalog. * * @return the catalog * * @throws SQLException * the SQL exception */ public String getCatalog() throws SQLException { return getValidConnection().getCatalog(); } /** * Sets the transaction isolation. * * @param level * the new transaction isolation * * @throws SQLException * the SQL exception */ public void setTransactionIsolation(int level) throws SQLException { getValidConnection().setTransactionIsolation(level); } /** * Gets the transaction isolation. * * @return the transaction isolation * * @throws SQLException * the SQL exception */ public int getTransactionIsolation() throws SQLException { return getValidConnection().getTransactionIsolation(); } /** * Gets the warnings. * * @return the warnings * * @throws SQLException * the SQL exception */ public SQLWarning getWarnings() throws SQLException { return getValidConnection().getWarnings(); } /** * Clear warnings. * * @throws SQLException * the SQL exception */ public void clearWarnings() throws SQLException { getValidConnection().clearWarnings(); } /** * Creates the statement. * * @param resultSetType * the result set type * @param resultSetConcurrency * the result set concurrency * * @return the statement * * @throws SQLException * the SQL exception */ public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return getValidConnection().createStatement(resultSetType, resultSetConcurrency); } /** * Prepare statement. * * @param sql * the sql * @param resultSetType * the result set type * @param resultSetConcurrency * the result set concurrency * * @return the prepared statement * * @throws SQLException * the SQL exception */ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency); } /** * Prepare call. * * @param sql * the sql * @param resultSetType * the result set type * @param resultSetConcurrency * the result set concurrency * * @return the callable statement * * @throws SQLException * the SQL exception */ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency); } /** * Gets the type map. * * @return the type map * * @throws SQLException * the SQL exception */ public Map getTypeMap() throws SQLException { return getValidConnection().getTypeMap(); } /** * Sets the type map. * * @param map * the new type map * * @throws SQLException * the SQL exception */ public void setTypeMap(Map map) throws SQLException { getValidConnection().setTypeMap(map); } // ********************************** // JDK 1.4 JDBC 3.0 Methods below // ********************************** /** * Sets the holdability. * * @param holdability * the new holdability * * @throws SQLException * the SQL exception */ public void setHoldability(int holdability) throws SQLException { getValidConnection().setHoldability(holdability); } /** * Gets the holdability. * * @return the holdability * * @throws SQLException * the SQL exception */ public int getHoldability() throws SQLException { return getValidConnection().getHoldability(); } /** * Sets the savepoint. * * @return the savepoint * * @throws SQLException * the SQL exception */ public Savepoint setSavepoint() throws SQLException { return getValidConnection().setSavepoint(); } /** * Sets the savepoint. * * @param name * the name * * @return the savepoint * * @throws SQLException * the SQL exception */ public Savepoint setSavepoint(String name) throws SQLException { return getValidConnection().setSavepoint(name); } /** * Rollback. * * @param savepoint * the savepoint * * @throws SQLException * the SQL exception */ public void rollback(Savepoint savepoint) throws SQLException { getValidConnection().rollback(savepoint); } /** * Release savepoint. * * @param savepoint * the savepoint * * @throws SQLException * the SQL exception */ public void releaseSavepoint(Savepoint savepoint) throws SQLException { getValidConnection().releaseSavepoint(savepoint); } /** * Creates the statement. * * @param resultSetType * the result set type * @param resultSetConcurrency * the result set concurrency * @param resultSetHoldability * the result set holdability * * @return the statement * * @throws SQLException * the SQL exception */ public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return getValidConnection().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); } /** * Prepare statement. * * @param sql * the sql * @param resultSetType * the result set type * @param resultSetConcurrency * the result set concurrency * @param resultSetHoldability * the result set holdability * * @return the prepared statement * * @throws SQLException * the SQL exception */ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return getValidConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } /** * Prepare call. * * @param sql * the sql * @param resultSetType * the result set type * @param resultSetConcurrency * the result set concurrency * @param resultSetHoldability * the result set holdability * * @return the callable statement * * @throws SQLException * the SQL exception */ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return getValidConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } /** * Prepare statement. * * @param sql * the sql * @param autoGeneratedKeys * the auto generated keys * * @return the prepared statement * * @throws SQLException * the SQL exception */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return getValidConnection().prepareStatement(sql, autoGeneratedKeys); } /** * Prepare statement. * * @param sql * the sql * @param columnIndexes * the column indexes * * @return the prepared statement * * @throws SQLException * the SQL exception */ public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException { return getValidConnection().prepareStatement(sql, columnIndexes); } /** * Prepare statement. * * @param sql * the sql * @param columnNames * the column names * * @return the prepared statement * * @throws SQLException * the SQL exception */ public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException { return getValidConnection().prepareStatement(sql, columnNames); } } public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO Auto-generated method stub return null; } public T unwrap(Class iface) throws SQLException { // TODO Auto-generated method stub return null; } public boolean isWrapperFor(Class iface) throws SQLException { // TODO Auto-generated method stub return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy