Author: steve.ebersole(a)jboss.com
Date: 2007-08-16 15:34:04 -0400 (Thu, 16 Aug 2007)
New Revision: 13929
Added:
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/AggressiveReleaseTest.java
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainer.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainerBuilder.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCServices.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/AbstractStatementDelegate.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ConnectionDelegate.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ResultSetDelegate.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/BasicJDBCContainer.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/ConnectionObserver.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImpl.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImplementor.java
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/util/ExceptionHelper.java
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/ConnectionProviderBuilder.java
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/BasicDelegationTests.java
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/TestingServiceImpl.java
Log:
import ideas from proxy approach + after_statement releases
Modified: sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainer.java
===================================================================
--- sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainer.java 2007-08-16
19:02:03 UTC (rev 13928)
+++ sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainer.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -19,18 +19,57 @@
import java.sql.Statement;
/**
- * JDBCContainer contract
+ * Contract for a JDBCContainer, which acts as a container (or holder) for JDBC objects.
*
* @author Steve Ebersole
*/
public interface JDBCContainer {
+ /**
+ * Register a JDBC statement with the container.
+ *
+ * @param statement The statement to register.
+ */
public void register(Statement statement);
+
+ /**
+ * Release a statement (un-register it).
+ *
+ * @param statement The statement to release.
+ */
public void release(Statement statement);
+ /**
+ * Register a JDBC resultset with the container.
+ *
+ * @param resultSet The resultset to register.
+ */
public void register(ResultSet resultSet);
+
+ /**
+ * Release a resultset (un-register it).
+ *
+ * @param resultSet The resultset to release.
+ */
public void release(ResultSet resultSet);
+ /**
+ * Does this container currently have registered resources?
+ *
+ * @return True if the container does have registered resources; false otherwise.
+ */
public boolean hasRegisteredResources();
+ /**
+ * Release all registered resources.
+ */
+ public void releaseResources();
+
+ /**
+ * Close this container. Also releases resources, but additionally indicates that
+ * the container is expected to no longer be used. As such the implemntation is
+ * free to render itself unusable.
+ *
+ * @see #releaseResources()
+ */
public void close();
}
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainerBuilder.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainerBuilder.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCContainerBuilder.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -24,5 +24,10 @@
* @author Steve Ebersole
*/
public interface JDBCContainerBuilder {
+ /**
+ * Build a container.
+ *
+ * @return The container.
+ */
public JDBCContainer buildJdbcContainer();
}
Modified: sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCServices.java
===================================================================
--- sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCServices.java 2007-08-16
19:02:03 UTC (rev 13928)
+++ sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/JDBCServices.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -20,13 +20,37 @@
import org.hibernate.jdbc.util.SQLStatementLogger;
/**
- * Contract for services around JDBC operations.
+ * Contract for services around JDBC operations. These represent
+ * shared resources, aka not varied by session/use.
*
* @author Steve Ebersole
*/
public interface JDBCServices {
+ /**
+ * Obtain service for providing JDBC connections.
+ *
+ * @return The connection provider.
+ */
public ConnectionProvider getConnectionProvider();
+
+ /**
+ * Obtain service for building JDBCContainers instances.
+ *
+ * @return The JDBCContainer builder.
+ */
public JDBCContainerBuilder getJdbcContainerBuilder();
+
+ /**
+ * Obtain service for logging SQL statements.
+ *
+ * @return The SQL statement logger.
+ */
public SQLStatementLogger getSqlStatementLogger();
+
+ /**
+ * Obtain service for dealing with exceptions.
+ *
+ * @return The exception helper service.
+ */
public ExceptionHelper getExceptionHelper();
}
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/AbstractStatementDelegate.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/AbstractStatementDelegate.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/AbstractStatementDelegate.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -27,6 +27,7 @@
import org.hibernate.HibernateException;
import org.hibernate.jdbc.JDBCServices;
import org.hibernate.jdbc.JDBCContainer;
+import org.hibernate.jdbc.impl.LogicalConnectionImplementor;
/**
* AbstractStatementDelegate implementation
@@ -36,15 +37,17 @@
public abstract class AbstractStatementDelegate implements Statement,
StatementWrapperImplementor {
private static final Logger log = LoggerFactory.getLogger(
AbstractStatementDelegate.class );
- private boolean valid = true;
private ConnectionDelegate connectionDelegate;
private Statement statement;
+ private boolean valid = true;
+ private final int hashCode;
private ResultSet currentResultSetDelegate;
public AbstractStatementDelegate(ConnectionDelegate connectionDelegate, Statement
statement) {
this.connectionDelegate = connectionDelegate;
this.statement = statement;
+ this.hashCode = statement.hashCode();
getJdbcContainer().register( this );
}
@@ -54,11 +57,13 @@
}
}
- /*package-protected*/ JDBCServices getJdbcServices() {
+ protected JDBCServices getJdbcServices() {
+ errorIfInvalid();
return connectionDelegate.getJdbcServices();
}
- /*package-protected*/ JDBCContainer getJdbcContainer() {
+ protected JDBCContainer getJdbcContainer() {
+ errorIfInvalid();
return connectionDelegate.getJdbcContainer();
}
@@ -77,7 +82,15 @@
valid = false;
}
+ public String toString() {
+ return super.toString() + "[valid=" + valid + "]";
+ }
+ public int hashCode() {
+ return hashCode;
+ }
+
+
// special Statement methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public Connection getConnection() throws SQLException {
@@ -87,7 +100,9 @@
public void close() throws SQLException {
if ( valid ) {
+ LogicalConnectionImplementor lc = connectionDelegate.getLogicalConnection();
getJdbcContainer().release( this );
+ lc.afterStatementExecution();
}
}
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ConnectionDelegate.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ConnectionDelegate.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ConnectionDelegate.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -44,8 +44,9 @@
public class ConnectionDelegate implements Connection, ConnectionWrapper,
ConnectionObserver {
private static final Logger log = LoggerFactory.getLogger( ConnectionDelegate.class );
+ private LogicalConnectionImplementor logicalConnection;
private boolean valid = true;
- private LogicalConnectionImplementor logicalConnection;
+ private final int hashCode;
/**
* Builds a connection delegate with the given logical connection.
@@ -56,13 +57,44 @@
public ConnectionDelegate(LogicalConnectionImplementor logicalConnection) {
this.logicalConnection = logicalConnection;
logicalConnection.addObserver( this );
+ this.hashCode = this.logicalConnection.hashCode();
}
- /*package-protected*/ JDBCServices getJdbcServices() {
+ /**
+ * Access to our logical connection.
+ *
+ * @return the logical connection
+ */
+ protected LogicalConnectionImplementor getLogicalConnection() {
+ errorIfInvalid();
+ return logicalConnection;
+ }
+
+ private void errorIfInvalid() {
+ if ( !valid ) {
+ throw new HibernateException( "connection handle is invalid" );
+ }
+ }
+
+ /**
+ * Provide access to JDBCServices.
+ * <p/>
+ * NOTE : package-protected
+ *
+ * @return JDBCServices
+ */
+ JDBCServices getJdbcServices() {
return logicalConnection.getJdbcServices();
}
- /*package-protected*/ JDBCContainer getJdbcContainer() {
+ /**
+ * Provide access to JDBCContainer.
+ * <p/>
+ * NOTE : package-protected
+ *
+ * @return JDBCContainer
+ */
+ JDBCContainer getJdbcContainer() {
return logicalConnection.getJdbcContainer();
}
@@ -77,12 +109,22 @@
return logicalConnection.getConnection();
}
- public void physicalConnectionObtained() {
+ /**
+ * {@inheritDoc}
+ */
+ public void physicalConnectionObtained(Connection connection) {
}
+ /**
+ * {@inheritDoc}
+ */
public void physicalConnectionReleased() {
+ log.info( "logical connection releasing its physical connection");
}
+ /**
+ * {@inheritDoc}
+ */
public void logicalConnectionClosed() {
log.info( "*** logical connection closed ***" );
invalidateDelegate();
@@ -94,7 +136,15 @@
valid = false;
}
+ public String toString() {
+ return super.toString() + "[valid=" + valid + "]";
+ }
+ public int hashCode() {
+ return hashCode;
+ }
+
+
// statement-creation methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public Statement createStatement() {
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ResultSetDelegate.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ResultSetDelegate.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/delegation/ResultSetDelegate.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -44,41 +44,53 @@
* @author Steve Ebersole
*/
public class ResultSetDelegate implements ResultSet, ResultSetWrapperImplementor {
+
+ private AbstractStatementDelegate statementDelegate;
+ private ResultSet resultSet;
private boolean valid = true;
- private AbstractStatementDelegate statementDelegate;
- private ResultSet wrappedResultSet;
+ private final int hashCode;
- public ResultSetDelegate(AbstractStatementDelegate statementDelegate, ResultSet
wrappedResultSet) {
+ public ResultSetDelegate(AbstractStatementDelegate statementDelegate, ResultSet
resultSet) {
this.statementDelegate = statementDelegate;
- this.wrappedResultSet = wrappedResultSet;
+ this.resultSet = resultSet;
+ this.hashCode = resultSet.hashCode();
+ statementDelegate.getJdbcContainer().register( this );
}
- /*package-protected*/ JDBCServices getJdbcServices() {
+ protected JDBCServices getJdbcServices() {
return statementDelegate.getJdbcServices();
}
- /*package-protected*/ JDBCContainer getJdbcContainer() {
+ protected JDBCContainer getJdbcContainer() {
return statementDelegate.getJdbcContainer();
}
public ResultSet getWrappedResultSetWithoutChecks() {
- return wrappedResultSet;
+ return resultSet;
}
public ResultSet getWrappedResultSet() {
if ( !valid ) {
throw new HibernateException( "resultset handle is invalid" );
}
- return wrappedResultSet;
+ return resultSet;
}
public void invalidate() {
statementDelegate = null;
- wrappedResultSet = null;
+ resultSet = null;
valid = false;
}
+ public String toString() {
+ return super.toString() + "[valid=" + valid + "]";
+ }
+ public int hashCode() {
+ return hashCode;
+ }
+
+
// special ResultSet methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public Statement getStatement() throws SQLException {
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/BasicJDBCContainer.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/BasicJDBCContainer.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/BasicJDBCContainer.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -16,20 +16,19 @@
package org.hibernate.jdbc.impl;
import java.sql.ResultSet;
+import java.sql.SQLException;
import java.sql.Statement;
-import java.sql.SQLException;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
-import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
+import org.hibernate.jdbc.JDBCContainer;
import org.hibernate.jdbc.util.ExceptionHelper;
-import org.hibernate.jdbc.JDBCContainer;
/**
* BasicJDBCContainer implementation
@@ -90,7 +89,7 @@
}
public void release(ResultSet resultSet) {
- log.trace( "releasing result set [" + resultSet + "]" );
+ log.trace( "releasing result set [{}]", resultSet );
Statement statement;
try {
statement = resultSet.getStatement();
@@ -112,8 +111,12 @@
return !xref.isEmpty();
}
- public void close() {
- log.trace( "closing JDBC container [" + this + "]" );
+ public void releaseResources() {
+ log.trace( "releasing JDBC container resources [{}]", this );
+ cleanup();
+ }
+
+ private void cleanup() {
Iterator statementIterator = xref.keySet().iterator();
while ( statementIterator.hasNext() ) {
final Statement statement = ( Statement ) statementIterator.next();
@@ -131,10 +134,15 @@
xref.clear();
}
+ public void close() {
+ log.trace( "closing JDBC container [{}]", this );
+ cleanup();
+ }
+
protected void close(Statement statement) {
log.trace( "closing prepared statement [" + statement + "]" );
try {
- // if we are unable to "clan" the prepared statement,
+ // if we are unable to "clean" the prepared statement,
// we do not close it
try {
if ( statement.getMaxRows() != 0 ) {
@@ -146,13 +154,13 @@
}
catch( SQLException sqle ) {
// there was a problem "cleaning" the prepared statement
- log.debug( "Exception clearing maxRows/queryTimeout [" + sqle.getMessage()
+ "]" );
+ log.debug( "Exception clearing maxRows/queryTimeout [{}]",
sqle.getMessage() );
return; // EARLY EXIT!!!
}
statement.close();
}
catch( SQLException sqle ) {
- log.debug( "Unable to release statement [" + sqle.getMessage() +
"]" );
+ log.debug( "Unable to release statement [{}]", sqle.getMessage() );
}
}
@@ -162,7 +170,7 @@
resultSet.close();
}
catch( SQLException e ) {
- log.debug( "Unable to release result set [" + e.getMessage() + "]"
);
+ log.debug( "Unable to release result set [{}]", e.getMessage() );
}
}
}
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/ConnectionObserver.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/ConnectionObserver.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/ConnectionObserver.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -15,13 +15,28 @@
*/
package org.hibernate.jdbc.impl;
+import java.sql.Connection;
+
/**
- * Observer contract
+ * An observer of logical connection events.
*
* @author Steve Ebersole
*/
public interface ConnectionObserver {
- public void physicalConnectionObtained();
+ /**
+ * A physical connection was obtained.
+ *
+ * @param connection The physical connection just obtained.
+ */
+ public void physicalConnectionObtained(Connection connection);
+
+ /**
+ * A physical connection was released.
+ */
public void physicalConnectionReleased();
+
+ /**
+ * The logical connection was closed.
+ */
public void logicalConnectionClosed();
}
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImpl.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImpl.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImpl.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -27,9 +27,8 @@
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
-import org.hibernate.jdbc.impl.ConnectionObserver;
+import org.hibernate.jdbc.JDBCServices;
import org.hibernate.jdbc.JDBCContainer;
-import org.hibernate.jdbc.JDBCServices;
/**
* LogicalConnectionImpl implementation
@@ -44,6 +43,7 @@
private final JDBCServices jdbcServices;
private final JDBCContainer jdbcContainer;
private final List observers = new ArrayList();
+ private boolean releasesEnabled = true;
private final boolean isUserSuppliedConnection;
private boolean isClosed;
@@ -52,6 +52,11 @@
Connection userSuppliedConnection,
ConnectionReleaseMode connectionReleaseMode,
JDBCServices jdbcServices) {
+ if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT &&
+ ! jdbcServices.getConnectionProvider().supportsAggressiveRelease() ) {
+ log.debug( "connection provider reports to not support aggressive release;
overriding" );
+ connectionReleaseMode = ConnectionReleaseMode.AFTER_TRANSACTION;
+ }
this.physicalConnection = userSuppliedConnection;
this.connectionReleaseMode = connectionReleaseMode;
this.jdbcServices = jdbcServices;
@@ -70,13 +75,6 @@
/**
* {@inheritDoc}
*/
- public ConnectionReleaseMode getConnectionReleaseMode() {
- return connectionReleaseMode;
- }
-
- /**
- * {@inheritDoc}
- */
public JDBCContainer getJdbcContainer() {
return jdbcContainer;
}
@@ -126,12 +124,12 @@
log.trace( "closing logical connection" );
Connection c = physicalConnection;
if ( !isUserSuppliedConnection && physicalConnection != null ) {
+ jdbcContainer.close();
releaseConnection();
}
// not matter what
physicalConnection = null;
isClosed = true;
- jdbcContainer.close();
for ( Iterator itr = observers.iterator(); itr.hasNext(); ) {
( ( ConnectionObserver ) itr.next() ).logicalConnectionClosed();
}
@@ -152,15 +150,56 @@
// }
/**
+ * {@inheritDoc}
+ */
+ public ConnectionReleaseMode getConnectionReleaseMode() {
+ return connectionReleaseMode;
+ }
+
+ public void afterStatementExecution() {
+ log.trace( "starting after statement execution processing [{}]",
connectionReleaseMode );
+ if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
+ if ( ! releasesEnabled ) {
+ log.debug( "skipping aggressive release due to manual disabling" );
+ return;
+ }
+ if ( jdbcContainer.hasRegisteredResources() ) {
+ log.debug( "skipping aggressive release due to registered resources" );
+ return;
+ }
+ releaseConnection();
+ }
+ }
+
+ public void afterTransaction() {
+ if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT ||
+ connectionReleaseMode == ConnectionReleaseMode.AFTER_TRANSACTION ) {
+ if ( jdbcContainer.hasRegisteredResources() ) {
+ log.info( "forcing container resource cleanup on transaction completion"
);
+ jdbcContainer.releaseResources();
+ }
+ aggressiveRelease();
+ }
+ }
+
+ public void disableReleases() {
+ log.trace( "disabling releases" );
+ releasesEnabled = false;
+ }
+
+ public void enableReleases() {
+ log.trace( "(re)enabling releases" );
+ releasesEnabled = true;
+ afterStatementExecution();
+ }
+
+ /**
* Force aggresive release of the underlying connection.
*/
public void aggressiveRelease() {
if ( isUserSuppliedConnection ) {
log.debug( "cannot aggresively release user-supplied connection; skipping"
);
}
- else if ( ! getJdbcServices().getConnectionProvider().supportsAggressiveRelease() ) {
- log.debug( "connection provider reports to not support aggreswsive release;
skipping" );
- }
else {
log.debug( "aggressively releasing JDBC connection" );
if ( physicalConnection != null ) {
@@ -171,7 +210,6 @@
/**
- * Obtains a physical connection from the
* Pysically opens a JDBC Connection.
*
* @throws JDBCException Indicates problem opening a connection
@@ -181,7 +219,7 @@
try {
physicalConnection = getJdbcServices().getConnectionProvider().getConnection();
for ( Iterator itr = observers.iterator(); itr.hasNext(); ) {
- ( ( ConnectionObserver ) itr.next() ).physicalConnectionObtained();
+ ( ( ConnectionObserver ) itr.next() ).physicalConnectionObtained( physicalConnection
);
}
log.debug( "obtained JDBC connection" );
}
@@ -191,9 +229,9 @@
}
/**
- * Releases the physical JDBC connection back to the provider.
+ * Physically closes the JDBC Connection.
*
- * @throws JDBCException Indicates problem releasing the connection
+ * @throws JDBCException Indicates problem closing a connection
*/
private void releaseConnection() throws JDBCException {
log.debug( "releasing JDBC connection" );
@@ -201,11 +239,11 @@
if ( !physicalConnection.isClosed() ) {
getJdbcServices().getExceptionHelper().logAndClearWarnings( physicalConnection );
}
- getJdbcServices().getConnectionProvider().closeConnection( physicalConnection );
- physicalConnection = null;
for ( Iterator itr = observers.iterator(); itr.hasNext(); ) {
( ( ConnectionObserver ) itr.next() ).physicalConnectionReleased();
}
+ getJdbcServices().getConnectionProvider().closeConnection( physicalConnection );
+ physicalConnection = null;
log.debug( "released JDBC connection" );
}
catch (SQLException sqle) {
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImplementor.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImplementor.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/impl/LogicalConnectionImplementor.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -16,24 +16,65 @@
package org.hibernate.jdbc.impl;
import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.jdbc.JDBCContainer;
import org.hibernate.jdbc.JDBCServices;
-import org.hibernate.jdbc.JDBCContainer;
-import org.hibernate.jdbc.impl.ConnectionObserver;
import org.hibernate.jdbc.LogicalConnection;
/**
- * LogicalConnectionImplementor implementation
+ * The "internal" contract for LogicalConnection
*
* @author Steve Ebersole
*/
public interface LogicalConnectionImplementor extends LogicalConnection {
+ /**
+ * Obtains the JDBC services associated with this logical connection.
+ *
+ * @return JDBC services
+ */
public JDBCServices getJdbcServices();
- public ConnectionReleaseMode getConnectionReleaseMode();
-
+ /**
+ * Obtains the JDBC object container associated with this logical connection.
+ *
+ * @return The JDBC object container.
+ */
public JDBCContainer getJdbcContainer();
+ /**
+ * Add an observer interested in notification of connection events.
+ *
+ * @param observer The observer.
+ */
public void addObserver(ConnectionObserver observer);
- public void aggressiveRelease();
+ /**
+ * The release mode under which this logical connection is operating.
+ *
+ * @return the release mode.
+ */
+ public ConnectionReleaseMode getConnectionReleaseMode();
+
+ /**
+ * Used to signify that a statement has completed execution which may
+ * indicate that this logical connection need to perform an
+ * aggressive release of its physical connection.
+ */
+ public void afterStatementExecution();
+
+ /**
+ * Used to signify that a transaction has completed which may indicate
+ * that this logical connection need to perform an aggressive release
+ * of its physical connection.
+ */
+ public void afterTransaction();
+
+ /**
+ * Manually (and termporarily) circumvent aggressive release processing.
+ */
+ public void disableReleases();
+
+ /**
+ * Re enable aggressive release processing (after a prior {@link #disableReleases()}
call.
+ */
+ public void enableReleases();
}
Modified:
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/util/ExceptionHelper.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/util/ExceptionHelper.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/main/java/org/hibernate/jdbc/util/ExceptionHelper.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -24,10 +24,12 @@
import org.hibernate.JDBCException;
import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.SQLStateConverter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
import org.hibernate.util.StringHelper;
/**
- * ExceptionHelper implementation
+ * Helper for handling SQLExceptions in various manners.
*
* @author Steve Ebersole
*/
@@ -35,31 +37,83 @@
public static final String DEFAULT_EXCEPTION_MSG = "SQL Exception";
public static final String DEFAULT_WARNING_MSG = "SQL Warning";
+ public static final SQLExceptionConverter DEFAULT_CONVERTER = new SQLStateConverter(
+ new ViolatedConstraintNameExtracter() {
+ public String extractConstraintName(SQLException e) {
+ return null;
+ }
+ }
+ );
+
private static final Logger log = LoggerFactory.getLogger( ExceptionHelper.class );
private SQLExceptionConverter sqlExceptionConverter;
+ /**
+ * Create an exception helper with a default exception converter.
+ */
+ public ExceptionHelper() {
+ sqlExceptionConverter = DEFAULT_CONVERTER;
+ }
+
+ /**
+ * Create an exception helper with a specific exception converter.
+ *
+ * @param sqlExceptionConverter The exception converter to use.
+ */
public ExceptionHelper(SQLExceptionConverter sqlExceptionConverter) {
this.sqlExceptionConverter = sqlExceptionConverter;
}
+ /**
+ * Access the current exception converter being used internally.
+ *
+ * @return The current exception converter.
+ */
public SQLExceptionConverter getSqlExceptionConverter() {
return sqlExceptionConverter;
}
+ /**
+ * Inject the exception converter to use.
+ * <p/>
+ * NOTE : <tt>null</tt> is allowed and signifies to use the default.
+ *
+ * @param sqlExceptionConverter The converter to use.
+ */
public void setSqlExceptionConverter(SQLExceptionConverter sqlExceptionConverter) {
- this.sqlExceptionConverter = sqlExceptionConverter;
+ this.sqlExceptionConverter = ( sqlExceptionConverter == null ? DEFAULT_CONVERTER :
sqlExceptionConverter );
}
+ /**
+ * Convert an SQLException using the current converter, doing some logging first.
+ *
+ * @param sqle The exception to convert
+ * @param message An error message.
+ * @return The converted exception
+ */
public JDBCException convert(SQLException sqle, String message) {
return convert( sqle, message, "n/a" );
}
+ /**
+ * Convert an SQLException using the current converter, doing some logging first.
+ *
+ * @param sqle The exception to convert
+ * @param message An error message.
+ * @param sql The SQL being executed when the exception occurred
+ * @return The converted exception
+ */
public JDBCException convert(SQLException sqle, String message, String sql) {
logExceptions( sqle, message + " [" + sql + "]" );
return sqlExceptionConverter.convert( sqle, message, sql );
}
+ /**
+ * Log any {@link SQLWarning}s registered with the connection.
+ *
+ * @param connection The connection to check for warnings.
+ */
public void logAndClearWarnings(Connection connection) {
if ( log.isWarnEnabled() ) {
try {
@@ -80,10 +134,21 @@
}
+ /**
+ * Log the given (and any nested) warning.
+ *
+ * @param warning The warning
+ */
public void logWarnings(SQLWarning warning) {
logWarnings( warning, null );
}
+ /**
+ * Log the given (and any nested) warning.
+ *
+ * @param warning The warning
+ * @param message The message text to use as a preamble.
+ */
public void logWarnings(SQLWarning warning, String message) {
if ( log.isWarnEnabled() ) {
if ( log.isDebugEnabled() && warning != null ) {
@@ -103,25 +168,36 @@
}
}
- public void logExceptions(SQLException ex) {
- logExceptions( ex, null );
+ /**
+ * Log the given (and any nested) exception.
+ *
+ * @param sqlException The exception to log
+ */
+ public void logExceptions(SQLException sqlException) {
+ logExceptions( sqlException, null );
}
- public void logExceptions(SQLException ex, String message) {
+ /**
+ * Log the given (and any nested) exception.
+ *
+ * @param sqlException The exception to log
+ * @param message The message text to use as a preamble.
+ */
+ public void logExceptions(SQLException sqlException, String message) {
if ( log.isErrorEnabled() ) {
if ( log.isDebugEnabled() ) {
message = StringHelper.isNotEmpty( message ) ? message : DEFAULT_EXCEPTION_MSG;
- log.debug( message, ex );
+ log.debug( message, sqlException );
}
- while ( ex != null ) {
+ while ( sqlException != null ) {
StringBuffer buf = new StringBuffer( 30 )
.append( "SQL Error: " )
- .append( ex.getErrorCode() )
+ .append( sqlException.getErrorCode() )
.append( ", SQLState: " )
- .append( ex.getSQLState() );
+ .append( sqlException.getSQLState() );
log.warn( buf.toString() );
- log.error( ex.getMessage() );
- ex = ex.getNextException();
+ log.error( sqlException.getMessage() );
+ sqlException = sqlException.getNextException();
}
}
}
Modified:
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/ConnectionProviderBuilder.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/ConnectionProviderBuilder.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/ConnectionProviderBuilder.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -28,11 +28,19 @@
*/
public class ConnectionProviderBuilder {
public static ConnectionProvider buildConnectionProvider() {
+ return buildConnectionProvider( false );
+ }
+
+ public static ConnectionProvider buildConnectionProvider(final boolean
allowAggressiveRelease) {
Properties props = new Properties( null );
props.put( Environment.DRIVER, "org.hsqldb.jdbcDriver" );
props.put( Environment.URL, "jdbc:hsqldb:mem:target/test/db/hsqldb/hibernate"
);
props.put( Environment.USER, "sa" );
- DriverManagerConnectionProvider connectionProvider = new
DriverManagerConnectionProvider();
+ DriverManagerConnectionProvider connectionProvider = new
DriverManagerConnectionProvider() {
+ public boolean supportsAggressiveRelease() {
+ return allowAggressiveRelease;
+ }
+ };
connectionProvider.configure( props );
return connectionProvider;
}
Added:
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/AggressiveReleaseTest.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/AggressiveReleaseTest.java
(rev 0)
+++
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/AggressiveReleaseTest.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, v. 2.1. This program is distributed in the
+ * hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details. You should have received a
+ * copy of the GNU Lesser General Public License, v.2.1 along with this
+ * distribution; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Red Hat Author(s): Steve Ebersole
+ */
+package org.hibernate.jdbc.delegation;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.fail;
+
+import org.hibernate.jdbc.impl.ConnectionObserver;
+import org.hibernate.jdbc.impl.LogicalConnectionImpl;
+import org.hibernate.ConnectionReleaseMode;
+
+/**
+ * AggressiveReleaseTest implementation
+ *
+ * @author Steve Ebersole
+ */
+public class AggressiveReleaseTest {
+ private static final Logger log = LoggerFactory.getLogger( AggressiveReleaseTest.class
);
+ private TestingServiceImpl services = new TestingServiceImpl();
+
+ private static class ConnectionCounter implements ConnectionObserver {
+ public int obtainCount = 0;
+ public int releaseCount = 0;
+
+ public void physicalConnectionObtained(Connection connection) {
+ obtainCount++;
+ }
+
+ public void physicalConnectionReleased() {
+ releaseCount++;
+ }
+
+ public void logicalConnectionClosed() {
+ }
+ }
+
+ @BeforeClass
+ protected void create() {
+ log.debug( "starting @BeforeClass" );
+ services.prepare( true );
+ }
+
+ @AfterClass
+ protected void destroy() {
+ services.release();
+ }
+
+ @BeforeMethod
+ protected void setUpSchema() throws SQLException {
+ log.debug( "starting @BeforeMethod" );
+ Connection connection = null;
+ Statement stmnt = null;
+ try {
+ connection = services.getConnectionProvider().getConnection();
+ stmnt = connection.createStatement();
+ stmnt.execute( "drop table SANDBOX_JDBC_TST if exists" );
+ stmnt.execute( "create table SANDBOX_JDBC_TST ( ID integer, NAME varchar(100)
)" );
+ }
+ finally {
+ if ( stmnt != null ) {
+ try {
+ stmnt.close();
+ }
+ catch ( SQLException ignore ) {
+ log.warn( "could not close statement used to set up schema", ignore );
+ }
+ }
+ if ( connection != null ) {
+ try {
+ connection.close();
+ }
+ catch ( SQLException ignore ) {
+ log.warn( "could not close connection used to set up schema", ignore );
+ }
+ }
+ }
+ }
+
+ @AfterMethod
+ protected void tearDownSchema() throws SQLException {
+ Connection connection = null;
+ Statement stmnt = null;
+ try {
+ connection = services.getConnectionProvider().getConnection();
+ stmnt = connection.createStatement();
+ stmnt.execute( "drop table SANDBOX_JDBC_TST if exists" );
+ }
+ finally {
+ if ( stmnt != null ) {
+ try {
+ stmnt.close();
+ }
+ catch ( SQLException ignore ) {
+ log.warn( "could not close statement used to set up schema", ignore );
+ }
+ }
+ if ( connection != null ) {
+ try {
+ connection.close();
+ }
+ catch ( SQLException ignore ) {
+ log.warn( "could not close connection used to set up schema", ignore );
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testBasicRelease() {
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null,
ConnectionReleaseMode.AFTER_STATEMENT, services );
+ Connection proxiedConnection = new ConnectionDelegate( logicalConnection );
+ ConnectionCounter observer = new ConnectionCounter();
+ logicalConnection.addObserver( observer );
+
+ try {
+ PreparedStatement ps = proxiedConnection.prepareStatement( "insert into
SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )" );
+ ps.setLong( 1, 1 );
+ ps.setString( 2, "name" );
+ ps.execute();
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 1, observer.obtainCount );
+ assertEquals( 0, observer.releaseCount );
+ ps.close();
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 1, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+ }
+ catch ( SQLException sqle ) {
+ fail( "incorrect exception type : sqlexception" );
+ }
+ finally {
+ logicalConnection.close();
+ }
+
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ }
+
+ @Test
+ public void testReleaseCircumventedByHeldResources() {
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null,
ConnectionReleaseMode.AFTER_STATEMENT, services );
+ Connection proxiedConnection = new ConnectionDelegate( logicalConnection );
+ ConnectionCounter observer = new ConnectionCounter();
+ logicalConnection.addObserver( observer );
+
+ try {
+ PreparedStatement ps = proxiedConnection.prepareStatement( "insert into
SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )" );
+ ps.setLong( 1, 1 );
+ ps.setString( 2, "name" );
+ ps.execute();
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 1, observer.obtainCount );
+ assertEquals( 0, observer.releaseCount );
+ ps.close();
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 1, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+
+ // open a result set and hold it open...
+ ps = proxiedConnection.prepareStatement( "select * from SANDBOX_JDBC_TST"
);
+ ps.executeQuery();
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+
+ // open a second result set
+ PreparedStatement ps2 = proxiedConnection.prepareStatement( "select * from
SANDBOX_JDBC_TST" );
+ ps2.execute();
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+ // and close it...
+ ps2.close();
+ // the release should be circumvented...
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+
+ // let the close of the logical connection below release all resources (hopefully)...
+ }
+ catch ( SQLException sqle ) {
+ fail( "incorrect exception type : sqlexception" );
+ }
+ finally {
+ logicalConnection.close();
+ }
+
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 2, observer.releaseCount );
+ }
+
+ @Test
+ public void testReleaseCircumventedManually() {
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null,
ConnectionReleaseMode.AFTER_STATEMENT, services );
+ Connection proxiedConnection = new ConnectionDelegate( logicalConnection );
+ ConnectionCounter observer = new ConnectionCounter();
+ logicalConnection.addObserver( observer );
+
+ try {
+ PreparedStatement ps = proxiedConnection.prepareStatement( "insert into
SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )" );
+ ps.setLong( 1, 1 );
+ ps.setString( 2, "name" );
+ ps.execute();
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 1, observer.obtainCount );
+ assertEquals( 0, observer.releaseCount );
+ ps.close();
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 1, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+
+ // disable releases...
+ logicalConnection.disableReleases();
+
+ // open a result set...
+ ps = proxiedConnection.prepareStatement( "select * from SANDBOX_JDBC_TST"
);
+ ps.executeQuery();
+ assertTrue( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+ // and close it...
+ ps.close();
+ // the release should be circumvented...
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 1, observer.releaseCount );
+
+ // let the close of the logical connection below release all resources (hopefully)...
+ }
+ catch ( SQLException sqle ) {
+ fail( "incorrect exception type : sqlexception" );
+ }
+ finally {
+ logicalConnection.close();
+ }
+
+ assertFalse( logicalConnection.getJdbcContainer().hasRegisteredResources() );
+ assertEquals( 2, observer.obtainCount );
+ assertEquals( 2, observer.releaseCount );
+ }
+}
Modified:
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/BasicDelegationTests.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/BasicDelegationTests.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/BasicDelegationTests.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -41,7 +41,7 @@
@BeforeClass
protected void create() {
- services.prepare();
+ services.prepare( false );
}
@AfterClass
@@ -50,18 +50,29 @@
}
@Test
+ public void testExceptionHandling() {
+ LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null,
ConnectionReleaseMode.AFTER_TRANSACTION, services );
+ ConnectionDelegate connection = new ConnectionDelegate( logicalConnection );
+ try {
+ connection.prepareStatement( "select count(*) from NON_EXISTENT"
).executeQuery();
+ }
+ catch ( SQLException sqle ) {
+ fail( "incorrect exception type : sqlexception" );
+ }
+ catch ( JDBCException ok ) {
+ // expected outcome
+ }
+ finally {
+ logicalConnection.close();
+ }
+ }
+
+ @Test
public void testBasicJdbcUsage() throws JDBCException {
LogicalConnectionImpl logicalConnection = new LogicalConnectionImpl( null,
ConnectionReleaseMode.AFTER_TRANSACTION, services );
ConnectionDelegate connection = new ConnectionDelegate( logicalConnection );
try {
- try {
- connection.prepareStatement( "select count(*) from NON_EXISTENT"
).executeQuery();
- }
- catch ( JDBCException ok ) {
- // expected outcome
- }
-
Statement stmnt = connection.createStatement();
stmnt.execute( "drop table SANDBOX_JDBC_TST if exists" );
stmnt.execute( "create table SANDBOX_JDBC_TST ( ID integer, NAME varchar(100)
)" );
Modified:
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/TestingServiceImpl.java
===================================================================
---
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/TestingServiceImpl.java 2007-08-16
19:02:03 UTC (rev 13928)
+++
sandbox/trunk/jdbc-impl/src/test/java/org/hibernate/jdbc/delegation/TestingServiceImpl.java 2007-08-16
19:34:04 UTC (rev 13929)
@@ -15,8 +15,6 @@
*/
package org.hibernate.jdbc.delegation;
-import java.sql.SQLException;
-
import org.hibernate.jdbc.JDBCServices;
import org.hibernate.jdbc.JDBCContainerBuilder;
import org.hibernate.jdbc.ConnectionProviderBuilder;
@@ -24,8 +22,6 @@
import org.hibernate.jdbc.util.SQLStatementLogger;
import org.hibernate.jdbc.util.ExceptionHelper;
import org.hibernate.connection.ConnectionProvider;
-import org.hibernate.exception.SQLStateConverter;
-import org.hibernate.exception.ViolatedConstraintNameExtracter;
/**
* TestingServiceImpl implementation
@@ -38,18 +34,10 @@
private JDBCContainerBuilder jdbcContainerBuilder;
private ExceptionHelper exceptionHelper;
- public void prepare() {
- connectionProvider = ConnectionProviderBuilder.buildConnectionProvider();
+ public void prepare(boolean allowAggressiveRelease) {
+ connectionProvider = ConnectionProviderBuilder.buildConnectionProvider(
allowAggressiveRelease );
sqlStatementLogger = new SQLStatementLogger( true );
- exceptionHelper = new ExceptionHelper(
- new SQLStateConverter(
- new ViolatedConstraintNameExtracter() {
- public String extractConstraintName(SQLException e) {
- return null;
- }
- }
- )
- );
+ exceptionHelper = new ExceptionHelper();
jdbcContainerBuilder = new JDBCContainerBuilder() {
public JDBCContainer buildJdbcContainer() {
return new DelegateJDBCContainer( exceptionHelper );