[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