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 can not 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 public TransactionContext getTransactionContext() {
155 return transactionContext;
156 }
157
158
159
160
161
162 public TransactionRegistry getTransactionRegistry() {
163 return transactionRegistry;
164 }
165
166
167
168
169
170
171 public boolean isAccessToUnderlyingConnectionAllowed() {
172 return accessToUnderlyingConnectionAllowed;
173 }
174
175 @Override
176 public void rollback() throws SQLException {
177 if (transactionContext != null) {
178 throw new SQLException("Commit can not be set while enrolled in a transaction");
179 }
180 super.rollback();
181 }
182
183 @Override
184 public void setAutoCommit(final boolean autoCommit) throws SQLException {
185 if (transactionContext != null) {
186 throw new SQLException("Auto-commit can not be set while enrolled in a transaction");
187 }
188 super.setAutoCommit(autoCommit);
189 }
190
191 @Override
192 public void setReadOnly(final boolean readOnly) throws SQLException {
193 if (transactionContext != null) {
194 throw new SQLException("Read-only can not be set while enrolled in a transaction");
195 }
196 super.setReadOnly(readOnly);
197 }
198
199
200
201
202 protected void transactionComplete() {
203 lock.lock();
204 try {
205 transactionContext.completeTransaction();
206 } finally {
207 lock.unlock();
208 }
209
210
211
212 if (isSharedConnection) {
213 setDelegate(null);
214 isSharedConnection = false;
215 }
216
217
218 clearCachedState();
219
220
221
222 final Connection delegate = getDelegateInternal();
223 if (isClosedInternal() && delegate != null) {
224 try {
225 setDelegate(null);
226
227 if (!delegate.isClosed()) {
228 delegate.close();
229 }
230 } catch (final SQLException ignored) {
231
232
233
234 }
235 }
236 }
237
238 private void updateTransactionStatus() throws SQLException {
239
240 if (transactionContext != null && !transactionContext.isTransactionComplete()) {
241 if (transactionContext.isActive()) {
242 if (transactionContext != transactionRegistry.getActiveTransactionContext()) {
243 throw new SQLException("Connection can not be used while enlisted in another transaction");
244 }
245 return;
246 }
247
248
249
250 transactionComplete();
251 }
252
253
254 transactionContext = transactionRegistry.getActiveTransactionContext();
255
256
257 if (transactionContext != null && transactionContext.getSharedConnection() != null) {
258
259
260
261
262 @SuppressWarnings("resource")
263 final C connection = getDelegateInternal();
264 setDelegate(null);
265 if (connection != null && transactionContext.getSharedConnection() != connection) {
266 try {
267 pool.returnObject(connection);
268 } catch (final Exception e) {
269
270 try {
271 pool.invalidateObject(connection);
272 } catch (final Exception ignored) {
273
274 }
275 }
276 }
277
278
279 transactionContext.addTransactionContextListener(new CompletionListener());
280
281
282
283
284 @SuppressWarnings("unchecked")
285 final C shared = (C) transactionContext.getSharedConnection();
286 setDelegate(shared);
287
288
289
290 isSharedConnection = true;
291 } else {
292 C connection = getDelegateInternal();
293
294 if (connection == null) {
295 try {
296
297 connection = pool.borrowObject();
298 setDelegate(connection);
299 } catch (final Exception e) {
300 throw new SQLException("Unable to acquire a new connection from the pool", e);
301 }
302 }
303
304
305 if (transactionContext != null) {
306
307 transactionContext.addTransactionContextListener(new CompletionListener());
308
309
310 try {
311 transactionContext.setSharedConnection(connection);
312 } catch (final SQLException e) {
313
314 transactionContext = null;
315 try {
316 pool.invalidateObject(connection);
317 } catch (final Exception ignored) {
318
319 }
320 throw e;
321 }
322 }
323 }
324
325
326 clearCachedState();
327 }
328 }