001: /*
002: * Licensed under the X license (see http://www.x.org/terms.htm)
003: */
004: package org.ofbiz.minerva.pool.jdbc.xa.wrapper;
005:
006: import java.io.ByteArrayOutputStream;
007: import java.io.PrintStream;
008: import java.sql.CallableStatement;
009: import java.sql.Connection;
010: import java.sql.DatabaseMetaData;
011: import java.sql.PreparedStatement;
012: import java.sql.SQLException;
013: import java.sql.SQLWarning;
014: import java.sql.Savepoint;
015: import java.sql.Statement;
016: import java.util.Collection;
017: import java.util.HashSet;
018: import java.util.Iterator;
019: import java.util.Map;
020: import java.util.Vector;
021:
022: import org.ofbiz.minerva.pool.PoolEvent;
023: import org.ofbiz.minerva.pool.cache.LeastRecentlyUsedCache;
024: import org.ofbiz.minerva.pool.cache.ObjectCache;
025: import org.ofbiz.minerva.pool.jdbc.ConnectionInPool;
026: import org.ofbiz.minerva.pool.jdbc.ConnectionWrapper;
027: import org.ofbiz.minerva.pool.jdbc.PreparedStatementFactory;
028: import org.ofbiz.minerva.pool.jdbc.PreparedStatementInPool;
029: import org.ofbiz.minerva.pool.jdbc.StatementInPool;
030:
031: import org.apache.log4j.Logger;
032:
033: /**
034: * Wrapper for database connections used by an XAConnection. When close is
035: * called, it does not close the underlying connection, just informs the
036: * XAConnection that close was called. The connection will not be closed (or
037: * returned to the pool) until the transactional details are taken care of.
038: * This instance only lives as long as one client is using it - though we
039: * probably want to consider reusing it to save object allocations.
040: *
041: * @author Aaron Mulder (ammulder@alumni.princeton.edu)
042: */
043: public class XAClientConnection implements ConnectionWrapper {
044:
045: private final static String CLOSED = "Connection has been closed!";
046:
047: private Connection con;
048: private HashSet statements;
049: private Vector listeners;
050: private XAConnectionImpl xaCon;
051: private int preparedStatementCacheSize = 0;
052: private ObjectCache preparedStatementCache;
053: private String stackTrace = null;
054: private static Logger log = Logger
055: .getLogger(XADataSourceImpl.class);
056:
057: /**
058: * Creates a new connection wrapper.
059: * @param xaCon The handler for all the transactional details.
060: * @param con The "real" database connection to wrap.
061: */
062: public XAClientConnection(XAConnectionImpl xaCon, Connection con,
063: boolean saveStackTrace) {
064: this .con = con;
065: this .xaCon = xaCon;
066: preparedStatementCache = (ObjectCache) ConnectionInPool.psCaches
067: .get(con);
068: if (preparedStatementCache == null) {
069: PreparedStatementFactory factory = new PreparedStatementFactory(
070: con);
071: preparedStatementCache = new LeastRecentlyUsedCache(
072: factory, preparedStatementCacheSize);
073: ConnectionInPool.psCaches.put(con, preparedStatementCache);
074: }
075: statements = new HashSet();
076: listeners = new Vector();
077: if (saveStackTrace) {
078: try {
079: ByteArrayOutputStream baos = new ByteArrayOutputStream();
080: PrintStream stream = new PrintStream(baos);
081: new Throwable().printStackTrace(stream);
082: baos.close();
083: stackTrace = baos.toString();
084: } catch (Exception ex) {
085: }
086: }
087: }
088:
089: /**
090: * Sets the number of PreparedStatements to be cached for each
091: * Connection. Your DB product may impose a limit on the number
092: * of open PreparedStatements.
093: */
094: public void setPSCacheSize(int maxSize) {
095: preparedStatementCacheSize = maxSize;
096: if (preparedStatementCache != null) {
097: preparedStatementCache.setSize(maxSize);
098: }
099: }
100:
101: /**
102: * Gets the number of PreparedStatements to be cached for each
103: * Connection.
104: */
105: public int getPSCacheSize() {
106: return preparedStatementCacheSize;
107: }
108:
109: /**
110: * Gets a reference to the "real" connection. This should only be used if
111: * you need to cast that to a specific type to call a proprietary method -
112: * you will defeat all the pooling if you use the underlying connection
113: * directly.
114: */
115: public Connection getUnderlyingConnection() {
116: return con;
117: }
118:
119: /**
120: * Closes this connection wrapper permanently. All further calls with throw
121: * a SQLException.
122: */
123: public void shutdown() {
124: con = null;
125: statements = null;
126: listeners = null;
127: xaCon = null;
128: }
129:
130: /**
131: * Updates the last used time for this connection to the current time.
132: * This is not used by the current implementation.
133: */
134: public void setLastUsed() {
135: xaCon
136: .firePoolEvent(new PoolEvent(xaCon,
137: PoolEvent.OBJECT_USED));
138: }
139:
140: /**
141: * Indicates that an error occured on this connection.
142: */
143: public void setError(SQLException e) {
144: xaCon.setConnectionError(e);
145: }
146:
147: /**
148: * Indicates that a statement has been closed and no longer needs to be
149: * tracked. Outstanding statements are closed when the connection is
150: * returned to the pool.
151: */
152: public void statementClosed(Statement st) {
153: statements.remove(st);
154: if ((con != null) && (st instanceof PreparedStatementInPool)
155: && preparedStatementCacheSize != 0) {
156:
157: // Now return the "real" statement to the pool
158: PreparedStatementInPool ps = (PreparedStatementInPool) st;
159: PreparedStatement ups = ps.getUnderlyingPreparedStatement();
160: preparedStatementCache.returnObject(ps.getSql(), ups);
161: /*
162: int rsType = ResultSet.TYPE_FORWARD_ONLY;
163: int rsConcur = ResultSet.CONCUR_READ_ONLY;
164:
165: // We may have JDBC 1.0 driver
166: try {
167: rsType = ups.getResultSetType();
168: rsConcur = ups.getResultSetConcurrency();
169: } catch (Throwable th) {
170: }
171: PreparedStatementInPool.preparedStatementCache.put(
172: new PSCacheKey(con, ps.getSql(), rsType, rsConcur), ups);
173: */
174: }
175: }
176:
177: // ---- Implementation of java.sql.Connection ----
178: public Statement createStatement() throws SQLException {
179: if (con == null)
180: throw new SQLException(CLOSED);
181: try {
182: StatementInPool st = new StatementInPool(con
183: .createStatement(), this );
184: statements.add(st);
185: return st;
186: } catch (SQLException e) {
187: setError(e);
188: throw e;
189: }
190: }
191:
192: public PreparedStatement prepareStatement(String sql)
193: throws SQLException {
194: if (con == null)
195: throw new SQLException(CLOSED);
196: try {
197: PreparedStatement ps;
198: if (preparedStatementCacheSize == 0) {
199: // cache disabled
200: ps = con.prepareStatement(sql);
201: } else {
202: ps = (PreparedStatement) preparedStatementCache
203: .useObject(sql);
204: }
205: if (ps == null)
206: throw new SQLException(
207: "Unable to create PreparedStatement!");
208: PreparedStatementInPool wrapper = new PreparedStatementInPool(
209: ps, this , sql);
210: statements.add(wrapper);
211: return wrapper;
212: } catch (SQLException e) {
213: setError(e);
214: throw e;
215: }
216: }
217:
218: public CallableStatement prepareCall(String sql)
219: throws SQLException {
220: if (con == null)
221: throw new SQLException(CLOSED);
222: try {
223: return con.prepareCall(sql);
224: } catch (SQLException e) {
225: setError(e);
226: throw e;
227: }
228: }
229:
230: public String nativeSQL(String sql) throws SQLException {
231: if (con == null)
232: throw new SQLException(CLOSED);
233: try {
234: return con.nativeSQL(sql);
235: } catch (SQLException e) {
236: setError(e);
237: throw e;
238: }
239: }
240:
241: public void setAutoCommit(boolean autoCommit) throws SQLException {
242: if (con == null)
243: throw new SQLException(CLOSED);
244: if (((XAResourceImpl) xaCon.getXAResource()).isTransaction()
245: && autoCommit)
246: throw new SQLException(
247: "Cannot set AutoCommit for a transactional connection: See JDBC 2.0 Optional Package Specification section 7.1 (p25)");
248:
249: try {
250: con.setAutoCommit(autoCommit);
251: } catch (SQLException e) {
252: setError(e);
253: throw e;
254: }
255:
256: }
257:
258: public boolean getAutoCommit() throws SQLException {
259: if (con == null)
260: throw new SQLException(CLOSED);
261: try {
262: return con.getAutoCommit();
263: } catch (SQLException e) {
264: setError(e);
265: throw e;
266: }
267: }
268:
269: public void commit() throws SQLException {
270: if (con == null)
271: throw new SQLException(CLOSED);
272: if (((XAResourceImpl) xaCon.getXAResource()).isTransaction())
273: throw new SQLException(
274: "Cannot commit a transactional connection: See JDBC 2.0 Optional Package Specification section 7.1 (p25)");
275: try {
276: con.commit();
277: } catch (SQLException e) {
278: setError(e);
279: throw e;
280: }
281: }
282:
283: public void rollback() throws SQLException {
284: if (con == null)
285: throw new SQLException(CLOSED);
286: if (((XAResourceImpl) xaCon.getXAResource()).isTransaction())
287: throw new SQLException(
288: "Cannot rollback a transactional connection: See JDBC 2.0 Optional Package Specification section 7.1 (p25)");
289: }
290:
291: public void forcedClose() throws SQLException {
292: if (stackTrace != null)
293: System.err
294: .println("A forced close because a non-closed connection:\n"
295: + stackTrace);
296: if (con == null)
297: throw new SQLException(CLOSED);
298: Collection copy = (Collection) statements.clone();
299: Iterator it = copy.iterator();
300: while (it.hasNext())
301: try {
302: ((Statement) it.next()).close();
303: } catch (SQLException e) {
304: }
305: shutdown();
306: }
307:
308: public void close() throws SQLException {
309: if (con == null)
310: throw new SQLException(CLOSED);
311: Collection copy = (Collection) statements.clone();
312: Iterator it = copy.iterator();
313: while (it.hasNext())
314: try {
315: ((Statement) it.next()).close();
316: } catch (SQLException e) {
317: log.warn("SQLException : ", e);
318: }
319:
320: xaCon.clientConnectionClosed(this );
321: shutdown();
322: }
323:
324: public boolean isClosed() throws SQLException {
325: if (con == null)
326: return true;
327: try {
328: return con.isClosed();
329: } catch (SQLException e) {
330: setError(e);
331: throw e;
332: }
333: }
334:
335: public DatabaseMetaData getMetaData() throws SQLException {
336: if (con == null)
337: throw new SQLException(CLOSED);
338: try {
339: return con.getMetaData();
340: } catch (SQLException e) {
341: setError(e);
342: throw e;
343: }
344: }
345:
346: public void setReadOnly(boolean readOnly) throws SQLException {
347: if (con == null)
348: throw new SQLException(CLOSED);
349: try {
350: con.setReadOnly(readOnly);
351: } catch (SQLException e) {
352: setError(e);
353: throw e;
354: }
355: }
356:
357: public boolean isReadOnly() throws SQLException {
358: if (con == null)
359: throw new SQLException(CLOSED);
360: try {
361: return con.isReadOnly();
362: } catch (SQLException e) {
363: setError(e);
364: throw e;
365: }
366: }
367:
368: public void setCatalog(String catalog) throws SQLException {
369: if (con == null)
370: throw new SQLException(CLOSED);
371: try {
372: con.setCatalog(catalog);
373: } catch (SQLException e) {
374: setError(e);
375: throw e;
376: }
377: }
378:
379: public String getCatalog() throws SQLException {
380: if (con == null)
381: throw new SQLException(CLOSED);
382: try {
383: return con.getCatalog();
384: } catch (SQLException e) {
385: setError(e);
386: throw e;
387: }
388: }
389:
390: public void setTransactionIsolation(int level) throws SQLException {
391: if (con == null)
392: throw new SQLException(CLOSED);
393: try {
394: con.setTransactionIsolation(level);
395: } catch (SQLException e) {
396: setError(e);
397: throw e;
398: }
399: }
400:
401: public int getTransactionIsolation() throws SQLException {
402: if (con == null)
403: throw new SQLException(CLOSED);
404: try {
405: return con.getTransactionIsolation();
406: } catch (SQLException e) {
407: setError(e);
408: throw e;
409: }
410: }
411:
412: public SQLWarning getWarnings() throws SQLException {
413: if (con == null)
414: throw new SQLException(CLOSED);
415: try {
416: return con.getWarnings();
417: } catch (SQLException e) {
418: setError(e);
419: throw e;
420: }
421: }
422:
423: public void clearWarnings() throws SQLException {
424: if (con == null)
425: throw new SQLException(CLOSED);
426: try {
427: con.clearWarnings();
428: } catch (SQLException e) {
429: setError(e);
430: throw e;
431: }
432: }
433:
434: public Statement createStatement(int resultSetType,
435: int resultSetConcurrency) throws SQLException {
436: if (con == null)
437: throw new SQLException(CLOSED);
438: try {
439: StatementInPool st = new StatementInPool(con
440: .createStatement(resultSetType,
441: resultSetConcurrency), this );
442: statements.add(st);
443: return st;
444: } catch (SQLException e) {
445: setError(e);
446: throw e;
447: }
448: }
449:
450: public PreparedStatement prepareStatement(String sql,
451: int resultSetType, int resultSetConcurrency)
452: throws SQLException {
453: if (con == null)
454: throw new SQLException(CLOSED);
455: try {
456: return con.prepareStatement(sql, resultSetType,
457: resultSetConcurrency);
458: } catch (SQLException e) {
459: setError(e);
460: throw e;
461: }
462: }
463:
464: public CallableStatement prepareCall(String sql, int resultSetType,
465: int resultSetConcurrency) throws SQLException {
466: if (con == null)
467: throw new SQLException(CLOSED);
468: try {
469: return con.prepareCall(sql, resultSetType,
470: resultSetConcurrency);
471: } catch (SQLException e) {
472: setError(e);
473: throw e;
474: }
475: }
476:
477: public Map getTypeMap() throws SQLException {
478: if (con == null)
479: throw new SQLException(CLOSED);
480: try {
481: return con.getTypeMap();
482: } catch (SQLException e) {
483: setError(e);
484: throw e;
485: }
486: }
487:
488: public void setTypeMap(Map map) throws SQLException {
489: if (con == null)
490: throw new SQLException(CLOSED);
491: try {
492: con.setTypeMap(map);
493: } catch (SQLException e) {
494: setError(e);
495: throw e;
496: }
497: }
498:
499: // JDK 1.4 methods
500:
501: /* (non-Javadoc)
502: * @see java.sql.Connection#setHoldability(int)
503: */
504: public void setHoldability(int arg0) throws SQLException {
505: // TODO Auto-generated method stub
506:
507: }
508:
509: /* (non-Javadoc)
510: * @see java.sql.Connection#getHoldability()
511: */
512: public int getHoldability() throws SQLException {
513: // TODO Auto-generated method stub
514: return 0;
515: }
516:
517: /* (non-Javadoc)
518: * @see java.sql.Connection#setSavepoint()
519: */
520: public Savepoint setSavepoint() throws SQLException {
521: // TODO Auto-generated method stub
522: return null;
523: }
524:
525: /* (non-Javadoc)
526: * @see java.sql.Connection#setSavepoint(java.lang.String)
527: */
528: public Savepoint setSavepoint(String arg0) throws SQLException {
529: // TODO Auto-generated method stub
530: return null;
531: }
532:
533: /* (non-Javadoc)
534: * @see java.sql.Connection#rollback(java.sql.Savepoint)
535: */
536: public void rollback(Savepoint arg0) throws SQLException {
537: // TODO Auto-generated method stub
538:
539: }
540:
541: /* (non-Javadoc)
542: * @see java.sql.Connection#releaseSavepoint(java.sql.Savepoint)
543: */
544: public void releaseSavepoint(Savepoint arg0) throws SQLException {
545: // TODO Auto-generated method stub
546:
547: }
548:
549: /* (non-Javadoc)
550: * @see java.sql.Connection#createStatement(int, int, int)
551: */
552: public Statement createStatement(int arg0, int arg1, int arg2)
553: throws SQLException {
554: // TODO Auto-generated method stub
555: return null;
556: }
557:
558: /* (non-Javadoc)
559: * @see java.sql.Connection#prepareStatement(java.lang.String, int, int, int)
560: */
561: public PreparedStatement prepareStatement(String arg0, int arg1,
562: int arg2, int arg3) throws SQLException {
563: // TODO Auto-generated method stub
564: return null;
565: }
566:
567: /* (non-Javadoc)
568: * @see java.sql.Connection#prepareCall(java.lang.String, int, int, int)
569: */
570: public CallableStatement prepareCall(String arg0, int arg1,
571: int arg2, int arg3) throws SQLException {
572: // TODO Auto-generated method stub
573: return null;
574: }
575:
576: /* (non-Javadoc)
577: * @see java.sql.Connection#prepareStatement(java.lang.String, int)
578: */
579: public PreparedStatement prepareStatement(String arg0, int arg1)
580: throws SQLException {
581: // TODO Auto-generated method stub
582: return null;
583: }
584:
585: /* (non-Javadoc)
586: * @see java.sql.Connection#prepareStatement(java.lang.String, int[])
587: */
588: public PreparedStatement prepareStatement(String arg0, int[] arg1)
589: throws SQLException {
590: // TODO Auto-generated method stub
591: return null;
592: }
593:
594: /* (non-Javadoc)
595: * @see java.sql.Connection#prepareStatement(java.lang.String, java.lang.String[])
596: */
597: public PreparedStatement prepareStatement(String arg0, String[] arg1)
598: throws SQLException {
599: // TODO Auto-generated method stub
600: return null;
601: }
602: }
|