1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.batch.item.database;
18
19 import java.io.PrintWriter;
20 import java.lang.reflect.InvocationHandler;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.sql.Connection;
25 import java.sql.SQLException;
26 import java.util.logging.Logger;
27
28 import javax.sql.DataSource;
29
30 import org.springframework.beans.factory.InitializingBean;
31 import org.springframework.jdbc.datasource.ConnectionProxy;
32 import org.springframework.jdbc.datasource.DataSourceUtils;
33 import org.springframework.jdbc.datasource.SmartDataSource;
34 import org.springframework.transaction.support.TransactionSynchronizationManager;
35 import org.springframework.util.Assert;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public class ExtendedConnectionDataSourceProxy implements SmartDataSource, InitializingBean {
85
86
87 private DataSource dataSource;
88
89
90 private Connection closeSuppressedConnection = null;
91
92
93 private boolean borrowedConnection = false;
94
95
96 private final Object connectionMonitor = new Object();
97
98
99
100
101 public ExtendedConnectionDataSourceProxy() {
102 }
103
104
105
106
107
108 public ExtendedConnectionDataSourceProxy(DataSource dataSource) {
109 this.dataSource = dataSource;
110 }
111
112
113
114
115
116
117 public void setDataSource(DataSource dataSource) {
118 this.dataSource = dataSource;
119 }
120
121
122
123
124 @Override
125 public boolean shouldClose(Connection connection) {
126 boolean shouldClose = !isCloseSuppressionActive(connection);
127 if (borrowedConnection && closeSuppressedConnection.equals(connection)) {
128 borrowedConnection = false;
129 }
130 return shouldClose;
131 }
132
133
134
135
136
137
138
139
140
141 public boolean isCloseSuppressionActive(Connection connection) {
142 if (connection == null) {
143 return false;
144 }
145 return connection.equals(closeSuppressedConnection) ? true : false;
146 }
147
148
149
150
151
152
153 public void startCloseSuppression(Connection connection) {
154 synchronized (this.connectionMonitor) {
155 closeSuppressedConnection = connection;
156 if (TransactionSynchronizationManager.isActualTransactionActive()) {
157 borrowedConnection = true;
158 }
159 }
160 }
161
162
163
164
165
166
167 public void stopCloseSuppression(Connection connection) {
168 synchronized (this.connectionMonitor) {
169 closeSuppressedConnection = null;
170 borrowedConnection = false;
171 }
172 }
173
174 @Override
175 public Connection getConnection() throws SQLException {
176 synchronized (this.connectionMonitor) {
177 return initConnection(null, null);
178 }
179 }
180
181 @Override
182 public Connection getConnection(String username, String password) throws SQLException {
183 synchronized (this.connectionMonitor) {
184 return initConnection(username, password);
185 }
186 }
187
188 private boolean completeCloseCall(Connection connection) {
189 if (borrowedConnection && closeSuppressedConnection.equals(connection)) {
190 borrowedConnection = false;
191 }
192 return isCloseSuppressionActive(connection);
193 }
194
195 private Connection initConnection(String username, String password) throws SQLException {
196 if (closeSuppressedConnection != null) {
197 if (!borrowedConnection) {
198 borrowedConnection = true;
199 return closeSuppressedConnection;
200 }
201 }
202 Connection target;
203 if (username != null) {
204 target = dataSource.getConnection(username, password);
205 }
206 else {
207 target = dataSource.getConnection();
208 }
209 Connection connection = getCloseSuppressingConnectionProxy(target);
210 return connection;
211 }
212
213 @Override
214 public PrintWriter getLogWriter() throws SQLException {
215 return dataSource.getLogWriter();
216 }
217
218 @Override
219 public int getLoginTimeout() throws SQLException {
220 return dataSource.getLoginTimeout();
221 }
222
223 @Override
224 public void setLogWriter(PrintWriter out) throws SQLException {
225 dataSource.setLogWriter(out);
226 }
227
228 @Override
229 public void setLoginTimeout(int seconds) throws SQLException {
230 dataSource.setLoginTimeout(seconds);
231 }
232
233
234
235
236
237
238
239 protected Connection getCloseSuppressingConnectionProxy(Connection target) {
240 return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(),
241 new Class[] { ConnectionProxy.class }, new CloseSuppressingInvocationHandler(target, this));
242 }
243
244
245
246
247
248
249 private static class CloseSuppressingInvocationHandler implements InvocationHandler {
250
251 private final Connection target;
252
253 private final ExtendedConnectionDataSourceProxy dataSource;
254
255 public CloseSuppressingInvocationHandler(Connection target, ExtendedConnectionDataSourceProxy dataSource) {
256 this.dataSource = dataSource;
257 this.target = target;
258 }
259
260 @Override
261 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
262
263
264 if (method.getName().equals("equals")) {
265
266 return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
267 }
268 else if (method.getName().equals("hashCode")) {
269
270 return new Integer(System.identityHashCode(proxy));
271 }
272 else if (method.getName().equals("close")) {
273
274
275 if (dataSource.completeCloseCall((Connection) proxy)) {
276 return null;
277 }
278 else {
279 target.close();
280 return null;
281 }
282 }
283 else if (method.getName().equals("getTargetConnection")) {
284
285
286 return this.target;
287 }
288
289
290 try {
291 return method.invoke(this.target, args);
292 }
293 catch (InvocationTargetException ex) {
294 throw ex.getTargetException();
295 }
296 }
297 }
298
299
300
301
302
303 @Override
304 public boolean isWrapperFor(Class<?> iface) throws SQLException {
305 if (iface.isAssignableFrom(SmartDataSource.class) || iface.isAssignableFrom(dataSource.getClass())) {
306 return true;
307 }
308 return false;
309 }
310
311
312
313
314
315
316 @Override
317 public <T> T unwrap(Class<T> iface) throws SQLException {
318 if (iface.isAssignableFrom(SmartDataSource.class)) {
319 @SuppressWarnings("unchecked")
320 T casted = (T) this;
321 return casted;
322 }
323 else if (iface.isAssignableFrom(dataSource.getClass())) {
324 @SuppressWarnings("unchecked")
325 T casted = (T) dataSource;
326 return casted;
327 }
328 throw new SQLException("Unsupported class " + iface.getSimpleName());
329 }
330
331 @Override
332 public void afterPropertiesSet() throws Exception {
333 Assert.notNull(dataSource);
334 }
335
336
337
338
339
340
341 public Logger getParentLogger() {
342 throw new UnsupportedOperationException();
343 }
344
345 }