org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorTrackingImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
JPMS Module-Info's for a few of the Jakarta Libraries just until they add them in themselves
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.resource.transaction.backend.jta.internal.synchronization;
import org.hibernate.HibernateException;
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
import org.hibernate.internal.CoreMessageLogger;
import static org.hibernate.internal.CoreLogging.messageLogger;
/**
* Extension of SynchronizationCallbackCoordinatorNonTrackingImpl that adds checking of whether a rollback comes from
* a thread other than the application thread (thread used to register the Synchronization)
*
* @author Steve Ebersole
* @author Brett Meyer
*/
public class SynchronizationCallbackCoordinatorTrackingImpl extends SynchronizationCallbackCoordinatorNonTrackingImpl {
private static final CoreMessageLogger log = messageLogger( SynchronizationCallbackCoordinatorTrackingImpl.class );
private volatile long registrationThreadId;
private volatile boolean delayedCompletionHandling;
public SynchronizationCallbackCoordinatorTrackingImpl(SynchronizationCallbackTarget target) {
// super ctor calls reset() followed by pulse()
super( target );
}
@Override
public void reset() {
super.reset();
// NOTE : reset is typically called:
// 1) on initialization, and
// 2) after "after completion" handling is finished.
//
// Here we use that to "clear out" all 'delayed after-completion" state. The registrationThreadId will
// "lazily" be re-populated on the next synchronizationRegistered call to allow for the potential of the
// next Session transaction occurring on a different thread (though that transaction would need to completely
// operate on that thread).
delayedCompletionHandling = false;
}
@Override
public void afterCompletion(int status) {
log.tracef( "Synchronization coordinator: afterCompletion(status=%s)", status );
// The whole concept of "tracking" comes down to this code block..
// Essentially we need to see if we can process the callback immediately. So here we check whether the
// current call is happening on the same thread as the thread under which we registered the Synchronization.
// As far as we know, this can only ever happen in the rollback case where the transaction had been rolled
// back on a separate "reaper" thread. Since we know the transaction status and that check is not as heavy
// as accessing the current thread, we check that first
if ( JtaStatusHelper.isRollback( status ) ) {
// we are processing a rollback, see if it is the same thread
final long currentThreadId = Thread.currentThread().getId();
final boolean isRegistrationThread = currentThreadId == registrationThreadId;
if ( ! isRegistrationThread ) {
// so we do have the condition of a rollback initiated from a separate thread. Set the flag here and
// check for it in SessionImpl. See HHH-7910.
delayedCompletionHandling = true;
log.rollbackFromBackgroundThread( status );
return;
}
}
// otherwise, do the callback immediately
doAfterCompletion( JtaStatusHelper.isCommitted( status ), false );
}
@Override
public void synchronizationRegistered() {
registrationThreadId = Thread.currentThread().getId();
}
@Override
public void processAnyDelayedAfterCompletion() {
if ( delayedCompletionHandling ) {
delayedCompletionHandling = false;
// false here (rather than how we used to keep and check the status) because as discussed above
// the delayed logic should only ever occur during rollback
doAfterCompletion( false, true );
// NOTE : doAfterCompletion calls reset
throw new HibernateException( "Transaction was rolled back in a different thread!" );
}
}
}