/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2006-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* 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 GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.connections;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.internal.util.SerializationHelper;
import org.junit.Test;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
/**
* Common test cases relating to session management and how the sessions
* manages its underlying jdbc connection across different config
* scenarios. The different config scenarios are controlled by the
* individual test subclasses.
* <p/>
* In general, all the tests required are defined here in templated fashion.
* Subclassed then override various hook methods specific to their given
* scneario being tested.
*
* @author Steve Ebersole
*/
public abstract class ConnectionManagementTestCase extends BaseCoreFunctionalTestCase {
@Override
public final String[] getMappings() {
return new String[] { "connections/Silly.hbm.xml" };
}
// hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Used to prepare the environment for testing (e.g., starting a
* JTA transaction or obtaining a user-supplied connection).
*
* @throws Throwable indicates problems preparing
*/
protected void prepare() throws Throwable {
}
/**
* Used to cleanup the environment after testing (e.g., ending a JTA
* transaction or closing a user-supplied connection).
*
* @throws Throwable indicates problems cleaning up
*/
protected void done() throws Throwable {
}
/**
* Used to get a session configured based on the config scenario being
* tested.
*
* @return The session to be used in testing.
* @throws Throwable Indicates problems building a test session fixture.
*/
protected abstract Session getSessionUnderTest() throws Throwable;
/**
* Used to release a {@link #getSessionUnderTest fixture session}.
* Overridden to perform session releasing/testing specific to the given
* config scenario being tested.
*
* @param session The session to be released.
*/
protected void release(Session session) {
if ( session != null && session.isOpen() ) {
try {
session.close();
}
catch( Throwable ignore ) {
}
}
}
protected void disconnect(Session session) throws Throwable {
session.disconnect();
}
/**
* Perform any steps needed to reconnect a fixture session.
*
* @param session The fixture session to be reconnected.
* @throws Throwable Indicates problems reconnecting.
*/
protected abstract void reconnect(Session session) throws Throwable;
/**
* Check the state of a fixture session after serialization, as well
* as validate the environmental state after session serialization.
*
* @param session The fixture session that was serialized.
*/
protected void checkSerializedState(Session session) {
}
/**
* Check the state of a fixture session after deserialization, as well
* as validate the environmental state after session deserialization.
*
* @param session The fixture session that was deserialized.
*/
protected void checkDeserializedState(Session session) {
}
// tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Tests to validate that a session holding JDBC resources will not
* be allowed to serialize.
*/
@Test
public final void testConnectedSerialization() throws Throwable {
prepare();
Session sessionUnderTest = getSessionUnderTest();
// force the connection to be retained
sessionUnderTest.createQuery( "from Silly" ).scroll();
try {
SerializationHelper.serialize( sessionUnderTest );
fail( "Serialization of connected session allowed!" );
}
catch( IllegalStateException e ) {
// expected behaviour
}
finally {
release( sessionUnderTest );
done();
}
}
/**
* Tests to validate that a session holding JDBC resources will not
* be allowed to serialize.
*/
@Test
public final void testEnabledFilterSerialization() throws Throwable {
prepare();
Session sessionUnderTest = getSessionUnderTest();
sessionUnderTest.enableFilter( "nameIsNull" );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
disconnect( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
byte[] bytes = SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
reconnect( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
disconnect( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
Session s2 = ( Session ) SerializationHelper.deserialize( bytes );
checkDeserializedState( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
reconnect( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
disconnect( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
reconnect( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
release( sessionUnderTest );
release( s2 );
done();
}
/**
* Test that a session which has been manually disconnected will be allowed
* to serialize.
*/
@Test
public final void testManualDisconnectedSerialization() throws Throwable {
prepare();
Session sessionUnderTest = getSessionUnderTest();
disconnect( sessionUnderTest );
SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
release( sessionUnderTest );
done();
}
/**
* Test that the legacy manual disconnect()/reconnect() chain works as
* expected in the given environment.
*/
@Test
public final void testManualDisconnectChain() throws Throwable {
prepare();
Session sessionUnderTest = getSessionUnderTest();
disconnect( sessionUnderTest );
byte[] bytes = SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
Session s2 = ( Session ) SerializationHelper.deserialize( bytes );
checkDeserializedState( s2 );
reconnect( s2 );
disconnect( s2 );
reconnect( s2 );
release( sessionUnderTest );
release( s2 );
done();
}
/**
* Test that the legacy manual disconnect()/reconnect() chain works as
* expected in the given environment. Similar to {@link #testManualDisconnectChain}
* expect that here we force the session to acquire and hold JDBC resources
* prior to disconnecting.
*/
@Test
public final void testManualDisconnectWithOpenResources() throws Throwable {
prepare();
Session sessionUnderTest = getSessionUnderTest();
Silly silly = new Silly( "tester" );
sessionUnderTest.save( silly );
sessionUnderTest.flush();
sessionUnderTest.createQuery( "from Silly" ).iterate();
disconnect( sessionUnderTest );
SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
reconnect( sessionUnderTest );
sessionUnderTest.createQuery( "from Silly" ).scroll();
disconnect( sessionUnderTest );
SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
reconnect( sessionUnderTest );
sessionUnderTest.delete( silly );
sessionUnderTest.flush();
release( sessionUnderTest );
done();
}
/**
* Test that the basic session usage template works in all environment
* scenarios.
*/
@Test
public void testBasicSessionUsage() throws Throwable {
prepare();
Session s = null;
Transaction txn = null;
try {
s = getSessionUnderTest();
txn = s.beginTransaction();
s.createQuery( "from Silly" ).list();
txn.commit();
}
catch( Throwable t ) {
if ( txn != null ) {
try {
txn.rollback();
}
catch( Throwable ignore ) {
}
}
}
finally {
if ( s != null && s.isOpen() ) {
try {
s.close();
}
catch( Throwable ignore ) {
}
}
}
done();
}
/**
* Test that session-closed protections work properly in all environments.
*/
@Test
public void testSessionClosedProtections() throws Throwable {
prepare();
Session s = getSessionUnderTest();
release( s );
done();
assertFalse( s.isOpen() );
assertFalse( s.isConnected() );
assertNotNull( s.getStatistics() );
assertNotNull( s.toString() );
try {
s.createQuery( "from Silly" ).list();
fail( "allowed to create query on closed session" );
}
catch( Throwable ignore ) {
}
try {
s.getTransaction();
fail( "allowed to access transaction on closed session" );
}
catch( Throwable ignore ) {
}
try {
s.close();
fail( "allowed to close already closed session" );
}
catch( Throwable ignore ) {
}
try {
s.isDirty();
fail( "allowed to check dirtiness of closed session" );
}
catch( Throwable ignore ) {
}
}
}