Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * $Id$
   * 
   * Firebird Open Source J2ee connector - jdbc driver
   *
   * Distributable under LGPL license.
   * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
   *
   * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * LGPL License for more details.
  *
  * This file was created by members of the firebird development team.
  * All individual contributions remain the Copyright (C) of those
  * individuals.  Contributors to this file are either listed here or
  * can be obtained from a CVS history command.
  *
  * All rights reserved.
  */
 
 package org.firebirdsql.pool;
 
 import static org.firebirdsql.ds.ReflectionHelper.getAllInterfaces;
 
 import java.sql.*;
 
 import javax.sql.*;
 
 
 import java.util.*;

This class implements javax.sql.PooledConnection interface.

Author(s):
Roman Rokytskyy
 
 public abstract class AbstractPingablePooledConnection implements PooledConnection,
 
     private static final boolean LOG_PREPARE_STATEMENT = .;
     private static final boolean LOG_POOL_CLEANING = .;
     
     private static final boolean LOG_META_DATA = .;
 
     private static final Logger log = LoggerFactory.getLogger(PingablePooledConnection.classfalse);
 
     protected Connection jdbcConnection;
     private final HashSet eventListeners = new HashSet();
 
     private boolean invalid;
     private boolean inPool;
     private long instantInPool = ;
 
 
     private String pingStatement;
     private long lastPingTime = System.currentTimeMillis();
     private int pingInterval = 0;
     
     private int maxStatements;
     private boolean keepStatements;
 
     private boolean supportsStatementsAccrossCommit;
     private boolean supportsStatementsAccrossRollback;
     private boolean statementPooling;
     
     private int transactionIsolation = -1;
 
     private final HashMap statements = new HashMap();
 
 
     protected Logger getLogChannel() {
         return ;
     }
 
     protected AbstractPingablePooledConnection(Connection connection
                                        boolean statementPooling
                                        /*int transactionIsolation,*/
                                        int maxStatementsboolean keepStatementsPooledConnectionQueue owningQueue)
         throws SQLException 
     {
         this. = connection;
         this. = statementPooling;
         //this.transactionIsolation = transactionIsolation;
         this. = maxStatements;
         this. = keepStatements;
         this. = new WeakReference<PooledConnectionQueue>(owningQueue);
 
         this. =
             connection.getMetaData().supportsOpenStatementsAcrossCommit();
 
        if ( && getLogChannel() != null) {
            getLogChannel().info(
                "Pool supports open statements across commit : " +
                );
        }
        this. =
            connection.getMetaData().supportsOpenStatementsAcrossRollback();
        if ( && getLogChannel() != null) {
            getLogChannel().info(
                "Pool supports open statements across rollback : " +
                );
        }
    }
    protected AbstractPingablePooledConnection(Connection connection,
        String pingStatementint pingIntervalboolean statementPooling
        int maxStatementsboolean keepStatementsPooledConnectionQueue owningQueue)
        throws SQLException 
    {
        this(connectionstatementPooling/*transactionIsolation,*/ maxStatementskeepStatementsowningQueue);
        this. = pingStatement;
        this. = pingInterval;
    }
    
    public void setDefaultTransactionIsolation(int isolation) {
        this. = isolation;
    }
    public long getLastPingTime() {
        return ;
    }
    
    public boolean isStatementPooling() {
        return ;
    }
    public boolean isKeepStatements() {
        return ;
    }
    
    
Ping connection by executing a ping statement.
    public synchronized boolean ping() {
        if ( == null) {
            return false;
        }
        try {
            Statement stmt = null;
            try {
                stmt = .createStatement();
                ResultSet rs = stmt.executeQuery();
                return rs.next();
            } finally {
                if (stmt != null) {
                    stmt.close();
                }
                 = System.currentTimeMillis();
            }
        } catch (SQLException sqlex) {
            return false;
        }
    }

    
Invalidate this instance. After invalidating, no operation can be executed on this instance.
    private void invalidate() {
         = true;
    }

    
Check if instance has correct state.

Throws:
java.lang.IllegalStateException if instance has illegal state.
    private void checkValidity() {
        if () {
            throw new IllegalStateException(
                "Cannot execute desired operation " +
                "because pooled connection has invalid state.");
        }
    }

    
Check if this pooled connection is still valid.

Returns:
true if this pooled connection is still valid.
    public boolean isValid() {
        if () {
            return false;
        }
        if ( > 0 &&
            (System.currentTimeMillis() - ) >  &&
             != null) {
            return ping();
        } else {
            return true;
        }
    }
    
    
Check whether this object is currently in pool or had been released to the application.

