1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.transaction.support;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.Serializable;
22 import java.util.List;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import org.springframework.core.Constants;
28 import org.springframework.transaction.IllegalTransactionStateException;
29 import org.springframework.transaction.InvalidTimeoutException;
30 import org.springframework.transaction.NestedTransactionNotSupportedException;
31 import org.springframework.transaction.PlatformTransactionManager;
32 import org.springframework.transaction.TransactionDefinition;
33 import org.springframework.transaction.TransactionException;
34 import org.springframework.transaction.TransactionStatus;
35 import org.springframework.transaction.TransactionSuspensionNotSupportedException;
36 import org.springframework.transaction.UnexpectedRollbackException;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 @SuppressWarnings("serial")
82 public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
83
84
85
86
87
88
89
90
91 public static final int SYNCHRONIZATION_ALWAYS = 0;
92
93
94
95
96
97
98
99
100
101 public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;
102
103
104
105
106 public static final int SYNCHRONIZATION_NEVER = 2;
107
108
109
110 private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class);
111
112
113 protected transient Log logger = LogFactory.getLog(getClass());
114
115 private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;
116
117 private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
118
119 private boolean nestedTransactionAllowed = false;
120
121 private boolean validateExistingTransaction = false;
122
123 private boolean globalRollbackOnParticipationFailure = true;
124
125 private boolean failEarlyOnGlobalRollbackOnly = false;
126
127 private boolean rollbackOnCommitFailure = false;
128
129
130
131
132
133
134
135
136 public final void setTransactionSynchronizationName(String constantName) {
137 setTransactionSynchronization(constants.asNumber(constantName).intValue());
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152 public final void setTransactionSynchronization(int transactionSynchronization) {
153 this.transactionSynchronization = transactionSynchronization;
154 }
155
156
157
158
159
160 public final int getTransactionSynchronization() {
161 return this.transactionSynchronization;
162 }
163
164
165
166
167
168
169
170
171
172 public final void setDefaultTimeout(int defaultTimeout) {
173 if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) {
174 throw new InvalidTimeoutException("Invalid default timeout", defaultTimeout);
175 }
176 this.defaultTimeout = defaultTimeout;
177 }
178
179
180
181
182
183
184
185 public final int getDefaultTimeout() {
186 return this.defaultTimeout;
187 }
188
189
190
191
192
193
194 public final void setNestedTransactionAllowed(boolean nestedTransactionAllowed) {
195 this.nestedTransactionAllowed = nestedTransactionAllowed;
196 }
197
198
199
200
201 public final boolean isNestedTransactionAllowed() {
202 return this.nestedTransactionAllowed;
203 }
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 public final void setValidateExistingTransaction(boolean validateExistingTransaction) {
219 this.validateExistingTransaction = validateExistingTransaction;
220 }
221
222
223
224
225
226 public final boolean isValidateExistingTransaction() {
227 return this.validateExistingTransaction;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 public final void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) {
263 this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure;
264 }
265
266
267
268
269
270 public final boolean isGlobalRollbackOnParticipationFailure() {
271 return this.globalRollbackOnParticipationFailure;
272 }
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 public final void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) {
290 this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly;
291 }
292
293
294
295
296
297 public final boolean isFailEarlyOnGlobalRollbackOnly() {
298 return this.failEarlyOnGlobalRollbackOnly;
299 }
300
301
302
303
304
305
306
307
308
309
310 public final void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) {
311 this.rollbackOnCommitFailure = rollbackOnCommitFailure;
312 }
313
314
315
316
317
318 public final boolean isRollbackOnCommitFailure() {
319 return this.rollbackOnCommitFailure;
320 }
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 @Override
336 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
337 Object transaction = doGetTransaction();
338
339
340 boolean debugEnabled = logger.isDebugEnabled();
341
342 if (definition == null) {
343
344 definition = new DefaultTransactionDefinition();
345 }
346
347 if (isExistingTransaction(transaction)) {
348
349 return handleExistingTransaction(definition, transaction, debugEnabled);
350 }
351
352
353 if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
354 throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
355 }
356
357
358 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
359 throw new IllegalTransactionStateException(
360 "No existing transaction found for transaction marked with propagation 'mandatory'");
361 }
362 else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
363 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
364 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
365 SuspendedResourcesHolder suspendedResources = suspend(null);
366 if (debugEnabled) {
367 logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
368 }
369 try {
370 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
371 DefaultTransactionStatus status = newTransactionStatus(
372 definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
373 doBegin(transaction, definition);
374 prepareSynchronization(status, definition);
375 return status;
376 }
377 catch (RuntimeException ex) {
378 resume(null, suspendedResources);
379 throw ex;
380 }
381 catch (Error err) {
382 resume(null, suspendedResources);
383 throw err;
384 }
385 }
386 else {
387
388 boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
389 return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
390 }
391 }
392
393
394
395
396 private TransactionStatus handleExistingTransaction(
397 TransactionDefinition definition, Object transaction, boolean debugEnabled)
398 throws TransactionException {
399
400 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
401 throw new IllegalTransactionStateException(
402 "Existing transaction found for transaction marked with propagation 'never'");
403 }
404
405 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
406 if (debugEnabled) {
407 logger.debug("Suspending current transaction");
408 }
409 Object suspendedResources = suspend(transaction);
410 boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
411 return prepareTransactionStatus(
412 definition, null, false, newSynchronization, debugEnabled, suspendedResources);
413 }
414
415 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
416 if (debugEnabled) {
417 logger.debug("Suspending current transaction, creating new transaction with name [" +
418 definition.getName() + "]");
419 }
420 SuspendedResourcesHolder suspendedResources = suspend(transaction);
421 try {
422 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
423 DefaultTransactionStatus status = newTransactionStatus(
424 definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
425 doBegin(transaction, definition);
426 prepareSynchronization(status, definition);
427 return status;
428 }
429 catch (RuntimeException beginEx) {
430 resumeAfterBeginException(transaction, suspendedResources, beginEx);
431 throw beginEx;
432 }
433 catch (Error beginErr) {
434 resumeAfterBeginException(transaction, suspendedResources, beginErr);
435 throw beginErr;
436 }
437 }
438
439 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
440 if (!isNestedTransactionAllowed()) {
441 throw new NestedTransactionNotSupportedException(
442 "Transaction manager does not allow nested transactions by default - " +
443 "specify 'nestedTransactionAllowed' property with value 'true'");
444 }
445 if (debugEnabled) {
446 logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
447 }
448 if (useSavepointForNestedTransaction()) {
449
450
451
452 DefaultTransactionStatus status =
453 prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
454 status.createAndHoldSavepoint();
455 return status;
456 }
457 else {
458
459
460
461 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
462 DefaultTransactionStatus status = newTransactionStatus(
463 definition, transaction, true, newSynchronization, debugEnabled, null);
464 doBegin(transaction, definition);
465 prepareSynchronization(status, definition);
466 return status;
467 }
468 }
469
470
471 if (debugEnabled) {
472 logger.debug("Participating in existing transaction");
473 }
474 if (isValidateExistingTransaction()) {
475 if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
476 Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
477 if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
478 Constants isoConstants = DefaultTransactionDefinition.constants;
479 throw new IllegalTransactionStateException("Participating transaction with definition [" +
480 definition + "] specifies isolation level which is incompatible with existing transaction: " +
481 (currentIsolationLevel != null ?
482 isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
483 "(unknown)"));
484 }
485 }
486 if (!definition.isReadOnly()) {
487 if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
488 throw new IllegalTransactionStateException("Participating transaction with definition [" +
489 definition + "] is not marked as read-only but existing transaction is");
490 }
491 }
492 }
493 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
494 return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
495 }
496
497
498
499
500
501
502
503 protected final DefaultTransactionStatus prepareTransactionStatus(
504 TransactionDefinition definition, Object transaction, boolean newTransaction,
505 boolean newSynchronization, boolean debug, Object suspendedResources) {
506
507 DefaultTransactionStatus status = newTransactionStatus(
508 definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
509 prepareSynchronization(status, definition);
510 return status;
511 }
512
513
514
515
516 protected DefaultTransactionStatus newTransactionStatus(
517 TransactionDefinition definition, Object transaction, boolean newTransaction,
518 boolean newSynchronization, boolean debug, Object suspendedResources) {
519
520 boolean actualNewSynchronization = newSynchronization &&
521 !TransactionSynchronizationManager.isSynchronizationActive();
522 return new DefaultTransactionStatus(
523 transaction, newTransaction, actualNewSynchronization,
524 definition.isReadOnly(), debug, suspendedResources);
525 }
526
527
528
529
530 protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
531 if (status.isNewSynchronization()) {
532 TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
533 TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
534 definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
535 definition.getIsolationLevel() : null);
536 TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
537 TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
538 TransactionSynchronizationManager.initSynchronization();
539 }
540 }
541
542
543
544
545
546
547
548
549
550
551 protected int determineTimeout(TransactionDefinition definition) {
552 if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
553 return definition.getTimeout();
554 }
555 return this.defaultTimeout;
556 }
557
558
559
560
561
562
563
564
565
566
567
568
569 protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
570 if (TransactionSynchronizationManager.isSynchronizationActive()) {
571 List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
572 try {
573 Object suspendedResources = null;
574 if (transaction != null) {
575 suspendedResources = doSuspend(transaction);
576 }
577 String name = TransactionSynchronizationManager.getCurrentTransactionName();
578 TransactionSynchronizationManager.setCurrentTransactionName(null);
579 boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
580 TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
581 Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
582 TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
583 boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
584 TransactionSynchronizationManager.setActualTransactionActive(false);
585 return new SuspendedResourcesHolder(
586 suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
587 }
588 catch (RuntimeException ex) {
589
590 doResumeSynchronization(suspendedSynchronizations);
591 throw ex;
592 }
593 catch (Error err) {
594
595 doResumeSynchronization(suspendedSynchronizations);
596 throw err;
597 }
598 }
599 else if (transaction != null) {
600
601 Object suspendedResources = doSuspend(transaction);
602 return new SuspendedResourcesHolder(suspendedResources);
603 }
604 else {
605
606 return null;
607 }
608 }
609
610
611
612
613
614
615
616
617
618
619
620 protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
621 throws TransactionException {
622
623 if (resourcesHolder != null) {
624 Object suspendedResources = resourcesHolder.suspendedResources;
625 if (suspendedResources != null) {
626 doResume(transaction, suspendedResources);
627 }
628 List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
629 if (suspendedSynchronizations != null) {
630 TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
631 TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
632 TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
633 TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
634 doResumeSynchronization(suspendedSynchronizations);
635 }
636 }
637 }
638
639
640
641
642 private void resumeAfterBeginException(
643 Object transaction, SuspendedResourcesHolder suspendedResources, Throwable beginEx) {
644
645 String exMessage = "Inner transaction begin exception overridden by outer transaction resume exception";
646 try {
647 resume(transaction, suspendedResources);
648 }
649 catch (RuntimeException resumeEx) {
650 logger.error(exMessage, beginEx);
651 throw resumeEx;
652 }
653 catch (Error resumeErr) {
654 logger.error(exMessage, beginEx);
655 throw resumeErr;
656 }
657 }
658
659
660
661
662
663
664 private List<TransactionSynchronization> doSuspendSynchronization() {
665 List<TransactionSynchronization> suspendedSynchronizations =
666 TransactionSynchronizationManager.getSynchronizations();
667 for (TransactionSynchronization synchronization : suspendedSynchronizations) {
668 synchronization.suspend();
669 }
670 TransactionSynchronizationManager.clearSynchronization();
671 return suspendedSynchronizations;
672 }
673
674
675
676
677
678
679 private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) {
680 TransactionSynchronizationManager.initSynchronization();
681 for (TransactionSynchronization synchronization : suspendedSynchronizations) {
682 synchronization.resume();
683 TransactionSynchronizationManager.registerSynchronization(synchronization);
684 }
685 }
686
687
688
689
690
691
692
693
694
695
696
697 @Override
698 public final void commit(TransactionStatus status) throws TransactionException {
699 if (status.isCompleted()) {
700 throw new IllegalTransactionStateException(
701 "Transaction is already completed - do not call commit or rollback more than once per transaction");
702 }
703
704 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
705 if (defStatus.isLocalRollbackOnly()) {
706 if (defStatus.isDebug()) {
707 logger.debug("Transactional code has requested rollback");
708 }
709 processRollback(defStatus);
710 return;
711 }
712 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
713 if (defStatus.isDebug()) {
714 logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
715 }
716 processRollback(defStatus);
717
718
719 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
720 throw new UnexpectedRollbackException(
721 "Transaction rolled back because it has been marked as rollback-only");
722 }
723 return;
724 }
725
726 processCommit(defStatus);
727 }
728
729
730
731
732
733
734
735 private void processCommit(DefaultTransactionStatus status) throws TransactionException {
736 try {
737 boolean beforeCompletionInvoked = false;
738 try {
739 prepareForCommit(status);
740 triggerBeforeCommit(status);
741 triggerBeforeCompletion(status);
742 beforeCompletionInvoked = true;
743 boolean globalRollbackOnly = false;
744 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
745 globalRollbackOnly = status.isGlobalRollbackOnly();
746 }
747 if (status.hasSavepoint()) {
748 if (status.isDebug()) {
749 logger.debug("Releasing transaction savepoint");
750 }
751 status.releaseHeldSavepoint();
752 }
753 else if (status.isNewTransaction()) {
754 if (status.isDebug()) {
755 logger.debug("Initiating transaction commit");
756 }
757 doCommit(status);
758 }
759
760
761 if (globalRollbackOnly) {
762 throw new UnexpectedRollbackException(
763 "Transaction silently rolled back because it has been marked as rollback-only");
764 }
765 }
766 catch (UnexpectedRollbackException ex) {
767
768 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
769 throw ex;
770 }
771 catch (TransactionException ex) {
772
773 if (isRollbackOnCommitFailure()) {
774 doRollbackOnCommitException(status, ex);
775 }
776 else {
777 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
778 }
779 throw ex;
780 }
781 catch (RuntimeException ex) {
782 if (!beforeCompletionInvoked) {
783 triggerBeforeCompletion(status);
784 }
785 doRollbackOnCommitException(status, ex);
786 throw ex;
787 }
788 catch (Error err) {
789 if (!beforeCompletionInvoked) {
790 triggerBeforeCompletion(status);
791 }
792 doRollbackOnCommitException(status, err);
793 throw err;
794 }
795
796
797
798 try {
799 triggerAfterCommit(status);
800 }
801 finally {
802 triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
803 }
804
805 }
806 finally {
807 cleanupAfterCompletion(status);
808 }
809 }
810
811
812
813
814
815
816
817
818 @Override
819 public final void rollback(TransactionStatus status) throws TransactionException {
820 if (status.isCompleted()) {
821 throw new IllegalTransactionStateException(
822 "Transaction is already completed - do not call commit or rollback more than once per transaction");
823 }
824
825 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
826 processRollback(defStatus);
827 }
828
829
830
831
832
833
834
835 private void processRollback(DefaultTransactionStatus status) {
836 try {
837 try {
838 triggerBeforeCompletion(status);
839 if (status.hasSavepoint()) {
840 if (status.isDebug()) {
841 logger.debug("Rolling back transaction to savepoint");
842 }
843 status.rollbackToHeldSavepoint();
844 }
845 else if (status.isNewTransaction()) {
846 if (status.isDebug()) {
847 logger.debug("Initiating transaction rollback");
848 }
849 doRollback(status);
850 }
851 else if (status.hasTransaction()) {
852 if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
853 if (status.isDebug()) {
854 logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
855 }
856 doSetRollbackOnly(status);
857 }
858 else {
859 if (status.isDebug()) {
860 logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
861 }
862 }
863 }
864 else {
865 logger.debug("Should roll back transaction but cannot - no transaction available");
866 }
867 }
868 catch (RuntimeException ex) {
869 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
870 throw ex;
871 }
872 catch (Error err) {
873 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
874 throw err;
875 }
876 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
877 }
878 finally {
879 cleanupAfterCompletion(status);
880 }
881 }
882
883
884
885
886
887
888
889
890 private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex) throws TransactionException {
891 try {
892 if (status.isNewTransaction()) {
893 if (status.isDebug()) {
894 logger.debug("Initiating transaction rollback after commit exception", ex);
895 }
896 doRollback(status);
897 }
898 else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) {
899 if (status.isDebug()) {
900 logger.debug("Marking existing transaction as rollback-only after commit exception", ex);
901 }
902 doSetRollbackOnly(status);
903 }
904 }
905 catch (RuntimeException rbex) {
906 logger.error("Commit exception overridden by rollback exception", ex);
907 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
908 throw rbex;
909 }
910 catch (Error rberr) {
911 logger.error("Commit exception overridden by rollback exception", ex);
912 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
913 throw rberr;
914 }
915 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
916 }
917
918
919
920
921
922
923 protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
924 if (status.isNewSynchronization()) {
925 if (status.isDebug()) {
926 logger.trace("Triggering beforeCommit synchronization");
927 }
928 TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
929 }
930 }
931
932
933
934
935
936 protected final void triggerBeforeCompletion(DefaultTransactionStatus status) {
937 if (status.isNewSynchronization()) {
938 if (status.isDebug()) {
939 logger.trace("Triggering beforeCompletion synchronization");
940 }
941 TransactionSynchronizationUtils.triggerBeforeCompletion();
942 }
943 }
944
945
946
947
948
949 private void triggerAfterCommit(DefaultTransactionStatus status) {
950 if (status.isNewSynchronization()) {
951 if (status.isDebug()) {
952 logger.trace("Triggering afterCommit synchronization");
953 }
954 TransactionSynchronizationUtils.triggerAfterCommit();
955 }
956 }
957
958
959
960
961
962
963 private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
964 if (status.isNewSynchronization()) {
965 List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
966 if (!status.hasTransaction() || status.isNewTransaction()) {
967 if (status.isDebug()) {
968 logger.trace("Triggering afterCompletion synchronization");
969 }
970
971
972 invokeAfterCompletion(synchronizations, completionStatus);
973 }
974 else if (!synchronizations.isEmpty()) {
975
976
977
978 registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
979 }
980 }
981 }
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996 protected final void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) {
997 TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus);
998 }
999
1000
1001
1002
1003
1004
1005
1006 private void cleanupAfterCompletion(DefaultTransactionStatus status) {
1007 status.setCompleted();
1008 if (status.isNewSynchronization()) {
1009 TransactionSynchronizationManager.clear();
1010 }
1011 if (status.isNewTransaction()) {
1012 doCleanupAfterCompletion(status.getTransaction());
1013 }
1014 if (status.getSuspendedResources() != null) {
1015 if (status.isDebug()) {
1016 logger.debug("Resuming suspended transaction after completion of inner transaction");
1017 }
1018 resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
1019 }
1020 }
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049 protected abstract Object doGetTransaction() throws TransactionException;
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066 protected boolean isExistingTransaction(Object transaction) throws TransactionException {
1067 return false;
1068 }
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 protected boolean useSavepointForNestedTransaction() {
1087 return true;
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107 protected abstract void doBegin(Object transaction, TransactionDefinition definition)
1108 throws TransactionException;
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123 protected Object doSuspend(Object transaction) throws TransactionException {
1124 throw new TransactionSuspensionNotSupportedException(
1125 "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
1126 }
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141 protected void doResume(Object transaction, Object suspendedResources) throws TransactionException {
1142 throw new TransactionSuspensionNotSupportedException(
1143 "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
1144 }
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 protected boolean shouldCommitOnGlobalRollbackOnly() {
1176 return false;
1177 }
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188 protected void prepareForCommit(DefaultTransactionStatus status) {
1189 }
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212 protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223 protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
1224 throw new IllegalTransactionStateException(
1225 "Participating in existing transactions is not supported - when 'isExistingTransaction' " +
1226 "returns true, appropriate 'doSetRollbackOnly' behavior must be provided");
1227 }
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244 protected void registerAfterCompletionWithExistingTransaction(
1245 Object transaction, List<TransactionSynchronization> synchronizations) throws TransactionException {
1246
1247 logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " +
1248 "processing Spring after-completion callbacks immediately, with outcome status 'unknown'");
1249 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
1250 }
1251
1252
1253
1254
1255
1256
1257
1258
1259 protected void doCleanupAfterCompletion(Object transaction) {
1260 }
1261
1262
1263
1264
1265
1266
1267 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1268
1269 ois.defaultReadObject();
1270
1271
1272 this.logger = LogFactory.getLog(getClass());
1273 }
1274
1275
1276
1277
1278
1279
1280 protected static class SuspendedResourcesHolder {
1281
1282 private final Object suspendedResources;
1283
1284 private List<TransactionSynchronization> suspendedSynchronizations;
1285
1286 private String name;
1287
1288 private boolean readOnly;
1289
1290 private Integer isolationLevel;
1291
1292 private boolean wasActive;
1293
1294 private SuspendedResourcesHolder(Object suspendedResources) {
1295 this.suspendedResources = suspendedResources;
1296 }
1297
1298 private SuspendedResourcesHolder(
1299 Object suspendedResources, List<TransactionSynchronization> suspendedSynchronizations,
1300 String name, boolean readOnly, Integer isolationLevel, boolean wasActive) {
1301 this.suspendedResources = suspendedResources;
1302 this.suspendedSynchronizations = suspendedSynchronizations;
1303 this.name = name;
1304 this.readOnly = readOnly;
1305 this.isolationLevel = isolationLevel;
1306 this.wasActive = wasActive;
1307 }
1308 }
1309
1310 }