1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.jdbc.datasource;
18
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Proxy;
23 import java.sql.Connection;
24 import java.sql.SQLException;
25
26 import org.springframework.beans.factory.DisposableBean;
27 import org.springframework.util.Assert;
28 import org.springframework.util.ObjectUtils;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class SingleConnectionDataSource extends DriverManagerDataSource
59 implements SmartDataSource, DisposableBean {
60
61
62 private boolean suppressClose;
63
64
65 private Boolean autoCommit;
66
67
68 private Connection target;
69
70
71 private Connection connection;
72
73
74 private final Object connectionMonitor = new Object();
75
76
77
78
79
80 public SingleConnectionDataSource() {
81 }
82
83
84
85
86
87
88
89
90
91
92
93 public SingleConnectionDataSource(String url, String username, String password, boolean suppressClose) {
94 super(url, username, password);
95 this.suppressClose = suppressClose;
96 }
97
98
99
100
101
102
103
104
105
106 public SingleConnectionDataSource(String url, boolean suppressClose) {
107 super(url);
108 this.suppressClose = suppressClose;
109 }
110
111
112
113
114
115
116
117
118
119 public SingleConnectionDataSource(Connection target, boolean suppressClose) {
120 Assert.notNull(target, "Connection must not be null");
121 this.target = target;
122 this.suppressClose = suppressClose;
123 this.connection = (suppressClose ? getCloseSuppressingConnectionProxy(target) : target);
124 }
125
126
127
128
129
130
131 public void setSuppressClose(boolean suppressClose) {
132 this.suppressClose = suppressClose;
133 }
134
135
136
137
138
139 protected boolean isSuppressClose() {
140 return this.suppressClose;
141 }
142
143
144
145
146 public void setAutoCommit(boolean autoCommit) {
147 this.autoCommit = (autoCommit);
148 }
149
150
151
152
153
154 protected Boolean getAutoCommitValue() {
155 return this.autoCommit;
156 }
157
158
159 @Override
160 public Connection getConnection() throws SQLException {
161 synchronized (this.connectionMonitor) {
162 if (this.connection == null) {
163
164 initConnection();
165 }
166 if (this.connection.isClosed()) {
167 throw new SQLException(
168 "Connection was closed in SingleConnectionDataSource. Check that user code checks " +
169 "shouldClose() before closing Connections, or set 'suppressClose' to 'true'");
170 }
171 return this.connection;
172 }
173 }
174
175
176
177
178
179
180 @Override
181 public Connection getConnection(String username, String password) throws SQLException {
182 if (ObjectUtils.nullSafeEquals(username, getUsername()) &&
183 ObjectUtils.nullSafeEquals(password, getPassword())) {
184 return getConnection();
185 }
186 else {
187 throw new SQLException("SingleConnectionDataSource does not support custom username and password");
188 }
189 }
190
191
192
193
194 @Override
195 public boolean shouldClose(Connection con) {
196 synchronized (this.connectionMonitor) {
197 return (con != this.connection && con != this.target);
198 }
199 }
200
201
202
203
204
205
206
207 @Override
208 public void destroy() {
209 synchronized (this.connectionMonitor) {
210 closeConnection();
211 }
212 }
213
214
215
216
217
218 public void initConnection() throws SQLException {
219 if (getUrl() == null) {
220 throw new IllegalStateException("'url' property is required for lazily initializing a Connection");
221 }
222 synchronized (this.connectionMonitor) {
223 closeConnection();
224 this.target = getConnectionFromDriver(getUsername(), getPassword());
225 prepareConnection(this.target);
226 if (logger.isInfoEnabled()) {
227 logger.info("Established shared JDBC Connection: " + this.target);
228 }
229 this.connection = (isSuppressClose() ? getCloseSuppressingConnectionProxy(this.target) : this.target);
230 }
231 }
232
233
234
235
236 public void resetConnection() {
237 synchronized (this.connectionMonitor) {
238 closeConnection();
239 this.target = null;
240 this.connection = null;
241 }
242 }
243
244
245
246
247
248
249
250
251 protected void prepareConnection(Connection con) throws SQLException {
252 Boolean autoCommit = getAutoCommitValue();
253 if (autoCommit != null && con.getAutoCommit() != autoCommit) {
254 con.setAutoCommit(autoCommit);
255 }
256 }
257
258
259
260
261 private void closeConnection() {
262 if (this.target != null) {
263 try {
264 this.target.close();
265 }
266 catch (Throwable ex) {
267 logger.warn("Could not close shared JDBC Connection", ex);
268 }
269 }
270 }
271
272
273
274
275
276
277
278 protected Connection getCloseSuppressingConnectionProxy(Connection target) {
279 return (Connection) Proxy.newProxyInstance(
280 ConnectionProxy.class.getClassLoader(),
281 new Class<?>[] {ConnectionProxy.class},
282 new CloseSuppressingInvocationHandler(target));
283 }
284
285
286
287
288
289 private static class CloseSuppressingInvocationHandler implements InvocationHandler {
290
291 private final Connection target;
292
293 public CloseSuppressingInvocationHandler(Connection target) {
294 this.target = target;
295 }
296
297 @Override
298 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
299
300
301 if (method.getName().equals("equals")) {
302
303 return (proxy == args[0]);
304 }
305 else if (method.getName().equals("hashCode")) {
306
307 return System.identityHashCode(proxy);
308 }
309 else if (method.getName().equals("unwrap")) {
310 if (((Class<?>) args[0]).isInstance(proxy)) {
311 return proxy;
312 }
313 }
314 else if (method.getName().equals("isWrapperFor")) {
315 if (((Class<?>) args[0]).isInstance(proxy)) {
316 return true;
317 }
318 }
319 else if (method.getName().equals("close")) {
320
321 return null;
322 }
323 else if (method.getName().equals("isClosed")) {
324 return false;
325 }
326 else if (method.getName().equals("getTargetConnection")) {
327
328 return this.target;
329 }
330
331
332 try {
333 return method.invoke(this.target, args);
334 }
335 catch (InvocationTargetException ex) {
336 throw ex.getTargetException();
337 }
338 }
339 }
340
341 }