From 1345d515d20ce6ef2db56e1629065b7be4ed18df Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Wed, 25 May 2016 10:27:57 +0300 Subject: [PATCH] HHH-10772 - RuntimeException during releaseStatements causes JDBC connection not to be closed --- .../jdbc/internal/JdbcCoordinatorImpl.java | 16 +++- .../jdbc/internal/JdbcCoordinatorTest.java | 92 +++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 29de85ce0a..f11cfa5ada 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -171,12 +171,18 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator { @Override public Connection close() { LOG.tracev( "Closing JDBC container [{0}]", this ); - if ( currentBatch != null ) { - LOG.closingUnreleasedBatch(); - currentBatch.release(); + Connection connection; + try { + if ( currentBatch != null ) { + LOG.closingUnreleasedBatch(); + currentBatch.release(); + } + cleanup(); } - cleanup(); - return logicalConnection.close(); + finally { + connection = logicalConnection.close(); + } + return connection; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java b/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java new file mode 100644 index 0000000000..458d9c2668 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java @@ -0,0 +1,92 @@ +package org.hibernate.engine.jdbc.internal; + +import java.lang.reflect.Field; +import java.sql.Connection; +import java.sql.SQLException; + +import org.hibernate.engine.jdbc.batch.spi.Batch; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.resource.jdbc.spi.JdbcObserver; +import org.hibernate.resource.jdbc.spi.JdbcSessionContext; +import org.hibernate.resource.jdbc.spi.JdbcSessionOwner; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.service.ServiceRegistry; + +import org.junit.Test; + +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Vlad Mihalcea + */ +public class JdbcCoordinatorTest { + + @Test + public void testConnectionClose() + throws NoSuchFieldException, IllegalAccessException, SQLException { + Connection connection = Mockito.mock( Connection.class ); + + JdbcSessionOwner sessionOwner = Mockito.mock( JdbcSessionOwner.class ); + + JdbcConnectionAccess jdbcConnectionAccess = Mockito.mock( + JdbcConnectionAccess.class ); + when( jdbcConnectionAccess.obtainConnection() ).thenReturn( connection ); + when( jdbcConnectionAccess.supportsAggressiveRelease() ).thenReturn( + false ); + + JdbcSessionContext sessionContext = Mockito.mock( JdbcSessionContext.class ); + when( sessionOwner.getJdbcSessionContext() ).thenReturn( sessionContext ); + when( sessionOwner.getJdbcConnectionAccess() ).thenReturn( + jdbcConnectionAccess ); + + ServiceRegistry serviceRegistry = Mockito.mock( ServiceRegistry.class ); + when( sessionContext.getServiceRegistry() ).thenReturn( serviceRegistry ); + when( sessionContext.getPhysicalConnectionHandlingMode() ).thenReturn( + PhysicalConnectionHandlingMode.IMMEDIATE_ACQUISITION_AND_HOLD ); + + JdbcObserver jdbcObserver = Mockito.mock( JdbcObserver.class ); + when( sessionContext.getObserver() ).thenReturn( jdbcObserver ); + + JdbcServices jdbcServices = Mockito.mock( JdbcServices.class ); + when( serviceRegistry.getService( eq( JdbcServices.class ) ) ).thenReturn( + jdbcServices ); + + SqlExceptionHelper sqlExceptionHelper = Mockito.mock( SqlExceptionHelper.class ); + when( jdbcServices.getSqlExceptionHelper() ).thenReturn( + sqlExceptionHelper ); + + JdbcCoordinatorImpl jdbcCoordinator = new JdbcCoordinatorImpl( + null, + sessionOwner + ); + + Batch currentBatch = Mockito.mock( Batch.class ); + Field currentBatchField = JdbcCoordinatorImpl.class.getDeclaredField( + "currentBatch" ); + currentBatchField.setAccessible( true ); + currentBatchField.set( jdbcCoordinator, currentBatch ); + + doThrow( IllegalStateException.class ).when( currentBatch ).release(); + + try { + jdbcCoordinator.close(); + fail( "Should throw IllegalStateException" ); + } + catch (Exception expected) { + assertEquals( IllegalStateException.class, expected.getClass() ); + } + verify( jdbcConnectionAccess, times( 1 ) ).releaseConnection( same( + connection ) ); + } +} \ No newline at end of file