Skip to content

Commit 3d1a39f

Browse files
committed
HHH-9976 - JdbcResourceLocalTransactionCoordinatorImpl does not rollback on failure during #beforeCompletionCallback
1 parent e2d6813 commit 3d1a39f

File tree

5 files changed

+144
-6
lines changed

5 files changed

+144
-6
lines changed

hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,19 @@ private void afterBeginCallback() {
142142

143143
private void beforeCompletionCallback() {
144144
log.trace( "ResourceLocalTransactionCoordinatorImpl#beforeCompletionCallback" );
145-
transactionCoordinatorOwner.beforeTransactionCompletion();
146-
synchronizationRegistry.notifySynchronizationsBeforeTransactionCompletion();
147-
for ( TransactionObserver observer : observers ) {
148-
observer.beforeCompletion();
145+
try {
146+
transactionCoordinatorOwner.beforeTransactionCompletion();
147+
synchronizationRegistry.notifySynchronizationsBeforeTransactionCompletion();
148+
for ( TransactionObserver observer : observers ) {
149+
observer.beforeCompletion();
150+
}
151+
}
152+
catch (RuntimeException e) {
153+
if ( physicalTransactionDelegate != null ) {
154+
// should never happen that the physicalTransactionDelegate is null, but to be safe
155+
physicalTransactionDelegate.markRollbackOnly();
156+
}
157+
throw e;
149158
}
150159
}
151160

hibernate-core/src/test/java/org/hibernate/test/resource/common/SynchronizationErrorImpl.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@ public class SynchronizationErrorImpl implements Synchronization {
1515
private final boolean errorOnBefore;
1616
private final boolean errorOnAfter;
1717

18-
public SynchronizationErrorImpl(boolean errorOnBefore, boolean errorOnAfter) {
18+
public static SynchronizationErrorImpl forBefore() {
19+
return new SynchronizationErrorImpl( true, false );
20+
}
21+
22+
public static SynchronizationErrorImpl forAfter() {
23+
return new SynchronizationErrorImpl( false, true );
24+
}
25+
26+
public static SynchronizationErrorImpl forBoth() {
27+
return new SynchronizationErrorImpl( true, true );
28+
}
29+
30+
private SynchronizationErrorImpl(boolean errorOnBefore, boolean errorOnAfter) {
1931
this.errorOnBefore = errorOnBefore;
2032
this.errorOnAfter = errorOnAfter;
2133
}

hibernate-core/src/test/java/org/hibernate/test/resource/transaction/SynchronizationRegistryStandardImplTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void basicUsageTests() {
8989
public void testUserSynchronizationExceptions() {
9090
// exception in beforeCompletion
9191
SynchronizationRegistryStandardImpl registry = new SynchronizationRegistryStandardImpl();
92-
Synchronization synchronization = new SynchronizationErrorImpl( true, false );
92+
Synchronization synchronization = SynchronizationErrorImpl.forBefore();
9393
registry.registerSynchronization( synchronization );
9494
try {
9595
registry.notifySynchronizationsBeforeTransactionCompletion();

hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/BasicJdbcTransactionTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
*/
77
package org.hibernate.test.resource.transaction.jdbc;
88

9+
import javax.transaction.Synchronization;
10+
11+
import org.hibernate.HibernateException;
912
import org.hibernate.TransactionException;
1013
import org.hibernate.resource.transaction.TransactionCoordinator;
1114
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
1215
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
1316
import org.hibernate.resource.transaction.spi.TransactionStatus;
1417

1518
import org.hibernate.test.resource.common.SynchronizationCollectorImpl;
19+
import org.hibernate.test.resource.common.SynchronizationErrorImpl;
1620
import org.junit.Test;
1721

1822
import static org.junit.Assert.assertEquals;
@@ -88,4 +92,38 @@ public boolean shouldAutoJoinTransaction() {
8892
transactionCoordinator.getTransactionDriverControl().rollback();
8993
}
9094
}
95+
96+
@Test
97+
@SuppressWarnings("EmptyCatchBlock")
98+
public void testSynchronizationFailureMarksTransactionForRollbackOnly() {
99+
final TransactionCoordinatorOwnerTestingImpl owner = new TransactionCoordinatorOwnerTestingImpl();
100+
final JdbcResourceLocalTransactionCoordinatorBuilderImpl transactionCoordinatorBuilder =
101+
new JdbcResourceLocalTransactionCoordinatorBuilderImpl();
102+
103+
final TransactionCoordinator transactionCoordinator = transactionCoordinatorBuilder.buildTransactionCoordinator(
104+
owner,
105+
new TransactionCoordinatorBuilder.TransactionCoordinatorOptions() {
106+
@Override
107+
public boolean shouldAutoJoinTransaction() {
108+
return false;
109+
}
110+
}
111+
);
112+
113+
assertEquals( TransactionStatus.NOT_ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() );
114+
transactionCoordinator.getLocalSynchronizations().registerSynchronization( SynchronizationErrorImpl.forBefore() );
115+
116+
transactionCoordinator.getTransactionDriverControl().begin();
117+
assertEquals( TransactionStatus.ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() );
118+
119+
try {
120+
transactionCoordinator.getTransactionDriverControl().commit();
121+
}
122+
catch (Exception expected) {
123+
}
124+
finally {
125+
assertEquals( TransactionStatus.MARKED_ROLLBACK, transactionCoordinator.getTransactionDriverControl().getStatus() );
126+
transactionCoordinator.getTransactionDriverControl().rollback();
127+
}
128+
}
91129
}

hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jta/AbstractBasicJtaTestScenarios.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@
77
package org.hibernate.test.resource.transaction.jta;
88

99
import javax.transaction.Status;
10+
import javax.transaction.Synchronization;
1011
import javax.transaction.SystemException;
1112
import javax.transaction.TransactionManager;
1213

14+
import org.hibernate.HibernateException;
15+
import org.hibernate.TransactionException;
16+
import org.hibernate.resource.transaction.TransactionCoordinator;
17+
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
1318
import org.hibernate.resource.transaction.TransactionRequiredForJoinException;
19+
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
1420
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
1521
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
1622
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorTrackingImpl;
23+
import org.hibernate.resource.transaction.spi.TransactionStatus;
1724

1825
import org.hibernate.test.resource.common.SynchronizationCollectorImpl;
26+
import org.hibernate.test.resource.common.SynchronizationErrorImpl;
1927
import org.junit.After;
2028
import org.junit.Before;
2129
import org.junit.Test;
@@ -359,4 +367,75 @@ public void basicThreadCheckingUsage() throws Exception {
359367

360368
tm.rollback();
361369
}
370+
371+
@Test
372+
@SuppressWarnings("EmptyCatchBlock")
373+
public void testMarkRollbackOnly() throws Exception {
374+
JtaTransactionCoordinatorImpl transactionCoordinator = new JtaTransactionCoordinatorImpl(
375+
transactionCoordinatorBuilder,
376+
owner,
377+
true,
378+
JtaPlatformStandardTestingImpl.INSTANCE,
379+
preferUserTransactions(),
380+
true
381+
);
382+
383+
// pre conditions
384+
final TransactionManager tm = JtaPlatformStandardTestingImpl.INSTANCE.transactionManager();
385+
assertEquals( Status.STATUS_NO_TRANSACTION, tm.getStatus() );
386+
387+
assertEquals( TransactionStatus.NOT_ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() );
388+
389+
transactionCoordinator.getTransactionDriverControl().begin();
390+
assertEquals( TransactionStatus.ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() );
391+
392+
transactionCoordinator.getTransactionDriverControl().markRollbackOnly();
393+
assertEquals(
394+
TransactionStatus.MARKED_ROLLBACK,
395+
transactionCoordinator.getTransactionDriverControl().getStatus()
396+
);
397+
398+
try {
399+
transactionCoordinator.getTransactionDriverControl().commit();
400+
}
401+
catch (TransactionException expected) {
402+
}
403+
finally {
404+
assertEquals( TransactionStatus.MARKED_ROLLBACK, transactionCoordinator.getTransactionDriverControl().getStatus() );
405+
transactionCoordinator.getTransactionDriverControl().rollback();
406+
}
407+
}
408+
409+
@Test
410+
@SuppressWarnings("EmptyCatchBlock")
411+
public void testSynchronizationFailureMarksTransactionForRollbackOnly() throws Exception {
412+
JtaTransactionCoordinatorImpl transactionCoordinator = new JtaTransactionCoordinatorImpl(
413+
transactionCoordinatorBuilder,
414+
owner,
415+
true,
416+
JtaPlatformStandardTestingImpl.INSTANCE,
417+
preferUserTransactions(),
418+
true
419+
);
420+
421+
// pre conditions
422+
final TransactionManager tm = JtaPlatformStandardTestingImpl.INSTANCE.transactionManager();
423+
assertEquals( Status.STATUS_NO_TRANSACTION, tm.getStatus() );
424+
425+
assertEquals( TransactionStatus.NOT_ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() );
426+
transactionCoordinator.getLocalSynchronizations().registerSynchronization( SynchronizationErrorImpl.forBefore() );
427+
428+
transactionCoordinator.getTransactionDriverControl().begin();
429+
assertEquals( TransactionStatus.ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() );
430+
431+
try {
432+
transactionCoordinator.getTransactionDriverControl().commit();
433+
}
434+
catch (Exception expected) {
435+
}
436+
finally {
437+
assertEquals( TransactionStatus.MARKED_ROLLBACK, transactionCoordinator.getTransactionDriverControl().getStatus() );
438+
transactionCoordinator.getTransactionDriverControl().rollback();
439+
}
440+
}
362441
}

0 commit comments

Comments
 (0)