File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\jdbcx\JdbcXAConnection.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.jdbcx;
8
9 import java.sql.Connection;
10 import java.sql.ResultSet;
11 import java.sql.SQLException;
12 import java.sql.Statement;
13 import java.util.ArrayList;
14 import javax.sql.ConnectionEvent;
15 import javax.sql.ConnectionEventListener;
16 import javax.sql.XAConnection;
17 import javax.transaction.xa.XAException;
18 import javax.transaction.xa.XAResource;
19 import javax.transaction.xa.Xid;
20 import org.h2.constant.ErrorCode;
21 import org.h2.jdbc.JdbcConnection;
22 import org.h2.util.JdbcUtils;
23 import org.h2.util.New;
24
25 import org.h2.message.DbException;
26 import org.h2.message.TraceObject;
27
28 //## Java 1.6 ##
29 import javax.sql.StatementEventListener;
30 //*/
31
32 /**
33 * This class provides support for distributed transactions.
34 * An application developer usually does not use this interface.
35 * It is used by the transaction manager internally.
36 */
37 public class JdbcXAConnection extends TraceObject implements XAConnection, XAResource {
38
39 private final JdbcDataSourceFactory factory;
40
41 // this connection is kept open as long as the XAConnection is alive
42 private JdbcConnection physicalConn;
43
44 // this connection is replaced whenever getConnection is called
45 private volatile Connection handleConn;
46 private final ArrayList<ConnectionEventListener> listeners = New.arrayList();
47 private Xid currentTransaction;
48 private boolean prepared;
49
50 static {
51 org.h2.Driver.load();
52 }
53
54 JdbcXAConnection(JdbcDataSourceFactory factory, int id, JdbcConnection physicalConn) {
55 this.factory = factory;
56 setTrace(factory.getTrace(), TraceObject.XA_DATA_SOURCE, id);
57 this.physicalConn = physicalConn;
58 }
59
60 /**
61 * Get the XAResource object.
62 *
63 * @return itself
64 */
65 public XAResource getXAResource() {
66 debugCodeCall("getXAResource");
67 return this;
68 }
69
70 /**
71 * Close the physical connection.
72 * This method is usually called by the connection pool.
73 *
74 * @throws SQLException
75 */
76 public void close() throws SQLException {
77 debugCodeCall("close");
78 Connection lastHandle = handleConn;
79 if (lastHandle != null) {
80 listeners.clear();
81 lastHandle.close();
82 }
83 if (physicalConn != null) {
84 try {
85 physicalConn.close();
86 } finally {
87 physicalConn = null;
88 }
89 }
90 }
91
92 /**
93 * Get a connection that is a handle to the physical connection. This method
94 * is usually called by the connection pool. This method closes the last
95 * connection handle if one exists.
96 *
97 * @return the connection
98 */
99 public Connection getConnection() throws SQLException {
100 debugCodeCall("getConnection");
101 Connection lastHandle = handleConn;
102 if (lastHandle != null) {
103 lastHandle.close();
104 }
105 // this will ensure the rollback command is cached
106 physicalConn.rollback();
107 handleConn = new PooledJdbcConnection(physicalConn);
108 return handleConn;
109 }
110
111 /**
112 * Register a new listener for the connection.
113 *
114 * @param listener the event listener
115 */
116 public void addConnectionEventListener(ConnectionEventListener listener) {
117 debugCode("addConnectionEventListener(listener);");
118 listeners.add(listener);
119 }
120
121 /**
122 * Remove the event listener.
123 *
124 * @param listener the event listener
125 */
126 public void removeConnectionEventListener(ConnectionEventListener listener) {
127 debugCode("removeConnectionEventListener(listener);");
128 listeners.remove(listener);
129 }
130
131 /**
132 * INTERNAL
133 */
134 void closedHandle() {
135 debugCode("closedHandle();");
136 ConnectionEvent event = new ConnectionEvent(this);
137 // go backward so that a listener can remove itself
138 // (otherwise we need to clone the list)
139 for (int i = listeners.size() - 1; i >= 0; i--) {
140 ConnectionEventListener listener = listeners.get(i);
141 listener.connectionClosed(event);
142 }
143 handleConn = null;
144 }
145
146 /**
147 * Get the transaction timeout.
148 *
149 * @return 0
150 */
151 public int getTransactionTimeout() {
152 debugCodeCall("getTransactionTimeout");
153 return 0;
154 }
155
156 /**
157 * Set the transaction timeout.
158 *
159 * @param seconds ignored
160 * @return false
161 */
162 public boolean setTransactionTimeout(int seconds) {
163 debugCodeCall("setTransactionTimeout", seconds);
164 return false;
165 }
166
167 /**
168 * Checks if this is the same XAResource.
169 *
170 * @param xares the other object
171 * @return true if this is the same object
172 */
173 public boolean isSameRM(XAResource xares) {
174 debugCode("isSameRM(xares);");
175 return xares == this;
176 }
177
178 /**
179 * Get the list of prepared transaction branches.
180 * This method is called by the transaction manager during recovery.
181 *
182 * @param flag TMSTARTRSCAN, TMENDRSCAN, or TMNOFLAGS. If no other flags are set,
183 * TMNOFLAGS must be used.
184 * @return zero or more Xid objects
185 * @throws XAException
186 */
187 public Xid[] recover(int flag) throws XAException {
188 debugCodeCall("recover", quoteFlags(flag));
189 checkOpen();
190 Statement stat = null;
191 try {
192 stat = physicalConn.createStatement();
193 ResultSet rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.IN_DOUBT ORDER BY TRANSACTION");
194 ArrayList<Xid> list = New.arrayList();
195 while (rs.next()) {
196 String tid = rs.getString("TRANSACTION");
197 int id = getNextId(XID);
198 Xid xid = new JdbcXid(factory, id, tid);
199 list.add(xid);
200 }
201 rs.close();
202 Xid[] result = new Xid[list.size()];
203 list.toArray(result);
204 if (list.size() > 0) {
205 prepared = true;
206 }
207 return result;
208 } catch (SQLException e) {
209 XAException xa = new XAException(XAException.XAER_RMERR);
210 xa.initCause(e);
211 throw xa;
212 } finally {
213 JdbcUtils.closeSilently(stat);
214 }
215 }
216
217 /**
218 * Prepare a transaction.
219 *
220 * @param xid the transaction id
221 * @return XA_OK
222 * @throws XAException
223 */
224 public int prepare(Xid xid) throws XAException {
225 if (isDebugEnabled()) {
226 debugCode("prepare("+JdbcXid.toString(xid)+");");
227 }
228 checkOpen();
229 if (!currentTransaction.equals(xid)) {
230 throw new XAException(XAException.XAER_INVAL);
231 }
232 Statement stat = null;
233 try {
234 stat = physicalConn.createStatement();
235 stat.execute("PREPARE COMMIT " + JdbcXid.toString(xid));
236 prepared = true;
237 } catch (SQLException e) {
238 throw convertException(e);
239 } finally {
240 JdbcUtils.closeSilently(stat);
241 }
242 return XA_OK;
243 }
244
245 /**
246 * Forget a transaction.
247 * This method does not have an effect for this database.
248 *
249 * @param xid the transaction id
250 */
251 public void forget(Xid xid) {
252 if (isDebugEnabled()) {
253 debugCode("forget("+JdbcXid.toString(xid)+");");
254 }
255 prepared = false;
256 }
257
258 /**
259 * Roll back a transaction.
260 *
261 * @param xid the transaction id
262 * @throws XAException
263 */
264 public void rollback(Xid xid) throws XAException {
265 if (isDebugEnabled()) {
266 debugCode("rollback("+JdbcXid.toString(xid)+");");
267 }
268 try {
269 physicalConn.rollback();
270 physicalConn.setAutoCommit(true);
271 if (prepared) {
272 Statement stat = null;
273 try {
274 stat = physicalConn.createStatement();
275 stat.execute("ROLLBACK TRANSACTION " + JdbcXid.toString(xid));
276 } finally {
277 JdbcUtils.closeSilently(stat);
278 }
279 prepared = false;
280 }
281 } catch (SQLException e) {
282 throw convertException(e);
283 }
284 currentTransaction = null;
285 }
286
287 /**
288 * End a transaction.
289 *
290 * @param xid the transaction id
291 * @param flags TMSUCCESS, TMFAIL, or TMSUSPEND
292 * @throws XAException
293 */
294 public void end(Xid xid, int flags) throws XAException {
295 if (isDebugEnabled()) {
296 debugCode("end("+JdbcXid.toString(xid)+", "+quoteFlags(flags)+");");
297 }
298 // TODO transaction end: implement this method
299 if (flags == TMSUSPEND) {
300 return;
301 }
302 if (!currentTransaction.equals(xid)) {
303 throw new XAException(XAException.XAER_OUTSIDE);
304 }
305 prepared = false;
306 }
307
308 /**
309 * Start or continue to work on a transaction.
310 *
311 * @param xid the transaction id
312 * @param flags TMNOFLAGS, TMJOIN, or TMRESUME
313 * @throws XAException
314 */
315 public void start(Xid xid, int flags) throws XAException {
316 if (isDebugEnabled()) {
317 debugCode("start("+JdbcXid.toString(xid)+", "+quoteFlags(flags)+");");
318 }
319 if (flags == TMRESUME) {
320 return;
321 }
322 if (flags == TMJOIN) {
323 if (currentTransaction != null && !currentTransaction.equals(xid)) {
324 throw new XAException(XAException.XAER_RMERR);
325 }
326 } else if (currentTransaction != null) {
327 throw new XAException(XAException.XAER_NOTA);
328 }
329 try {
330 physicalConn.setAutoCommit(false);
331 } catch (SQLException e) {
332 throw convertException(e);
333 }
334 currentTransaction = xid;
335 prepared = false;
336 }
337
338 /**
339 * Commit a transaction.
340 *
341 * @param xid the transaction id
342 * @param onePhase use a one-phase protocol if true
343 * @throws XAException
344 */
345 public void commit(Xid xid, boolean onePhase) throws XAException {
346 if (isDebugEnabled()) {
347 debugCode("commit("+JdbcXid.toString(xid)+", "+onePhase+");");
348 }
349 Statement stat = null;
350 try {
351 if (onePhase) {
352 physicalConn.commit();
353 } else {
354 stat = physicalConn.createStatement();
355 stat.execute("COMMIT TRANSACTION " + JdbcXid.toString(xid));
356 prepared = false;
357 }
358 physicalConn.setAutoCommit(true);
359 } catch (SQLException e) {
360 throw convertException(e);
361 } finally {
362 JdbcUtils.closeSilently(stat);
363 }
364 currentTransaction = null;
365 }
366
367 /**
368 * [Not supported] Add a statement event listener.
369 *
370 * @param listener the new statement event listener
371 */
372 //## Java 1.6 ##
373 public void addStatementEventListener(StatementEventListener listener) {
374 throw new UnsupportedOperationException();
375 }
376 //*/
377
378 /**
379 * [Not supported] Remove a statement event listener.
380 *
381 * @param listener the statement event listener
382 */
383 //## Java 1.6 ##
384 public void removeStatementEventListener(StatementEventListener listener) {
385 throw new UnsupportedOperationException();
386 }
387 //*/
388
389 /**
390 * INTERNAL
391 */
392 public String toString() {
393 return getTraceObjectName() + ": " + physicalConn;
394 }
395
396 private static XAException convertException(SQLException e) {
397 XAException xa = new XAException(e.getMessage());
398 xa.initCause(e);
399 return xa;
400 }
401
402 private static String quoteFlags(int flags) {
403 StringBuilder buff = new StringBuilder();
404 if ((flags & XAResource.TMENDRSCAN) != 0) {
405 buff.append("|XAResource.TMENDRSCAN");
406 }
407 if ((flags & XAResource.TMFAIL) != 0) {
408 buff.append("|XAResource.TMFAIL");
409 }
410 if ((flags & XAResource.TMJOIN) != 0) {
411 buff.append("|XAResource.TMJOIN");
412 }
413 if ((flags & XAResource.TMONEPHASE) != 0) {
414 buff.append("|XAResource.TMONEPHASE");
415 }
416 if ((flags & XAResource.TMRESUME) != 0) {
417 buff.append("|XAResource.TMRESUME");
418 }
419 if ((flags & XAResource.TMSTARTRSCAN) != 0) {
420 buff.append("|XAResource.TMSTARTRSCAN");
421 }
422 if ((flags & XAResource.TMSUCCESS) != 0) {
423 buff.append("|XAResource.TMSUCCESS");
424 }
425 if ((flags & XAResource.TMSUSPEND) != 0) {
426 buff.append("|XAResource.TMSUSPEND");
427 }
428 if ((flags & XAResource.XA_RDONLY) != 0) {
429 buff.append("|XAResource.XA_RDONLY");
430 }
431 if (buff.length() == 0) {
432 buff.append("|XAResource.TMNOFLAGS");
433 }
434 return buff.toString().substring(1);
435 }
436
437 private void checkOpen() throws XAException {
438 if (physicalConn == null) {
439 throw new XAException(XAException.XAER_RMERR);
440 }
441 }
442
443 /**
444 * A pooled connection.
445 */
446 class PooledJdbcConnection extends JdbcConnection {
447
448 private boolean isClosed;
449
450 public PooledJdbcConnection(JdbcConnection conn) {
451 super(conn);
452 }
453
454 public synchronized void close() throws SQLException {
455 if (!isClosed) {
456 try {
457 rollback();
458 setAutoCommit(true);
459 } catch (SQLException e) {
460 // ignore
461 }
462 closedHandle();
463 isClosed = true;
464 }
465 }
466
467 public synchronized boolean isClosed() throws SQLException {
468 return isClosed || super.isClosed();
469 }
470
471 protected synchronized void checkClosed(boolean write) {
472 if (isClosed) {
473 throw DbException.get(ErrorCode.OBJECT_CLOSED);
474 }
475 super.checkClosed(write);
476 }
477
478 }
479
480 }
481