| /* |
| * 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; |
| |
| /** |
| * 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. name was pooo |
| */ |
| @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 { |
| 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() |
| ).getConstructor().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 true if the connection pool is configured |
| * to do validation for a certain action. |
| * @param 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 true if the object is still valid. if not |
| * the pool will call the getExpiredAction() and follow up with one |
| * of the four expired methods |
| */ |
| 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.error("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*/} |
| |
| try { |
| if(!connection.getAutoCommit()) { |
| connection.rollback(); |
| } |
| } catch (SQLException e) { |
| // do nothing |
| } |
| } finally { |
| try { |
| if(!connection.getAutoCommit()) { |
| connection.commit(); |
| } |
| } catch (SQLException e) { |
| // do nothing |
| } |
| } |
| 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 |
| */ |
| 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; |
| } |
| |
| public void clearWarnings() { |
| try { |
| connection.clearWarnings(); |
| } catch (SQLException e) { |
| log.warn("Unable to clear Warnings, connection will be closed.", e); |
| } |
| } |
| } |