001: /*
002: * XAPool: Open Source XA JDBC Pool
003: * Copyright (C) 2003 Objectweb.org
004: * Initial Developer: Lutris Technologies Inc.
005: * Contact: xapool-public@lists.debian-sf.objectweb.org
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020: * USA
021: */
022: package org.enhydra.jdbc.standard;
023:
024: import java.sql.PreparedStatement;
025: import java.sql.SQLException;
026: import java.sql.Statement;
027: import java.sql.CallableStatement;
028: import java.util.Hashtable;
029: import javax.transaction.Transaction;
030: import javax.transaction.TransactionManager;
031: import javax.transaction.RollbackException;
032: import javax.transaction.SystemException;
033:
034: public class StandardXAConnectionHandle extends
035: StandardConnectionHandle {
036:
037: boolean resetTxonResume = false;
038: boolean globalTransaction; // true if a global transaction is in effect
039: public TransactionManager transactionManager;
040: public Transaction tx;
041: public StandardXAConnection xacon;
042: public boolean this AutoCommit = true;
043:
044: /**
045: * Constructor
046: */
047: public StandardXAConnectionHandle(StandardXAConnection pooledCon,
048: Hashtable preparedStatementCache,
049: int preparedStmtCacheSize, TransactionManager tm) {
050: super (pooledCon, preparedStatementCache, preparedStmtCacheSize);
051: // setup StandardXAConnectionHandle
052: xacon = pooledCon;
053: transactionManager = tm;
054: log = pooledCon.dataSource.log;
055:
056: // This will have set the Connection to the current Connection.
057: // However this might change if a global transaction gets selected.
058: }
059:
060: public void setTransactionManager(TransactionManager tm) {
061: this .transactionManager = tm;
062: }
063:
064: synchronized public void close() throws SQLException {
065: Transaction ttx = tx;
066: // note: ttx is used instead of tx because super.close(), call end()
067: // on StdXAConnection which call tx = null;
068: // super.close();
069: log.debug("StandardXAConnectionHandle:close");
070: log
071: .debug("StandardXAConnectionHandle:close globalTransaction='"
072: + globalTransaction
073: + "' con.getAutoCommit='"
074: + con.getAutoCommit() + "' ttx='" + ttx + "'");
075:
076: if ((!con.getAutoCommit()) && (ttx == null)) {
077: log
078: .debug("StandardXAConnectionHandle:close rollback the connection");
079: con.rollback();
080: con.setAutoCommit(this AutoCommit);
081: } else
082: log
083: .debug("StandardXAConnectionHandle:close do nothing else");
084: isReallyUsed = false;
085: log
086: .debug("StandardXAConnectionHandle:close AFTER globalTransaction='"
087: + globalTransaction
088: + "' con.getAutoCommit='"
089: + con.getAutoCommit() + "' ttx='" + ttx + "'");
090: super .close();
091: }
092:
093: /**
094: * Called by the StandardXADataSource when a global transaction
095: * gets associated with this connection.
096: */
097: void setGlobalTransaction(boolean setting) throws SQLException {
098: log
099: .debug("StandardXAConnectionHandle:setGlobalTransaction gTransaction='"
100: + setting + "'");
101: globalTransaction = setting; // set global flag
102: con = pooledCon.getPhysicalConnection(); // get the real connection
103: if (con == null)
104: log
105: .warn("StandardXAConnectionHandle:setGlobalTransaction con is null before setupPreparedStatementCache");
106: else
107: log
108: .debug("StandardXAConnectionHandle:setGlobalTransaction con is *NOT* null before setupPreparedStatementCache");
109: //setupPreparedStatementCache();
110: if (!isClosed())
111: super .setAutoCommit(!setting);
112: // commits must be done by transaction manager
113: }
114:
115: public void setAutoCommit(boolean autoCommit) throws SQLException {
116: if (globalTransaction) // if taking part in a global transaction
117: throw new SQLException(
118: "StandardXAConnectionHandle:setAutoCommit This connection is part of a global transaction");
119: super .setAutoCommit(autoCommit);
120: }
121:
122: public void commit() throws SQLException {
123: if (globalTransaction) // if taking part in a global transaction
124: throw new SQLException(
125: "StandardXAConnectionHandle:commit:This connection is part of a global transaction");
126: super .commit();
127: //tx = null;
128: }
129:
130: public void rollback() throws SQLException {
131: if (globalTransaction) // if taking part in a global transaction
132: throw new SQLException(
133: "StandardXAConnectionHandle:rollback:This connection is part of a global transaction");
134: super .rollback();
135: //tx = null;
136: }
137:
138: synchronized PreparedStatement checkPreparedCache(String sql,
139: int type, int concurrency, int holdability, Object lookupKey)
140: throws SQLException {
141: PreparedStatement ret = null; // the return value
142: // NOTE - We include the Connection in the lookup key. This has no
143: // effect here but is needed by StandardXAConnection where the the physical
144: // Connection used can vary over time depending on the global transaction.
145: if (preparedStatementCache != null) {
146: Object obj = preparedStatementCache.get(lookupKey);
147: // see if there's a PreparedStatement already
148: if (obj != null) { // if there is
149: log
150: .debug("StandardXAConnectionHandle:checkPreparedCache object is found");
151: ret = (PreparedStatement) obj; // use as return value
152: try {
153: ret.clearParameters(); // make it look like new
154: } catch (SQLException e) {
155: // Bad statement, so we have to create a new one
156: ret = createPreparedStatement(sql, type,
157: concurrency, holdability);
158: // create new prepared statement
159: }
160: preparedStatementCache.remove(lookupKey);
161: // make sure it cannot be re-used
162: inUse.put(lookupKey, ret);
163: // make sure it gets reused by later delegates
164: } else { // no PreparedStatement ready
165: log
166: .debug("StandardXAConnectionHandle:checkPreparedCache object is *NOT* found");
167: ret = createPreparedStatement(sql, type, concurrency,
168: holdability);
169: // create new prepared statement
170: inUse.put(lookupKey, ret);
171: // will get saved in prepared statement cache
172: }
173: } else {
174: log
175: .debug("StandardXAConnectionHandle:checkPreparedCache object the cache is out");
176: ret = createPreparedStatement(sql, type, concurrency,
177: holdability);
178: // create new prepared statement
179: }
180: // We don't actually give the application a real PreparedStatement. Instead
181: // they get a StandardPreparedStatement that delegates everything except
182: // PreparedStatement.close();
183: log
184: .debug("StandardXAConnectionHandle:checkPreparedCache pstmt='"
185: + ret.toString() + "'");
186: return ret;
187: }
188:
189: synchronized PreparedStatement checkPreparedCache(String sql,
190: int autogeneratedkeys, Object lookupKey)
191: throws SQLException {
192: PreparedStatement ret = null; // the return value
193: // NOTE - We include the Connection in the lookup key. This has no
194: // effect here but is needed by StandardXAConnection where the the physical
195: // Connection used can vary over time depending on the global transaction.
196: if (preparedStatementCache != null) {
197: Object obj = preparedStatementCache.get(lookupKey);
198: // see if there's a PreparedStatement already
199: if (obj != null) { // if there is
200: log
201: .debug("StandardXAConnectionHandle:checkPreparedCache object is found");
202: ret = (PreparedStatement) obj; // use as return value
203: try {
204: ret.clearParameters(); // make it look like new
205: } catch (SQLException e) {
206: // Bad statement, so we have to create a new one
207: ret = createPreparedStatement(sql,
208: autogeneratedkeys);
209: // create new prepared statement
210: }
211: preparedStatementCache.remove(lookupKey);
212: // make sure it cannot be re-used
213: inUse.put(lookupKey, ret);
214: // make sure it gets reused by later delegates
215: } else { // no PreparedStatement ready
216: log
217: .debug("StandardXAConnectionHandle:checkPreparedCache object is *NOT* found");
218: ret = createPreparedStatement(sql, autogeneratedkeys);
219: // create new prepared statement
220: inUse.put(lookupKey, ret);
221: // will get saved in prepared statement cache
222: }
223: } else {
224: log
225: .debug("StandardXAConnectionHandle:checkPreparedCache object the cache is out");
226: ret = createPreparedStatement(sql, autogeneratedkeys);
227: // create new prepared statement
228: }
229: // We don't actually give the application a real PreparedStatement. Instead
230: // they get a StandardPreparedStatement that delegates everything except
231: // PreparedStatement.close();
232: log
233: .debug("StandardXAConnectionHandle:checkPreparedCache pstmt='"
234: + ret.toString() + "'");
235: return ret;
236: }
237:
238: /**
239: * Creates a PreparedStatement for the given SQL. If possible, the
240: * statement is fetched from the cache.
241: */
242: public PreparedStatement prepareStatement(String sql)
243: throws SQLException {
244: return prepareStatement(sql, 0, 0, 0);
245: }
246:
247: public PreparedStatement prepareStatement(String sql,
248: int resultSetType, int resultSetConcurrency)
249: throws SQLException {
250: return prepareStatement(sql, resultSetType,
251: resultSetConcurrency, 0);
252: }
253:
254: /**
255: * Creates a PreparedStatement for the given SQL, type and concurrency.
256: * If possible, the statement is fetched from the cache.
257: */
258: public PreparedStatement prepareStatement(String sql,
259: int resultSetType, int resultSetConcurrency,
260: int resultSetHoldability) throws SQLException {
261: if (tx == null) {
262: log
263: .debug("StandardXAConnectionHandle:prepareStatement tx==null");
264: try {
265: try {
266: Transaction ntx = this .getTransaction();
267: if (ntx != null) {
268: log
269: .debug("StandardXAConnectionHandle:prepareStatement (found a transaction)");
270: tx = ntx;
271: xacon.this AutoCommit = this .getAutoCommit();
272: if (this .getAutoCommit()) {
273: this .setAutoCommit(false);
274: }
275: try {
276: tx.enlistResource(xacon.getXAResource());
277: // enlist the xaResource in the transaction
278: } catch (RollbackException n) {
279: log
280: .debug("StandardXAConnectionHandle:prepareStatemnet enlistResource exception : "
281: + n.toString());
282: }
283: } else {
284: log
285: .debug("StandardXAConnectionHandle:prepareStatement (no transaction found)");
286: }
287: } catch (SystemException n) {
288: n.printStackTrace();
289: throw new SQLException(
290: "StandardXAConnectionHandle:prepareStatement getTransaction exception: "
291: + n.toString());
292: }
293: } catch (NullPointerException n) {
294: // current is null: we are not in EJBServer.
295: n.printStackTrace();
296: throw new SQLException(
297: "StandardXAConnectionHandle:prepareStatement should not be used outside an EJBServer");
298: }
299: } else
300: log
301: .debug("StandardXAConnectionHandle:prepareStatement tx!=null");
302:
303: // if you want to use a REAL PrepareStatement object, please
304: // uncomment the 2 following lines and comment the last ones.
305: //PreparedStatement ops = con.prepareStatement(sql, resultSetType, resultSetConcurrency);
306: //return ops;
307:
308: isReallyUsed = true;
309: return new StandardXAPreparedStatement(this , sql,
310: resultSetType, resultSetConcurrency,
311: resultSetHoldability);
312: }
313:
314: public PreparedStatement prepareStatement(String sql,
315: int autoGeneratedKeys) throws SQLException {
316: if (tx == null) {
317: log
318: .debug("StandardXAConnectionHandle:prepareStatement tx==null");
319: try {
320: try {
321: Transaction ntx = this .getTransaction();
322: if (ntx != null) {
323: log
324: .debug("StandardXAConnectionHandle:prepareStatement (found a transaction)");
325: tx = ntx;
326: xacon.this AutoCommit = this .getAutoCommit();
327: if (this .getAutoCommit()) {
328: this .setAutoCommit(false);
329: }
330: try {
331: tx.enlistResource(xacon.getXAResource());
332: // enlist the xaResource in the transaction
333: } catch (RollbackException n) {
334: log
335: .debug("StandardXAConnectionHandle:prepareStatemnet enlistResource exception : "
336: + n.toString());
337: }
338: } else {
339: log
340: .debug("StandardXAConnectionHandle:prepareStatement (no transaction found)");
341: }
342: } catch (SystemException n) {
343: n.printStackTrace();
344: throw new SQLException(
345: "StandardXAConnectionHandle:prepareStatement getTransaction exception: "
346: + n.toString());
347: }
348: } catch (NullPointerException n) {
349: // current is null: we are not in EJBServer.
350: n.printStackTrace();
351: throw new SQLException(
352: "StandardXAConnectionHandle:prepareStatement should not be used outside an EJBServer");
353: }
354: } else
355: log
356: .debug("StandardXAConnectionHandle:prepareStatement tx!=null");
357:
358: isReallyUsed = true;
359: return new StandardXAPreparedStatement(this , sql,
360: autoGeneratedKeys);
361: }
362:
363: /**
364: * not yet implemented
365: */
366: public PreparedStatement prepareStatement(String sql,
367: int[] columnIndexes) throws SQLException {
368: throw new UnsupportedOperationException();
369: }
370:
371: /**
372: * not yet implemented
373: */
374: public PreparedStatement prepareStatement(String sql,
375: String[] columnNames) throws SQLException {
376: throw new UnsupportedOperationException();
377: }
378:
379: /**
380: * Creates a CallableStatement for the given SQL, result set type and concurency
381: */
382: public CallableStatement prepareCall(String sql, int resultSetType,
383: int resultSetConcurrency) throws SQLException {
384: return new StandardXACallableStatement(this , sql,
385: resultSetType, resultSetConcurrency, 0);
386: }
387:
388: /**
389: * Creates a CallableStatement for the given SQL
390: */
391: public CallableStatement prepareCall(String sql)
392: throws SQLException {
393: return new StandardXACallableStatement(this , sql, 0, 0, 0);
394: }
395:
396: public CallableStatement prepareCall(String sql, int resultSetType,
397: int resultSetConcurrency, int resultSetHoldability)
398: throws SQLException {
399: return new StandardXACallableStatement(this , sql,
400: resultSetType, resultSetConcurrency,
401: resultSetHoldability);
402: }
403:
404: public Statement createStatement() throws SQLException {
405: return createStatement(0, 0, 0);
406: }
407:
408: public Statement createStatement(int resultSetType,
409: int resultSetConcurrency) throws SQLException {
410: return createStatement(resultSetType, resultSetConcurrency, 0);
411: }
412:
413: public Statement createStatement(int resultSetType,
414: int resultSetConcurrency, int resultSetHoldability)
415: throws SQLException {
416:
417: if (tx == null) {
418: log
419: .debug("StandardXAConnectionHandle:createStatement tx==null");
420: try {
421: try {
422: Transaction ntx = this .getTransaction();
423: if (ntx != null) {
424: log
425: .debug("StandardXAConnectionHandle:createStatement (found a transaction)");
426: tx = ntx;
427: xacon.this AutoCommit = this .getAutoCommit();
428: if (this .getAutoCommit()) {
429: this .setAutoCommit(false);
430: }
431: try {
432: tx.enlistResource(xacon.getXAResource());
433: // enlist the xaResource in the transaction
434: } catch (RollbackException n) {
435: log
436: .debug("StandardXAConnectionHandle:createStatement enlistResource exception: "
437: + n.toString());
438: }
439: } else {
440: log
441: .debug("StandardXAConnectionHandle:createStatement (no transaction found)");
442: }
443:
444: } catch (SystemException n) {
445: throw new SQLException(
446: "StandardXAConnectionHandle:createStatement getTransaction exception: "
447: + n.toString());
448: }
449: } catch (NullPointerException n) {
450: // current is null: we are not in EJBServer.
451: throw new SQLException(
452: "StandardXAConnectionHandle:createStatement should not be used outside an EJBServer: "
453: + n.toString());
454: }
455: }
456: isReallyUsed = true;
457: return new StandardXAStatement(this , resultSetType,
458: resultSetConcurrency, resultSetHoldability);
459: }
460:
461: private Transaction getTransaction() throws SystemException {
462: Transaction ntx = null;
463: if (transactionManager != null) {
464: ntx = transactionManager.getTransaction();
465: } else {
466: log
467: .debug("StandardXAConnectionHandle:getTransaction (null transaction manager)");
468: }
469:
470: return ntx;
471: }
472:
473: public String toString() {
474: StringBuffer sb = new StringBuffer();
475: sb.append("StandardXAConnectionHandle:\n");
476: sb.append(" global transaction =<" + this .globalTransaction
477: + ">\n");
478: sb.append(" is really used =<" + this .isReallyUsed + ">\n");
479: sb.append(" this autoCommit =<" + this .this AutoCommit
480: + ">\n");
481: sb.append(" in use size =<" + this .inUse.size() + ">\n");
482: sb.append(" master prepared stmt cache size =<"
483: + this .masterPrepStmtCache.size() + ">\n");
484: sb.append(" transaction =<" + this .tx + ">\n");
485: sb.append(" connection =<" + this .con.toString() + ">\n");
486:
487: return sb.toString();
488: }
489: }
|