001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc.conn;
012:
013: import com.versant.core.logging.LogEventStore;
014: import com.versant.core.jdbc.logging.JdbcConnectionEvent;
015: import com.versant.core.jdbc.logging.JdbcPsPoolEvent;
016: import com.versant.core.common.Debug;
017:
018: import java.sql.*;
019:
020: /**
021: * A JDBC connection wrapped for event logging with a PreparedStatement cache.
022: * These concerns should really be separated but we have not had time to do
023: * that yet.
024: */
025: public class LoggingConnection implements Connection {
026:
027: private final Connection con;
028: private final LogEventStore pes;
029: private final PreparedStatementPool psPool;
030: private final boolean clearBatch;
031: private boolean needsValidation; // set for Exception's on us
032:
033: public LoggingConnection(Connection con, LogEventStore pes,
034: boolean usePsPool, int psCacheMax, boolean clearBatch) {
035: this .clearBatch = clearBatch;
036: this .con = con;
037: this .pes = pes;
038: if (usePsPool) {
039: psPool = new PreparedStatementPool(this , psCacheMax);
040: } else {
041: psPool = null;
042: }
043: }
044:
045: public PreparedStatementPool getPsPool() {
046: return psPool;
047: }
048:
049: public void setNeedsValidation(boolean needsValidation) {
050: this .needsValidation = needsValidation;
051: }
052:
053: public boolean isNeedsValidation() {
054: return needsValidation;
055: }
056:
057: public java.sql.Connection getCon() {
058: return con;
059: }
060:
061: /**
062: * Return ps to the pool.
063: *
064: * @see PooledPreparedStatement#close
065: */
066: public void returnPreparedStatement(PooledPreparedStatement ps)
067: throws SQLException {
068: try {
069: if (clearBatch)
070: ps.clearBatch();
071: psPool.returnPS(ps);
072: if (pes.isFiner()) {
073: logPsPoolEvent(ps.getKey(),
074: JdbcConnectionEvent.PSPOOL_RELEASE, ps);
075: }
076: } catch (Exception e) {
077: throw convertException(e);
078: }
079: }
080:
081: /**
082: * This is just going to return the connection to the pool.
083: */
084: public void close() throws SQLException {
085: closeRealConnection();
086: }
087:
088: public void setHoldability(int holdability) throws SQLException {
089: con.setHoldability(holdability);
090: }
091:
092: public int getHoldability() throws SQLException {
093: return con.getHoldability();
094: }
095:
096: public Savepoint setSavepoint() throws SQLException {
097: return con.setSavepoint();
098: }
099:
100: public Savepoint setSavepoint(String name) throws SQLException {
101: return con.setSavepoint(name);
102: }
103:
104: public void rollback(Savepoint savepoint) throws SQLException {
105: con.rollback(savepoint);
106: }
107:
108: public void releaseSavepoint(Savepoint savepoint)
109: throws SQLException {
110: con.releaseSavepoint(savepoint);
111: }
112:
113: public Statement createStatement(int resultSetType,
114: int resultSetConcurrency, int resultSetHoldability)
115: throws SQLException {
116: return con.createStatement(resultSetType, resultSetConcurrency,
117: resultSetHoldability);
118: }
119:
120: public PreparedStatement prepareStatement(String sql,
121: int resultSetType, int resultSetConcurrency,
122: int resultSetHoldability) throws SQLException {
123: return con.prepareStatement(sql, resultSetType,
124: resultSetConcurrency, resultSetHoldability);
125: }
126:
127: public CallableStatement prepareCall(String sql, int resultSetType,
128: int resultSetConcurrency, int resultSetHoldability)
129: throws SQLException {
130: return con.prepareCall(sql, resultSetType,
131: resultSetConcurrency, resultSetHoldability);
132: }
133:
134: public PreparedStatement prepareStatement(String sql,
135: int autoGeneratedKeys) throws SQLException {
136: return con.prepareStatement(sql, autoGeneratedKeys);
137: }
138:
139: public PreparedStatement prepareStatement(String sql,
140: int columnIndexes[]) throws SQLException {
141: return con.prepareStatement(sql, columnIndexes);
142: }
143:
144: public PreparedStatement prepareStatement(String sql,
145: String columnNames[]) throws SQLException {
146: return con.prepareStatement(sql, columnNames);
147: }
148:
149: public String nativeSQL(String sql) throws SQLException {
150: return con.nativeSQL(sql);
151: }
152:
153: public boolean getAutoCommit() throws SQLException {
154: return con.getAutoCommit();
155: }
156:
157: public boolean isClosed() throws SQLException {
158: return con.isClosed();
159: }
160:
161: public DatabaseMetaData getMetaData() throws SQLException {
162: return con.getMetaData();
163: }
164:
165: public void setReadOnly(boolean readOnly) throws SQLException {
166: con.setReadOnly(readOnly);
167: }
168:
169: public boolean isReadOnly() throws SQLException {
170: return con.isReadOnly();
171: }
172:
173: public void setCatalog(String catalog) throws SQLException {
174: con.setCatalog(catalog);
175: }
176:
177: public String getCatalog() throws SQLException {
178: return con.getCatalog();
179: }
180:
181: public void setTransactionIsolation(int level) throws SQLException {
182: JdbcConnectionEvent ev = null;
183: if (pes.isFiner()) {
184: ev = new JdbcConnectionEvent(0, this ,
185: toIsolationStr(level),
186: JdbcConnectionEvent.CON_ISOLATION);
187: pes.log(ev);
188: }
189: try {
190: con.setTransactionIsolation(level);
191: } catch (SQLException e) {
192: needsValidation = true;
193: if (ev != null)
194: ev.setErrorMsg(e);
195: throw e;
196: } catch (RuntimeException e) {
197: needsValidation = true;
198: if (ev != null)
199: ev.setErrorMsg(e);
200: throw e;
201: } finally {
202: if (ev != null)
203: ev.updateTotalMs();
204: }
205: }
206:
207: private static String toIsolationStr(int lvl) {
208: switch (lvl) {
209: case TRANSACTION_NONE:
210: return "NONE(0)";
211: case TRANSACTION_READ_UNCOMMITTED:
212: return "READ_UNCOMMITTED(1)";
213: case TRANSACTION_READ_COMMITTED:
214: return "READ_COMMITTED(2)";
215: case TRANSACTION_REPEATABLE_READ:
216: return "REPEATABLE_READ(4)";
217: case TRANSACTION_SERIALIZABLE:
218: return "SERIALIZABLE(8)";
219: }
220: return "UNKNOWN(" + lvl + ")";
221: }
222:
223: public int getTransactionIsolation() throws SQLException {
224: return con.getTransactionIsolation();
225: }
226:
227: public SQLWarning getWarnings() throws SQLException {
228: return con.getWarnings();
229: }
230:
231: public void clearWarnings() throws SQLException {
232: con.clearWarnings();
233: }
234:
235: public Statement createStatement(int resultSetType,
236: int resultSetConcurrency) throws SQLException {
237: return con.createStatement(resultSetType, resultSetConcurrency);
238: }
239:
240: public CallableStatement prepareCall(String sql, int resultSetType,
241: int resultSetConcurrency) throws SQLException {
242: return con
243: .prepareCall(sql, resultSetType, resultSetConcurrency);
244: }
245:
246: public java.util.Map getTypeMap() throws SQLException {
247: return con.getTypeMap();
248: }
249:
250: public void setTypeMap(java.util.Map map) throws SQLException {
251: con.setTypeMap(map);
252: }
253:
254: public Statement createStatement() throws SQLException {
255: if (!pes.isFine())
256: return con.createStatement();
257: JdbcConnectionEvent ev = null;
258: if (pes.isFiner()) {
259: ev = new JdbcConnectionEvent(0, this , null,
260: JdbcConnectionEvent.CON_CREATE_STATEMENT);
261: pes.log(ev);
262: }
263: try {
264: Statement stat = new LoggingStatement(this , con
265: .createStatement(), pes);
266: if (ev != null)
267: ev.updateStatementID(stat);
268: return stat;
269: } catch (SQLException e) {
270: needsValidation = true;
271: if (ev != null)
272: ev.setErrorMsg(e);
273: throw e;
274: } catch (RuntimeException e) {
275: needsValidation = true;
276: if (ev != null)
277: ev.setErrorMsg(e);
278: throw e;
279: } finally {
280: if (ev != null)
281: ev.updateTotalMs();
282: }
283: }
284:
285: public PreparedStatement prepareStatement(String sql)
286: throws SQLException {
287: if (psPool == null) {
288: return prepareStatementImp(sql, 0, 0, null);
289: } else {
290: try {
291: PreparedStatementPool.Key key = new PreparedStatementPool.Key(
292: sql);
293: PooledPreparedStatement ps = psPool.borrowPS(key);
294: if (pes.isFiner()) {
295: logPsPoolEvent(key,
296: JdbcConnectionEvent.PSPOOL_ALLOC, ps);
297: }
298: return ps;
299: } catch (Exception e) {
300: throw convertException(e);
301: }
302: }
303: }
304:
305: private void logPsPoolEvent(PreparedStatementPool.Key key,
306: int type, PreparedStatement ps) {
307: JdbcPsPoolEvent ev = new JdbcPsPoolEvent(0, this , key.getSql(),
308: type, psPool.getNumActive(), psPool.getNumIdle());
309: ev.zeroTotalMs();
310: ev.setResultSetType(key.getResultSetType());
311: ev.setResultSetConcurrency(key.getResultSetConcurrency());
312: ev.setStatementID(System.identityHashCode(ps));
313: pes.log(ev);
314: }
315:
316: private SQLException convertException(Exception x) {
317: if (x instanceof SQLException)
318: return (SQLException) x;
319: if (Debug.DEBUG)
320: x.printStackTrace(System.out);
321: return new SQLException(x.getClass().getName() + ": "
322: + x.getMessage());
323: }
324:
325: public PreparedStatement prepareStatement(String sql,
326: int resultSetType, int resultSetConcurrency)
327: throws SQLException {
328: if (psPool == null) {
329: return prepareStatementImp(sql, resultSetType,
330: resultSetConcurrency, null);
331: } else {
332: try {
333: PreparedStatementPool.Key key = new PreparedStatementPool.Key(
334: sql, resultSetType, resultSetConcurrency);
335: PooledPreparedStatement ps = psPool.borrowPS(key);
336: if (pes.isFiner()) {
337: logPsPoolEvent(key,
338: JdbcConnectionEvent.PSPOOL_ALLOC, ps);
339: }
340: return ps;
341: } catch (Exception e) {
342: throw convertException(e);
343: }
344: }
345: }
346:
347: /**
348: * Create a new ps. This will not use the PS pool.
349: */
350: public PooledPreparedStatement prepareStatementImp(String sql,
351: int resultSetType, int resultSetConcurrency,
352: PreparedStatementPool.Key key) throws SQLException {
353: JdbcConnectionEvent ev = null;
354: if (pes.isFiner()) {
355: ev = new JdbcConnectionEvent(0, this , sql,
356: JdbcConnectionEvent.CON_PREPARE_STATEMENT);
357: ev.setResultSetType(resultSetType);
358: ev.setResultSetConcurrency(resultSetConcurrency);
359: pes.log(ev);
360: }
361: try {
362: PreparedStatement stat;
363: if (resultSetType != 0) {
364: stat = con.prepareStatement(sql, resultSetType,
365: resultSetConcurrency);
366: } else {
367: stat = con.prepareStatement(sql);
368: }
369: PooledPreparedStatement ps;
370: if (pes.isFiner()) {
371: ps = new PooledPSWithParamLogging(this , stat, pes, sql,
372: key);
373: } else {
374: ps = new PooledPreparedStatement(this , stat, pes, sql,
375: key);
376: }
377: if (ev != null)
378: ev.updateStatementID(ps);
379: return ps;
380: } catch (SQLException e) {
381: needsValidation = true;
382: if (ev != null)
383: ev.setErrorMsg(e);
384: throw e;
385: } catch (RuntimeException e) {
386: needsValidation = true;
387: if (ev != null)
388: ev.setErrorMsg(e);
389: throw e;
390: } finally {
391: if (ev != null)
392: ev.updateTotalMs();
393: }
394: }
395:
396: public CallableStatement prepareCall(String sql)
397: throws SQLException {
398: if (!pes.isFine())
399: return con.prepareCall(sql);
400: JdbcConnectionEvent ev = null;
401: if (pes.isFiner()) {
402: ev = new JdbcConnectionEvent(0, this , sql,
403: JdbcConnectionEvent.CON_PREPARE_CALL);
404: pes.log(ev);
405: }
406: try {
407: CallableStatement stat = new LoggingCallableStatement(this ,
408: con.prepareCall(sql), pes);
409: if (ev != null)
410: ev.updateStatementID(stat);
411: return stat;
412: } catch (SQLException e) {
413: needsValidation = true;
414: if (ev != null)
415: ev.setErrorMsg(e);
416: throw e;
417: } catch (RuntimeException e) {
418: needsValidation = true;
419: if (ev != null)
420: ev.setErrorMsg(e);
421: throw e;
422: } finally {
423: if (ev != null)
424: ev.updateTotalMs();
425: }
426: }
427:
428: public void setAutoCommit(boolean autoCommit) throws SQLException {
429: JdbcConnectionEvent ev = null;
430: if (pes.isFiner()) {
431: ev = new JdbcConnectionEvent(0, this , autoCommit ? "true"
432: : "false", JdbcConnectionEvent.CON_AUTOCOMMIT);
433: pes.log(ev);
434: }
435: try {
436: con.setAutoCommit(autoCommit);
437: } catch (SQLException e) {
438: needsValidation = true;
439: if (ev != null)
440: ev.setErrorMsg(e);
441: throw e;
442: } catch (RuntimeException e) {
443: needsValidation = true;
444: if (ev != null)
445: ev.setErrorMsg(e);
446: throw e;
447: } finally {
448: if (ev != null)
449: ev.updateTotalMs();
450: }
451: }
452:
453: public void commit() throws SQLException {
454: JdbcConnectionEvent ev = null;
455: if (pes.isFine()) {
456: ev = new JdbcConnectionEvent(0, this , null,
457: JdbcConnectionEvent.CON_COMMIT);
458: pes.log(ev);
459: }
460: try {
461: con.commit();
462: } catch (SQLException e) {
463: needsValidation = true;
464: if (ev != null)
465: ev.setErrorMsg(e);
466: throw e;
467: } catch (RuntimeException e) {
468: needsValidation = true;
469: if (ev != null)
470: ev.setErrorMsg(e);
471: throw e;
472: } finally {
473: if (ev != null)
474: ev.updateTotalMs();
475: }
476: }
477:
478: public void rollback() throws SQLException {
479: JdbcConnectionEvent ev = null;
480: if (pes.isFine()) {
481: ev = new JdbcConnectionEvent(0, this , null,
482: JdbcConnectionEvent.CON_ROLLBACK);
483: pes.log(ev);
484: }
485: try {
486: con.rollback();
487: } catch (SQLException e) {
488: needsValidation = true;
489: if (ev != null)
490: ev.setErrorMsg(e);
491: throw e;
492: } catch (RuntimeException e) {
493: needsValidation = true;
494: if (ev != null)
495: ev.setErrorMsg(e);
496: throw e;
497: } finally {
498: if (ev != null)
499: ev.updateTotalMs();
500: }
501: }
502:
503: /**
504: * Realy close this connection i.e. do not return it to the pool.
505: */
506: public void closeRealConnection() throws SQLException {
507: if (!pes.isFine()) {
508: con.close();
509: return;
510: }
511: JdbcConnectionEvent ev = new JdbcConnectionEvent(0, this , null,
512: JdbcConnectionEvent.CON_CLOSE);
513: pes.log(ev);
514: try {
515: con.close();
516: } catch (SQLException e) {
517: ev.setErrorMsg(e);
518: throw e;
519: } catch (RuntimeException e) {
520: ev.setErrorMsg(e);
521: throw e;
522: } finally {
523: ev.updateTotalMs();
524: }
525: }
526:
527: }
|