001: /*
002: $Header: /cvsroot/xorm/xorm/src/org/xorm/datastore/sql/PooledConnection.java,v 1.4 2003/07/12 00:21:11 sbendar Exp $
003:
004: This file is part of XORM.
005:
006: XORM is free software; you can redistribute it and/or modify
007: it under the terms of the GNU General Public License as published by
008: the Free Software Foundation; either version 2 of the License, or
009: (at your option) any later version.
010:
011: XORM is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU General Public License for more details.
015:
016: You should have received a copy of the GNU General Public License
017: along with XORM; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package org.xorm.datastore.sql;
021:
022: import java.sql.*;
023: import java.util.Map;
024: import java.util.logging.*;
025:
026: /**
027: * A wrapper around a DB connection that provides the concept of being
028: * logically closed, while keeping the db connection alive. This allows
029: * the connection to be reused without having to re-establish a db connection.
030: *
031: * @author Scott Bendar
032: * @version $Revision: 1.4 $
033: */
034: class PooledConnection implements Connection {
035:
036: private static final String CONNECTION_CLOSED = "PooledConnection: Trying to use a closed connection!";
037:
038: private static final Logger logger = Logger
039: .getLogger("org.xorm.datastore.sql");
040:
041: /**
042: * The owner of the connection
043: */
044: protected PooledDataSource owner;
045:
046: /**
047: * default transaction isolation level
048: */
049: protected int defaultIsolation = 0;
050:
051: /**
052: * default type map
053: */
054: protected Map defaultTypeMap = null;
055:
056: /**
057: * default catalog
058: */
059: protected String defaultCatalog = null;
060:
061: /**
062: * The physical db connection
063: */
064: protected Connection conn = null;
065:
066: /**
067: * flag indicates if connection has been logically closed or not
068: */
069: protected boolean closed = false;
070:
071: /**
072: * flag that indicates if we received an exception from any of our
073: * wrapped connction methods. This will force a connection test
074: * before giving the connection to anyone else.
075: */
076: protected boolean exceptionOccurred = false;
077:
078: /**
079: * The time the connection was last used
080: */
081: protected long lastUsed = System.currentTimeMillis();
082:
083: /**
084: * the current state of commit mode
085: */
086: protected boolean autoCommit = true;
087:
088: /**
089: * the current state of read only mode
090: */
091: protected boolean readOnly = false;
092:
093: /**
094: * Creates a new connection wrapper around the passed base connection.
095: * The wrapper intercepts the close call and performs a logical close
096: * and returns the connection to the owning pool.
097: *
098: * The other calls are passed through to the real connection object.
099: *
100: * @param dataSource the owning pooled data source
101: * @param newConn the actual db connection being wrapped
102: */
103: public PooledConnection(PooledDataSource dataSource,
104: Connection newConn) throws SQLException {
105:
106: owner = dataSource;
107: conn = newConn;
108:
109: // Retrieve default values so we can restore back when conn closes
110: defaultCatalog = conn.getCatalog();
111: defaultIsolation = conn.getTransactionIsolation();
112:
113: // This isn't supported by all JDBC drivers so watch out
114: try {
115: defaultTypeMap = conn.getTypeMap();
116: } catch (Exception e) {
117: }
118:
119: }
120:
121: /**
122: * Try to reset a connection back to the state it was in when it was
123: * first created so when its reused, the user doesn't get a connection
124: * left in some funky state by the previous user.
125: */
126: protected void resetConnection() throws SQLException {
127:
128: long startTime = System.currentTimeMillis();
129:
130: try {
131: if (!autoCommit)
132: conn.setAutoCommit(true);
133: } catch (SQLException e) {
134: }
135:
136: try {
137: if (readOnly)
138: conn.setReadOnly(false);
139: } catch (SQLException e) {
140: }
141:
142: try {
143: conn.setCatalog(defaultCatalog);
144: } catch (SQLException e) {
145: }
146:
147: try {
148: if (conn.getTransactionIsolation() != defaultIsolation)
149: conn.setTransactionIsolation(defaultIsolation);
150: } catch (SQLException e) {
151: }
152:
153: try {
154: conn.setTypeMap(defaultTypeMap);
155: } catch (Exception e) {
156: /*
157: * Different drivers throw different exceptions if its not
158: * supported.
159: */
160: }
161:
162: try {
163: conn.clearWarnings();
164: } catch (SQLException e) {
165: }
166:
167: logger.fine("resetConnection time is "
168: + (System.currentTimeMillis() - startTime));
169: }
170:
171: /**
172: * Closes the underlying db connection. This is called by the owning
173: * data source when it wants to get rid of the connection.
174: */
175: protected void closeConnection() {
176:
177: closed = true;
178: try {
179: conn.rollback();
180: } catch (SQLException e) {
181: }
182:
183: try {
184: conn.close();
185: } catch (SQLException e) {
186: }
187:
188: }
189:
190: /**
191: * Returns the state of this logical wrapper, not of the underlying
192: * db connection.
193: */
194: public boolean isClosed() {
195: return closed;
196: }
197:
198: /**
199: * Sets the closed state. This is called by the owning pool when
200: * its ready to return the connection to a caller.
201: */
202: protected void setClosed(boolean c) {
203: closed = c;
204: }
205:
206: /**
207: * Tests if the underlying connection is closed or not
208: */
209: protected boolean isConnectionClosed() throws SQLException {
210: long startTime = System.currentTimeMillis();
211:
212: boolean status = conn.isClosed();
213: // Calling isClosed can open a transaction with some implementations
214: if (autoCommit == false)
215: conn.commit();
216:
217: logger.fine("isClosed time: "
218: + (System.currentTimeMillis() - startTime));
219: return status;
220: }
221:
222: /**
223: * Performs a logical close on the connection. Any outstanding transaction
224: * is rolled back and the connection properties are reset to their original
225: * settings.
226: *
227: * The connection is set to a closed state causing most methods to
228: * throw an exception in case someone tries to use it after calling close.
229: *
230: * Lastly, it tells its owner it is again available for use.
231: */
232: public void close() throws SQLException {
233:
234: if (closed)
235: throw new SQLException(CONNECTION_CLOSED);
236:
237: closed = true;
238:
239: lastUsed = System.currentTimeMillis();
240:
241: // No need to rollback if auto commit is true
242: if (autoCommit == false) {
243: // Abort any outstanding transactions
244: try {
245: conn.rollback();
246: } catch (SQLException e) {
247: }
248: }
249:
250: logger.fine("close:Rollback time is "
251: + (System.currentTimeMillis() - lastUsed));
252:
253: // Reset the values to the defaults
254: resetConnection();
255:
256: if (owner.getCheckReturnedConnection()) {
257: try {
258: if (isConnectionClosed()) {
259: owner.dropConnection(this );
260: logger
261: .log(
262: Level.WARNING,
263: "Returning a closed connection to the pool.",
264: new Throwable(
265: "Trying to return a closed connection"));
266: }
267: } catch (SQLException se) {
268: logger.log(Level.WARNING,
269: "Unable to check connection.", se);
270: }
271: }
272:
273: // Put the connection back in the available queue
274: owner.makeAvailable(this );
275: }
276:
277: /**
278: * Returns true if any of the wrapped connections threw an exception
279: * while the connection was in use.
280: */
281: public boolean hasReceivedException() {
282: return exceptionOccurred;
283: }
284:
285: /**
286: * Clears the exception indicator
287: */
288: public void clearReceivedException() {
289: exceptionOccurred = false;
290: }
291:
292: /**
293: * Return time in milliseconds when the connection was last used.
294: */
295: public long getLastUsedTime() {
296: return lastUsed;
297: }
298:
299: /**
300: * Called when an Exception is thrown by one of the wrapped JDBC Connection
301: * methods. It logs that a connection was received which will force
302: * the connection pool to recheck the connection before giving it to
303: * someone else.
304: */
305: protected void checkException(SQLException sqe) throws SQLException {
306:
307: logger.log(Level.FINE, "Wrapped method exception", sqe);
308: exceptionOccurred = true;
309: throw sqe;
310: }
311:
312: /*----------------------------------------------------------------------
313: *
314: * The following methods are wrappers around the DB Connection.
315: * It just forwards the calls on and returns any results.
316: *
317: *----------------------------------------------------------------------
318: */
319:
320: public void clearWarnings() throws SQLException {
321: if (closed)
322: throw new SQLException(CONNECTION_CLOSED);
323:
324: try {
325: conn.clearWarnings();
326: } catch (SQLException sqe) {
327: checkException(sqe);
328: }
329: }
330:
331: public void commit() throws SQLException {
332: if (closed)
333: throw new SQLException(CONNECTION_CLOSED);
334:
335: try {
336: conn.commit();
337: } catch (SQLException sqe) {
338: checkException(sqe);
339: }
340: }
341:
342: public Statement createStatement() throws SQLException {
343: if (closed)
344: throw new SQLException(CONNECTION_CLOSED);
345:
346: try {
347: return conn.createStatement();
348: } catch (SQLException sqe) {
349: checkException(sqe);
350: return null;
351: }
352: }
353:
354: public Statement createStatement(int resultSetType,
355: int resultSetConcurrency) throws SQLException {
356:
357: if (closed)
358: throw new SQLException(CONNECTION_CLOSED);
359:
360: try {
361: return conn.createStatement(resultSetType,
362: resultSetConcurrency);
363: } catch (SQLException sqe) {
364: checkException(sqe);
365: return null;
366: }
367: }
368:
369: public Statement createStatement(int resultSetType,
370: int resultSetConcurrency, int resultSetHoldability)
371: throws SQLException {
372: if (closed)
373: throw new SQLException(CONNECTION_CLOSED);
374:
375: try {
376: return conn.createStatement(resultSetType,
377: resultSetConcurrency, resultSetHoldability);
378: } catch (SQLException sqe) {
379: checkException(sqe);
380: return null;
381: }
382: }
383:
384: public boolean getAutoCommit() throws SQLException {
385: if (closed)
386: throw new SQLException(CONNECTION_CLOSED);
387:
388: try {
389: autoCommit = conn.getAutoCommit();
390: return autoCommit;
391: } catch (SQLException sqe) {
392: checkException(sqe);
393: return false;
394: }
395: }
396:
397: public void setAutoCommit(boolean autoCommit) throws SQLException {
398: if (closed)
399: throw new SQLException(CONNECTION_CLOSED);
400:
401: try {
402: conn.setAutoCommit(autoCommit);
403: this .autoCommit = autoCommit;
404: } catch (SQLException sqe) {
405: checkException(sqe);
406: }
407: }
408:
409: public String getCatalog() throws SQLException {
410: if (closed)
411: throw new SQLException(CONNECTION_CLOSED);
412:
413: try {
414: return conn.getCatalog();
415: } catch (SQLException sqe) {
416: checkException(sqe);
417: return null;
418: }
419: }
420:
421: public void setCatalog(String catalog) throws SQLException {
422: if (closed)
423: throw new SQLException(CONNECTION_CLOSED);
424:
425: try {
426: conn.setCatalog(catalog);
427: } catch (SQLException sqe) {
428: checkException(sqe);
429: }
430: }
431:
432: public int getHoldability() throws SQLException {
433: if (closed)
434: throw new SQLException(CONNECTION_CLOSED);
435:
436: try {
437: return conn.getHoldability();
438: } catch (SQLException sqe) {
439: checkException(sqe);
440: return 0;
441: }
442: }
443:
444: public void setHoldability(int hold) throws SQLException {
445: if (closed)
446: throw new SQLException(CONNECTION_CLOSED);
447:
448: try {
449: conn.setHoldability(hold);
450: } catch (SQLException sqe) {
451: checkException(sqe);
452: }
453: }
454:
455: public DatabaseMetaData getMetaData() throws SQLException {
456: if (closed)
457: throw new SQLException(CONNECTION_CLOSED);
458:
459: try {
460: return conn.getMetaData();
461: } catch (SQLException sqe) {
462: checkException(sqe);
463: return null;
464: }
465: }
466:
467: public int getTransactionIsolation() throws SQLException {
468: if (closed)
469: throw new SQLException(CONNECTION_CLOSED);
470:
471: try {
472: return conn.getTransactionIsolation();
473: } catch (SQLException sqe) {
474: checkException(sqe);
475: return 0;
476: }
477: }
478:
479: public void setTransactionIsolation(int iso) throws SQLException {
480: if (closed)
481: throw new SQLException(CONNECTION_CLOSED);
482:
483: try {
484: conn.setTransactionIsolation(iso);
485: } catch (SQLException sqe) {
486: checkException(sqe);
487: }
488: }
489:
490: public Map getTypeMap() throws SQLException {
491: if (closed)
492: throw new SQLException(CONNECTION_CLOSED);
493:
494: try {
495: return conn.getTypeMap();
496: } catch (SQLException sqe) {
497: checkException(sqe);
498: return null;
499: }
500: }
501:
502: public void setTypeMap(Map typeMap) throws SQLException {
503: if (closed)
504: throw new SQLException(CONNECTION_CLOSED);
505:
506: try {
507: conn.setTypeMap(typeMap);
508: } catch (SQLException sqe) {
509: checkException(sqe);
510: }
511: }
512:
513: public SQLWarning getWarnings() throws SQLException {
514: if (closed)
515: throw new SQLException(CONNECTION_CLOSED);
516:
517: try {
518: return conn.getWarnings();
519: } catch (SQLException sqe) {
520: checkException(sqe);
521: return null;
522: }
523: }
524:
525: public boolean isReadOnly() throws SQLException {
526: if (closed)
527: throw new SQLException(CONNECTION_CLOSED);
528:
529: try {
530: readOnly = conn.isReadOnly();
531: return readOnly;
532: } catch (SQLException sqe) {
533: checkException(sqe);
534: return false;
535: }
536: }
537:
538: public void setReadOnly(boolean ro) throws SQLException {
539: if (closed)
540: throw new SQLException(CONNECTION_CLOSED);
541:
542: try {
543: conn.setReadOnly(ro);
544: readOnly = ro;
545: } catch (SQLException sqe) {
546: checkException(sqe);
547: }
548: }
549:
550: public String nativeSQL(String sql) throws SQLException {
551: if (closed)
552: throw new SQLException(CONNECTION_CLOSED);
553:
554: try {
555: return conn.nativeSQL(sql);
556: } catch (SQLException sqe) {
557: checkException(sqe);
558: return null;
559: }
560: }
561:
562: public CallableStatement prepareCall(String sql)
563: throws SQLException {
564: if (closed)
565: throw new SQLException(CONNECTION_CLOSED);
566:
567: try {
568: return conn.prepareCall(sql);
569: } catch (SQLException sqe) {
570: checkException(sqe);
571: return null;
572: }
573: }
574:
575: public CallableStatement prepareCall(String sql, int resultSetType,
576: int resultSetConcurrency) throws SQLException {
577: if (closed)
578: throw new SQLException(CONNECTION_CLOSED);
579:
580: try {
581: return conn.prepareCall(sql, resultSetType,
582: resultSetConcurrency);
583: } catch (SQLException sqe) {
584: checkException(sqe);
585: return null;
586: }
587: }
588:
589: public CallableStatement prepareCall(String sql, int resultSetType,
590: int resultSetConcurrency, int resultSetHoldability)
591: throws SQLException {
592: if (closed)
593: throw new SQLException(CONNECTION_CLOSED);
594:
595: try {
596: return conn.prepareCall(sql, resultSetType,
597: resultSetConcurrency, resultSetHoldability);
598: } catch (SQLException sqe) {
599: checkException(sqe);
600: return null;
601: }
602: }
603:
604: public PreparedStatement prepareStatement(String sql)
605: throws SQLException {
606: if (closed)
607: throw new SQLException(CONNECTION_CLOSED);
608:
609: try {
610: return conn.prepareStatement(sql);
611: } catch (SQLException sqe) {
612: checkException(sqe);
613: return null;
614: }
615: }
616:
617: public PreparedStatement prepareStatement(String sql,
618: int autoGeneratedKeys) throws SQLException {
619:
620: if (closed)
621: throw new SQLException(CONNECTION_CLOSED);
622:
623: try {
624: return conn.prepareStatement(sql, autoGeneratedKeys);
625: } catch (SQLException sqe) {
626: checkException(sqe);
627: return null;
628: }
629: }
630:
631: public PreparedStatement prepareStatement(String sql,
632: int[] columnIndexes) throws SQLException {
633:
634: if (closed)
635: throw new SQLException(CONNECTION_CLOSED);
636:
637: try {
638: return conn.prepareStatement(sql, columnIndexes);
639: } catch (SQLException sqe) {
640: checkException(sqe);
641: return null;
642: }
643: }
644:
645: public PreparedStatement prepareStatement(String sql,
646: int resultSetType, int resultSetConcurrency)
647: throws SQLException {
648:
649: if (closed)
650: throw new SQLException(CONNECTION_CLOSED);
651:
652: try {
653: return conn.prepareStatement(sql, resultSetType,
654: resultSetConcurrency);
655: } catch (SQLException sqe) {
656: checkException(sqe);
657: return null;
658: }
659: }
660:
661: public PreparedStatement prepareStatement(String sql,
662: int resultSetType, int resultSetConcurrency,
663: int resultSetHoldability) throws SQLException {
664:
665: if (closed)
666: throw new SQLException(CONNECTION_CLOSED);
667:
668: try {
669: return conn.prepareStatement(sql, resultSetType,
670: resultSetConcurrency, resultSetHoldability);
671: } catch (SQLException sqe) {
672: checkException(sqe);
673: return null;
674: }
675: }
676:
677: public PreparedStatement prepareStatement(String sql,
678: String[] columnNames) throws SQLException {
679:
680: if (closed)
681: throw new SQLException(CONNECTION_CLOSED);
682:
683: try {
684: return conn.prepareStatement(sql, columnNames);
685: } catch (SQLException sqe) {
686: checkException(sqe);
687: return null;
688: }
689: }
690:
691: public void releaseSavepoint(Savepoint savepoint)
692: throws SQLException {
693: if (closed)
694: throw new SQLException(CONNECTION_CLOSED);
695:
696: try {
697: conn.releaseSavepoint(savepoint);
698: } catch (SQLException sqe) {
699: checkException(sqe);
700: }
701: }
702:
703: public void rollback() throws SQLException {
704: if (closed)
705: throw new SQLException(CONNECTION_CLOSED);
706:
707: try {
708: conn.rollback();
709: } catch (SQLException sqe) {
710: checkException(sqe);
711: }
712: }
713:
714: public void rollback(Savepoint savepoint) throws SQLException {
715: if (closed)
716: throw new SQLException(CONNECTION_CLOSED);
717:
718: try {
719: conn.rollback(savepoint);
720: } catch (SQLException sqe) {
721: checkException(sqe);
722: }
723: }
724:
725: public Savepoint setSavepoint() throws SQLException {
726: if (closed)
727: throw new SQLException(CONNECTION_CLOSED);
728:
729: try {
730: return conn.setSavepoint();
731: } catch (SQLException sqe) {
732: checkException(sqe);
733: return null;
734: }
735: }
736:
737: public Savepoint setSavepoint(String name) throws SQLException {
738: if (closed)
739: throw new SQLException(CONNECTION_CLOSED);
740:
741: try {
742: return conn.setSavepoint(name);
743: } catch (SQLException sqe) {
744: checkException(sqe);
745: return null;
746: }
747: }
748: }
|