1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.dbcp2.managed;
18
19 import java.lang.ref.WeakReference;
20 import java.sql.Connection;
21 import java.sql.SQLException;
22 import java.util.Objects;
23
24 import javax.transaction.RollbackException;
25 import javax.transaction.Status;
26 import javax.transaction.Synchronization;
27 import javax.transaction.SystemException;
28 import javax.transaction.Transaction;
29 import javax.transaction.TransactionSynchronizationRegistry;
30 import javax.transaction.xa.XAResource;
31
32
33
34
35
36
37
38
39 public class TransactionContext {
40 private final TransactionRegistry transactionRegistry;
41 private final WeakReference<Transaction> transactionRef;
42 private final TransactionSynchronizationRegistry transactionSynchronizationRegistry;
43 private Connection sharedConnection;
44 private boolean transactionComplete;
45
46
47
48
49
50
51
52
53 public TransactionContext(final TransactionRegistry transactionRegistry, final Transaction transaction) {
54 this (transactionRegistry, transaction, null);
55 }
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public TransactionContext(final TransactionRegistry transactionRegistry, final Transaction transaction,
70 final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
71 Objects.requireNonNull(transactionRegistry, "transactionRegistry");
72 Objects.requireNonNull(transaction, "transaction");
73 this.transactionRegistry = transactionRegistry;
74 this.transactionRef = new WeakReference<>(transaction);
75 this.transactionComplete = false;
76 this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
77 }
78
79
80
81
82
83
84
85
86
87 public void addTransactionContextListener(final TransactionContextListener listener) throws SQLException {
88 try {
89 if (!isActive()) {
90 final Transaction transaction = this.transactionRef.get();
91 listener.afterCompletion(this, transaction != null && transaction.getStatus() == Status.STATUS_COMMITTED);
92 return;
93 }
94 final Synchronization s = new SynchronizationAdapter() {
95 @Override
96 public void afterCompletion(final int status) {
97 listener.afterCompletion(TransactionContext.this, status == Status.STATUS_COMMITTED);
98 }
99 };
100 if (transactionSynchronizationRegistry != null) {
101 transactionSynchronizationRegistry.registerInterposedSynchronization(s);
102 } else {
103 getTransaction().registerSynchronization(s);
104 }
105 } catch (final RollbackException ignored) {
106
107
108 } catch (final Exception e) {
109 throw new SQLException("Unable to register transaction context listener", e);
110 }
111 }
112
113
114
115
116
117
118 public void completeTransaction() {
119 this.transactionComplete = true;
120 }
121
122
123
124
125
126
127
128 public Connection getSharedConnection() {
129 return sharedConnection;
130 }
131
132 private Transaction getTransaction() throws SQLException {
133 final Transaction transaction = this.transactionRef.get();
134 if (transaction == null) {
135 throw new SQLException("Unable to enlist connection because the transaction has been garbage collected");
136 }
137 return transaction;
138 }
139
140
141
142
143
144
145
146
147 public boolean isActive() throws SQLException {
148 try {
149 final Transaction transaction = this.transactionRef.get();
150 if (transaction == null) {
151 return false;
152 }
153 final int status = transaction.getStatus();
154 return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK;
155 } catch (final SystemException e) {
156 throw new SQLException("Unable to get transaction status", e);
157 }
158 }
159
160
161
162
163
164
165
166
167 public boolean isTransactionComplete() {
168 return this.transactionComplete;
169 }
170
171
172
173
174
175
176
177
178
179
180 public void setSharedConnection(final Connection sharedConnection) throws SQLException {
181 if (this.sharedConnection != null) {
182 throw new IllegalStateException("A shared connection is already set");
183 }
184
185
186
187 final Transaction transaction = getTransaction();
188 try {
189 final XAResource xaResource = transactionRegistry.getXAResource(sharedConnection);
190 if (!transaction.enlistResource(xaResource)) {
191 throw new SQLException("Unable to enlist connection in transaction: enlistResource returns 'false'.");
192 }
193 } catch (final IllegalStateException e) {
194
195 throw new SQLException("Unable to enlist connection in the transaction", e);
196 } catch (final RollbackException ignored) {
197
198 } catch (final SystemException e) {
199 throw new SQLException("Unable to enlist connection the transaction", e);
200 }
201
202 this.sharedConnection = sharedConnection;
203 }
204 }