Returns:
true if the object is currently in pool.
    public synchronized boolean isInPool() {
        return ;
    }
    
    
Set the "inPool" flag to this object. This method should be called only by the pool implementation.

Parameters:
inPool true if object is in pool, otherwise false.
    public synchronized void setInPool(boolean inPool) {
        this. = inPool;
         = inPool ? System.currentTimeMillis() : .;
    }
    public synchronized long getInstantInPool() {
        return ;
    }
    private synchronized void checkInPool() throws SQLException {
        if ()
            throw new FBSQLException(
                "Physical connection is currently in pool, you cannot allocate logical connections now.");
    }
    
    
Add connection listener to be notified about connection events.

Parameters:
listener listener to add.
    public synchronized
        void addConnectionEventListener(ConnectionEventListener listener) {
        .add(listener);
    }

    
Remove connection listener from this pooled connection.

Parameters:
listener listener to remove.
    public synchronized
        void removeConnectionEventListener(ConnectionEventListener listener) {
        .remove(listener);
    }

    
Close this pooled connection. This operation closes physical connection to the database. Should not be called by applications directly.

    public void close() throws SQLException {
        internalClose();
        
        ConnectionEvent event = new ConnectionEvent(this);
        
        List tempListeners = new ArrayList();
        
        Iterator iter = tempListeners.iterator();
        while (iter.hasNext()) {
            ConnectionEventListener listener = 
                (ConnectionEventListener)iter.next();
            
            if (!(listener instanceof PooledConnectionEventListener))
                continue;
            
            PooledConnectionEventListener pooledEventListener = 
                (PooledConnectionEventListener)listener;
            
            pooledEventListener.physicalConnectionClosed(event);
        }
    }
    
    
Close this connection.

