Skip to content

Commit a8b912c

Browse files
committed
DATACMNS-310 - Integrated ChainedTransactionManager from Spring Data Neo4j.
We now integrate a best-effort PlatformTransactionManager that will propagate commits and rollbacks to all PlatformTransactionManager instances registered. This assumes that the exceptional cases which will cause a rollback to happen either before the commits or during the first commit.
1 parent a93c2bf commit a8b912c

File tree

6 files changed

+830
-0
lines changed

6 files changed

+830
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright 2011-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.transaction;
17+
18+
import static java.util.Arrays.*;
19+
20+
import java.util.ArrayList;
21+
import java.util.Collection;
22+
import java.util.Collections;
23+
import java.util.List;
24+
import java.util.Map;
25+
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
import org.springframework.transaction.CannotCreateTransactionException;
29+
import org.springframework.transaction.HeuristicCompletionException;
30+
import org.springframework.transaction.PlatformTransactionManager;
31+
import org.springframework.transaction.TransactionDefinition;
32+
import org.springframework.transaction.TransactionException;
33+
import org.springframework.transaction.TransactionStatus;
34+
import org.springframework.transaction.UnexpectedRollbackException;
35+
import org.springframework.util.Assert;
36+
37+
/**
38+
* {@link PlatformTransactionManager} implementation that orchestrates transaction creation, commits and rollbacks to a
39+
* list of delegates. Using this implementation assumes that errors causing a transaction rollback will usually happen
40+
* before the transaction completion or during the commit of the most inner {@link PlatformTransactionManager}.
41+
* <p />
42+
* The configured instances will start transactions in the order given and commit/rollback in <em>reverse</em> order,
43+
* which means the {@link PlatformTransactionManager} most likely to break the transaction should be the <em>last</em>
44+
* in the list configured. A {@link PlatformTransactionManager} throwing an exception during commit will automatically
45+
* cause the remaining transaction managers to roll back instead of committing.
46+
*
47+
* @author Michael Hunger
48+
* @author Oliver Gierke
49+
* @since 1.6
50+
*/
51+
public class ChainedTransactionManager implements PlatformTransactionManager {
52+
53+
private final static Logger LOGGER = LoggerFactory.getLogger(ChainedTransactionManager.class);
54+
55+
private final List<PlatformTransactionManager> transactionManagers;
56+
private final SynchronizationManager synchronizationManager;
57+
58+
/**
59+
* Creates a new {@link ChainedTransactionManager} delegating to the given {@link PlatformTransactionManager}s.
60+
*
61+
* @param transactionManagers must not be {@literal null} or empty.
62+
*/
63+
public ChainedTransactionManager(PlatformTransactionManager... transactionManagers) {
64+
this(SpringTransactionSynchronizationManager.INSTANCE, transactionManagers);
65+
}
66+
67+
/**
68+
* Creates a new {@link ChainedTransactionManager} using the given {@link SynchronizationManager} and
69+
* {@link PlatformTransactionManager}s.
70+
*
71+
* @param synchronizationManager must not be {@literal null}.
72+
* @param transactionManagers must not be {@literal null} or empty.
73+
*/
74+
ChainedTransactionManager(SynchronizationManager synchronizationManager,
75+
PlatformTransactionManager... transactionManagers) {
76+
77+
Assert.notNull(synchronizationManager, "SynchronizationManager must not be null!");
78+
Assert.notNull(transactionManagers, "Transaction managers must not be null!");
79+
Assert.isTrue(transactionManagers.length > 0, "At least one PlatformTransactionManager must be given!");
80+
81+
this.synchronizationManager = synchronizationManager;
82+
this.transactionManagers = asList(transactionManagers);
83+
}
84+
85+
/*
86+
* (non-Javadoc)
87+
* @see org.springframework.transaction.PlatformTransactionManager#getTransaction(org.springframework.transaction.TransactionDefinition)
88+
*/
89+
public MultiTransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
90+
91+
MultiTransactionStatus mts = new MultiTransactionStatus(transactionManagers.get(0));
92+
93+
if (!synchronizationManager.isSynchronizationActive()) {
94+
synchronizationManager.initSynchronization();
95+
mts.setNewSynchonization();
96+
}
97+
98+
try {
99+
100+
for (PlatformTransactionManager transactionManager : transactionManagers) {
101+
mts.registerTransactionManager(definition, transactionManager);
102+
}
103+
104+
} catch (Exception ex) {
105+
106+
Map<PlatformTransactionManager, TransactionStatus> transactionStatuses = mts.getTransactionStatuses();
107+
108+
for (PlatformTransactionManager transactionManager : transactionManagers) {
109+
try {
110+
if (transactionStatuses.get(transactionManager) != null) {
111+
transactionManager.rollback(transactionStatuses.get(transactionManager));
112+
}
113+
} catch (Exception ex2) {
114+
LOGGER.warn("Rollback exception (" + transactionManager + ") " + ex2.getMessage(), ex2);
115+
}
116+
}
117+
118+
if (mts.isNewSynchonization()) {
119+
synchronizationManager.clearSynchronization();
120+
}
121+
122+
throw new CannotCreateTransactionException(ex.getMessage(), ex);
123+
}
124+
125+
return mts;
126+
}
127+
128+
/*
129+
* (non-Javadoc)
130+
* @see org.springframework.transaction.PlatformTransactionManager#commit(org.springframework.transaction.TransactionStatus)
131+
*/
132+
public void commit(TransactionStatus status) throws TransactionException {
133+
134+
MultiTransactionStatus multiTransactionStatus = (MultiTransactionStatus) status;
135+
136+
boolean commit = true;
137+
Exception commitException = null;
138+
PlatformTransactionManager commitExceptionTransactionManager = null;
139+
140+
for (PlatformTransactionManager transactionManager : reverse(transactionManagers)) {
141+
142+
if (commit) {
143+
144+
try {
145+
multiTransactionStatus.commit(transactionManager);
146+
} catch (Exception ex) {
147+
commit = false;
148+
commitException = ex;
149+
commitExceptionTransactionManager = transactionManager;
150+
}
151+
152+
} else {
153+
154+
// after unsucessfull commit we must try to rollback remaining transaction managers
155+
156+
try {
157+
multiTransactionStatus.rollback(transactionManager);
158+
} catch (Exception ex) {
159+
LOGGER.warn("Rollback exception (after commit) (" + transactionManager + ") " + ex.getMessage(), ex);
160+
}
161+
}
162+
}
163+
164+
if (multiTransactionStatus.isNewSynchonization()) {
165+
synchronizationManager.clearSynchronization();
166+
}
167+
168+
if (commitException != null) {
169+
boolean firstTransactionManagerFailed = commitExceptionTransactionManager == getLastTransactionManager();
170+
int transactionState = firstTransactionManagerFailed ? HeuristicCompletionException.STATE_ROLLED_BACK
171+
: HeuristicCompletionException.STATE_MIXED;
172+
throw new HeuristicCompletionException(transactionState, commitException);
173+
}
174+
}
175+
176+
/*
177+
* (non-Javadoc)
178+
* @see org.springframework.transaction.PlatformTransactionManager#rollback(org.springframework.transaction.TransactionStatus)
179+
*/
180+
public void rollback(TransactionStatus status) throws TransactionException {
181+
182+
Exception rollbackException = null;
183+
PlatformTransactionManager rollbackExceptionTransactionManager = null;
184+
185+
MultiTransactionStatus multiTransactionStatus = (MultiTransactionStatus) status;
186+
187+
for (PlatformTransactionManager transactionManager : reverse(transactionManagers)) {
188+
try {
189+
multiTransactionStatus.rollback(transactionManager);
190+
} catch (Exception ex) {
191+
if (rollbackException == null) {
192+
rollbackException = ex;
193+
rollbackExceptionTransactionManager = transactionManager;
194+
} else {
195+
LOGGER.warn("Rollback exception (" + transactionManager + ") " + ex.getMessage(), ex);
196+
}
197+
}
198+
}
199+
200+
if (multiTransactionStatus.isNewSynchonization()) {
201+
synchronizationManager.clearSynchronization();
202+
}
203+
204+
if (rollbackException != null) {
205+
throw new UnexpectedRollbackException("Rollback exception, originated at (" + rollbackExceptionTransactionManager
206+
+ ") " + rollbackException.getMessage(), rollbackException);
207+
}
208+
}
209+
210+
private <T> Iterable<T> reverse(Collection<T> collection) {
211+
212+
List<T> list = new ArrayList<T>(collection);
213+
Collections.reverse(list);
214+
return list;
215+
}
216+
217+
private PlatformTransactionManager getLastTransactionManager() {
218+
return transactionManagers.get(lastTransactionManagerIndex());
219+
}
220+
221+
private int lastTransactionManagerIndex() {
222+
return transactionManagers.size() - 1;
223+
}
224+
}

0 commit comments

Comments
 (0)