[hibernate-issues] [Hibernate-JIRA] Created: (HHH-6644) JTA CacheSynchronization can cause exceptions with stateless sessions
Andrew Flegg (JIRA)
noreply at atlassian.com
Fri Sep 9 10:33:02 EDT 2011
JTA CacheSynchronization can cause exceptions with stateless sessions
---------------------------------------------------------------------
Key: HHH-6644
URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-6644
Project: Hibernate Core
Issue Type: Bug
Components: core
Affects Versions: 3.5.6
Environment: Hibernate Core 3.5.6, Oracle v10.2.0.3.0, JDBC driver v11.2.0.2.0, WebSphere Application Server 7.0.0.13
Reporter: Andrew Flegg
h2. 1. Background
In a JTA environment (using an XA data source), {{JDBCContext}} registers {{CacheSynchronization}} objects for running before & after transaction completion.
If a stateless session is opened _within_ an existing session and using the same connection, a {{CacheSynchronization}} will be registered against the {{StatelessSessionImpl}}. However, {{org.hibernate.transaction.CacheSynchronization.beforeCompletion()}} is called when the transaction ends; not when the stateless session ends.
In pseudo-code this might look like, using Spring's {{@Transactional}} aspect:
{code:java}
@Transactional
public void test() {
Session session = sessionFactory.getCurrentSession();
session.createQuery("from People").list();
StatelessSession statelessSession = sessionFactory.openStatelessSession(session.connection());
statelessSession.createQuery("from Dogs").list();
statelessSession.close();
session.createQuery("from People").list();
}
{code}
{{org.hibernate.impl.StatelessSessionImpl.managedFlush()}} will error if the session has been closed. [{{CacheSynchronization.beforeCompletion()}}|https://github.com/hibernate/hibernate-core/blob/3.5/core/src/main/java/org/hibernate/transaction/CacheSynchronization.java#L68] will call {{managedFlush()}} if:
* {{StatelessSessionImpl.isFlushModeNever()}} is false.
* _and_ {{StatelessSessionImpl.isFlushBeforeCompletionEnabled()}} is true
* _and_ the transaction isn't rollback-only.
As expected, {{StatelessSessionImpl}} has hardcoded _false_ and _true_ for {{isFlushModeNever()}} and {{isFlushBeforeCompletionEnabled()}}, respectively.
The above pseudo-code will therefore fail when the transaction is committed. The top of the stack-trace is:
{noformat}
Exception caught from before_completion synchronization operation: org.hibernate.SessionException: Session is closed!
at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:72)
at org.hibernate.impl.StatelessSessionImpl.managedFlush(StatelessSessionImpl.java:332)
at org.hibernate.transaction.CacheSynchronization.beforeCompletion(CacheSynchronization.java:88)
at com.ibm.tx.jta.RegisteredSyncs.coreDistributeBefore(RegisteredSyncs.java:289)
at com.ibm.ws.tx.jta.RegisteredSyncs.distributeBefore(RegisteredSyncs.java:150)
at com.ibm.ws.tx.jta.TransactionImpl.prePrepare(TransactionImpl.java:2316)
at com.ibm.ws.tx.jta.TransactionImpl.stage1CommitProcessing(TransactionImpl.java:536)
at com.ibm.tx.jta.TransactionImpl.processCommit(TransactionImpl.java:983)
at com.ibm.tx.jta.TransactionImpl.commit(TransactionImpl.java:918)
{noformat}
Looking at github, this still affects [{{CacheSynchronization}} in Hibernate 3.6|https://github.com/hibernate/hibernate-core/blob/3.6/hibernate-core/src/main/java/org/hibernate/transaction/CacheSynchronization.java#L68].
h2. 2. Proposed fix
The proposed fix is to make {{CacheSynchronization}} check {{TransactionFactory.Context#isClosed()}}.
*From:*
{code:java}
flush = !ctx.isFlushModeNever() &&
ctx.isFlushBeforeCompletionEnabled() &&
!JTAHelper.isRollback( transaction.getStatus() );
//actually, this last test is probably unnecessary, since
//beforeCompletion() doesn't get called during rollback
{code}
*To:*
{code:java}
flush = !ctx.isFlushModeNever() &&
ctx.isFlushBeforeCompletionEnabled() &&
!ctx.isClosed() &&
!JTAHelper.isRollback( transaction.getStatus() );
//actually, this last test is probably unnecessary, since
//beforeCompletion() doesn't get called during rollback
{code}
h3. 2.1. Mockito-based test
{code:java}
/**
* Check the behaviour of {@link CacheSynchronization} when the session has
* been closed.
*
* @throws SystemException if something goes wrong
*/
public void testCacheSynchronisation() throws SystemException {
final TransactionFactory.Context ctx = mock(TransactionFactory.Context.class);
doThrow(new SessionException("Session is closed!")).when(ctx).managedFlush();
when(ctx.isClosed()).thenReturn(true);
when(ctx.isFlushModeNever()).thenReturn(false);
when(ctx.isFlushBeforeCompletionEnabled()).thenReturn(true);
final javax.transaction.Transaction transaction = mock(javax.transaction.Transaction.class);
when(transaction.getStatus()).thenReturn(0);
final org.hibernate.Transaction tx = mock(org.hibernate.Transaction.class);
final JDBCContext jdbcContext = mock(JDBCContext.class);
new CacheSynchronization(ctx, jdbcContext, transaction, tx).beforeCompletion();
verify(jdbcContext).beforeTransactionCompletion(tx);
}
{code}
h3. 2.2. Alternatives
{{StatelessSessionImpl.isFlushBeforeCompletionEnabled()}} could return a boolean based on whether or not it was closed, rather than {{CacheSynchronization}} checking for closedness itself.
--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira
More information about the hibernate-issues
mailing list