Throws:
java.sql.SQLException if something went wrong.
    protected void internalClose() throws SQLException {
        checkValidity();
        if ( != null
            .deallocate();
        
        .close();
        .clear();
        
        invalidate();
    }
    
    
Deallocate this object.
	public void deallocate() {
        try {
            internalClose();
        } catch(SQLException ex) {
            if ( != null)
                .warn("Could not cleanly deallocate connection."ex);
            
        } finally {
            // and finally notify about the event
            ConnectionEvent event = new ConnectionEvent(this);
            
            List tempListeners = new ArrayList();
            
            Iterator iter = tempListeners.iterator();
            while (iter.hasNext()) {
                ConnectionEventListener listener = 
                    (ConnectionEventListener)iter.next();
                
                if (!(listener instanceof PooledConnectionEventListener))
                    continue;
                
                PooledConnectionEventListener pooledEventListener = 
                    (PooledConnectionEventListener)listener;
                
                pooledEventListener.physicalConnectionDeallocated(event);
            }
        }
	}

    
Get JDBC connection corresponding to this pooled connection instance.

Returns:
instance of java.sql.Connection
Throws:
java.sql.SQLException if some error happened.
    public
        Connection getConnection() throws SQLException {
        checkValidity();
        
        checkInPool();
        if ( != null) {
            throw new IllegalStateException(
                "Cannot provide new connection while old one is still in use.");
            //currentConnection.close();
        }
        Connection result = .getProxy();
        
        configureConnectionDefaults(result);
        return result;
    }

    
Configure default values for this connection.

Parameters:
connection instance of java.sql.Connection to configure.
Throws:
java.sql.SQLException if something went wrong.
    protected void configureConnectionDefaults(Connection connectionthrows SQLException {
        connection.setAutoCommit(true);
        connection.setReadOnly(false);
        
        if ( != -1)
            connection.setTransactionIsolation();
    }

    
Handle java.sql.Connection.prepareStatement(java.lang.String) method call. This method check internal cache first and returns prepared statement if found. Otherwise, it prepares statement and caches it.

Parameters:
statement statement to prepare.
resultSetType result set type.
resultSetConcurrency result set concurrency.
resultSetHoldability result set holdability.
Returns:
instance of java.sql.PreparedStatement corresponding to the statement.
Throws:
java.sql.SQLException if there was problem preparing statement.
    public PreparedStatement getPreparedStatement(String statement,
        int resultSetTypeint resultSetConcurrencyint resultSetHoldabilitythrows SQLException {
        
        if (!isStatementPooling())
            return .prepareStatement(
                statementresultSetTyperesultSetConcurrencyresultSetHoldability);
        
        synchronized () {
            XPreparedStatementModel key = new XPreparedStatementModel(statement,
                    resultSetTyperesultSetConcurrencyresultSetHoldability);
            
            XPreparedStatementCache stmtCache =
                (XPreparedStatementCache).get(key);
            if (stmtCache == null) {
                stmtCache = new XPreparedStatementCache(thiskey);
                .put(keystmtCache);
            }
            PreparedStatement stmt = stmtCache.take(.getProxy());
            
            return stmt;
        }
    }
            int resultSetTypeint resultSetConcurrencythrows SQLException {
        return getPreparedStatement(sqlresultSetTyperesultSetConcurrency,
            .);
    }
    public PreparedStatement getPreparedStatement(String statementint[] keyIndexes,
            String[] keyColumnsthrows SQLException {
        
        if (!isStatementPooling()) {
            if (keyIndexes == null && keyColumns == null)
                return .prepareStatement(
                    statement.);
            else
            if (keyIndexes != null)
                return .prepareStatement(statementkeyIndexes);
            else
            if (keyColumns != null)
                return .prepareStatement(statementkeyColumns);
            else
                throw new IllegalStateException();
        }
        
        synchronized () {
            XPreparedStatementModel key;
            
            if (keyIndexes == null && keyColumns == null)
                key = new XPreparedStatementModel(statement.);
            else
            if (keyIndexes != null)
                key = new XPreparedStatementModel(statementkeyIndexes);
            else
            if (keyColumns != null)
                key = new XPreparedStatementModel(statementkeyColumns);
            else
                throw new IllegalStateException();
            
            XPreparedStatementCache stmtCache =
                (XPreparedStatementCache).get(key);
            if (stmtCache == null) {
                stmtCache = new XPreparedStatementCache(thiskey);
                .put(keystmtCache);
            }
            PreparedStatement stmt = stmtCache.take(.getProxy());
            
            return stmt;
        }    }

    
Prepare the specified statement and wrap it with cache notification wrapper.

Parameters:
key Statement model of the statement to prepare
cached true if prepared statement will be cached
Returns:
prepared and wrapped statement.
Throws:
java.sql.SQLException if underlying connection threw this exception.
            XPreparedStatementModel keyboolean cachedthrows SQLException {
        if ( && getLogChannel() != null) {
            getLogChannel().info("Prepared statement for SQL '" + key.getSql() +
                "'");
        }
        
        if (!key.isGeneratedKeys())
            return prepareStatementNoGeneratedKeys(keycached);
        else
            return prepareStatementGeneratedKeys(keycached);
    }

    
Prepare specified SQL statement. This method should call java.sql.Connection.prepareStatement(java.lang.String) method on physical JDBC connection.

Deprecated:
use prepareStatement(org.firebirdsql.pool.XPreparedStatementModel,boolean) instead.
Parameters:
sql SQL statement to prepare.
resultSetType type of result set
resultSetConcurrency result set concurrency
cached true if prepared statement will be cached.
Returns:
instance of java.sql.PreparedStatement corresponding to the specified SQL statement.
Throws:
java.sql.SQLException if something went wrong.
See also:
java.sql.Connection.prepareStatement(java.lang.String,int,int)
            int resultSetTypeint resultSetConcurrency,
            boolean cachedthrows SQLException {
        
        return prepareStatement(
            new XPreparedStatementModel(sqlresultSetTyperesultSetConcurrency,
                    .), cached);
    }
            XPreparedStatementModel keyboolean cachedthrows SQLException {
            key.getResultSetType(), key.getResultSetConcurrency(), key
                    .getResultSetHoldability());
            
        return wrapPreparedStatement(keycachedstmt);
    }
    
            XPreparedStatementModel keyboolean cached)
            throws SQLException {
        
        PreparedStatement stmt;
        if (key.getKeyIndexes() == null && key.getKeyColumns() == null)
            stmt = .prepareStatement(
                key.getSql(), .);
        else
        if (key.getKeyIndexes() != null)
            stmt = .prepareStatement(key.getSql(), key.getKeyIndexes());
        else
        if (key.getKeyColumns() != null)
            stmt = .prepareStatement(key.getSql(), key.getKeyColumns());
        else
            throw new IllegalArgumentException();
            
        return wrapPreparedStatement(keycachedstmt);
    }
            XPreparedStatementModel keyboolean cachedPreparedStatement stmt) {
        
        Class[] implementedInterfaces = 
            getAllInterfaces(stmt.getClass());
        PooledPreparedStatementHandler handler =
            new PooledPreparedStatementHandler(keystmtthiscached);
        // copy all implemented interfaces from the original prepared statement
        // and add XCachablePreparedStatement interface
        Class[] interfacesToImplement = 
            new Class[implementedInterfaces.length + 1];
            
        System.arraycopy(
            implementedInterfaces, 0, 
            interfacesToImplement, 0, 
            implementedInterfaces.length);
            
        interfacesToImplement[implementedInterfaces.length] = 
            XCachablePreparedStatement.class;     
        
        // create a dynamic proxy for the specified handler
        return (XCachablePreparedStatement)Proxy.newProxyInstance(
            getClass().getClassLoader(),
            interfacesToImplement,
            handler);
    }
    public void statementClosed(String sqlObject proxy)
            throws SQLException {
        throw new UnsupportedOperationException();
    }


    
Clean the cache.

Throws:
java.sql.SQLException if at least one of the cached statement could not be closed.
    private void cleanCache() throws SQLException {
        if ( && getLogChannel() != null) {
            getLogChannel().info("Prepared statement cache cleaned.");
        }
        
        SQLExceptionChainBuilder chain = new SQLExceptionChainBuilder();
        
        synchronized () {
            Iterator iter = .entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry item = (Map.Entry)iter.next();
                XPreparedStatementCache stmtCache =
                    (XPreparedStatementCache)item.getValue();
                iter.remove();
                
                try {
                    stmtCache.invalidate();
                } catch(SQLException ex) {
                    chain.append(ex);
                }
            }
        }
        
        if (chain.hasException())
                throw chain.getException();
    }

    
