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.sql.Connection;
20 import java.sql.SQLException;
21 import java.util.concurrent.locks.Lock;
22 import java.util.concurrent.locks.ReentrantLock;
23
24 import org.apache.commons.dbcp2.DelegatingConnection;
25 import org.apache.commons.pool2.ObjectPool;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class ManagedConnection<C extends Connection> extends DelegatingConnection<C> {
47
48
49
50
51
52
53 protected class CompletionListener implements TransactionContextListener {
54 @Override
55 public void afterCompletion(final TransactionContext completedContext, final boolean committed) {
56 if (completedContext == transactionContext) {
57 transactionComplete();
58 }
59 }
60 }
61
62 private final ObjectPool<C> pool;
63 private final TransactionRegistry transactionRegistry;
64 private final boolean accessToUnderlyingConnectionAllowed;
65 private TransactionContext transactionContext;
66 private boolean isSharedConnection;
67 private final Lock lock;
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public ManagedConnection(final ObjectPool<C> pool, final TransactionRegistry transactionRegistry,
82 final boolean accessToUnderlyingConnectionAllowed) throws SQLException {
83 super(null);
84 this.pool = pool;
85 this.transactionRegistry = transactionRegistry;
86 this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
87 this.lock = new ReentrantLock();
88 updateTransactionStatus();
89 }
90
91 @Override
92 protected void checkOpen() throws SQLException {
93 super.checkOpen();
94 updateTransactionStatus();
95 }
96
97 @Override
98 public void close() throws SQLException {
99 if (!isClosedInternal()) {
100
101
102
103
104
105
106
107 lock.lock();
108 try {
109 if (transactionContext == null || transactionContext.isTransactionComplete()) {
110 super.close();
111 }
112 } finally {
113 try {
114 setClosedInternal(true);
115 } finally {
116 lock.unlock();
117 }
118 }
119 }
120 }
121
122 @Override
123 public void commit() throws SQLException {
124 if (transactionContext != null) {
125 throw new SQLException("Commit cannot be set while enrolled in a transaction");
126 }
127 super.commit();
128 }
129
130 @Override
131 public C getDelegate() {
132 if (isAccessToUnderlyingConnectionAllowed()) {
133 return getDelegateInternal();
134 }
135 return null;
136 }
137
138
139
140
141
142 @Override
143 public Connection getInnermostDelegate() {
144 if (isAccessToUnderlyingConnectionAllowed()) {
145 return super.getInnermostDelegateInternal();
146 }
147 return null;
148 }
149
150
151
152
153
154
155
156 public TransactionContext getTransactionContext() {
157 return transactionContext;
158 }
159
160
161
162
163
164
165
166 public TransactionRegistry getTransactionRegistry() {
167 return transactionRegistry;
168 }
169
170
171
172
173
174
175 public boolean isAccessToUnderlyingConnectionAllowed() {
176 return accessToUnderlyingConnectionAllowed;
177 }
178
179 @Override
180 public void rollback() throws SQLException {
181 if (transactionContext != null) {
182 throw new SQLException("Commit cannot be set while enrolled in a transaction");
183 }
184 super.rollback();
185 }
186
187 @Override
188 public void setAutoCommit(final boolean autoCommit) throws SQLException {
189 if (transactionContext != null) {
190 throw new SQLException("Auto-commit cannot be set while enrolled in a transaction");
191 }
192 super.setAutoCommit(autoCommit);
193 }
194
195 @Override
196 public void setReadOnly(final boolean readOnly) throws SQLException {
197 if (transactionContext != null) {
198 throw new SQLException("Read-only cannot be set while enrolled in a transaction");
199 }
200 super.setReadOnly(readOnly);
201 }
202
203
204
205
206 protected void transactionComplete() {
207 lock.lock();
208 try {
209 transactionContext.completeTransaction();
210 } finally {
211 lock.unlock();
212 }
213
214
215
216 if (isSharedConnection) {
217 setDelegate(null);
218 isSharedConnection = false;
219 }
220
221
222 clearCachedState();
223
224
225
226 final Connection delegate = getDelegateInternal();
227 if (isClosedInternal() && delegate != null) {
228 try {
229 setDelegate(null);
230
231 if (!delegate.isClosed()) {
232 delegate.close();
233 }
234 } catch (final SQLException ignored) {
235
236
237
238 }
239 }
240 }
241
242 private void updateTransactionStatus() throws SQLException {
243
244 if (transactionContext != null && !transactionContext.isTransactionComplete()) {
245 if (transactionContext.isActive()) {
246 if (transactionContext != transactionRegistry.getActiveTransactionContext()) {
247 throw new SQLException("Connection cannot be used while enlisted in another transaction");
248 }
249 return;
250 }
251
252
253
254 transactionComplete();
255 }
256
257
258 transactionContext = transactionRegistry.getActiveTransactionContext();
259
260
261 if (transactionContext != null && transactionContext.getSharedConnection() != null) {
262
263
264
265
266 @SuppressWarnings("resource")
267 final C connection = getDelegateInternal();
268 setDelegate(null);
269 if (connection != null && transactionContext.getSharedConnection() != connection) {
270 try {
271 pool.returnObject(connection);
272 } catch (final Exception e) {
273
274 try {
275 pool.invalidateObject(connection);
276 } catch (final Exception ignored) {
277
278 }
279 }
280 }
281
282
283 transactionContext.addTransactionContextListener(new CompletionListener());
284
285
286
287
288 @SuppressWarnings("unchecked")
289 final C shared = (C) transactionContext.getSharedConnection();
290 setDelegate(shared);
291
292
293
294 isSharedConnection = true;
295 } else {
296 C connection = getDelegateInternal();
297
298 if (connection == null) {
299 try {
300
301 connection = pool.borrowObject();
302 setDelegate(connection);
303 } catch (final Exception e) {
304 throw new SQLException("Unable to acquire a new connection from the pool", e);
305 }
306 }
307
308
309 if (transactionContext != null) {
310
311 transactionContext.addTransactionContextListener(new CompletionListener());
312
313
314 try {
315 transactionContext.setSharedConnection(connection);
316 } catch (final SQLException e) {
317
318 transactionContext = null;
319 try {
320 pool.invalidateObject(connection);
321 } catch (final Exception ignored) {
322
323 }
324 throw e;
325 }
326 }
327 }
328
329
330 clearCachedState();
331 }
332 }