|
From: Andy D. <an...@ma...> - 2005-06-20 22:28:32
|
I'm attempting to do some advanced transaction synchronization. During
synchronization, I need to update a status row in a DB table. Of course,
since DB access is itself transactional, I create a new transaction for the
duration of the update using PROPAGATION_REQUIRES_NEW. The problem occurs
when I attempt to execute this in "afterCompletion". I sometimes get this
exception:
java.lang.IllegalStateException: No value for key
[org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
Here is an abbreviated version of my code:
------------
TransactionSynchronizationManager.registerSynchronization(new
TransactionSynchronization() {
public void suspend() { }
public void resume() { }
public void beforeCommit(boolean readOnly)
{
getTransactionTemplate().execute(new
TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(final TransactionStatus
status) {
update("update m_t set trans_time=null where trans_id=?",
new Object[] { transId });
}
});
}
public void beforeCompletion() { }
public void afterCompletion(final int status) {
getTransactionTemplate().execute(new
TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(final TransactionStatus
ts) {
if(status == STATUS_ROLLED_BACK) {
update("update m_t set s_last=trans_s_start, trans_id=null,
trans_time=null where trans_id=?",
new Object[] { transId} );
} else {
update("update m_t set trans_id=null, trans_time=null,
trans_s_start=null where trans_id=?",
new Object[] { transId });
}
}
});
}
});
---------------
The TransactionTemplate returned from getTransactionTemplate() was created
like this:
transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate.setTimeout(getTransactionTimeoutInMs());
transactionTemplate.setReadOnly(false);
My question is, should I be able to do this? The exception is not thrown in
many cases - however, in this particular case, an exception was thrown via
other code in the context of the main transaction, though it did not cause a
rollback to occur, so the transaction was in the process of being committed -
and while committing the main transaction, the process of starting a new
transaction caused this related exception to be thrown.
This is against Spring 1.2.1.
- Andy
PS. Here is a full stack trace, for what its worth:
java.lang.IllegalStateException: No value for key
[org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
at
org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager.java:175)
at
org.springframework.orm.hibernate3.SessionFactoryUtils$SpringSessionSynchronization.suspend(SessionFactoryUtils.java:846)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.suspend(AbstractPlatformTransactionManager.java:350)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.getExistingTransaction(AbstractPlatformTransactionManager.java:271)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:215)
at
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:111)
at com.marathon.service.dao.jdbc.PSDAO$2.afterCompletion(PSDAO.java:185)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerAfterCompletion(AbstractPlatformTransactionManager.java:610)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:467)
at
org.springframework.transaction.interceptor.TransactionAspectSupport.doCloseTransactionAfterThrowing(TransactionAspectSupport.java:294)
at
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:61)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
at
net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:80)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:174)
at $Proxy58.postApproved(Unknown Source)
|
|
From: Andy D. <an...@ma...> - 2005-06-20 23:26:55
|
I have some additional information on this exception. It appears that during
transaction committment things are left in an inconsistent state between the
time that AbstractPlatformTransactionManager.processCommit(...) calls
triggerBeforeCompletion(...) and the time it calls
triggerAfterCompletion(...) - though it is not
AbstractPlatformTransactionManager's fault. In my case, one of the
TransactionSynchronizations attached to the transaction is
org.springframework.orm.hibernate3.SessionFactoryUtils. This class's
implementation of beforeCompletion() invokes
TransactionSynchronizationManager.unbindResource(this.sessionFactory). If
you then attempt to suspend the transaction in a TransactionSynchronization
implementation's afterCompletion method (as I do), you will get an exception
since SessionFactoryUtils' suspend() implementation will blindly call
TransactionSynchronizationManager.unbindResource(this.sessionFactory). Of
course, since beforeCompletion() already did this, the sessionFactory is no
longer bound to the transaction, thus the exception is thrown. Following is
a log with detailed debug output (and comments):
2005-06-20 14:22:54,355 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Bound value [org.springframework.orm.hibernate3.SessionHolder@383c13] for key
[org.hibernate.impl.SessionFactoryImpl@1f37bf1] to thread [Timer-3]
2005-06-20 14:22:54,355 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Resuming JTA
transaction
2005-06-20 14:22:54,355 DEBUG [com.marathon.service.dao.jdbc.PSDAO]
-----++++]]]]>>>> TRACKING (synchronizing) with caller's transaction...
2005-06-20 14:22:54,355 DEBUG [com.marathon.service.dao.jdbc.PSDAO]
-----++++]]]]>>>> ENTERING
PSDAO.synchronizeWithTransaction(org.springframework.transaction.support.DefaultTransactionStatus@1e6356d)
... Apparently, the parent service code threw an exception at this point ...
2005-06-20 14:22:54,359 DEBUG
[org.springframework.transaction.interceptor.RuleBasedTransactionAttribute]
Applying rules to determine whether transaction should rollback on
com.marathon.AlreadyPostedException: Unable to perform operation: this entry
has already been submitted.
2005-06-20 14:22:54,359 DEBUG
[org.springframework.transaction.interceptor.RuleBasedTransactionAttribute]
Winning rollback rule is: null
2005-06-20 14:22:54,360 DEBUG
[org.springframework.transaction.interceptor.RuleBasedTransactionAttribute]
No relevant rollback rule found: applying superclass default
2005-06-20 14:22:54,360 DEBUG
[org.springframework.transaction.interceptor.TransactionInterceptor]
com.marathon.EntryManager.postApproved threw throwable
[com.marathon.AlreadyPostedException: Unable to perform operation: this entry
has already been submitted.] but this does not force transaction rollback
... Notice that the exception did not cause a rollback
2005-06-20 14:22:54,360 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Triggering
beforeCommit synchronization
2005-06-20 14:22:54,360 DEBUG
[org.springframework.orm.hibernate3.SessionFactoryUtils] Flushing Hibernate
Session on transaction synchronization
2005-06-20 14:22:54,365 DEBUG [com.marathon.service.dao.jdbc.PSDAO]
-----++++]]]]>>>> ENTERING
PSDAO.TransactionSynchronization.beforeCommit(false)
... At this point, our TransactionSynchronization starts a new transaction
using REQUIRES_NEW, causing the current transaction to suspend.
2005-06-20 14:22:54,367 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Using transaction
object [org.springframework.transaction.jta.JtaTransactionObject@bd7173]
2005-06-20 14:22:54,367 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Creating new
transaction, suspending current one
2005-06-20 14:22:54,367 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Suspending JTA
transaction
2005-06-20 14:22:54,368 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Removed value [org.springframework.orm.hibernate3.SessionHolder@383c13] for
key [org.hibernate.impl.SessionFactoryImpl@1f37bf1] from thread [Timer-3]
2005-06-20 14:22:54,368 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Clearing transaction synchronization
2005-06-20 14:22:54,368 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Beginning JTA
transaction
2005-06-20 14:22:54,368 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Initializing transaction synchronization
2005-06-20 14:22:54,368 DEBUG
[org.springframework.jdbc.datasource.DataSourceUtils] Opening JDBC Connection
2005-06-20 14:22:54,369 DEBUG
[org.springframework.jdbc.datasource.DataSourceUtils] Registering transaction
synchronization for JDBC Connection
2005-06-20 14:22:54,369 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Bound value [org.springframework.jdbc.datasource.ConnectionHolder@2b3223] for
key [org.jboss.resource.adapter.jdbc.WrapperDataSource@bed1fd] to thread
[Timer-3]
2005-06-20 14:22:54,371 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@2b3223]
for key [org.jboss.resource.adapter.jdbc.WrapperDataSource@bed1fd] bound to
thread [Timer-3]
... Our new transaction uses straight SQL (no Hibernate) and completes
successfully...
2005-06-20 14:22:54,371 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Triggering
beforeCommit synchronization
2005-06-20 14:22:54,371 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Triggering
beforeCompletion synchronization
2005-06-20 14:22:54,371 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Removed value [org.springframework.jdbc.datasource.ConnectionHolder@2b3223]
for key [org.jboss.resource.adapter.jdbc.WrapperDataSource@bed1fd] from
thread [Timer-3]
2005-06-20 14:22:54,371 DEBUG
[org.springframework.jdbc.datasource.DataSourceUtils] Closing JDBC Connection
2005-06-20 14:22:54,371 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Initiating
transaction commit
2005-06-20 14:22:54,371 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Committing JTA
transaction
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Triggering
afterCompletion synchronization
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Clearing transaction synchronization
... Our new transaction committed without problems, so the main transaction is
now resumed...
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Resuming
suspended transaction
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Initializing transaction synchronization
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Bound value [org.springframework.orm.hibernate3.SessionHolder@383c13] for key
[org.hibernate.impl.SessionFactoryImpl@1f37bf1] to thread [Timer-3]
... Notice that the SessionFactoryImpl key that causes our woe later on is set
back up properly.
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Resuming JTA
transaction
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Triggering
beforeCompletion synchronization
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Removed value [org.springframework.orm.hibernate3.SessionHolder@383c13] for
key [org.hibernate.impl.SessionFactoryImpl@1f37bf1] from thread [Timer-3]
... At this point, it appears that Hibernate's synchronization code has
removed the infamous SessionFactoryImpl key from the
TransactionSynchronizationManager, however, as we will see later,
Hibernate3's TransactionSynchronization implementation (SessionFactoryUtils)
still holds a reference to this SessionFactoryImpl, and will try to call
TransactionSynchronizationManager.unbindResource with it once we attempt to
suspend the transaction.
2005-06-20 14:22:54,373 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Triggering
afterCompletion synchronization
2005-06-20 14:22:54,374 DEBUG
[org.springframework.orm.hibernate3.SessionFactoryUtils] Closing Hibernate
Session
2005-06-20 14:22:54,374 DEBUG [com.marathon.service.dao.jdbc.PSDAO]
-----++++]]]]>>>> ENTERING
PSDAO.TransactionSynchronization.afterCompletion(0)
... Here we have entered our custom TransactionSynchronization.afterCompletion
method, and will attempt to enter our REQUIRES_NEW TransactionTemplate
(causing the current transaction to suspend)...
2005-06-20 14:22:54,377 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Using transaction
object [org.springframework.transaction.jta.JtaTransactionObject@9b415c]
2005-06-20 14:22:54,377 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Creating new
transaction, suspending current one
2005-06-20 14:22:54,377 DEBUG
[org.springframework.transaction.jta.JtaTransactionManager] Suspending JTA
transaction
2005-06-20 14:22:54,377 DEBUG
[org.springframework.transaction.support.TransactionSynchronizationManager]
Clearing transaction synchronization
2005-06-20 14:22:54,378 WARN java.lang.IllegalStateException: No value for
key [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
On Monday 20 June 2005 03:28 pm, Andy Depue wrote:
> I'm attempting to do some advanced transaction synchronization. During
> synchronization, I need to update a status row in a DB table. Of course,
> since DB access is itself transactional, I create a new transaction for the
> duration of the update using PROPAGATION_REQUIRES_NEW. The problem occurs
> when I attempt to execute this in "afterCompletion". I sometimes get this
> exception:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
>
> Here is an abbreviated version of my code:
>
> ------------
> TransactionSynchronizationManager.registerSynchronization(new
> TransactionSynchronization() {
>
> public void suspend() { }
> public void resume() { }
> public void beforeCommit(boolean readOnly)
> {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus status) {
> update("update m_t set trans_time=null where trans_id=?",
> new Object[] { transId });
> }
> });
> }
> public void beforeCompletion() { }
> public void afterCompletion(final int status) {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus ts) {
> if(status == STATUS_ROLLED_BACK) {
> update("update m_t set s_last=trans_s_start, trans_id=null,
> trans_time=null where trans_id=?",
> new Object[] { transId} );
> } else {
> update("update m_t set trans_id=null, trans_time=null,
> trans_s_start=null where trans_id=?",
> new Object[] { transId });
> }
> }
> });
> }
> });
> ---------------
>
> The TransactionTemplate returned from getTransactionTemplate() was created
> like this:
> transactionTemplate = new TransactionTemplate(transactionManager);
>
> transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATIO
>N_REQUIRES_NEW);
> transactionTemplate.setTimeout(getTransactionTimeoutInMs());
> transactionTemplate.setReadOnly(false);
>
>
> My question is, should I be able to do this? The exception is not thrown
> in many cases - however, in this particular case, an exception was thrown
> via other code in the context of the main transaction, though it did not
> cause a rollback to occur, so the transaction was in the process of being
> committed - and while committing the main transaction, the process of
> starting a new transaction caused this related exception to be thrown.
> This is against Spring 1.2.1.
>
> - Andy
>
> PS. Here is a full stack trace, for what its worth:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
> at
> org.springframework.transaction.support.TransactionSynchronizationManager.u
>nbindResource(TransactionSynchronizationManager.java:175) at
> org.springframework.orm.hibernate3.SessionFactoryUtils$SpringSessionSynchro
>nization.suspend(SessionFactoryUtils.java:846) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>suspend(AbstractPlatformTransactionManager.java:350) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>getExistingTransaction(AbstractPlatformTransactionManager.java:271) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>getTransaction(AbstractPlatformTransactionManager.java:215) at
> org.springframework.transaction.support.TransactionTemplate.execute(Transac
>tionTemplate.java:111) at
> com.marathon.service.dao.jdbc.PSDAO$2.afterCompletion(PSDAO.java:185) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>triggerAfterCompletion(AbstractPlatformTransactionManager.java:610) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>commit(AbstractPlatformTransactionManager.java:467) at
> org.springframework.transaction.interceptor.TransactionAspectSupport.doClos
>eTransactionAfterThrowing(TransactionAspectSupport.java:294) at
> org.springframework.transaction.interceptor.TransactionInterceptor.invoke(T
>ransactionInterceptor.java:61) at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflec
>tiveMethodInvocation.java:144) at
> net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor
>.invoke(MethodSecurityInterceptor.java:80) at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflec
>tiveMethodInvocation.java:144) at
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopPr
>oxy.java:174) at $Proxy58.postApproved(Unknown Source)
|
|
From: Andy D. <an...@ma...> - 2005-06-21 21:27:18
|
As I track this problem further, I see that it lies Hibernate3's
SessionFactoryUtils. SessionFactoryUtils has an inner class,
"SpringSessionSynchronization", that is instantiated and registered against
the current transaction when using Spring for session management. During
transaction suspend and resume, this implementation will *always* call
TransactionSynchronizationManager.unbindResource(this.sessionFactory) and
TransactionSynchronizationManager.bindResource(this.sessionFactory,
this.sessionHolder), respectively. When Spring performs a commit, it calls
synchronization methods in this order:
1. beforeCommit
2. beforeCompletion
3. afterCompletion
In beforeCompletion, SpringSessionSynchronization will
unbindResource(this.sessionFactory), and sometimes close the Hibernate
Session. However, SpringSessionSynchronization will not reflect this in its
internal state in any way. Now imagine that a TransactionSynchronization
implementation suspends the transaction in afterCompletion.
SessionFactoryUtils.SpringSessionSynchronization will have its suspend method
called, which will attempt to unbindResource(this.sessionFactory), causing an
exception since it has already been unbound. Moreover, even if it performed
a check and did not unbind the resource, resume() would still attempt to
rebind the resource (whose Hibernate Session was most likely closed in
beforeCompletion), leaving that closed resource bound to the current thread
even after the main transaction has been committed.
Basically, SpringSessionSynchronization needs to be modified to handle this
call sequence:
1. beforeCommit
2. beforeCompletion
3. suspend
4. resume
5. afterCompletion
Which it does not now support (though this was working in older versions of
Spring). I'm willing to open a JIRA issue, make these changes, and submit a
patch if no one has a problem with them.
- Andy
On Monday 20 June 2005 03:28 pm, Andy Depue wrote:
> I'm attempting to do some advanced transaction synchronization. During
> synchronization, I need to update a status row in a DB table. Of course,
> since DB access is itself transactional, I create a new transaction for the
> duration of the update using PROPAGATION_REQUIRES_NEW. The problem occurs
> when I attempt to execute this in "afterCompletion". I sometimes get this
> exception:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
>
> Here is an abbreviated version of my code:
>
> ------------
> TransactionSynchronizationManager.registerSynchronization(new
> TransactionSynchronization() {
>
> public void suspend() { }
> public void resume() { }
> public void beforeCommit(boolean readOnly)
> {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus status) {
> update("update m_t set trans_time=null where trans_id=?",
> new Object[] { transId });
> }
> });
> }
> public void beforeCompletion() { }
> public void afterCompletion(final int status) {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus ts) {
> if(status == STATUS_ROLLED_BACK) {
> update("update m_t set s_last=trans_s_start, trans_id=null,
> trans_time=null where trans_id=?",
> new Object[] { transId} );
> } else {
> update("update m_t set trans_id=null, trans_time=null,
> trans_s_start=null where trans_id=?",
> new Object[] { transId });
> }
> }
> });
> }
> });
> ---------------
>
> The TransactionTemplate returned from getTransactionTemplate() was created
> like this:
> transactionTemplate = new TransactionTemplate(transactionManager);
>
> transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATIO
>N_REQUIRES_NEW);
> transactionTemplate.setTimeout(getTransactionTimeoutInMs());
> transactionTemplate.setReadOnly(false);
>
>
> My question is, should I be able to do this? The exception is not thrown
> in many cases - however, in this particular case, an exception was thrown
> via other code in the context of the main transaction, though it did not
> cause a rollback to occur, so the transaction was in the process of being
> committed - and while committing the main transaction, the process of
> starting a new transaction caused this related exception to be thrown.
> This is against Spring 1.2.1.
>
> - Andy
>
> PS. Here is a full stack trace, for what its worth:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
> at
> org.springframework.transaction.support.TransactionSynchronizationManager.u
>nbindResource(TransactionSynchronizationManager.java:175) at
> org.springframework.orm.hibernate3.SessionFactoryUtils$SpringSessionSynchro
>nization.suspend(SessionFactoryUtils.java:846) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>suspend(AbstractPlatformTransactionManager.java:350) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>getExistingTransaction(AbstractPlatformTransactionManager.java:271) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>getTransaction(AbstractPlatformTransactionManager.java:215) at
> org.springframework.transaction.support.TransactionTemplate.execute(Transac
>tionTemplate.java:111) at
> com.marathon.service.dao.jdbc.PSDAO$2.afterCompletion(PSDAO.java:185) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>triggerAfterCompletion(AbstractPlatformTransactionManager.java:610) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>commit(AbstractPlatformTransactionManager.java:467) at
> org.springframework.transaction.interceptor.TransactionAspectSupport.doClos
>eTransactionAfterThrowing(TransactionAspectSupport.java:294) at
> org.springframework.transaction.interceptor.TransactionInterceptor.invoke(T
>ransactionInterceptor.java:61) at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflec
>tiveMethodInvocation.java:144) at
> net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor
>.invoke(MethodSecurityInterceptor.java:80) at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflec
>tiveMethodInvocation.java:144) at
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopPr
>oxy.java:174) at $Proxy58.postApproved(Unknown Source)
>
>
> -------------------------------------------------------
> SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
> from IBM. Find simple to follow Roadmaps, straightforward articles,
> informative Webcasts and more! Get everything you need to get up to
> speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
> _______________________________________________
> Springframework-developer mailing list
> Spr...@li...
> https://lists.sourceforge.net/lists/listinfo/springframework-developer
|
|
From: Juergen H. <ju...@in...> - 2005-06-21 21:34:18
|
Sounds plausible - thanks for tracking this down. Please do create a =
JIRA
issue for this; I'll fix the issue for Spring 1.2.2.
Essentially, SpringSessionSynchronization needs to keep a marker that it =
has
already removed the current Session from the thread, and suppress =
suspend
and resume accordingly. It could simply hold a boolean flag as instance
variable for this.
Juergen
-----Urspr=FCngliche Nachricht-----
Von: spr...@li...
[mailto:spr...@li...] Im =
Auftrag
von Andy Depue
Gesendet: Dienstag, 21. Juni 2005 23:27
An: spr...@li...
Betreff: Re: [Springframework-developer] Problems with advanced =
transaction
synchronization
As I track this problem further, I see that it lies Hibernate3's=20
SessionFactoryUtils. SessionFactoryUtils has an inner class,=20
"SpringSessionSynchronization", that is instantiated and registered =
against=20
the current transaction when using Spring for session management. =
During=20
transaction suspend and resume, this implementation will *always* call=20
TransactionSynchronizationManager.unbindResource(this.sessionFactory) =
and=20
TransactionSynchronizationManager.bindResource(this.sessionFactory,=20
this.sessionHolder), respectively. When Spring performs a commit, it =
calls=20
synchronization methods in this order:
1. beforeCommit
2. beforeCompletion
3. afterCompletion
In beforeCompletion, SpringSessionSynchronization will=20
unbindResource(this.sessionFactory), and sometimes close the Hibernate=20
Session. However, SpringSessionSynchronization will not reflect this in =
its
internal state in any way. Now imagine that a =
TransactionSynchronization=20
implementation suspends the transaction in afterCompletion. =20
SessionFactoryUtils.SpringSessionSynchronization will have its suspend
method=20
called, which will attempt to unbindResource(this.sessionFactory), =
causing
an=20
exception since it has already been unbound. Moreover, even if it =
performed
a check and did not unbind the resource, resume() would still attempt to =
rebind the resource (whose Hibernate Session was most likely closed in=20
beforeCompletion), leaving that closed resource bound to the current =
thread=20
even after the main transaction has been committed.
Basically, SpringSessionSynchronization needs to be modified to handle =
this=20
call sequence:
1. beforeCommit
2. beforeCompletion
3. suspend
4. resume
5. afterCompletion
Which it does not now support (though this was working in older versions =
of=20
Spring). I'm willing to open a JIRA issue, make these changes, and =
submit a
patch if no one has a problem with them.
- Andy
On Monday 20 June 2005 03:28 pm, Andy Depue wrote:
> I'm attempting to do some advanced transaction synchronization. =
During
> synchronization, I need to update a status row in a DB table. Of =
course,
> since DB access is itself transactional, I create a new transaction =
for
the
> duration of the update using PROPAGATION_REQUIRES_NEW. The problem =
occurs
> when I attempt to execute this in "afterCompletion". I sometimes get =
this
> exception:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread =
[Timer-3]
>
> Here is an abbreviated version of my code:
>
> ------------
> TransactionSynchronizationManager.registerSynchronization(new
> TransactionSynchronization() {
>
> public void suspend() { }
> public void resume() { }
> public void beforeCommit(boolean readOnly)
> {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus status) {
> update("update m_t set trans_time=3Dnull where =
trans_id=3D?",
> new Object[] { transId });
> }
> });
> }
> public void beforeCompletion() { }
> public void afterCompletion(final int status) {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus ts) {
> if(status =3D=3D STATUS_ROLLED_BACK) {
> update("update m_t set s_last=3Dtrans_s_start, =
trans_id=3Dnull,
> trans_time=3Dnull where trans_id=3D?",
> new Object[] { transId} );
> } else {
> update("update m_t set trans_id=3Dnull, =
trans_time=3Dnull,
> trans_s_start=3Dnull where trans_id=3D?",
> new Object[] { transId });
> }
> }
> });
> }
> });
> ---------------
>
> The TransactionTemplate returned from getTransactionTemplate() was =
created
> like this:
> transactionTemplate =3D new =
TransactionTemplate(transactionManager);
> =20
>
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGAT=
IO
>N_REQUIRES_NEW);
> transactionTemplate.setTimeout(getTransactionTimeoutInMs());
> transactionTemplate.setReadOnly(false);
>
>
> My question is, should I be able to do this? The exception is not =
thrown
> in many cases - however, in this particular case, an exception was =
thrown
> via other code in the context of the main transaction, though it did =
not
> cause a rollback to occur, so the transaction was in the process of =
being
> committed - and while committing the main transaction, the process of
> starting a new transaction caused this related exception to be thrown.
> This is against Spring 1.2.1.
>
> - Andy
>
> PS. Here is a full stack trace, for what its worth:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread =
[Timer-3]
> at
>
org.springframework.transaction.support.TransactionSynchronizationManager=
.u
>nbindResource(TransactionSynchronizationManager.java:175) at
>
org.springframework.orm.hibernate3.SessionFactoryUtils$SpringSessionSynch=
ro
>nization.suspend(SessionFactoryUtils.java:846) at
>
org.springframework.transaction.support.AbstractPlatformTransactionManage=
r.
>suspend(AbstractPlatformTransactionManager.java:350) at
>
org.springframework.transaction.support.AbstractPlatformTransactionManage=
r.
>getExistingTransaction(AbstractPlatformTransactionManager.java:271) at
>
org.springframework.transaction.support.AbstractPlatformTransactionManage=
r.
>getTransaction(AbstractPlatformTransactionManager.java:215) at
>
org.springframework.transaction.support.TransactionTemplate.execute(Trans=
ac
>tionTemplate.java:111) at
> com.marathon.service.dao.jdbc.PSDAO$2.afterCompletion(PSDAO.java:185) =
at
>
org.springframework.transaction.support.AbstractPlatformTransactionManage=
r.
>triggerAfterCompletion(AbstractPlatformTransactionManager.java:610) at
>
org.springframework.transaction.support.AbstractPlatformTransactionManage=
r.
>commit(AbstractPlatformTransactionManager.java:467) at
>
org.springframework.transaction.interceptor.TransactionAspectSupport.doCl=
os
>eTransactionAfterThrowing(TransactionAspectSupport.java:294) at
>
org.springframework.transaction.interceptor.TransactionInterceptor.invoke=
(T
>ransactionInterceptor.java:61) at
>
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Refl=
ec
>tiveMethodInvocation.java:144) at
>
net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityIntercept=
or
>.invoke(MethodSecurityInterceptor.java:80) at
>
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Refl=
ec
>tiveMethodInvocation.java:144) at
>
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAop=
Pr
>oxy.java:174) at $Proxy58.postApproved(Unknown Source)
>
>
> -------------------------------------------------------
> SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
> from IBM. Find simple to follow Roadmaps, straightforward articles,
> informative Webcasts and more! Get everything you need to get up to
> speed, fast. =
http://ads.osdn.com/?ad_id=3D7477&alloc_id=3D16492&op=3Dclick
> _______________________________________________
> Springframework-developer mailing list
> Spr...@li...
> https://lists.sourceforge.net/lists/listinfo/springframework-developer
-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. =
http://ads.osdn.com/?ad_id=3D7477&alloc_id=3D16492&op=3Dclick
_______________________________________________
Springframework-developer mailing list
Spr...@li...
https://lists.sourceforge.net/lists/listinfo/springframework-developer
|
|
From: Andy D. <an...@ma...> - 2005-06-21 22:53:27
|
JIRA issue created and a proof-of-concept patch supplied (which happens to fix the issue for me): http://opensource.atlassian.com/projects/spring/browse/SPR-1066 - Andy On Tuesday 21 June 2005 02:34 pm, Juergen Hoeller wrote: > Sounds plausible - thanks for tracking this down. Please do create a JIRA > issue for this; I'll fix the issue for Spring 1.2.2. > > Essentially, SpringSessionSynchronization needs to keep a marker that it > has already removed the current Session from the thread, and suppress > suspend and resume accordingly. It could simply hold a boolean flag as > instance variable for this. > > Juergen > |