1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.lib.jdbc;
20
21 import java.lang.reflect.Method;
22 import java.sql.CallableStatement;
23 import java.sql.Connection;
24 import java.sql.DatabaseMetaData;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.sql.SQLWarning;
29 import java.sql.Savepoint;
30 import java.sql.Statement;
31 import java.util.HashMap;
32 import java.util.Map;
33
34 import org.apache.commons.lang.exception.NestableRuntimeException;
35 import org.apache.openjpa.lib.util.Closeable;
36 import org.apache.openjpa.lib.util.Localizer;
37 import serp.util.Numbers;
38
39 /**
40 * Wrapper around an existing connection. Subclasses can override the
41 * methods whose behavior they mean to change. The <code>equals</code> and
42 * <code>hashCode</code> methods pass through to the base underlying data
43 * store connection.
44 *
45 * @author Abe White
46 */
47 public class DelegatingConnection implements Connection, Closeable {
48
49 // jdbc 3 method keys
50 private static final Object SET_HOLDABILITY = new Object();
51 private static final Object GET_HOLDABILITY = new Object();
52 private static final Object SET_SAVEPOINT_NONAME = new Object();
53 private static final Object SET_SAVEPOINT = new Object();
54 private static final Object ROLLBACK_SAVEPOINT = new Object();
55 private static final Object RELEASE_SAVEPOINT = new Object();
56 private static final Object CREATE_STATEMENT = new Object();
57 private static final Object PREPARE_STATEMENT = new Object();
58 private static final Object PREPARE_CALL = new Object();
59 private static final Object PREPARE_WITH_KEYS = new Object();
60 private static final Object PREPARE_WITH_INDEX = new Object();
61 private static final Object PREPARE_WITH_NAMES = new Object();
62
63 private static final Localizer _loc = Localizer.forPackage
64 (DelegatingConnection.class);
65
66 private static final Map _jdbc3;
67
68 static {
69 boolean jdbc3 = false;
70 Method m = null;
71 try {
72 m = Connection.class.getMethod("setSavepoint",
73 new Class[]{ String.class });
74 jdbc3 = true;
75 } catch (Throwable t) {
76 }
77
78 if (jdbc3) {
79 _jdbc3 = new HashMap();
80 _jdbc3.put(SET_SAVEPOINT, m);
81 } else
82 _jdbc3 = null;
83 }
84
85 private final Connection _conn;
86 private final DelegatingConnection _del;
87
88 public DelegatingConnection(Connection conn) {
89 _conn = conn;
90 if (conn instanceof DelegatingConnection)
91 _del = (DelegatingConnection) _conn;
92 else
93 _del = null;
94 }
95
96 /**
97 * Return the wrapped connection.
98 */
99 public Connection getDelegate() {
100 return _conn;
101 }
102
103 /**
104 * Return the base underlying data store connection.
105 */
106 public Connection getInnermostDelegate() {
107 return (_del == null) ? _conn : _del.getInnermostDelegate();
108 }
109
110 public int hashCode() {
111 return getInnermostDelegate().hashCode();
112 }
113
114 public boolean equals(Object other) {
115 if (other == this)
116 return true;
117 if (other instanceof DelegatingConnection)
118 other = ((DelegatingConnection) other).getInnermostDelegate();
119 return getInnermostDelegate().equals(other);
120 }
121
122 public String toString() {
123 StringBuffer buf = new StringBuffer("conn ").append(hashCode());
124 appendInfo(buf);
125 return buf.toString();
126 }
127
128 protected void appendInfo(StringBuffer buf) {
129 if (_del != null)
130 _del.appendInfo(buf);
131 }
132
133 public Statement createStatement() throws SQLException {
134 return createStatement(true);
135 }
136
137 /**
138 * Create a statement, with the option of not wrapping it in a
139 * {@link DelegatingStatement}, which is the default.
140 */
141 protected Statement createStatement(boolean wrap) throws SQLException {
142 Statement stmnt;
143 if (_del != null)
144 stmnt = _del.createStatement(false);
145 else
146 stmnt = _conn.createStatement();
147 if (wrap)
148 stmnt = new DelegatingStatement(stmnt, this);
149 return stmnt;
150 }
151
152 public PreparedStatement prepareStatement(String str) throws SQLException {
153 return prepareStatement(str, true);
154 }
155
156 /**
157 * Prepare a statement, with the option of not wrapping it in a
158 * {@link DelegatingPreparedStatement}, which is the default.
159 */
160 protected PreparedStatement prepareStatement(String str, boolean wrap)
161 throws SQLException {
162 PreparedStatement stmnt;
163 if (_del != null)
164 stmnt = _del.prepareStatement(str, false);
165 else
166 stmnt = _conn.prepareStatement(str, ResultSet.TYPE_FORWARD_ONLY,
167 ResultSet.CONCUR_READ_ONLY);
168 if (wrap)
169 stmnt = new DelegatingPreparedStatement(stmnt, this);
170 return stmnt;
171 }
172
173 public CallableStatement prepareCall(String str) throws SQLException {
174 return prepareCall(str, true);
175 }
176
177 /**
178 * Prepare a call, with the option of not wrapping it in a
179 * {@link DelegatingCallableStatement}, which is the default.
180 */
181 protected CallableStatement prepareCall(String str, boolean wrap)
182 throws SQLException {
183 CallableStatement stmnt;
184 if (_del != null)
185 stmnt = _del.prepareCall(str, false);
186 else
187 stmnt = _conn.prepareCall(str);
188 if (wrap)
189 stmnt = new DelegatingCallableStatement(stmnt, this);
190 return stmnt;
191 }
192
193 public String nativeSQL(String str) throws SQLException {
194 return _conn.nativeSQL(str);
195 }
196
197 public void setAutoCommit(boolean bool) throws SQLException {
198 _conn.setAutoCommit(bool);
199 }
200
201 public boolean getAutoCommit() throws SQLException {
202 return _conn.getAutoCommit();
203 }
204
205 public void commit() throws SQLException {
206 _conn.commit();
207 }
208
209 public void rollback() throws SQLException {
210 _conn.rollback();
211 }
212
213 public void close() throws SQLException {
214 _conn.close();
215 }
216
217 public boolean isClosed() throws SQLException {
218 return _conn.isClosed();
219 }
220
221 public DatabaseMetaData getMetaData() throws SQLException {
222 return getMetaData(true);
223 }
224
225 /**
226 * Return the metadata, with the option of not wrapping it in a
227 * {@link DelegatingDatabaseMetaData}, which is the default.
228 */
229 protected DatabaseMetaData getMetaData(boolean wrap) throws SQLException {
230 DatabaseMetaData meta;
231 if (_del != null)
232 meta = _del.getMetaData(false);
233 else
234 meta = _conn.getMetaData();
235 if (wrap)
236 meta = new DelegatingDatabaseMetaData(meta, this);
237 return meta;
238 }
239
240 public void setReadOnly(boolean bool) throws SQLException {
241 _conn.setReadOnly(bool);
242 }
243
244 public boolean isReadOnly() throws SQLException {
245 return _conn.isReadOnly();
246 }
247
248 public void setCatalog(String str) throws SQLException {
249 _conn.setCatalog(str);
250 }
251
252 public String getCatalog() throws SQLException {
253 return _conn.getCatalog();
254 }
255
256 public void setTransactionIsolation(int i) throws SQLException {
257 _conn.setTransactionIsolation(i);
258 }
259
260 public int getTransactionIsolation() throws SQLException {
261 return _conn.getTransactionIsolation();
262 }
263
264 public SQLWarning getWarnings() throws SQLException {
265 return _conn.getWarnings();
266 }
267
268 public void clearWarnings() throws SQLException {
269 _conn.clearWarnings();
270 }
271
272 public Statement createStatement(int type, int concur) throws SQLException {
273 return createStatement(type, concur, true);
274 }
275
276 /**
277 * Create a statement, with the option of not wrapping it in a
278 * {@link DelegatingStatement}, which is the default.
279 */
280 protected Statement createStatement(int type, int concur, boolean wrap)
281 throws SQLException {
282 Statement stmnt;
283 if (_del != null)
284 stmnt = _del.createStatement(type, concur, false);
285 else
286 stmnt = _conn.createStatement(type, concur);
287 if (wrap)
288 stmnt = new DelegatingStatement(stmnt, this);
289 return stmnt;
290 }
291
292 public PreparedStatement prepareStatement(String str, int type, int concur)
293 throws SQLException {
294 return prepareStatement(str, type, concur, true);
295 }
296
297 /**
298 * Prepare a statement, with the option of not wrapping it in a
299 * {@link DelegatingPreparedStatement}, which is the default.
300 */
301 protected PreparedStatement prepareStatement(String str, int type,
302 int concur, boolean wrap) throws SQLException {
303 PreparedStatement stmnt;
304 if (_del != null)
305 stmnt = _del.prepareStatement(str, type, concur, false);
306 else
307 stmnt = _conn.prepareStatement(str, type, concur);
308 if (wrap)
309 stmnt = new DelegatingPreparedStatement(stmnt, this);
310 return stmnt;
311 }
312
313 public CallableStatement prepareCall(String str, int type, int concur)
314 throws SQLException {
315 return prepareCall(str, type, concur, true);
316 }
317
318 /**
319 * Prepare a call, with the option of not wrapping it in a
320 * {@link DelegatingCallableStatement}, which is the default.
321 */
322 protected CallableStatement prepareCall(String str, int type, int concur,
323 boolean wrap) throws SQLException {
324 CallableStatement stmnt;
325 if (_del != null)
326 stmnt = _del.prepareCall(str, type, concur, false);
327 else
328 stmnt = _conn.prepareCall(str, type, concur);
329 if (wrap)
330 stmnt = new DelegatingCallableStatement(stmnt, this);
331 return stmnt;
332 }
333
334 public Map getTypeMap() throws SQLException {
335 return _conn.getTypeMap();
336 }
337
338 public void setTypeMap(Map map) throws SQLException {
339 _conn.setTypeMap(map);
340 }
341
342 // JDBC 3.0 methods follow; these are required to be able to
343 // compile against JDK 1.4; these methods will not work on
344 // previous JVMs
345
346 public void setHoldability(int holdability) throws SQLException {
347 assertJDBC3();
348 Method m = (Method) _jdbc3.get(SET_HOLDABILITY);
349 if (m == null)
350 m = createJDBC3Method(SET_HOLDABILITY, "setHoldability",
351 new Class[]{ int.class });
352 invokeJDBC3(m, new Object[]{ Numbers.valueOf(holdability) });
353 }
354
355 public int getHoldability() throws SQLException {
356 assertJDBC3();
357 Method m = (Method) _jdbc3.get(GET_HOLDABILITY);
358 if (m == null)
359 m = createJDBC3Method(GET_HOLDABILITY, "getHoldability", null);
360 return ((Number) invokeJDBC3(m, null)).intValue();
361 }
362
363 public Savepoint setSavepoint() throws SQLException {
364 assertJDBC3();
365 Method m = (Method) _jdbc3.get(SET_SAVEPOINT_NONAME);
366 if (m == null)
367 m = createJDBC3Method(SET_SAVEPOINT_NONAME, "setSavepoint", null);
368 return (Savepoint) invokeJDBC3(m, null);
369 }
370
371 public Savepoint setSavepoint(String savepoint) throws SQLException {
372 assertJDBC3();
373 Method m = (Method) _jdbc3.get(SET_SAVEPOINT);
374 if (m == null)
375 m = createJDBC3Method(SET_SAVEPOINT, "setSavepoint",
376 new Class[]{ String.class });
377 return (Savepoint) invokeJDBC3(m, new Object[]{ savepoint });
378 }
379
380 public void rollback(Savepoint savepoint) throws SQLException {
381 assertJDBC3();
382 Method m = (Method) _jdbc3.get(ROLLBACK_SAVEPOINT);
383 if (m == null)
384 m = createJDBC3Method(ROLLBACK_SAVEPOINT, "rollback",
385 new Class[]{ Savepoint.class });
386 invokeJDBC3(m, new Object[]{ savepoint });
387 }
388
389 public void releaseSavepoint(Savepoint savepoint) throws SQLException {
390 assertJDBC3();
391 Method m = (Method) _jdbc3.get(RELEASE_SAVEPOINT);
392 if (m == null)
393 m = createJDBC3Method(RELEASE_SAVEPOINT, "releaseSavepoint",
394 new Class[]{ Savepoint.class });
395 invokeJDBC3(m, new Object[]{ savepoint });
396 }
397
398 public Statement createStatement(int resultSetType,
399 int resultSetConcurrency, int resultSetHoldability)
400 throws SQLException {
401 assertJDBC3();
402 return createStatement(resultSetType, resultSetConcurrency,
403 resultSetHoldability, true);
404 }
405
406 protected Statement createStatement(int resultSetType,
407 int resultSetConcurrency, int resultSetHoldability, boolean wrap)
408 throws SQLException {
409 Statement stmnt;
410 if (_del != null)
411 stmnt = _del.createStatement(resultSetType, resultSetConcurrency,
412 resultSetHoldability, false);
413 else {
414 Method m = (Method) _jdbc3.get(CREATE_STATEMENT);
415 if (m == null)
416 m = createJDBC3Method(CREATE_STATEMENT, "createStatement",
417 new Class[]{ int.class, int.class, int.class });
418 stmnt = (Statement) invokeJDBC3(m, new Object[]{
419 Numbers.valueOf(resultSetType),
420 Numbers.valueOf(resultSetConcurrency),
421 Numbers.valueOf(resultSetHoldability) });
422 }
423 if (wrap)
424 stmnt = new DelegatingStatement(stmnt, this);
425 return stmnt;
426 }
427
428 public PreparedStatement prepareStatement(String sql,
429 int resultSetType, int resultSetConcurrency, int resultSetHoldability)
430 throws SQLException {
431 assertJDBC3();
432 return prepareStatement(sql, resultSetType, resultSetConcurrency,
433 resultSetHoldability, true);
434 }
435
436 protected PreparedStatement prepareStatement(String sql,
437 int resultSetType, int resultSetConcurrency, int resultSetHoldability,
438 boolean wrap) throws SQLException {
439 PreparedStatement stmnt;
440 if (_del != null)
441 stmnt = _del.prepareStatement(sql, resultSetType,
442 resultSetConcurrency, resultSetHoldability, false);
443 else {
444 Method m = (Method) _jdbc3.get(PREPARE_STATEMENT);
445 if (m == null)
446 m = createJDBC3Method(PREPARE_STATEMENT, "prepareStatement",
447 new Class[]{ String.class, int.class, int.class,
448 int.class });
449 stmnt = (PreparedStatement) invokeJDBC3(m, new Object[]{ sql,
450 Numbers.valueOf(resultSetType),
451 Numbers.valueOf(resultSetConcurrency),
452 Numbers.valueOf(resultSetHoldability) });
453 }
454 if (wrap)
455 stmnt = new DelegatingPreparedStatement(stmnt, this);
456 return stmnt;
457 }
458
459 public CallableStatement prepareCall(String sql,
460 int resultSetType, int resultSetConcurrency, int resultSetHoldability)
461 throws SQLException {
462 assertJDBC3();
463 return prepareCall(sql, resultSetType, resultSetConcurrency,
464 resultSetHoldability, true);
465 }
466
467 protected CallableStatement prepareCall(String sql, int resultSetType,
468 int resultSetConcurrency, int resultSetHoldability, boolean wrap)
469 throws SQLException {
470 CallableStatement stmnt;
471 if (_del != null)
472 stmnt = _del.prepareCall(sql, resultSetType,
473 resultSetConcurrency, resultSetHoldability, false);
474 else {
475 Method m = (Method) _jdbc3.get(PREPARE_CALL);
476 if (m == null)
477 m = createJDBC3Method(PREPARE_CALL, "prepareCall",
478 new Class[]{ String.class, int.class, int.class,
479 int.class });
480 stmnt = (CallableStatement) invokeJDBC3(m, new Object[]{ sql,
481 Numbers.valueOf(resultSetType),
482 Numbers.valueOf(resultSetConcurrency),
483 Numbers.valueOf(resultSetHoldability) });
484 }
485 if (wrap)
486 stmnt = new DelegatingCallableStatement(stmnt, this);
487 return stmnt;
488 }
489
490 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
491 throws SQLException {
492 assertJDBC3();
493 return prepareStatement(sql, autoGeneratedKeys, true);
494 }
495
496 protected PreparedStatement prepareStatement(String sql,
497 int autoGeneratedKeys, boolean wrap) throws SQLException {
498 PreparedStatement stmnt;
499 if (_del != null)
500 stmnt = _del.prepareStatement(sql, autoGeneratedKeys);
501 else {
502 Method m = (Method) _jdbc3.get(PREPARE_WITH_KEYS);
503 if (m == null)
504 m = createJDBC3Method(PREPARE_WITH_KEYS, "prepareStatement",
505 new Class[]{ String.class, int.class });
506 stmnt = (PreparedStatement) invokeJDBC3(m, new Object[]{ sql,
507 Numbers.valueOf(autoGeneratedKeys) });
508 }
509 if (wrap)
510 stmnt = new DelegatingPreparedStatement(stmnt, this);
511 return stmnt;
512 }
513
514 public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
515 throws SQLException {
516 assertJDBC3();
517 return prepareStatement(sql, columnIndexes, true);
518 }
519
520 protected PreparedStatement prepareStatement(String sql,
521 int[] columnIndexes, boolean wrap) throws SQLException {
522 PreparedStatement stmnt;
523 if (_del != null)
524 stmnt = _del.prepareStatement(sql, columnIndexes, wrap);
525 else {
526 Method m = (Method) _jdbc3.get(PREPARE_WITH_INDEX);
527 if (m == null)
528 m = createJDBC3Method(PREPARE_WITH_INDEX, "prepareStatement",
529 new Class[]{ String.class, int[].class });
530 stmnt = (PreparedStatement) invokeJDBC3(m, new Object[]{ sql,
531 columnIndexes });
532 }
533 if (wrap)
534 stmnt = new DelegatingPreparedStatement(stmnt, this);
535 return stmnt;
536 }
537
538 public PreparedStatement prepareStatement(String sql, String[] columnNames)
539 throws SQLException {
540 assertJDBC3();
541 return prepareStatement(sql, columnNames, true);
542 }
543
544 protected PreparedStatement prepareStatement(String sql,
545 String[] columnNames, boolean wrap) throws SQLException {
546 assertJDBC3();
547 PreparedStatement stmnt;
548 if (_del != null)
549 stmnt = _del.prepareStatement(sql, columnNames, wrap);
550 else {
551 Method m = (Method) _jdbc3.get(PREPARE_WITH_NAMES);
552 if (m == null)
553 m = createJDBC3Method(PREPARE_WITH_NAMES, "prepareStatement",
554 new Class[]{ String.class, String[].class });
555 stmnt = (PreparedStatement) invokeJDBC3(m, new Object[]{ sql,
556 columnNames });
557 }
558 if (wrap)
559 stmnt = new DelegatingPreparedStatement(stmnt, this);
560 return stmnt;
561 }
562
563 private static void assertJDBC3() {
564 if (_jdbc3 == null)
565 throw new UnsupportedOperationException(_loc.get("not-jdbc3")
566 .getMessage());
567 }
568
569 private Object invokeJDBC3(Method m, Object[] args) throws SQLException {
570 try {
571 return m.invoke(_conn, args);
572 } catch (Throwable t) {
573 if (t instanceof SQLException)
574 throw(SQLException) t;
575 throw new NestableRuntimeException(_loc.get("invoke-jdbc3")
576 .getMessage(), t);
577 }
578 }
579
580 private static Method createJDBC3Method(Object key, String name,
581 Class[] args) {
582 try {
583 Method m = Connection.class.getMethod(name, args);
584 _jdbc3.put(key, m);
585 return m;
586 } catch (Throwable t) {
587 throw new NestableRuntimeException(_loc.get("error-jdbc3")
588 .getMessage(), t);
589 }
590 }
591 }