1 | /* |
2 | Copyright (C) 2005 MySQL AB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of version 2 of the GNU General Public License as |
6 | published by the Free Software Foundation. |
7 | |
8 | There are special exceptions to the terms and conditions of the GPL |
9 | as it is applied to this software. View the full text of the |
10 | exception in file EXCEPTIONS-CONNECTOR-J in the directory of this |
11 | software distribution. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | |
22 | */ |
23 | package com.mysql.jdbc.jdbc2.optional; |
24 | |
25 | import java.sql.Connection; |
26 | import java.sql.ResultSet; |
27 | import java.sql.SQLException; |
28 | import java.sql.Statement; |
29 | import java.util.ArrayList; |
30 | import java.util.Collections; |
31 | import java.util.HashMap; |
32 | import java.util.List; |
33 | import java.util.Map; |
34 | |
35 | import javax.sql.XAConnection; |
36 | import javax.transaction.xa.XAException; |
37 | import javax.transaction.xa.XAResource; |
38 | import javax.transaction.xa.Xid; |
39 | |
40 | import com.mysql.jdbc.log.Log; |
41 | |
42 | /* |
43 | * XA BEGIN <xid> [JOIN | RESUME] XA START TRANSACTION <xid> [JOIN | RESUME] XA |
44 | * COMMIT <xid> [ONE PHASE] XA END <xid> [SUSPEND [FOR MIGRATE]] XA PREPARE |
45 | * <xid> XA RECOVER XA ROLLBACK <xid> |
46 | */ |
47 | |
48 | /** |
49 | * An object that provides support for distributed transactions. An |
50 | * <code>XAConnection</code> object may be enlisted in a distributed |
51 | * transaction by means of an <code>XAResource</code> object. A transaction |
52 | * manager, usually part of a middle tier server, manages an |
53 | * <code>XAConnection</code> object through the <code>XAResource</code> |
54 | * object. |
55 | * <P> |
56 | * An application programmer does not use this interface directly; rather, it is |
57 | * used by a transaction manager working in the middle tier server. |
58 | * |
59 | * @since 1.4 |
60 | */ |
61 | public class MysqlXAConnection extends MysqlPooledConnection implements |
62 | XAConnection, XAResource { |
63 | |
64 | private com.mysql.jdbc.Connection underlyingConnection; |
65 | |
66 | private final static Map MYSQL_ERROR_CODES_TO_XA_ERROR_CODES; |
67 | |
68 | private Log log; |
69 | |
70 | static { |
71 | HashMap temp = new HashMap(); |
72 | |
73 | temp.put(new Integer(1397), new Integer(XAException.XAER_NOTA)); |
74 | temp.put(new Integer(1398), new Integer(XAException.XAER_INVAL)); |
75 | temp.put(new Integer(1399), new Integer(XAException.XAER_RMFAIL)); |
76 | temp.put(new Integer(1400), new Integer(XAException.XAER_OUTSIDE)); |
77 | temp.put(new Integer(1401), new Integer(XAException.XAER_RMERR)); |
78 | temp.put(new Integer(1402), new Integer(XAException.XA_RBROLLBACK)); |
79 | |
80 | MYSQL_ERROR_CODES_TO_XA_ERROR_CODES = Collections.unmodifiableMap(temp); |
81 | } |
82 | |
83 | /** |
84 | * @param connection |
85 | */ |
86 | public MysqlXAConnection(com.mysql.jdbc.Connection connection) |
87 | throws SQLException { |
88 | super(connection); |
89 | this.underlyingConnection = connection; |
90 | this.log = connection.getLog(); |
91 | } |
92 | |
93 | /** |
94 | * Retrieves an <code>XAResource</code> object that the transaction |
95 | * manager will use to manage this <code>XAConnection</code> object's |
96 | * participation in a distributed transaction. |
97 | * |
98 | * @return the <code>XAResource</code> object |
99 | * @exception SQLException |
100 | * if a database access error occurs |
101 | */ |
102 | public XAResource getXAResource() throws SQLException { |
103 | return this; |
104 | } |
105 | |
106 | /** |
107 | * Obtains the current transaction timeout value set for this XAResource |
108 | * instance. If XAResource.setTransactionTimeout was not used prior to |
109 | * invoking this method, the return value is the default timeout set for the |
110 | * resource manager; otherwise, the value used in the previous |
111 | * setTransactionTimeout call is returned. |
112 | * |
113 | * @return the transaction timeout value in seconds. |
114 | * |
115 | * @throws XAException |
116 | * An error has occurred. Possible exception values are |
117 | * XAER_RMERR and XAER_RMFAIL. |
118 | */ |
119 | public int getTransactionTimeout() throws XAException { |
120 | // TODO Auto-generated method stub |
121 | return 0; |
122 | } |
123 | |
124 | /** |
125 | * Sets the current transaction timeout value for this XAResource instance. |
126 | * Once set, this timeout value is effective until setTransactionTimeout is |
127 | * invoked again with a different value. |
128 | * |
129 | * To reset the timeout value to the default value used by the resource |
130 | * manager, set the value to zero. If the timeout operation is performed |
131 | * successfully, the method returns true; otherwise false. |
132 | * |
133 | * If a resource manager does not support explicitly setting the transaction |
134 | * timeout value, this method returns false. |
135 | * |
136 | * @parameter seconds The transaction timeout value in seconds. |
137 | * |
138 | * @return true if the transaction timeout value is set successfully; |
139 | * otherwise false. |
140 | * |
141 | * @throws XAException |
142 | * An error has occurred. Possible exception values are |
143 | * XAER_RMERR, XAER_RMFAIL, or XAER_INVAL. |
144 | */ |
145 | public boolean setTransactionTimeout(int arg0) throws XAException { |
146 | // TODO Auto-generated method stub |
147 | return false; |
148 | } |
149 | |
150 | /** |
151 | * This method is called to determine if the resource manager instance |
152 | * represented by the target object is the same as the resouce manager |
153 | * instance represented by the parameter xares. |
154 | * |
155 | * @parameter xares An XAResource object whose resource manager instance is |
156 | * to be compared with the resource manager instance of the |
157 | * target object. |
158 | * |
159 | * @return true if it's the same RM instance; otherwise false. |
160 | * |
161 | * @throws XAException |
162 | * An error has occurred. Possible exception values are |
163 | * XAER_RMERR and XAER_RMFAIL. |
164 | */ |
165 | public boolean isSameRM(XAResource xares) throws XAException { |
166 | |
167 | if (xares instanceof MysqlXAConnection) { |
168 | return this.underlyingConnection |
169 | .isSameResource(((MysqlXAConnection) xares).underlyingConnection); |
170 | } |
171 | |
172 | return false; |
173 | } |
174 | |
175 | /** |
176 | * This method is called to obtain a list of prepared transaction branches |
177 | * from a resource manager. The transaction manager calls this method during |
178 | * recovery to obtain the list of transaction branches that are currently in |
179 | * prepared or heuristically completed states. |
180 | * |
181 | * The flag parameter indicates where the recover scan should start or end, |
182 | * or start and end. This method may be invoked one or more times during a |
183 | * recovery scan. The resource manager maintains a cursor which marks the |
184 | * current position of the prepared or heuristically completed transaction list. |
185 | * Each invocation of the recover method moves the cursor passed the set of Xids |
186 | * that are returned. |
187 | * |
188 | * Two consecutive invocation of this method that starts from the |
189 | * beginning of the list must return the same list of transaction branches |
190 | * unless one of the following takes place: |
191 | * |
192 | * - the transaction manager invokes the commit, forget, prepare, or rollback method for that resource |
193 | * manager, between the two consecutive invocation of the recovery scan. |
194 | * |
195 | * - the resource manager heuristically completes some transaction branches |
196 | * between the two invocation of the recovery scan. |
197 | * |
198 | * @param flag |
199 | * One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be |
200 | * used when no other flags are set in the parameter. |
201 | * |
202 | * @returns The resource manager returns zero or more XIDs of the |
203 | * transaction branches that are currently in a prepared or |
204 | * heuristically completed state. If an error occurs during the |
205 | * operation, the resource manager should throw the appropriate |
206 | * XAException. |
207 | * |
208 | * @throws XAException |
209 | * An error has occurred. Possible values are XAER_RMERR, |
210 | * XAER_RMFAIL, XAER_INVAL, and XAER_PROTO. |
211 | */ |
212 | public Xid[] recover(int flag) throws XAException { |
213 | return recover(this.underlyingConnection, flag); |
214 | } |
215 | |
216 | protected static Xid[] recover(Connection c, int flag) throws XAException { |
217 | /* |
218 | The XA RECOVER statement returns information for those XA transactions on the MySQL server that are in the PREPARED state. (See Section 13.4.7.2, ìXA Transaction Statesî.) The output includes a row for each such XA transaction on the server, regardless of which client started it. |
219 | |
220 | XA RECOVER output rows look like this (for an example xid value consisting of the parts 'abc', 'def', and 7): |
221 | |
222 | mysql> XA RECOVER; |
223 | +----------+--------------+--------------+--------+ |
224 | | formatID | gtrid_length | bqual_length | data | |
225 | +----------+--------------+--------------+--------+ |
226 | | 7 | 3 | 3 | abcdef | |
227 | +----------+--------------+--------------+--------+ |
228 | |
229 | The output columns have the following meanings: |
230 | |
231 | formatID is the formatID part of the transaction xid |
232 | gtrid_length is the length in bytes of the gtrid part of the xid |
233 | bqual_length is the length in bytes of the bqual part of the xid |
234 | data is the concatenation of the gtrid and bqual parts of the xid |
235 | */ |
236 | |
237 | boolean startRscan = ((flag & TMSTARTRSCAN) > 0); |
238 | boolean endRscan = ((flag & TMENDRSCAN) > 0); |
239 | |
240 | if (!startRscan && !endRscan && flag != TMNOFLAGS) { |
241 | throw new MysqlXAException(XAException.XAER_INVAL, |
242 | "Invalid flag, must use TMNOFLAGS, or any combination of TMSTARTRSCAN and TMENDRSCAN", |
243 | null); |
244 | } |
245 | |
246 | // |
247 | // We return all recovered XIDs at once, so if not |
248 | // TMSTARTRSCAN, return no new XIDs |
249 | // |
250 | // We don't attempt to maintain state to check for TMNOFLAGS |
251 | // "outside" of a scan |
252 | // |
253 | |
254 | if (!startRscan) { |
255 | return new Xid[0]; |
256 | } |
257 | |
258 | ResultSet rs = null; |
259 | Statement stmt = null; |
260 | |
261 | List recoveredXidList = new ArrayList(); |
262 | |
263 | try { |
264 | // TODO: Cache this for lifetime of XAConnection |
265 | stmt = c.createStatement(); |
266 | |
267 | rs = stmt.executeQuery("XA RECOVER"); |
268 | |
269 | while (rs.next()) { |
270 | final int formatId = rs.getInt(1); |
271 | int gtridLength = rs.getInt(2); |
272 | int bqualLength = rs.getInt(3); |
273 | byte[] gtridAndBqual = rs.getBytes(4); |
274 | |
275 | final byte[] gtrid = new byte[gtridLength]; |
276 | final byte[] bqual = new byte[bqualLength]; |
277 | |
278 | if (gtridAndBqual.length != (gtridLength + bqualLength)) { |
279 | throw new MysqlXAException(XAException.XA_RBPROTO, |
280 | "Error while recovering XIDs from RM. GTRID and BQUAL are wrong sizes", |
281 | null); |
282 | } |
283 | |
284 | System.arraycopy(gtridAndBqual, 0, gtrid, 0, |
285 | gtridLength); |
286 | System.arraycopy(gtridAndBqual, gtridLength, bqual, 0, |
287 | bqualLength); |
288 | |
289 | recoveredXidList.add(new MysqlXid(gtrid, bqual, |
290 | formatId)); |
291 | } |
292 | } catch (SQLException sqlEx) { |
293 | throw mapXAExceptionFromSQLException(sqlEx); |
294 | } finally { |
295 | if (rs != null) { |
296 | try { |
297 | rs.close(); |
298 | } catch (SQLException sqlEx) { |
299 | throw mapXAExceptionFromSQLException(sqlEx); |
300 | } |
301 | } |
302 | |
303 | if (stmt != null) { |
304 | try { |
305 | stmt.close(); |
306 | } catch (SQLException sqlEx) { |
307 | throw mapXAExceptionFromSQLException(sqlEx); |
308 | } |
309 | } |
310 | } |
311 | |
312 | int numXids = recoveredXidList.size(); |
313 | |
314 | Xid[] asXids = new Xid[numXids]; |
315 | Object[] asObjects = recoveredXidList.toArray(); |
316 | |
317 | for (int i = 0; i < numXids; i++) { |
318 | asXids[i] = (Xid) asObjects[i]; |
319 | } |
320 | |
321 | return asXids; |
322 | } |
323 | |
324 | /** |
325 | * Asks the resource manager to prepare for a transaction commit of the |
326 | * transaction specified in xid. |
327 | * |
328 | * @parameter xid A global transaction identifier. |
329 | * |
330 | * @returns A value indicating the resource manager's vote on the outcome of |
331 | * the transaction. |
332 | * |
333 | * The possible values are: XA_RDONLY or XA_OK. If the resource manager |
334 | * wants to roll back the transaction, it should do so by raising an |
335 | * appropriate XAException in the prepare method. |
336 | * |
337 | * @throws XAException |
338 | * An error has occurred. Possible exception values are: XA_RB*, |
339 | * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or |
340 | * XAER_PROTO. |
341 | */ |
342 | public int prepare(Xid xid) throws XAException { |
343 | StringBuffer commandBuf = new StringBuffer(); |
344 | commandBuf.append("XA PREPARE "); |
345 | commandBuf.append(xidToString(xid)); |
346 | |
347 | dispatchCommand(commandBuf.toString()); |
348 | |
349 | return XA_OK; // TODO: Check for read-only |
350 | } |
351 | |
352 | /** |
353 | * Tells the resource manager to forget about a heuristically completed |
354 | * transaction branch. |
355 | * |
356 | * @parameter xid A global transaction identifier. |
357 | * |
358 | * @throws XAException |
359 | * An error has occurred. Possible exception values are |
360 | * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or |
361 | * XAER_PROTO. |
362 | */ |
363 | public void forget(Xid xid) throws XAException { |
364 | // TODO Auto-generated method stub |
365 | } |
366 | |
367 | /** |
368 | * Informs the resource manager to roll back work done on behalf of a |
369 | * transaction branch. |
370 | * |
371 | * @parameter xid A global transaction identifier. |
372 | * |
373 | * @throws XAException |
374 | * An error has occurred. Possible XAExceptions are XA_HEURHAZ, |
375 | * XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, |
376 | * XAER_NOTA, XAER_INVAL, or XAER_PROTO. |
377 | * |
378 | * If the transaction branch is already marked rollback-only the resource |
379 | * manager may throw one of the XA_RB* exceptions. |
380 | * |
381 | * Upon return, the resource manager has rolled back the branch's work and |
382 | * has released all held resources. |
383 | */ |
384 | public void rollback(Xid xid) throws XAException { |
385 | StringBuffer commandBuf = new StringBuffer(); |
386 | commandBuf.append("XA ROLLBACK "); |
387 | commandBuf.append(xidToString(xid)); |
388 | |
389 | try { |
390 | dispatchCommand(commandBuf.toString()); |
391 | } finally { |
392 | this.underlyingConnection.setInGlobalTx(false); |
393 | } |
394 | } |
395 | |
396 | /** |
397 | * Ends the work performed on behalf of a transaction branch. |
398 | * |
399 | * The resource manager disassociates the XA resource from the transaction |
400 | * branch specified and lets the transaction complete. |
401 | * |
402 | * If TMSUSPEND is specified in the flags, the transaction branch is |
403 | * temporarily suspended in an incomplete state. The transaction context is |
404 | * in a suspended state and must be resumed via the start method with |
405 | * TMRESUME specified. |
406 | * |
407 | * If TMFAIL is specified, the portion of work has failed. The resource |
408 | * manager may mark the transaction as rollback-only |
409 | * |
410 | * If TMSUCCESS is specified, the portion of work has completed |
411 | * successfully. |
412 | * |
413 | * @parameter xid A global transaction identifier that is the same as the |
414 | * identifier used previously in the start method. |
415 | * |
416 | * @parameter flags One of TMSUCCESS, TMFAIL, or TMSUSPEND. |
417 | * |
418 | * @throws XAException - |
419 | * An error has occurred. Possible XAException values are |
420 | * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, XAER_PROTO, |
421 | * or XA_RB*. |
422 | */ |
423 | public void end(Xid xid, int flags) throws XAException { |
424 | StringBuffer commandBuf = new StringBuffer(); |
425 | commandBuf.append("XA END "); |
426 | commandBuf.append(xidToString(xid)); |
427 | |
428 | switch (flags) { |
429 | case TMSUCCESS: |
430 | break; // no-op |
431 | case TMSUSPEND: |
432 | commandBuf.append(" SUSPEND"); |
433 | break; |
434 | case TMFAIL: |
435 | break; // no-op |
436 | default: |
437 | throw new XAException(XAException.XAER_INVAL); |
438 | } |
439 | |
440 | dispatchCommand(commandBuf.toString()); |
441 | } |
442 | |
443 | /** |
444 | * Starts work on behalf of a transaction branch specified in xid. |
445 | * |
446 | * If TMJOIN is specified, the start applies to joining a transaction |
447 | * previously seen by the resource manager. |
448 | * |
449 | * If TMRESUME is specified, the start applies to resuming a suspended |
450 | * transaction specified in the parameter xid. |
451 | * |
452 | * If neither TMJOIN nor TMRESUME is specified and the transaction specified |
453 | * by xid has previously been seen by the resource manager, the resource |
454 | * manager throws the XAException exception with XAER_DUPID error code. |
455 | * |
456 | * @parameter xid A global transaction identifier to be associated with the |
457 | * resource. |
458 | * |
459 | * @parameter flags One of TMNOFLAGS, TMJOIN, or TMRESUME. |
460 | * |
461 | * @throws XAException |
462 | * An error has occurred. Possible exceptions are XA_RB*, |
463 | * XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, |
464 | * XAER_INVAL, or XAER_PROTO. |
465 | */ |
466 | public void start(Xid xid, int flags) throws XAException { |
467 | StringBuffer commandBuf = new StringBuffer(); |
468 | commandBuf.append("XA START "); |
469 | commandBuf.append(xidToString(xid)); |
470 | |
471 | switch (flags) { |
472 | case TMJOIN: |
473 | commandBuf.append(" JOIN"); |
474 | break; |
475 | case TMRESUME: |
476 | commandBuf.append(" RESUME"); |
477 | break; |
478 | case TMNOFLAGS: |
479 | // no-op |
480 | break; |
481 | default: |
482 | throw new XAException(XAException.XAER_INVAL); |
483 | } |
484 | |
485 | dispatchCommand(commandBuf.toString()); |
486 | |
487 | this.underlyingConnection.setInGlobalTx(true); |
488 | } |
489 | |
490 | /** |
491 | * Commits the global transaction specified by xid. |
492 | * |
493 | * @parameter xid A global transaction identifier |
494 | * @parameter onePhase - If true, the resource manager should use a |
495 | * one-phase commit protocol to commit the work done on behalf of |
496 | * xid. |
497 | * |
498 | * @throws XAException |
499 | * An error has occurred. Possible XAExceptions are XA_HEURHAZ, |
500 | * XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, |
501 | * XAER_NOTA, XAER_INVAL, or XAER_PROTO. |
502 | * |
503 | * If the resource manager did not commit the transaction and the parameter |
504 | * onePhase is set to true, the resource manager may throw one of the XA_RB* |
505 | * exceptions. |
506 | * |
507 | * Upon return, the resource manager has rolled back the branch's work and |
508 | * has released all held resources. |
509 | */ |
510 | |
511 | public void commit(Xid xid, boolean onePhase) throws XAException { |
512 | StringBuffer commandBuf = new StringBuffer(); |
513 | commandBuf.append("XA COMMIT "); |
514 | commandBuf.append(xidToString(xid)); |
515 | |
516 | if (onePhase) { |
517 | commandBuf.append(" ONE PHASE"); |
518 | } |
519 | |
520 | try { |
521 | dispatchCommand(commandBuf.toString()); |
522 | } finally { |
523 | this.underlyingConnection.setInGlobalTx(false); |
524 | } |
525 | } |
526 | |
527 | private ResultSet dispatchCommand(String command) throws XAException { |
528 | Statement stmt = null; |
529 | |
530 | try { |
531 | if (this.log.isDebugEnabled()) { |
532 | this.log.logDebug("Executing XA statement: " + command); |
533 | } |
534 | |
535 | // TODO: Cache this for lifetime of XAConnection |
536 | stmt = this.underlyingConnection.createStatement(); |
537 | |
538 | |
539 | stmt.execute(command); |
540 | |
541 | ResultSet rs = stmt.getResultSet(); |
542 | |
543 | return rs; |
544 | } catch (SQLException sqlEx) { |
545 | throw mapXAExceptionFromSQLException(sqlEx); |
546 | } finally { |
547 | if (stmt != null) { |
548 | try { |
549 | stmt.close(); |
550 | } catch (SQLException sqlEx) { |
551 | } |
552 | } |
553 | } |
554 | } |
555 | |
556 | protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) { |
557 | |
558 | Integer xaCode = (Integer) MYSQL_ERROR_CODES_TO_XA_ERROR_CODES |
559 | .get(new Integer(sqlEx.getErrorCode())); |
560 | |
561 | if (xaCode != null) { |
562 | return new MysqlXAException(xaCode.intValue(), sqlEx.getMessage(), null); |
563 | } |
564 | |
565 | // Punt? We don't know what the error code is here |
566 | return new MysqlXAException(sqlEx.getMessage(), null); |
567 | } |
568 | |
569 | private static String xidToString(Xid xid) { |
570 | byte[] gtrid = xid.getGlobalTransactionId(); |
571 | |
572 | byte[] btrid = xid.getBranchQualifier(); |
573 | |
574 | int lengthAsString = 6; // for (0x and ,) * 2 |
575 | |
576 | if (gtrid != null) { |
577 | lengthAsString += (2 * gtrid.length); |
578 | } |
579 | |
580 | if (btrid != null) { |
581 | lengthAsString += (2 * btrid.length); |
582 | } |
583 | |
584 | String formatIdInHex = Integer.toHexString(xid.getFormatId()); |
585 | |
586 | lengthAsString += formatIdInHex.length(); |
587 | lengthAsString += 3; // for the '.' after formatId |
588 | |
589 | StringBuffer asString = new StringBuffer(lengthAsString); |
590 | |
591 | asString.append("0x"); |
592 | |
593 | if (gtrid != null) { |
594 | for (int i = 0; i < gtrid.length; i++) { |
595 | String asHex = Integer.toHexString(gtrid[i] & 0xff); |
596 | |
597 | if (asHex.length() == 1) { |
598 | asString.append("0"); |
599 | } |
600 | |
601 | asString.append(asHex); |
602 | } |
603 | } |
604 | |
605 | asString.append(","); |
606 | |
607 | if (btrid != null) { |
608 | asString.append("0x"); |
609 | |
610 | for (int i = 0; i < btrid.length; i++) { |
611 | String asHex = Integer.toHexString(btrid[i] & 0xff); |
612 | |
613 | if (asHex.length() == 1) { |
614 | asString.append("0"); |
615 | } |
616 | |
617 | asString.append(asHex); |
618 | } |
619 | } |
620 | |
621 | asString.append(",0x"); |
622 | asString.append(formatIdInHex); |
623 | |
624 | return asString.toString(); |
625 | } |
626 | |
627 | /* |
628 | * (non-Javadoc) |
629 | * |
630 | * @see javax.sql.PooledConnection#getConnection() |
631 | */ |
632 | public synchronized Connection getConnection() throws SQLException { |
633 | Connection connToWrap = getConnection(false, true); |
634 | |
635 | return connToWrap; |
636 | } |
637 | } |