1 /*
2 * XAPool: Open Source XA JDBC Pool
3 * Copyright (C) 2003 Objectweb.org
4 * Initial Developer: Lutris Technologies Inc.
5 * Contact: xapool-public@lists.debian-sf.objectweb.org
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 */
22 package org.enhydra.jdbc.standard;
23
24 import java.sql.PreparedStatement;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.sql.CallableStatement;
28 import java.util.Hashtable;
29 import javax.transaction.Transaction;
30 import javax.transaction.TransactionManager;
31 import javax.transaction.RollbackException;
32 import javax.transaction.SystemException;
33
34 public class StandardXAConnectionHandle extends StandardConnectionHandle {
35
36 boolean resetTxonResume =false;
37 boolean globalTransaction; // true if a global transaction is in effect
38 public TransactionManager transactionManager;
39 public Transaction tx;
40 public StandardXAConnection xacon;
41 public boolean thisAutoCommit = true;
42
43 /**
44 * Constructor
45 */
46 public StandardXAConnectionHandle(
47 StandardXAConnection pooledCon,
48 Hashtable preparedStatementCache,
49 int preparedStmtCacheSize,
50 TransactionManager tm) {
51 super(pooledCon, preparedStatementCache, preparedStmtCacheSize);
52 // setup StandardXAConnectionHandle
53 xacon = pooledCon;
54 transactionManager = tm;
55 log = pooledCon.dataSource.log;
56
57 // This will have set the Connection to the current Connection.
58 // However this might change if a global transaction gets selected.
59 }
60
61 public void setTransactionManager(TransactionManager tm) {
62 this.transactionManager = tm;
63 }
64
65 synchronized public void close() throws SQLException {
66 Transaction ttx = tx;
67 // note: ttx is used instead of tx because super.close(), call end()
68 // on StdXAConnection which call tx = null;
69 super.close();
70 log.debug("StandardXAConnectionHandle:close");
71 log.debug(
72 "StandardXAConnectionHandle:close globalTransaction='"
73 + globalTransaction
74 + "' con.getAutoCommit='"
75 + con.getAutoCommit()
76 + "' ttx='"
77 + ttx
78 + "'");
79
80 if ((!con.getAutoCommit()) && (ttx == null)) {
81 log.debug(
82 "StandardXAConnectionHandle:close rollback the connection");
83 con.rollback();
84 con.setAutoCommit(thisAutoCommit);
85 } else
86 log.debug("StandardXAConnectionHandle:close do nothing else");
87 isReallyUsed = false;
88 log.debug(
89 "StandardXAConnectionHandle:close AFTER globalTransaction='"
90 + globalTransaction
91 + "' con.getAutoCommit='"
92 + con.getAutoCommit()
93 + "' ttx='"
94 + ttx
95 + "'");
96 }
97
98 /**
99 * Called by the StandardXADataSource when a global transaction
100 * gets associated with this connection.
101 */
102 void setGlobalTransaction(boolean setting) throws SQLException {
103 log.debug(
104 "StandardXAConnectionHandle:setGlobalTransaction gTransaction='"
105 + setting
106 + "'");
107 globalTransaction = setting; // set global flag
108 con = pooledCon.getPhysicalConnection(); // get the real connection
109 if (con == null)
110 log.warn(
111 "StandardXAConnectionHandle:setGlobalTransaction con is null before setupPreparedStatementCache");
112 else
113 log.debug(
114 "StandardXAConnectionHandle:setGlobalTransaction con is *NOT* null before setupPreparedStatementCache");
115 //setupPreparedStatementCache();
116 if(!isClosed())
117 super.setAutoCommit(!setting);
118 // commits must be done by transaction manager
119 }
120
121 public void setAutoCommit(boolean autoCommit) throws SQLException {
122 if (globalTransaction) // if taking part in a global transaction
123 throw new SQLException("StandardXAConnectionHandle:setAutoCommit This connection is part of a global transaction");
124 super.setAutoCommit(autoCommit);
125 }
126
127 public void commit() throws SQLException {
128 if (globalTransaction) // if taking part in a global transaction
129 throw new SQLException("StandardXAConnectionHandle:commit:This connection is part of a global transaction");
130 super.commit();
131 //tx = null;
132 }
133
134 public void rollback() throws SQLException {
135 if (globalTransaction) // if taking part in a global transaction
136 throw new SQLException("StandardXAConnectionHandle:rollback:This connection is part of a global transaction");
137 super.rollback();
138 //tx = null;
139 }
140
141 synchronized PreparedStatement checkPreparedCache(
142 String sql,
143 int type,
144 int concurrency,
145 int holdability,
146 Object lookupKey)
147 throws SQLException {
148 PreparedStatement ret = null; // the return value
149 // NOTE - We include the Connection in the lookup key. This has no
150 // effect here but is needed by StandardXAConnection where the the physical
151 // Connection used can vary over time depending on the global transaction.
152 if (preparedStatementCache != null) {
153 Object obj = preparedStatementCache.get(lookupKey);
154 // see if there's a PreparedStatement already
155 if (obj != null) { // if there is
156 log.debug(
157 "StandardXAConnectionHandle:checkPreparedCache object is found");
158 ret = (PreparedStatement) obj; // use as return value
159 try {
160 ret.clearParameters(); // make it look like new
161 } catch (SQLException e) {
162 // Bad statement, so we have to create a new one
163 ret = createPreparedStatement(sql, type, concurrency, holdability);
164 // create new prepared statement
165 }
166 preparedStatementCache.remove(lookupKey);
167 // make sure it cannot be re-used
168 inUse.put(lookupKey, ret);
169 // make sure it gets reused by later delegates
170 } else { // no PreparedStatement ready
171 log.debug(
172 "StandardXAConnectionHandle:checkPreparedCache object is *NOT* found");
173 ret = createPreparedStatement(sql, type, concurrency, holdability);
174 // create new prepared statement
175 inUse.put(lookupKey, ret);
176 // will get saved in prepared statement cache
177 }
178 } else {
179 log.debug(
180 "StandardXAConnectionHandle:checkPreparedCache object the cache is out");
181 ret = createPreparedStatement(sql, type, concurrency, holdability);
182 // create new prepared statement
183 }
184 // We don't actually give the application a real PreparedStatement. Instead
185 // they get a StandardPreparedStatement that delegates everything except
186 // PreparedStatement.close();
187 log.debug(
188 "StandardXAConnectionHandle:checkPreparedCache pstmt='"
189 + ret.toString()
190 + "'");
191 return ret;
192 }
193
194
195
196 synchronized PreparedStatement checkPreparedCache(
197 String sql,
198 int autogeneratedkeys,
199 Object lookupKey)
200 throws SQLException {
201 PreparedStatement ret = null; // the return value
202 // NOTE - We include the Connection in the lookup key. This has no
203 // effect here but is needed by StandardXAConnection where the the physical
204 // Connection used can vary over time depending on the global transaction.
205 if (preparedStatementCache != null) {
206 Object obj = preparedStatementCache.get(lookupKey);
207 // see if there's a PreparedStatement already
208 if (obj != null) { // if there is
209 log.debug(
210 "StandardXAConnectionHandle:checkPreparedCache object is found");
211 ret = (PreparedStatement) obj; // use as return value
212 try {
213 ret.clearParameters(); // make it look like new
214 } catch (SQLException e) {
215 // Bad statement, so we have to create a new one
216 ret = createPreparedStatement(sql, autogeneratedkeys);
217 // create new prepared statement
218 }
219 preparedStatementCache.remove(lookupKey);
220 // make sure it cannot be re-used
221 inUse.put(lookupKey, ret);
222 // make sure it gets reused by later delegates
223 } else { // no PreparedStatement ready
224 log.debug(
225 "StandardXAConnectionHandle:checkPreparedCache object is *NOT* found");
226 ret = createPreparedStatement(sql, autogeneratedkeys);
227 // create new prepared statement
228 inUse.put(lookupKey, ret);
229 // will get saved in prepared statement cache
230 }
231 } else {
232 log.debug(
233 "StandardXAConnectionHandle:checkPreparedCache object the cache is out");
234 ret = createPreparedStatement(sql, autogeneratedkeys);
235 // create new prepared statement
236 }
237 // We don't actually give the application a real PreparedStatement. Instead
238 // they get a StandardPreparedStatement that delegates everything except
239 // PreparedStatement.close();
240 log.debug(
241 "StandardXAConnectionHandle:checkPreparedCache pstmt='"
242 + ret.toString()
243 + "'");
244 return ret;
245 }
246
247
248
249 /**
250 * Creates a PreparedStatement for the given SQL. If possible, the
251 * statement is fetched from the cache.
252 */
253 public PreparedStatement prepareStatement(String sql) throws SQLException {
254 return prepareStatement(sql, 0, 0, 0);
255 }
256
257 public PreparedStatement prepareStatement(
258 String sql,
259 int resultSetType,
260 int resultSetConcurrency)
261 throws SQLException {
262 return prepareStatement(sql, resultSetType, resultSetConcurrency, 0);
263 }
264
265 /**
266 * Creates a PreparedStatement for the given SQL, type and concurrency.
267 * If possible, the statement is fetched from the cache.
268 */
269 public PreparedStatement prepareStatement(
270 String sql,
271 int resultSetType,
272 int resultSetConcurrency,
273 int resultSetHoldability)
274 throws SQLException {
275 if (tx == null) {
276 log.debug("StandardXAConnectionHandle:prepareStatement tx==null");
277 try {
278 try {
279 Transaction ntx = this.getTransaction();
280 if (ntx != null) {
281 log.debug(
282 "StandardXAConnectionHandle:prepareStatement (found a transaction)");
283 tx = ntx;
284 xacon.thisAutoCommit = this.getAutoCommit();
285 if (this.getAutoCommit()) {
286 this.setAutoCommit(false);
287 }
288 try {
289 tx.enlistResource(xacon.getXAResource());
290 // enlist the xaResource in the transaction
291 } catch (RollbackException n) {
292 log.debug(
293 "StandardXAConnectionHandle:prepareStatemnet enlistResource exception : "
294 + n.toString());
295 }
296 } else {
297 log.debug(
298 "StandardXAConnectionHandle:prepareStatement (no transaction found)");
299 }
300 } catch (SystemException n) {
301 n.printStackTrace();
302 throw new SQLException(
303 "StandardXAConnectionHandle:prepareStatement getTransaction exception: "
304 + n.toString());
305 }
306 } catch (NullPointerException n) {
307 // current is null: we are not in EJBServer.
308 n.printStackTrace();
309 throw new SQLException("StandardXAConnectionHandle:prepareStatement should not be used outside an EJBServer");
310 }
311 } else
312 log.debug("StandardXAConnectionHandle:prepareStatement tx!=null");
313
314 // if you want to use a REAL PrepareStatement object, please
315 // uncomment the 2 following lines and comment the last ones.
316 //PreparedStatement ops = con.prepareStatement(sql, resultSetType, resultSetConcurrency);
317 //return ops;
318
319 isReallyUsed = true;
320 return new StandardXAPreparedStatement(
321 this,
322 sql,
323 resultSetType,
324 resultSetConcurrency,
325 resultSetHoldability);
326 }
327
328 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
329 throws SQLException {
330 if (tx == null) {
331 log.debug("StandardXAConnectionHandle:prepareStatement tx==null");
332 try {
333 try {
334 Transaction ntx = this.getTransaction();
335 if (ntx != null) {
336 log.debug(
337 "StandardXAConnectionHandle:prepareStatement (found a transaction)");
338 tx = ntx;
339 xacon.thisAutoCommit = this.getAutoCommit();
340 if (this.getAutoCommit()) {
341 this.setAutoCommit(false);
342 }
343 try {
344 tx.enlistResource(xacon.getXAResource());
345 // enlist the xaResource in the transaction
346 } catch (RollbackException n) {
347 log.debug(
348 "StandardXAConnectionHandle:prepareStatemnet enlistResource exception : "
349 + n.toString());
350 }
351 } else {
352 log.debug(
353 "StandardXAConnectionHandle:prepareStatement (no transaction found)");
354 }
355 } catch (SystemException n) {
356 n.printStackTrace();
357 throw new SQLException(
358 "StandardXAConnectionHandle:prepareStatement getTransaction exception: "
359 + n.toString());
360 }
361 } catch (NullPointerException n) {
362 // current is null: we are not in EJBServer.
363 n.printStackTrace();
364 throw new SQLException("StandardXAConnectionHandle:prepareStatement should not be used outside an EJBServer");
365 }
366 } else
367 log.debug("StandardXAConnectionHandle:prepareStatement tx!=null");
368
369 isReallyUsed = true;
370 return new StandardXAPreparedStatement(
371 this,
372 sql,
373 autoGeneratedKeys);
374 }
375
376 /**
377 * not yet implemented
378 */
379 public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
380 throw new UnsupportedOperationException();
381 }
382
383 /**
384 * not yet implemented
385 */
386 public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
387 throw new UnsupportedOperationException();
388 }
389
390
391 /**
392 * Creates a CallableStatement for the given SQL, result set type and concurency
393 */
394 public CallableStatement prepareCall(
395 String sql,
396 int resultSetType,
397 int resultSetConcurrency)
398 throws SQLException {
399 return new StandardXACallableStatement(
400 this,
401 sql,
402 resultSetType,
403 resultSetConcurrency, 0);
404 }
405
406 /**
407 * Creates a CallableStatement for the given SQL
408 */
409 public CallableStatement prepareCall(String sql) throws SQLException {
410 return new StandardXACallableStatement(this, sql, 0, 0, 0);
411 }
412
413 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
414 throws SQLException {
415 return new StandardXACallableStatement(
416 this,
417 sql,
418 resultSetType,
419 resultSetConcurrency,
420 resultSetHoldability);
421 }
422
423
424 public Statement createStatement() throws SQLException {
425 return createStatement(0,0,0);
426 }
427
428 public Statement createStatement(
429 int resultSetType,
430 int resultSetConcurrency)
431 throws SQLException {
432 return createStatement(resultSetType, resultSetConcurrency, 0);
433 }
434
435 public Statement createStatement(
436 int resultSetType,
437 int resultSetConcurrency,
438 int resultSetHoldability)
439 throws SQLException {
440
441 if (tx == null) {
442 log.debug("StandardXAConnectionHandle:createStatement tx==null");
443 try {
444 try {
445 Transaction ntx = this.getTransaction();
446 if (ntx != null) {
447 log.debug(
448 "StandardXAConnectionHandle:createStatement (found a transaction)");
449 tx = ntx;
450 xacon.thisAutoCommit = this.getAutoCommit();
451 if (this.getAutoCommit()) {
452 this.setAutoCommit(false);
453 }
454 try {
455 tx.enlistResource(xacon.getXAResource());
456 // enlist the xaResource in the transaction
457 } catch (RollbackException n) {
458 log.debug(
459 "StandardXAConnectionHandle:createStatement enlistResource exception: "
460 + n.toString());
461 }
462 } else {
463 log.debug(
464 "StandardXAConnectionHandle:createStatement (no transaction found)");
465 }
466
467 } catch (SystemException n) {
468 throw new SQLException(
469 "StandardXAConnectionHandle:createStatement getTransaction exception: "
470 + n.toString());
471 }
472 } catch (NullPointerException n) {
473 // current is null: we are not in EJBServer.
474 throw new SQLException(
475 "StandardXAConnectionHandle:createStatement should not be used outside an EJBServer: "
476 + n.toString());
477 }
478 }
479 isReallyUsed = true;
480 return new StandardXAStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);
481 }
482
483 private Transaction getTransaction() throws SystemException {
484 Transaction ntx = null;
485 if (transactionManager != null) {
486 ntx = transactionManager.getTransaction();
487 } else {
488 log.debug(
489 "StandardXAConnectionHandle:getTransaction (null transaction manager)");
490 }
491
492 return ntx;
493 }
494
495 public String toString() {
496 StringBuffer sb = new StringBuffer();
497 sb.append("StandardXAConnectionHandle:\n");
498 sb.append(" global transaction =<"+this.globalTransaction+ ">\n");
499 sb.append(" is really used =<"+this.isReallyUsed+ ">\n");
500 sb.append(" this autoCommit =<"+this.thisAutoCommit+ ">\n");
501 sb.append(" in use size =<"+this.inUse.size()+ ">\n");
502 sb.append(" master prepared stmt cache size =<"+this.masterPrepStmtCache.size()+ ">\n");
503 sb.append(" transaction =<"+this.tx+ ">\n");
504 sb.append(" connection =<"+this.con.toString()+ ">\n");
505
506 return sb.toString();
507 }
508 }