001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.sql;
031:
032: import com.caucho.log.Log;
033: import com.caucho.util.L10N;
034:
035: import java.sql.*;
036: import java.util.ArrayList;
037: import java.util.Map;
038: import java.util.Properties;
039: import java.util.logging.Level;
040: import java.util.logging.Logger;
041:
042: /**
043: * Adapts the JDBC 2.0 connection pooling and XA support for database drivers
044: * which don't understand it.
045: *
046: * <p>Closing the connection will return the real connection to the pool
047: * and close any statements.
048: */
049: public class UserConnection implements java.sql.Connection {
050: protected static final Logger log = Log.open(UserConnection.class);
051: protected static final L10N L = new L10N(UserConnection.class);
052:
053: // The parent XAConnectionAdapter
054: private ManagedConnectionImpl _mConn;
055:
056: // Maximum statements saved for automatic closing
057:
058: private Statement _statement;
059: // ArrayList of all the statements created by this connection
060: private ArrayList<Statement> _statements;
061:
062: /**
063: * Creates a new PooledConnection.
064: *
065: * @param pool the pool the connection belongs to.
066: * @param conn the underlying connection.
067: */
068: UserConnection(ManagedConnectionImpl mConn) {
069: if (mConn == null || mConn.getDriverConnection() == null)
070: throw new NullPointerException();
071:
072: _mConn = mConn;
073: }
074:
075: /**
076: * Returns the underlying connection.
077: */
078: public Connection getConnection() throws SQLException {
079: Connection conn = getDriverConnection();
080:
081: if (conn instanceof com.caucho.sql.spy.SpyConnection)
082: conn = ((com.caucho.sql.spy.SpyConnection) conn)
083: .getConnection();
084:
085: return conn;
086: }
087:
088: public Class getDriverClass() {
089: return getMConn().getDriverClass();
090: }
091:
092: public String getURL() {
093: return getMConn().getDBPool().getURL();
094: }
095:
096: /**
097: * Associates with a different mConn.
098: */
099: void associate(ManagedConnectionImpl mConn) {
100: _mConn = mConn;
101: }
102:
103: /**
104: * JDBC api to create a new statement. Any SQL exception thrown here
105: * will make the connection invalid, i.e. it can't be put back into
106: * the pool.
107: *
108: * @return a new JDBC statement.
109: */
110: public Statement createStatement() throws SQLException {
111: Statement stmt;
112:
113: Connection conn = getDriverConnection();
114:
115: try {
116: stmt = conn.createStatement();
117: } catch (SQLException e) {
118: fatalEvent();
119: throw e;
120: }
121:
122: addStatement(stmt);
123:
124: if (_mConn.isWrapStatements())
125: return new UserStatement(this , stmt);
126: else
127: return stmt;
128: }
129:
130: /**
131: * JDBC api to create a new statement. Any SQL exception thrown here
132: * will make the connection invalid, i.e. it can't be put back into
133: * the pool.
134: *
135: * @return a new JDBC statement.
136: */
137: public Statement createStatement(int resultSetType,
138: int resultSetConcurrency) throws SQLException {
139: Statement stmt;
140:
141: Connection conn = getDriverConnection();
142:
143: try {
144: stmt = conn.createStatement(resultSetType,
145: resultSetConcurrency);
146: } catch (SQLException e) {
147: fatalEvent();
148: throw e;
149: }
150:
151: addStatement(stmt);
152:
153: if (_mConn.isWrapStatements())
154: return new UserStatement(this , stmt);
155: else
156: return stmt;
157: }
158:
159: /**
160: * Creates a statement.
161: */
162: public Statement createStatement(int resultSetType,
163: int resultSetConcurrency, int resultSetHoldability)
164: throws SQLException {
165: Statement stmt;
166:
167: Connection conn = getDriverConnection();
168:
169: try {
170: stmt = conn.createStatement(resultSetType,
171: resultSetConcurrency, resultSetHoldability);
172: } catch (SQLException e) {
173: fatalEvent();
174: throw e;
175: }
176:
177: addStatement(stmt);
178:
179: if (_mConn.isWrapStatements())
180: return new UserStatement(this , stmt);
181: else
182: return stmt;
183: }
184:
185: /**
186: * Returns a prepared statement with the given sql.
187: *
188: * @param sql the prepared sql.
189: */
190: public PreparedStatement prepareStatement(String sql)
191: throws SQLException {
192: PreparedStatement stmt;
193:
194: if (getDriverConnection() == null) {
195: fatalEvent();
196: throw new SQLException(
197: L
198: .l("can't create statement from closed connection."));
199: }
200:
201: try {
202: stmt = getMConn().prepareStatement(this , sql);
203: } catch (SQLException e) {
204: getMConn().fatalEvent(e);
205: throw e;
206: }
207:
208: addStatement(stmt);
209:
210: if (_mConn.isWrapStatements())
211: return new UserPreparedStatement(this , stmt);
212: else
213: return stmt;
214: }
215:
216: /**
217: * Returns a prepared statement with the given sql.
218: *
219: * @param sql the prepared sql.
220: */
221: public PreparedStatement prepareStatement(String sql,
222: int resultSetType, int resultSetConcurrency)
223: throws SQLException {
224: PreparedStatement stmt;
225:
226: if (getDriverConnection() == null) {
227: fatalEvent();
228: throw new SQLException(
229: L
230: .l("can't create statement from closed connection."));
231: }
232:
233: try {
234: stmt = getDriverConnection().prepareStatement(sql,
235: resultSetType, resultSetConcurrency);
236: } catch (SQLException e) {
237: fatalEvent();
238: throw e;
239: }
240:
241: addStatement(stmt);
242:
243: if (_mConn.isWrapStatements())
244: return new UserPreparedStatement(this , stmt);
245: else
246: return stmt;
247: }
248:
249: /**
250: * Returns a prepared statement with the given sql.
251: *
252: * @param sql the prepared sql.
253: */
254: public PreparedStatement prepareStatement(String sql,
255: int resultSetType, int resultSetConcurrency,
256: int resultSetHoldability) throws SQLException {
257: PreparedStatement stmt;
258:
259: if (getDriverConnection() == null) {
260: fatalEvent();
261: throw new SQLException(
262: L
263: .l("can't create statement from closed connection."));
264: }
265:
266: try {
267: stmt = getDriverConnection().prepareStatement(sql,
268: resultSetType, resultSetConcurrency,
269: resultSetHoldability);
270: } catch (SQLException e) {
271: fatalEvent();
272: throw e;
273: }
274:
275: addStatement(stmt);
276:
277: if (_mConn.isWrapStatements())
278: return new UserPreparedStatement(this , stmt);
279: else
280: return stmt;
281: }
282:
283: /**
284: * Returns a prepared statement with the given sql.
285: *
286: * @param sql the prepared sql.
287: */
288: public PreparedStatement prepareStatement(String sql,
289: int resultSetType) throws SQLException {
290: PreparedStatement stmt;
291:
292: if (getDriverConnection() == null) {
293: fatalEvent();
294: throw new SQLException(
295: L
296: .l("can't create statement from closed connection."));
297: }
298:
299: try {
300: stmt = getMConn()
301: .prepareStatement(this , sql, resultSetType);
302: } catch (SQLException e) {
303: fatalEvent();
304: throw e;
305: }
306:
307: addStatement(stmt);
308:
309: if (_mConn.isWrapStatements())
310: return new UserPreparedStatement(this , stmt);
311: else
312: return stmt;
313: }
314:
315: /**
316: * Returns a prepared statement with the given sql.
317: *
318: * @param sql the prepared sql.
319: */
320: public PreparedStatement prepareStatement(String sql,
321: int[] columnIndexes) throws SQLException {
322: PreparedStatement stmt;
323:
324: if (getDriverConnection() == null) {
325: fatalEvent();
326: throw new SQLException(
327: L
328: .l("can't create statement from closed connection."));
329: }
330:
331: try {
332: stmt = getDriverConnection().prepareStatement(sql,
333: columnIndexes);
334: } catch (SQLException e) {
335: fatalEvent();
336: throw e;
337: }
338:
339: addStatement(stmt);
340:
341: if (_mConn.isWrapStatements())
342: return new UserPreparedStatement(this , stmt);
343: else
344: return stmt;
345: }
346:
347: /**
348: * Returns a prepared statement with the given sql.
349: *
350: * @param sql the prepared sql.
351: */
352: public PreparedStatement prepareStatement(String sql,
353: String[] columnNames) throws SQLException {
354: PreparedStatement stmt;
355:
356: if (getDriverConnection() == null) {
357: fatalEvent();
358: throw new SQLException(
359: L
360: .l("can't create statement from closed connection."));
361: }
362:
363: try {
364: stmt = getDriverConnection().prepareStatement(sql,
365: columnNames);
366: } catch (SQLException e) {
367: fatalEvent();
368: throw e;
369: }
370:
371: addStatement(stmt);
372:
373: if (_mConn.isWrapStatements())
374: return new UserPreparedStatement(this , stmt);
375: else
376: return stmt;
377: }
378:
379: public CallableStatement prepareCall(String sql, int resultSetType,
380: int resultSetConcurrency) throws SQLException {
381: CallableStatement stmt;
382:
383: if (getDriverConnection() == null) {
384: fatalEvent();
385: throw new SQLException(
386: L
387: .l("can't create statement from closed connection."));
388: }
389:
390: try {
391: stmt = getDriverConnection().prepareCall(sql,
392: resultSetType, resultSetConcurrency);
393: } catch (SQLException e) {
394: fatalEvent();
395: throw e;
396: }
397:
398: addStatement(stmt);
399:
400: if (_mConn.isWrapStatements())
401: return new UserCallableStatement(this , stmt);
402: else
403: return stmt;
404: }
405:
406: public CallableStatement prepareCall(String sql)
407: throws SQLException {
408: CallableStatement stmt;
409:
410: if (getDriverConnection() == null) {
411: fatalEvent();
412: throw new SQLException(
413: L
414: .l("can't create statement from closed connection."));
415: }
416:
417: try {
418: stmt = getDriverConnection().prepareCall(sql);
419: } catch (SQLException e) {
420: fatalEvent();
421: throw e;
422: }
423:
424: addStatement(stmt);
425:
426: if (_mConn.isWrapStatements())
427: return new UserCallableStatement(this , stmt);
428: else
429: return stmt;
430: }
431:
432: public CallableStatement prepareCall(String sql, int resultSetType,
433: int resultSetConcurrency, int resultSetHoldability)
434: throws SQLException {
435: CallableStatement stmt;
436:
437: if (getDriverConnection() == null) {
438: fatalEvent();
439: throw new SQLException(
440: L
441: .l("can't create statement from closed connection."));
442: }
443:
444: try {
445: stmt = getDriverConnection().prepareCall(sql,
446: resultSetType, resultSetConcurrency,
447: resultSetHoldability);
448: } catch (SQLException e) {
449: fatalEvent();
450: throw e;
451: }
452:
453: addStatement(stmt);
454:
455: if (_mConn.isWrapStatements())
456: return new UserCallableStatement(this , stmt);
457: else
458: return stmt;
459: }
460:
461: /**
462: * JDBC api to return the connection's catalog.
463: *
464: * @return the JDBC catalog.
465: */
466: public String getCatalog() throws SQLException {
467: try {
468: return getDriverConnection().getCatalog();
469: } catch (SQLException e) {
470: fatalEvent();
471: throw e;
472: }
473: }
474:
475: /**
476: * Sets the JDBC catalog.
477: */
478: public void setCatalog(String catalog) throws SQLException {
479: getMConn().setCatalog(catalog);
480: }
481:
482: /**
483: * Gets the connection's metadata.
484: */
485: public DatabaseMetaData getMetaData() throws SQLException {
486: try {
487: return getDriverConnection().getMetaData();
488: } catch (SQLException e) {
489: fatalEvent();
490: throw e;
491: }
492: }
493:
494: /**
495: * Returns the connection's type map.
496: */
497: public Map getTypeMap() throws SQLException {
498: try {
499: return getDriverConnection().getTypeMap();
500: } catch (SQLException e) {
501: fatalEvent();
502: throw e;
503: }
504: }
505:
506: /**
507: * Sets the connection's type map.
508: */
509: public void setTypeMap(Map<String, Class<?>> map)
510: throws SQLException {
511: getMConn().setTypeMap(map);
512: }
513:
514: /**
515: * Calls the nativeSQL method for the connection.
516: */
517: public String nativeSQL(String sql) throws SQLException {
518: try {
519: return getDriverConnection().nativeSQL(sql);
520: } catch (SQLException e) {
521: fatalEvent();
522: throw e;
523: }
524: }
525:
526: public int getTransactionIsolation() throws SQLException {
527: try {
528: return getDriverConnection().getTransactionIsolation();
529: } catch (SQLException e) {
530: fatalEvent();
531: throw e;
532: }
533: }
534:
535: public void setTransactionIsolation(int isolation)
536: throws SQLException {
537: getMConn().setTransactionIsolation(isolation);
538: }
539:
540: public SQLWarning getWarnings() throws SQLException {
541: try {
542: Connection conn = getDriverConnection();
543:
544: if (conn != null)
545: return conn.getWarnings();
546: else
547: return null;
548: } catch (SQLException e) {
549: fatalEvent();
550: throw e;
551: }
552: }
553:
554: public void clearWarnings() throws SQLException {
555: try {
556: Connection conn = getDriverConnection();
557:
558: if (conn != null)
559: conn.clearWarnings();
560: } catch (SQLException e) {
561: fatalEvent();
562: throw e;
563: }
564: }
565:
566: public void setReadOnly(boolean readOnly) throws SQLException {
567: getMConn().setReadOnly(readOnly);
568: }
569:
570: public boolean isReadOnly() throws SQLException {
571: try {
572: return getDriverConnection().isReadOnly();
573: } catch (SQLException e) {
574: fatalEvent();
575: throw e;
576: }
577: }
578:
579: public boolean getAutoCommit() throws SQLException {
580: try {
581: return getDriverConnection().getAutoCommit();
582: } catch (SQLException e) {
583: fatalEvent();
584: throw e;
585: }
586: }
587:
588: public void setAutoCommit(boolean autoCommit) throws SQLException {
589: getMConn().setAutoCommit(autoCommit);
590: }
591:
592: public void commit() throws SQLException {
593: try {
594: Connection conn = getDriverConnection();
595:
596: if (conn != null)
597: conn.commit();
598: } catch (SQLException e) {
599: fatalEvent();
600: throw e;
601: }
602: }
603:
604: public void rollback() throws SQLException {
605: try {
606: Connection conn = getDriverConnection();
607:
608: if (conn != null)
609: conn.rollback();
610: } catch (SQLException e) {
611: fatalEvent();
612: throw e;
613: }
614: }
615:
616: /**
617: * Returns true if the connection is closed.
618: */
619: public boolean isClosed() throws SQLException {
620: try {
621: return _mConn == null || getDriverConnection() == null
622: || getDriverConnection().isClosed();
623: } catch (SQLException e) {
624: log.log(Level.FINER, e.toString(), e);
625:
626: return true;
627: }
628: }
629:
630: /**
631: * Reset the connection and return the underlying JDBC connection to
632: * the pool.
633: */
634: public void close() throws SQLException {
635: ManagedConnectionImpl mConn;
636:
637: synchronized (this ) {
638: mConn = _mConn;
639: _mConn = null;
640:
641: if (mConn == null)
642: return;
643: }
644:
645: try {
646: // Clean up the connection and put it back in the pool
647: resetConnection(mConn);
648:
649: } catch (Throwable e) {
650: mConn.fatalEvent();
651:
652: log.log(Level.WARNING, e.toString(), e);
653: /*
654: if (e instanceof SQLException)
655: throw (SQLException) e;
656: else if (e instanceof RuntimeException)
657: throw (RuntimeException) e;
658: else
659: throw new SQLExceptionWrapper(e);
660: */
661: } finally {
662: mConn.closeEvent(this );
663: }
664: }
665:
666: /**
667: * Adds the statement to a list, so they can be automatically closed
668: * when the connection returns to the pool.
669: */
670: private void addStatement(Statement stmt) {
671: if (_statement == null)
672: _statement = stmt;
673: else if (_statements != null)
674: _statements.add(stmt);
675: else {
676: _statements = new ArrayList<Statement>();
677: _statements.add(stmt);
678: }
679: }
680:
681: public void setHoldability(int hold) throws SQLException {
682: getDriverConnection().setHoldability(hold);
683: }
684:
685: public int getHoldability() throws SQLException {
686: return getDriverConnection().getHoldability();
687: }
688:
689: public Savepoint setSavepoint() throws SQLException {
690: return getDriverConnection().setSavepoint();
691: }
692:
693: public Savepoint setSavepoint(String name) throws SQLException {
694: return getDriverConnection().setSavepoint(name);
695: }
696:
697: public void releaseSavepoint(Savepoint savepoint)
698: throws SQLException {
699: getDriverConnection().releaseSavepoint(savepoint);
700: }
701:
702: public void rollback(Savepoint savepoint) throws SQLException {
703: getDriverConnection().rollback(savepoint);
704: }
705:
706: /**
707: * Resets the underlying connection to its initial state and closes
708: * any open statements.
709: */
710: private void resetConnection(ManagedConnectionImpl mConn) {
711: closeStatements(mConn);
712: }
713:
714: /**
715: * Closes a single statement.
716: */
717: void closeStatement(Statement stmt) {
718: if (_statement == stmt)
719: _statement = null;
720: else if (_statements != null)
721: _statements.remove(stmt);
722: }
723:
724: /**
725: * Closes the connection's statements.
726: */
727: private void closeStatements(ManagedConnectionImpl mConn) {
728: ArrayList<Statement> statements = _statements;
729: _statements = null;
730:
731: Statement stmt = _statement;
732: _statement = null;
733:
734: try {
735: if (stmt != null)
736: stmt.close();
737: } catch (Throwable e) {
738: log.log(Level.FINE, e.toString(), e);
739:
740: // Can't set fatalEvent because Sybase throws an exception
741: // if statements are closed twice
742: // fatalEvent();
743: }
744:
745: for (int i = 0; statements != null && i < statements.size(); i++) {
746: try {
747: stmt = statements.get(i);
748:
749: if (stmt != null)
750: stmt.close();
751: } catch (Throwable e) {
752: log.log(Level.FINE, e.toString(), e);
753:
754: // Can't set fatalEvent because Sybase throws an exception
755: // if statements are closed twice
756: // fatalEvent();
757: }
758: }
759: }
760:
761: /**
762: * Returns the underlying connection.
763: */
764: public Connection getDriverConnection() throws SQLException {
765: ManagedConnectionImpl mConn = getMConn();
766:
767: if (mConn == null)
768: throw new IllegalStateException(
769: L
770: .l("Cannot use closed connection. Check max-active-time and review application code. "));
771:
772: Connection conn = mConn.getDriverConnection();
773:
774: return conn;
775: }
776:
777: /**
778: * Returns the underlying connection.
779: */
780: public ManagedConnectionImpl getMConn() {
781: ManagedConnectionImpl mConn = _mConn;
782:
783: if (mConn == null)
784: throw new IllegalStateException("connection is closed");
785:
786: return mConn;
787: }
788:
789: /**
790: * Marks the connection as error.
791: */
792: public void discardConnection() {
793: fatalEvent();
794: }
795:
796: /**
797: * Returns the underlying connection.
798: */
799: private void fatalEvent() {
800: ManagedConnectionImpl mConn = _mConn;
801:
802: if (mConn != null)
803: mConn.fatalEvent();
804: }
805:
806: /**
807: * Returns the underlying connection.
808: */
809: private void fatalEvent(SQLException exn) {
810: ManagedConnectionImpl mConn = _mConn;
811:
812: if (mConn != null)
813: mConn.fatalEvent(exn);
814: }
815:
816: /**
817: * Returns the underlying connection.
818: */
819: void killPool() {
820: ManagedConnectionImpl mConn = _mConn;
821:
822: if (mConn != null)
823: mConn.killPool();
824: }
825:
826: protected void finalize() throws Exception {
827: close();
828: }
829:
830: public String toString() {
831: return "UserConnection[" + _mConn + "]";
832: }
833:
834: public Clob createClob() throws SQLException {
835: throw new UnsupportedOperationException("Not supported yet.");
836: }
837:
838: public Blob createBlob() throws SQLException {
839: throw new UnsupportedOperationException("Not supported yet.");
840: }
841:
842: public NClob createNClob() throws SQLException {
843: throw new UnsupportedOperationException("Not supported yet.");
844: }
845:
846: public SQLXML createSQLXML() throws SQLException {
847: throw new UnsupportedOperationException("Not supported yet.");
848: }
849:
850: public boolean isValid(int timeout) throws SQLException {
851: throw new UnsupportedOperationException("Not supported yet.");
852: }
853:
854: public void setClientInfo(String name, String value)
855: throws SQLClientInfoException {
856: throw new UnsupportedOperationException("Not supported yet.");
857: }
858:
859: public void setClientInfo(Properties properties)
860: throws SQLClientInfoException {
861: throw new UnsupportedOperationException("Not supported yet.");
862: }
863:
864: public String getClientInfo(String name) throws SQLException {
865: throw new UnsupportedOperationException("Not supported yet.");
866: }
867:
868: public Properties getClientInfo() throws SQLException {
869: throw new UnsupportedOperationException("Not supported yet.");
870: }
871:
872: public Array createArrayOf(String typeName, Object[] elements)
873: throws SQLException {
874: throw new UnsupportedOperationException("Not supported yet.");
875: }
876:
877: public Struct createStruct(String typeName, Object[] attributes)
878: throws SQLException {
879: throw new UnsupportedOperationException("Not supported yet.");
880: }
881:
882: public <T> T unwrap(Class<T> iface) throws SQLException {
883: return (T) getConnection();
884: }
885:
886: public boolean isWrapperFor(Class<?> iface) throws SQLException {
887: return iface.isAssignableFrom(getConnection().getClass());
888: }
889: }
|