org.firebirdsql.ds.FBPooledConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird Show documentation
Show all versions of jaybird Show documentation
JDBC Driver for the Firebird RDBMS
/*
* Firebird Open Source JavaEE 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 source control history command.
*
* All rights reserved.
*/
package org.firebirdsql.ds;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;
import org.firebirdsql.gds.ng.LockCloseable;
import org.firebirdsql.jdbc.FBSQLException;
import org.firebirdsql.jdbc.SQLStateConstants;
/**
* PooledConnection implementation for {@link FBConnectionPoolDataSource}
*
* @author Mark Rotteveel
* @since 2.2
*/
public class FBPooledConnection implements PooledConnection {
private final List connectionEventListeners = new CopyOnWriteArrayList<>();
private final Lock lock = new ReentrantLock();
private final LockCloseable unlock = lock::unlock;
private Connection connection;
private PooledConnectionHandler handler;
protected FBPooledConnection(Connection connection) {
this.connection = connection;
}
protected LockCloseable withLock() {
lock.lock();
return unlock;
}
@Override
public Connection getConnection() throws SQLException {
try (LockCloseable ignored = withLock()) {
if (connection == null) {
FBSQLException ex = new FBSQLException("The PooledConnection has been closed",
SQLStateConstants.SQL_STATE_CONNECTION_CLOSED);
fireFatalConnectionError(ex);
throw ex;
}
try {
if (handler != null) {
handler.close();
}
resetConnection(connection);
} catch (SQLException ex) {
fireFatalConnectionError(ex);
throw ex;
}
handler = createConnectionHandler(connection);
return handler.getProxy();
}
}
protected void resetConnection(Connection connection) throws SQLException {
connection.setAutoCommit(true);
}
/**
* Creates the PooledConnectionHandler for the connection.
*
* Subclasses may override this method to return their own subclass of PooledConnectionHandler.
*
*
* @param connection Connection
* @return PooledConnectionHandler
*/
protected PooledConnectionHandler createConnectionHandler(Connection connection) {
return new PooledConnectionHandler(connection, this);
}
@Override
public void close() throws SQLException {
try (LockCloseable ignored = withLock()) {
SQLException receivedException = null;
if (handler != null) {
try {
handler.close();
} catch (SQLException se) {
receivedException = se;
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException se) {
// We want the exception of closing the physical connection to be the first
if (receivedException != null) {
se.setNextException(receivedException);
}
receivedException = se;
} finally {
connection = null;
}
}
if (receivedException != null) {
throw receivedException;
}
}
}
/**
* Helper method to fire the connectionErrorOccurred event. To be used with
* fatal (connection) errors only.
*
* @param ex
* The exception
*/
protected void fireFatalConnectionError(SQLException ex) {
ConnectionEvent evt = new ConnectionEvent(this, ex);
for (ConnectionEventListener listener : connectionEventListeners) {
listener.connectionErrorOccurred(evt);
}
}
/**
* Helper method to fire the connectionErrorOccurred event.
*
* This method will decide which errors warrant a connectionErrorOccurred
* event to be reported or not.
*
*
* @param ex
* The exception
*/
protected void fireConnectionError(SQLException ex) {
SQLException currentException = ex;
while (currentException != null) {
String sqlState = currentException.getSQLState();
if (isFatalState(sqlState)) {
fireFatalConnectionError(ex);
}
currentException = ex.getNextException();
}
}
/**
* Decides if the given SQL state is a fatal connection error.
*
* @param sqlState
* SQL State value
* @return true
if the SQL state is considered fatal
*/
private boolean isFatalState(String sqlState) {
if (sqlState == null || sqlState.length() < 2) {
// No SQL State or no class specified, assume it's fatal
return true;
}
for (String fatalSqlStateClass : FATAL_SQL_STATE_CLASSES) {
if (sqlState.startsWith(fatalSqlStateClass)) {
return true;
}
}
return false;
}
private static final String[] FATAL_SQL_STATE_CLASSES = {
// TODO double check firebird and Jaybird implementation for other states
"08", // Connection errors
"XX", // Internal errors
"01002", // Disconnect error
"01S00", // Invalid connection string attribute
"2D000", // Invalid transaction termination
"2E000", // Invalid connection name
"HY000", // General error (TODO: maybe too general?)
"HY001", // Memory allocation error
"HYT00", // Timeout expired
"HYT01", // Connection timeout expired
};
/**
* Helper method to fire the connectionClosed event.
*/
protected void fireConnectionClosed() {
ConnectionEvent evt = new ConnectionEvent(this);
for (ConnectionEventListener listener : connectionEventListeners) {
listener.connectionClosed(evt);
}
}
/**
* Releases the current handler if it is equal to the handler passed in pch
.
*
* To be called by the PooledConnectionHandler when it has been closed.
*
*
* @param pch PooledConnectionHandler to release.
*/
protected void releaseConnectionHandler(PooledConnectionHandler pch) {
try (LockCloseable ignored = withLock()) {
if (handler == pch) {
handler = null;
}
}
}
public void addConnectionEventListener(ConnectionEventListener listener) {
connectionEventListeners.add(listener);
}
public void removeConnectionEventListener(ConnectionEventListener listener) {
connectionEventListeners.remove(listener);
}
public void addStatementEventListener(StatementEventListener listener) {
throw new UnsupportedOperationException("Not yet implemented");
}
public void removeStatementEventListener(StatementEventListener listener) {
throw new UnsupportedOperationException("Not yet implemented");
}
}