Handle java.sql.Statement.close() method. This implementation dereferences proxy in cache.

Parameters:
key Statement model of the statement to prepare
proxy proxy wrapping the connection.
Throws:
java.sql.SQLException if prepared statement cannot be added to the pool.
    public void statementClosed(XPreparedStatementModel keyObject proxy
        throws SQLException 
    {
        synchronized () {
            XPreparedStatementCache stmtCache =
                (XPreparedStatementCache).get(key);
            if (stmtCache == null) {
                if (getLogChannel() != null) {
                    getLogChannel().error(
                        "Cannot find statement cache for SQL \"" + key.getSql() +
                        "\". Trying to close statement to release resources."
                        );
                }
                if (proxy instanceof XCachablePreparedStatement) {
                    ((XCachablePreparedStatement)proxy).forceClose();
                }
            } else {
                stmtCache.put(proxy);
            }
        }
    }

    
Is calling rollback on the physical connection allowed when the logical connection close is signalled.

Reason to return false is when the physical connection is participating in a distributed connection.

Returns:
true when calling rollback is allowed, false otherwise
    protected boolean isRollbackAllowed() {
        return true;
    }
    public void connectionClosed(PooledConnectionHandler connectionthrows SQLException {
        if (connection != ) {
            throw new IllegalArgumentException(
                "Notified about a connection that is not under my control.");
        }
        if (!)
            cleanCache();
        
        try {
            if (isRollbackAllowed() && !.getAutoCommit() && !connection.isClosed())
                .rollback();
        } catch(SQLException ex) {
            if ( != null && .isWarnEnabled())
                .warn("Exception while trying to rollback transaction " +
                        "before returning connection to pool."ex);
            
            close();
            
            throw ex;
        }
         = null;
        ConnectionEvent event = new ConnectionEvent(this);
        
        List tempListeners = new ArrayList();
        
        Iterator iter = tempListeners.iterator();
        while (iter.hasNext()) {
            ((ConnectionEventListener)iter.next()).connectionClosed(event);
        }
    }
    public void connectionErrorOccured(PooledConnectionHandler connectionSQLException ex) {
        ConnectionEvent event = new ConnectionEvent(thisex);
        List tempListeners = new ArrayList();
        
        Iterator iter = tempListeners.iterator();
        while (iter.hasNext()) {
            ((ConnectionEventListener)iter.next()).connectionErrorOccurred(
                event);
        }
    }
    public boolean isValid(PooledConnectionHandler connection) {
        return connection == ;
    }

    
Notify this class that transaction was committed.

Parameters:
connection connection that was commited.
See also:
XConnectionManager.connectionCommitted(org.firebirdsql.pool.PooledConnectionHandler)
    public void connectionCommitted(PooledConnectionHandler connectionthrows SQLException {
        if (connection != ) {
            throw new IllegalArgumentException(
                "Specified connection does not correspond " +
                "current physical connection");
        }
        if (!) {
            cleanCache();
        }
    }

    
Notify this class that transaction was rolled back.

Parameters:
connection connection that was commited.
See also:
XConnectionManager.connectionRolledBack(org.firebirdsql.pool.PooledConnectionHandler)
    public void connectionRolledBack(PooledConnectionHandler connectionthrows
        SQLException {
        if (connection != ) {
            throw new IllegalArgumentException(
                "Specified connection does not correspond " +
                "current physical connection");
        }
        if (!) {
            cleanCache();
        }
    }
        return .get();
    }
New to GrepCode? Check out our FAQ X