1 2 Previous Next 25 Replies Latest reply on Jan 16, 2010 3:20 AM by christinechan

    Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction manage

    adepue

      I'm experimenting using Envers with Spring + Hibernate (using the versions of Spring and Hibernate we currently use in production here), and have an issue. At transaction commit time, this exception is thrown:

      org.hibernate.SessionException: Session is closed!
       at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:49)
       at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:531)
       at org.hibernate.impl.SessionImpl.save(SessionImpl.java:523)
       at org.jboss.envers.revisioninfo.DefaultRevisionInfoGenerator.generate(DefaultRevisionInfoGenerator.java:81)
       at org.jboss.envers.synchronization.VersionsSync.executeInSession(VersionsSync.java:109)
       at org.jboss.envers.synchronization.VersionsSync.beforeCompletion(VersionsSync.java:145)
       at org.apache.geronimo.transaction.manager.TransactionImpl.beforeCompletion(TransactionImpl.java:514)
       at org.apache.geronimo.transaction.manager.TransactionImpl.beforeCompletion(TransactionImpl.java:498)
       at org.apache.geronimo.transaction.manager.TransactionImpl.beforePrepare(TransactionImpl.java:400)
       at org.apache.geronimo.transaction.manager.TransactionImpl.commit(TransactionImpl.java:257)
       at org.apache.geronimo.transaction.manager.TransactionManagerImpl.commit(TransactionManagerImpl.java:238)
       at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:842)
       at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
       at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
       at org.jencks.GeronimoPlatformTransactionManager.commit(GeronimoPlatformTransactionManager.java:76)
       at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:139)
       ...

      It appears that Spring's org.springframework.orm.hibernate3.SpringSessionSynchronization closes the session in its own beforeCompletion() method, which is invoked before Envers' beforeCompletion().
      Is this a bug, or just misconfiguration? And if it is a bug, any ideas on a workaround? I wonder if org.jboss.envers.synchronization.VersionsSync.beforeCompletion() could be modified to detect this case and use a new session, or even a temporary session like what is done for manual flush mode?

        • 1. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
          adamw

          Hello,

          well that's very weird that the session is closed before the end of transaction. Do you have any special setup? How are your transactions managed?

          I think that it's possible to check if a session is closed and open a temporary one if so, when Envers does the synchornization. But I would need a test program for this :)

          But I don't have much experience with Spring (well, none to be exact) so maybe some other Spring+Envers users can help you :).

          --
          Adam

          • 2. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
            adepue

            For whatever reason, Spring closes the session in its beforeCompletion... it makes at least one exception (there is a way to tell Spring to defer close, but that is used primarily for the OpenSessionInView pattern - besides, I don't think this is simple to enable when one is using Spring managed session with their DAO support). I'm not familiar enough with the way Spring and Hibernate interact with JTA to know why - though I did see a message by Juergen (a main Spring developer) on the Hibernate forums indicating that it is because of an issue with WebLogic's JTA (if I'm reading it right). I've copied the code from Spring's beforeCompletion method below. It almost looks like Spring might be assuming that Hibernate's JTA integration will defer close all on its own - but that isn't happening in my case.
            I might experiment with modifying the Envers beforeCompletion to open a temporary Session if the main one is closed...

            Just so you know, my debugger shows that, in my case, I'm hitting the second SessionFactoryUtils.closeSessionOrRegisterDeferredClose(...) call (the one in the "if (this.newSession)" block), not the first.

            public void beforeCompletion() {
             if (this.jtaTransaction != null) {
             // Typically in case of a suspended JTA transaction:
             // Remove the Session for the current JTA transaction, but keep the holder.
             Session session = this.sessionHolder.removeSession(this.jtaTransaction);
             if (session != null) {
             if (this.sessionHolder.isEmpty()) {
             // No Sessions for JTA transactions bound anymore -> could remove it.
             if (TransactionSynchronizationManager.hasResource(this.sessionFactory)) {
             // Explicit check necessary because of remote transaction propagation:
             // The synchronization callbacks will execute in a different thread
             // in such a scenario, as they're triggered by a remote server.
             // The best we can do is to leave the SessionHolder bound to the
             // thread that originally performed the data access. It will be
             // reused when a new data access operation starts on that thread.
             TransactionSynchronizationManager.unbindResource(this.sessionFactory);
             }
             this.holderActive = false;
             }
             // Do not close a pre-bound Session. In that case, we'll find the
             // transaction-specific Session the same as the default Session.
             if (session != this.sessionHolder.getSession()) {
             SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, this.sessionFactory);
             }
             else if (this.sessionHolder.getPreviousFlushMode() != null) {
             // In case of pre-bound Session, restore previous flush mode.
             session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
             }
             return;
             }
             }
             // We'll only get here if there was no specific JTA transaction to handle.
             if (this.newSession) {
             // Default behavior: unbind and close the thread-bound Hibernate Session.
             TransactionSynchronizationManager.unbindResource(this.sessionFactory);
             this.holderActive = false;
             if (this.hibernateTransactionCompletion) {
             // Close the Hibernate Session here in case of a Hibernate TransactionManagerLookup:
             // Hibernate will automatically defer the actual closing until JTA transaction completion.
             // Else, the Session will be closed in the afterCompletion method, to provide the
             // correct transaction status for releasing the Session's cache locks.
             SessionFactoryUtils.closeSessionOrRegisterDeferredClose(this.sessionHolder.getSession(), this.sessionFactory);
             }
             }
             else if (this.sessionHolder.getPreviousFlushMode() != null) {
             // In case of pre-bound Session, restore previous flush mode.
             this.sessionHolder.getSession().setFlushMode(this.sessionHolder.getPreviousFlushMode());
             }
             }


            • 3. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
              adepue

              OK, I threw in a little change just to see what would happen. I modified line 129 in Envers beforeCompletion from:

              if (FlushMode.isManualFlushMode(session.getFlushMode())) {

              to:
              if (FlushMode.isManualFlushMode(session.getFlushMode()) || session.isClosed()) {


              This makes the problem go away. But I wonder - does the temporary session participate in the surrounding JTA transaction at all?

              • 4. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                adamw

                Hello,

                well, the transaction isn't commited then, so it's active, so I would bet that it does :). I think if you tried doing some illegal operations there (for ex. violating a unique constraint), or just throwing an exception, we could see if the transaction gets rolled back. Do you have the test case isolated?

                --
                Thanks,
                Adam

                • 5. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                  skomarla

                  Did anything happen with this discussion? I now have this same exception with JBoss JTA, Spring 2.5.6, hibernate 3.3.2.GA and Envers 1.2.1GA.

                  I will try to reproduce this and get to a junit that can reproduce the issue, but not sure if can suceed since none of my existing junits use JTA, and I would have to roll together JTA into my unit test framework, but I will try to.

                  my stack trace..

                  org.hibernate.SessionException: Session is closed!
                   at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:72)
                   at org.hibernate.impl.SessionImpl.getTransaction(SessionImpl.java:1342)
                   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                   at java.lang.reflect.Method.invoke(Method.java:585)
                   at org.springframework.orm.hibernate3.HibernateTemplate$CloseSuppressingInvocationHandler.invoke(HibernateTemplate.java:1293)
                   at $Proxy205.getTransaction(Unknown Source)
                   at org.hibernate.envers.synchronization.AuditSync.beforeCompletion(AuditSync.java:171)
                   at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:114)
                   at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:247)
                   at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:86)
                   at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:177)
                   at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1389)
                   at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:135)
                   at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:87)
                   at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:140)
                   at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1028)
                   at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:732)
                   at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701)
                   at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321)
                   at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
                   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
                   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
                   at $Proxy132.updateNodeRoutingDetails(Unknown Source)
                   at com.sterling.ifn.controller.NetworkNodesController.routingSave(NetworkNodesController.java:788)
                  
                  


                  The last line above is a springmvc controller that is calling a transactional service ($Proxy132.updateNodeRoutingDetails). When the proxy returns, the txn should be closed.. but as you can see in the stack, it hasn't returned yet.

                  • 6. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                    skomarla

                    I see that this exception indicates that a RuntimeException happened and is doing a rollback.. however.. the session is closed, and so, cannot rollback.

                    - probably worth logging the exception before issueing the session rollback
                    - also, it would make sense to remember which session needs a rollback. I assume in this case, that it is the temporarySession that needs a rollback.

                     public void beforeCompletion() {
                     if (workUnits.size() == 0 && undoQueue.size() == 0) {
                     return;
                     }
                    
                     try {
                     // see: http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4178431
                     if (FlushMode.isManualFlushMode(session.getFlushMode()) || session.isClosed()) {
                     Session temporarySession = null;
                     try {
                     temporarySession = session.getFactory().openTemporarySession();
                    
                     executeInSession(temporarySession);
                    
                     temporarySession.flush();
                     } finally {
                     if (temporarySession != null) {
                     temporarySession.close();
                     }
                     }
                     } else {
                     executeInSession(session);
                    
                     // Explicity flushing the session, as the auto-flush may have already happened.
                     session.flush();
                     }
                     } catch (RuntimeException e) {
                     // Rolling back the transaction in case of any exceptions
                     session.getTransaction().rollback();
                     throw e;
                     }
                     }
                    


                    • 7. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                      skomarla

                      Maybe I am reading this wrong since I don't know much about the transaction Synchronization "contract", but the way I am interpreting this, I see a problem.

                      the temporary session is being used to persist the revision information to the db, while the originating session is closed. When the originating session is closed, it should either be due to an executing commit or a rollback. I don't think the temporary session will participate in the outer JTA transaction.. but again, I could certainly be wrong.

                      assuming I am right in that the temporary txn does not participate in the outer txn, if the work done in the temporary session completes properly, but the outer transaction does rollback for whatever reason, you might have committed false audit records.

                      i guess it boils down to what this means from hibernate javadoc (https://www.hibernate.org/hib_docs/v3/api/org/hibernate/engine/SessionFactoryImplementor.html#openTemporarySession%28%29)

                      openTemporarySession
                      
                      public Session openTemporarySession()
                       throws HibernateException
                      
                       Get a nontransactional "current" session for Hibernate EntityManager
                      
                       Throws:
                       HibernateException
                      


                      does it warrant checking to see if the transaction is committing or being rolledback using the transaction api https://www.hibernate.org/hib_docs/v3/api/org/hibernate/Transaction.html?


                      • 8. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                        skomarla

                        Adam,

                        I have an additional observation. I grabbed the source from the svn link in the other post here http://www.jboss.org/index.html?module=bb&op=viewtopic&t=161717

                        I traced the exception I saw to the rollback call in the catch block. I just dumped the original exception that was caught in the catch block locally, and it is also a "org.hibernate.SessionException: Session is closed!" Exception. (Sorry for the printStackTrace...)

                        14:24:03,433 ERROR [STDERR] org.hibernate.SessionException: Session is closed!
                        14:24:03,433 ERROR [STDERR] at org.hibernate.impl.AbstractSessionImpl.errorI
                        fClosed(AbstractSessionImpl.java:72)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.impl.SessionImpl.getEntityPersi
                        ster(SessionImpl.java:1364)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.tools.Tools.getIdentifie
                        r(Tools.java:66)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.tools.Tools.entitiesEqua
                        l(Tools.java:49)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.entities.mapper.relation
                        .ToOneIdMapper.mapToMapFromEntity(ToOneIdMapper.java:65)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.entities.mapper.MultiPro
                        pertyMapper.map(MultiPropertyMapper.java:86)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.synchronization.work.Add
                        WorkUnit.perform(AddWorkUnit.java:60)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.synchronization.AuditSyn
                        c.executeInSession(AuditSync.java:125)
                        14:24:03,433 ERROR [STDERR] at org.hibernate.envers.synchronization.AuditSyn
                        c.beforeCompletion(AuditSync.java:155)
                        14:24:03,433 ERROR [STDERR] at com.arjuna.ats.internal.jta.resources.arjunac
                        ore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:114)
                        14:24:03,433 ERROR [STDERR] at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoo
                        rdinator.beforeCompletion(TwoPhaseCoordinator.java:247)
                        14:24:03,433 ERROR [STDERR] at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoo


                        I traced the "new" exception to AddWorkUnit line 60, and CollectionChangeWorkUnit line 57. Both those lines use the sessionImplementor protected variable instead of the session instance being passed into the perform method. This sessionImplementor, ofcourse, is closed. I will attempt a local patch to use the passed in session instance, and see if this gets me past this current issue.

                        Thanks.

                        • 9. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                          skomarla

                          uggh.. I can't pass in the session passed in because it is not a SessionImplementor..

                          any suggestions?

                          • 10. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                            adamw

                            Hello,

                            sorry for the late replies - lots of other work ;)
                            Normally, a Session can be case to a SessionImplementor as far as I remember. If not, there's a delegate that can be cast. Are you able to look in the debugger what are the real implementations?

                            I also thought that the temporary session participates in the same JTA transaction as the original one. It is quite easy to check this, did you maybe verify?

                            You are completely right about AddWorkUnit and CollectionChangeWorkUnit using the wrong session. A patch would be great! :)

                            Adam

                            • 11. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                              skomarla

                              Adam,

                              No worries regarding the late replies.. it is completely understandable. Since I work for a capitalistic company ;), my employer is urging me to move on for now, but I will try to debug some more soon.. will post it here when I have any additional info..

                              • 12. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                                adamw

                                Hello,

                                you could try changing AuditSync to not use a temporary session and verify if it works. There have been other problems with manual flush mode, and that's why the temporary session has been introduced, but I can't recall what they exactly where.

                                Adam

                                • 13. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                                  cmercer

                                  From the spring viewpoint it works if you "preopen" a session with a OpenSessionInViewInterceptor or OpenSessionInViewFilter.

                                  I think envers changes around the coordination/session management with regards to transactions and breaks the way spring is trying to coordinate between various spring platform transaction managers.

                                  • 14. Re: Spring 2.0.6 + Hibernate 3.2.6 + Geronimo transaction ma
                                    skomarla

                                    how would preopening via the interceptor work with @Transactional(propagation=Propagation.REQUIRES_NEW)?  Would there be any side effects?

                                     

                                    If you have a method with the above annotation in spring executing via a transactional proxy, where that method needs to capture some audit logic, i'm not sure if preopening with OpenSessionInViewFilter will do anything, unless ofcourse that annotation processing would do the pre-opening.

                                    1 2 Previous Next