diff options
Diffstat (limited to 'dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java')
-rw-r--r-- | dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java | 795 |
1 files changed, 795 insertions, 0 deletions
diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java new file mode 100644 index 0000000..b704c7f --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java @@ -0,0 +1,795 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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 + * + * http://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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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 org.apache.tomcat.jdbc.pool; + + +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; + +import com.mysql.jdbc.Driver; + +/** + * Represents a pooled connection + * and holds a reference to the {@link java.sql.Connection} object + * @version 1.0 + */ +public class PooledConnection { + /** + * Logger + */ + private static final Log log = LogFactory.getLog(PooledConnection.class); + + public static final String PROP_USER = PoolUtilities.PROP_USER; + + public static final String PROP_PASSWORD = PoolUtilities.PROP_PASSWORD; + + /** + * Validate when connection is borrowed flag + */ + public static final int VALIDATE_BORROW = 1; + /** + * Validate when connection is returned flag + */ + public static final int VALIDATE_RETURN = 2; + /** + * Validate when connection is idle flag + */ + public static final int VALIDATE_IDLE = 3; + /** + * Validate when connection is initialized flag + */ + public static final int VALIDATE_INIT = 4; + /** + * The properties for the connection pool + */ + protected PoolConfiguration poolProperties; + /** + * The underlying database connection + */ + private volatile java.sql.Connection connection; + + /** + * If using a XAConnection underneath. + */ + protected volatile javax.sql.XAConnection xaConnection; + /** + * When we track abandon traces, this string holds the thread dump + */ + private String abandonTrace = null; + /** + * Timestamp the connection was last 'touched' by the pool + */ + private volatile long timestamp; + /** + * Lock for this connection only + */ + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false); + /** + * Set to true if this connection has been discarded by the pool + */ + private volatile boolean discarded = false; + /** + * The Timestamp when the last time the connect() method was called successfully + */ + private volatile long lastConnected = -1; + /** + * timestamp to keep track of validation intervals + */ + private volatile long lastValidated = System.currentTimeMillis(); + /** + * The parent + */ + protected ConnectionPool parent; + + private HashMap<Object, Object> attributes = new HashMap<>(); + + private volatile long connectionVersion=0; + + /** + * Weak reference to cache the list of interceptors for this connection + * so that we don't create a new list of interceptors each time we borrow + * the connection + */ + private volatile JdbcInterceptor handler = null; + + private AtomicBoolean released = new AtomicBoolean(false); + + private volatile boolean suspect = false; + + private java.sql.Driver driver = null; + + /** + * Constructor + * @param prop - pool properties + * @param parent - the parent connection pool + */ + public PooledConnection(PoolConfiguration prop, ConnectionPool parent) { + poolProperties = prop; + this.parent = parent; + connectionVersion = parent.getPoolVersion(); + } + + public long getConnectionVersion() { + return connectionVersion; + } + + /** + * @deprecated use {@link #shouldForceReconnect(String, String)} + * method kept since it was public, to avoid changing interface. + * @param username The user name + * @param password The password + * @return <code>true</code>if the pool does not need to reconnect + */ + @Deprecated + public boolean checkUser(String username, String password) { + return !shouldForceReconnect(username, password); + } + + /** + * Returns true if we must force reconnect based on credentials passed in. + * Returns false if {@link PoolConfiguration#isAlternateUsernameAllowed()} method returns false. + * Returns false if the username/password has not changed since this connection was connected + * @param username the username you wish to connect with, pass in null to accept the default username from {@link PoolConfiguration#getUsername()} + * @param password the password you wish to connect with, pass in null to accept the default username from {@link org.apache.tomcat.jdbc.pool.PoolConfiguration#getPassword()} + * @return true is the pool must reconnect + */ + public boolean shouldForceReconnect(String username, String password) { + + if (!getPoolProperties().isAlternateUsernameAllowed()) return false; + + if (username==null) username = poolProperties.getUsername(); + if (password==null) password = poolProperties.getPassword(); + + String storedUsr = (String)getAttributes().get(PROP_USER); + String storedPwd = (String)getAttributes().get(PROP_PASSWORD); + + boolean noChangeInCredentials = (username==null && storedUsr==null); + noChangeInCredentials = (noChangeInCredentials || (username!=null && username.equals(storedUsr))); + + noChangeInCredentials = noChangeInCredentials && ((password==null && storedPwd==null) || (password!=null && password.equals(storedPwd))); + + if (username==null) getAttributes().remove(PROP_USER); else getAttributes().put(PROP_USER, username); + if (password==null) getAttributes().remove(PROP_PASSWORD); else getAttributes().put(PROP_PASSWORD, password); + + return !noChangeInCredentials; + } + + /** + * Connects the underlying connection to the database. + * @throws SQLException if the method {@link #release()} has been called. + * @throws SQLException if driver instantiation fails + * @throws SQLException if a call to {@link java.sql.Driver#connect(String, java.util.Properties)} fails. + * @throws SQLException if default properties are configured and a call to + * {@link java.sql.Connection#setAutoCommit(boolean)}, {@link java.sql.Connection#setCatalog(String)}, + * {@link java.sql.Connection#setTransactionIsolation(int)} or {@link java.sql.Connection#setReadOnly(boolean)} fails. + */ + public void connect() throws SQLException { + if (released.get()) throw new SQLException("A connection once released, can't be reestablished."); + if (connection != null) { + try { + this.disconnect(false); + } catch (Exception x) { + log.debug("Unable to disconnect previous connection.", x); + } //catch + } //end if + if (poolProperties.getDataSource()==null && poolProperties.getDataSourceJNDI()!=null) { + //TODO lookup JNDI name + } + + if (poolProperties.getDataSource()!=null) { + connectUsingDataSource(); + } else { + connectUsingDriver(); + } + + //set up the default state, unless we expect the interceptor to do it + if (poolProperties.getJdbcInterceptors()==null || poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getName())<0 || + poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getSimpleName())<0) { + if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation()); + if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue()); + if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue()); + if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog()); + } + this.discarded = false; + this.lastConnected = System.currentTimeMillis(); + } + + protected void connectUsingDataSource() throws SQLException { + String usr = null; + String pwd = null; + if (getAttributes().containsKey(PROP_USER)) { + usr = (String) getAttributes().get(PROP_USER); + } else { + usr = poolProperties.getUsername(); + getAttributes().put(PROP_USER, usr); + } + if (getAttributes().containsKey(PROP_PASSWORD)) { + pwd = (String) getAttributes().get(PROP_PASSWORD); + } else { + pwd = poolProperties.getPassword(); + getAttributes().put(PROP_PASSWORD, pwd); + } + if (poolProperties.getDataSource() instanceof javax.sql.XADataSource) { + javax.sql.XADataSource xds = (javax.sql.XADataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + xaConnection = xds.getXAConnection(usr, pwd); + connection = xaConnection.getConnection(); + } else { + xaConnection = xds.getXAConnection(); + connection = xaConnection.getConnection(); + } + } else if (poolProperties.getDataSource() instanceof javax.sql.DataSource){ + javax.sql.DataSource ds = (javax.sql.DataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + connection = ds.getConnection(usr, pwd); + } else { + connection = ds.getConnection(); + } + } else if (poolProperties.getDataSource() instanceof javax.sql.ConnectionPoolDataSource){ + javax.sql.ConnectionPoolDataSource ds = (javax.sql.ConnectionPoolDataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + connection = ds.getPooledConnection(usr, pwd).getConnection(); + } else { + connection = ds.getPooledConnection().getConnection(); + } + } else { + throw new SQLException("DataSource is of unknown class:"+(poolProperties.getDataSource()!=null?poolProperties.getDataSource().getClass():"null")); + } + } + protected void connectUsingDriver() throws SQLException { + + try { + Class.forName("com.mysql.jdbc.Driver") ; + Driver dr = new com.mysql.jdbc.Driver(); + if(dr == null) + log.warn("Driver NOT CREATED"); + } catch (ClassNotFoundException e) { + log.warn("Driver NOT CREATED", e); + } + + try { + if (driver==null) { + if (log.isDebugEnabled()) { + log.debug("Instantiating driver using class: "+poolProperties.getDriverClassName()+" [url="+poolProperties.getUrl()+"]"); + } + if (poolProperties.getDriverClassName()==null) { + //rely on DriverManager + log.warn("Not loading a JDBC driver as driverClassName property is null."); + } else { + driver = (java.sql.Driver) + ClassLoaderUtil.loadClass( + poolProperties.getDriverClassName(), + PooledConnection.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ).newInstance(); + } + } + } catch (java.lang.Exception cn) { + if (log.isDebugEnabled()) { + log.debug("Unable to instantiate JDBC driver.", cn); + } + SQLException ex = new SQLException(cn.getMessage()); + ex.initCause(cn); + throw ex; + } + String driverURL = poolProperties.getUrl(); + String usr = null; + String pwd = null; + if (getAttributes().containsKey(PROP_USER)) { + usr = (String) getAttributes().get(PROP_USER); + } else { + usr = poolProperties.getUsername(); + getAttributes().put(PROP_USER, usr); + } + if (getAttributes().containsKey(PROP_PASSWORD)) { + pwd = (String) getAttributes().get(PROP_PASSWORD); + } else { + pwd = poolProperties.getPassword(); + getAttributes().put(PROP_PASSWORD, pwd); + } + Properties properties = PoolUtilities.clone(poolProperties.getDbProperties()); + if (usr != null) properties.setProperty(PROP_USER, usr); + if (pwd != null) properties.setProperty(PROP_PASSWORD, pwd); + + try { + if (driver==null) { + connection = DriverManager.getConnection(driverURL, properties); + } else { + connection = driver.connect(driverURL, properties); + } + } catch (Exception x) { + if (log.isDebugEnabled()) { + log.debug("Unable to connect to database.", x); + } + if (parent.jmxPool!=null) { + parent.jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_CONNECT, + ConnectionPool.getStackTrace(x)); + } + if (x instanceof SQLException) { + throw (SQLException)x; + } else { + SQLException ex = new SQLException(x.getMessage()); + ex.initCause(x); + throw ex; + } + } + if (connection==null) { + throw new SQLException("Driver:"+driver+" returned null for URL:"+driverURL); + } + } + + /** + * + * @return true if connect() was called successfully and disconnect has not yet been called + */ + public boolean isInitialized() { + return connection!=null; + } + + /** + * Returns true if the connection has been connected more than + * {@link PoolConfiguration#getMaxAge()} milliseconds. false otherwise. + * @return Returns true if the connection has been connected more than + * {@link PoolConfiguration#getMaxAge()} milliseconds. false otherwise. + */ + public boolean isMaxAgeExpired() { + if (getPoolProperties().getMaxAge()>0 ) { + return (System.currentTimeMillis() - getLastConnected()) > getPoolProperties().getMaxAge(); + } else { + return false; + } + } + /** + * Issues a call to {@link #disconnect(boolean)} with the argument false followed by a call to + * {@link #connect()} + * @throws SQLException if the call to {@link #connect()} fails. + */ + public void reconnect() throws SQLException { + this.disconnect(false); + this.connect(); + } //reconnect + + /** + * Disconnects the connection. All exceptions are logged using debug level. + * @param finalize if set to true, a call to {@link ConnectionPool#finalize(PooledConnection)} is called. + */ + private void disconnect(boolean finalize) { + if (isDiscarded() && connection == null) { + return; + } + setDiscarded(true); + if (connection != null) { + try { + parent.disconnectEvent(this, finalize); + if (xaConnection == null) { + connection.close(); + } else { + xaConnection.close(); + } + }catch (Exception ignore) { + if (log.isDebugEnabled()) { + log.debug("Unable to close underlying SQL connection",ignore); + } + } + } + connection = null; + xaConnection = null; + lastConnected = -1; + if (finalize) parent.finalize(this); + } + + +//============================================================================ +// +//============================================================================ + + /** + * Returns abandon timeout in milliseconds + * @return abandon timeout in milliseconds + */ + public long getAbandonTimeout() { + if (poolProperties.getRemoveAbandonedTimeout() <= 0) { + return Long.MAX_VALUE; + } else { + return poolProperties.getRemoveAbandonedTimeout() * 1000L; + } //end if + } + + /** + * Returns <code>true</code> if the connection pool is configured + * to do validation for a certain action. + * @param action The validation action + */ + private boolean doValidate(int action) { + if (action == PooledConnection.VALIDATE_BORROW && + poolProperties.isTestOnBorrow()) + return true; + else if (action == PooledConnection.VALIDATE_RETURN && + poolProperties.isTestOnReturn()) + return true; + else if (action == PooledConnection.VALIDATE_IDLE && + poolProperties.isTestWhileIdle()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.isTestOnConnect()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.getInitSQL()!=null) + return true; + else + return false; + } + + /** + * Returns <code>true</code> if the object is still valid. if not + * the pool will call the getExpiredAction() and follow up with one + * of the four expired methods + * @param validateAction The value + * @return <code>true</code> if the connection is valid + */ + public boolean validate(int validateAction) { + return validate(validateAction,null); + } + + /** + * Validates a connection. + * @param validateAction the action used. One of {@link #VALIDATE_BORROW}, {@link #VALIDATE_IDLE}, + * {@link #VALIDATE_INIT} or {@link #VALIDATE_RETURN} + * @param sql the SQL to be used during validation. If the {@link PoolConfiguration#setInitSQL(String)} has been called with a non null + * value and the action is {@link #VALIDATE_INIT} the init SQL will be used for validation. + * + * @return true if the connection was validated successfully. It returns true even if validation was not performed, such as when + * {@link PoolConfiguration#setValidationInterval(long)} has been called with a positive value. + * <p> + * false if the validation failed. The caller should close the connection if false is returned since a session could have been left in + * an unknown state during initialization. + */ + public boolean validate(int validateAction,String sql) { + if (this.isDiscarded()) { + return false; + } + + if (!doValidate(validateAction)) { + //no validation required, no init sql and props not set + return true; + } + + //Don't bother validating if already have recently enough + long now = System.currentTimeMillis(); + if (validateAction!=VALIDATE_INIT && + poolProperties.getValidationInterval() > 0 && + (now - this.lastValidated) < + poolProperties.getValidationInterval()) { + return true; + } + + if (poolProperties.getValidator() != null) { + if (poolProperties.getValidator().validate(connection, validateAction)) { + this.lastValidated = now; + return true; + } else { + if (getPoolProperties().getLogValidationErrors()) { + log.error("Custom validation through "+poolProperties.getValidator()+" failed."); + } + return false; + } + } + + String query = sql; + + if (validateAction == VALIDATE_INIT && poolProperties.getInitSQL() != null) { + query = poolProperties.getInitSQL(); + } + + if (query == null) { + query = poolProperties.getValidationQuery(); + } + + if (query == null) { + int validationQueryTimeout = poolProperties.getValidationQueryTimeout(); + if (validationQueryTimeout < 0) validationQueryTimeout = 0; + try { + if (connection.isValid(validationQueryTimeout)) { + this.lastValidated = now; + return true; + } else { + if (getPoolProperties().getLogValidationErrors()) { + log.error("isValid() returned false."); + } + return false; + } + } catch (SQLException e) { + if (getPoolProperties().getLogValidationErrors()) { + log.error("isValid() failed.", e); + } else if (log.isDebugEnabled()) { + log.debug("isValid() failed.", e); + } + return false; + } + } + + Statement stmt = null; + try { + stmt = connection.createStatement(); + + int validationQueryTimeout = poolProperties.getValidationQueryTimeout(); + if (validationQueryTimeout > 0) { + stmt.setQueryTimeout(validationQueryTimeout); + } + + stmt.execute(query); + stmt.close(); + this.lastValidated = now; + return true; + } catch (Exception ex) { + if (getPoolProperties().getLogValidationErrors()) { + log.warn("SQL Validation error", ex); + } else if (log.isDebugEnabled()) { + log.debug("Unable to validate object:",ex); + } + if (stmt!=null) + try { stmt.close();} catch (Exception ignore2){/*NOOP*/} + } + return false; + } //validate + + /** + * The time limit for how long the object + * can remain unused before it is released + * @return {@link PoolConfiguration#getMinEvictableIdleTimeMillis()} + */ + public long getReleaseTime() { + return this.poolProperties.getMinEvictableIdleTimeMillis(); + } + + /** + * This method is called if (Now - timeCheckedIn > getReleaseTime()) + * This method disconnects the connection, logs an error in debug mode if it happens + * then sets the {@link #released} flag to false. Any attempts to connect this cached object again + * will fail per {@link #connect()} + * The connection pool uses the atomic return value to decrement the pool size counter. + * @return true if this is the first time this method has been called. false if this method has been called before. + */ + public boolean release() { + try { + disconnect(true); + } catch (Exception x) { + if (log.isDebugEnabled()) { + log.debug("Unable to close SQL connection",x); + } + } + return released.compareAndSet(false, true); + + } + + /** + * The pool will set the stack trace when it is check out and + * checked in + * @param trace the stack trace for this connection + */ + + public void setStackTrace(String trace) { + abandonTrace = trace; + } + + /** + * Returns the stack trace from when this connection was borrowed. Can return null if no stack trace was set. + * @return the stack trace or null of no trace was set + */ + public String getStackTrace() { + return abandonTrace; + } + + /** + * Sets a timestamp on this connection. A timestamp usually means that some operation + * performed successfully. + * @param timestamp the timestamp as defined by {@link System#currentTimeMillis()} + */ + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + setSuspect(false); + } + + + public boolean isSuspect() { + return suspect; + } + + public void setSuspect(boolean suspect) { + this.suspect = suspect; + } + + /** + * An interceptor can call this method with the value true, and the connection will be closed when it is returned to the pool. + * @param discarded - only valid value is true + * @throws IllegalStateException if this method is called with the value false and the value true has already been set. + */ + public void setDiscarded(boolean discarded) { + if (this.discarded && !discarded) throw new IllegalStateException("Unable to change the state once the connection has been discarded"); + this.discarded = discarded; + } + + /** + * Set the timestamp the connection was last validated. + * This flag is used to keep track when we are using a {@link PoolConfiguration#setValidationInterval(long) validation-interval}. + * @param lastValidated a timestamp as defined by {@link System#currentTimeMillis()} + */ + public void setLastValidated(long lastValidated) { + this.lastValidated = lastValidated; + } + + /** + * Sets the pool configuration for this connection and connection pool. + * Object is shared with the {@link ConnectionPool} + * @param poolProperties The pool properties + */ + public void setPoolProperties(PoolConfiguration poolProperties) { + this.poolProperties = poolProperties; + } + + /** + * Return the timestamps of last pool action. Timestamps are typically set when connections + * are borrowed from the pool. It is used to keep track of {@link PoolConfiguration#setRemoveAbandonedTimeout(int) abandon-timeouts}. + * This timestamp can also be reset by the {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer#invoke(Object, java.lang.reflect.Method, Object[])} + * @return the timestamp of the last pool action as defined by {@link System#currentTimeMillis()} + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Returns the discarded flag. + * @return the discarded flag. If the value is true, + * either {@link #disconnect(boolean)} has been called or it will be called when the connection is returned to the pool. + */ + public boolean isDiscarded() { + return discarded; + } + + /** + * Returns the timestamp of the last successful validation query execution. + * @return the timestamp of the last successful validation query execution as defined by {@link System#currentTimeMillis()} + */ + public long getLastValidated() { + return lastValidated; + } + + /** + * Returns the configuration for this connection and pool + * @return the configuration for this connection and pool + */ + public PoolConfiguration getPoolProperties() { + return poolProperties; + } + + /** + * Locks the connection only if either {@link PoolConfiguration#isPoolSweeperEnabled()} or + * {@link PoolConfiguration#getUseLock()} return true. The per connection lock ensures thread safety is + * multiple threads are performing operations on the connection. + * Otherwise this is a noop for performance + */ + public void lock() { + if (poolProperties.getUseLock() || this.poolProperties.isPoolSweeperEnabled()) { + //optimized, only use a lock when there is concurrency + lock.writeLock().lock(); + } + } + + /** + * Unlocks the connection only if the sweeper is enabled + * Otherwise this is a noop for performance + */ + public void unlock() { + if (poolProperties.getUseLock() || this.poolProperties.isPoolSweeperEnabled()) { + //optimized, only use a lock when there is concurrency + lock.writeLock().unlock(); + } + } + + /** + * Returns the underlying connection + * @return the underlying JDBC connection as it was returned from the JDBC driver + * @see javax.sql.PooledConnection#getConnection() + */ + public java.sql.Connection getConnection() { + return this.connection; + } + + /** + * Returns the underlying XA connection + * @return the underlying XA connection as it was returned from the Datasource + */ + public javax.sql.XAConnection getXAConnection() { + return this.xaConnection; + } + + + /** + * Returns the timestamp of when the connection was last connected to the database. + * ie, a successful call to {@link java.sql.Driver#connect(String, java.util.Properties)}. + * @return the timestamp when this connection was created as defined by {@link System#currentTimeMillis()} + */ + public long getLastConnected() { + return lastConnected; + } + + /** + * Returns the first handler in the interceptor chain + * @return the first interceptor for this connection + */ + public JdbcInterceptor getHandler() { + return handler; + } + + public void setHandler(JdbcInterceptor handler) { + if (this.handler!=null && this.handler!=handler) { + JdbcInterceptor interceptor = this.handler; + while (interceptor!=null) { + interceptor.reset(null, null); + interceptor = interceptor.getNext(); + }//while + }//end if + this.handler = handler; + } + + @Override + public String toString() { + return "PooledConnection["+(connection!=null?connection.toString():"null")+"]"; + } + + /** + * Returns true if this connection has been released and wont be reused. + * @return true if the method {@link #release()} has been called + */ + public boolean isReleased() { + return released.get(); + } + + public HashMap<Object,Object> getAttributes() { + return attributes; + } + +} |