001 /** 002 * Copyright 2005-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.objectweb.jotm; 017 018 import java.nio.ByteBuffer; 019 import java.rmi.RemoteException; 020 import java.util.List; 021 import java.util.Vector; 022 023 import javax.rmi.PortableRemoteObject; 024 import javax.transaction.InvalidTransactionException; 025 import javax.transaction.RollbackException; 026 import javax.transaction.Status; 027 import javax.transaction.Synchronization; 028 import javax.transaction.SystemException; 029 import javax.transaction.Transaction; 030 import javax.transaction.TransactionManager; 031 import javax.transaction.TransactionRolledbackException; 032 import javax.transaction.xa.XAException; 033 import javax.transaction.xa.XAResource; 034 035 import org.objectweb.howl.log.xa.XACommittingTx; 036 037 /** 038 * Log associated to this transaction coordinator 039 */ 040 class SLog { 041 042 private Vector loggedResources = new Vector(); 043 private Vector loggedByteResources = new Vector(); 044 045 private Vector loggedXids = new Vector(); 046 047 private Vector loggedJavaxXids = new Vector(); 048 049 int decision; 050 static final int DECISION_TO_COMMIT = 1; 051 static final int DECISION_TO_ROLLBACK = 2; 052 053 public void addResource(XAResource res, Xid xid, javax.transaction.xa.Xid javaxxid) { 054 if (TraceTm.jta.isDebugEnabled()) { 055 TraceTm.jta.debug("res= " + res); 056 TraceTm.jta.debug("xid= " + xid); 057 TraceTm.jta.debug("javaxxid= " + javaxxid); 058 } 059 loggedResources.addElement(res); 060 loggedByteResources.addElement(res.toString().getBytes()); 061 loggedXids.addElement(xid); 062 loggedJavaxXids.addElement(javaxxid); 063 } 064 065 /** 066 * @return a List of logged Resources 067 */ 068 public List getLoggedResources() { 069 if (TraceTm.jta.isDebugEnabled()) { 070 TraceTm.jta.debug("logged resources=" + loggedResources); 071 } 072 return loggedResources; 073 } 074 075 public List getByteLoggedResources() { 076 if (TraceTm.jta.isDebugEnabled()) { 077 TraceTm.jta.debug("logged resources=" + loggedResources); 078 } 079 return loggedByteResources; 080 } 081 082 public List getLoggedXids() { 083 return loggedXids; 084 } 085 086 public List getLoggedJavaxXids() { 087 return loggedJavaxXids; 088 } 089 090 public void flushLog(int decide) { 091 if (TraceTm.jta.isDebugEnabled()) { 092 TraceTm.jta.debug("decide=" + decide); 093 } 094 decision = decide; 095 096 // XXX serialize log on disk 097 } 098 099 public void forgetLog() { 100 if (TraceTm.jta.isDebugEnabled()) { 101 TraceTm.jta.debug("forget log"); 102 } 103 // XXX remove file on disk 104 } 105 } 106 107 class JotmTransactionRolledbackException extends TransactionRolledbackException { 108 private static final long serialVersionUID = 1L; 109 110 public JotmTransactionRolledbackException() { 111 super(); 112 } 113 114 public JotmTransactionRolledbackException(String message) { 115 super(message); 116 } 117 118 public JotmTransactionRolledbackException(String message, Throwable cause) { 119 super(message); 120 detail = cause; 121 } 122 123 public JotmTransactionRolledbackException(Throwable cause) { 124 super(); 125 detail = cause; 126 } 127 } 128 129 /** 130 * This object is the local coordinator. It may be registered as sub-coordinator in case of distributed transaction, so 131 * it must be callable remotely and implement Resource 132 */ 133 public class SubCoordinator extends PortableRemoteObject implements Resource { 134 135 // ------------------------------------------------------------------ 136 // Object state 137 // ------------------------------------------------------------------ 138 139 /** 140 * @serial 141 */ 142 private TransactionImpl tx = null; 143 144 /** 145 * List of Synchronization objects TODO should synchronize this list. 146 * 147 * @serial 148 */ 149 private Vector synchroList = new Vector(); 150 151 /** 152 * List of XAResource objects 153 * 154 * @serial 155 */ 156 private Vector resourceList = new Vector(); 157 private Vector javaxxidList = new Vector(); 158 159 /** 160 * Keep a reference on TransactionManager 161 * 162 * @serial 163 */ 164 private TransactionManager tm; 165 166 /** 167 * @serial 168 */ 169 private Xid xid = null; 170 171 /** 172 * @serial 173 */ 174 private SLog log = null; 175 176 /** 177 * javax.transaction.Status 178 * <dl> 179 * <dt>ACTIVE</dt> 180 * <dd>transaction started, commit phase not started</dd> 181 * <dt>PREPARING</dt> 182 * <dd>prepare is being sent to resources</dd> 183 * <dt>PREPARED</dt> 184 * <dd>prepare is done. Must commit now</dd> 185 * <dt>COMMITTING</dt> 186 * <dd>commit is being sent to resources</dd> 187 * <dt>COMMITTED</dt> 188 * <dd>commit was successful</dd> 189 * <dt>ROLLING_BACK</dt> 190 * <dd>not used</dd> 191 * <dt>MARKED_ROLLBACK</dt> 192 * <dd>setRollbackOnly has been called</dd> 193 * <dt>ROLLEDBACK</dt> 194 * <dd>transaction has been rolled back (or prepare returned "vote_rollback")</dd> 195 * <dt>UNKNOWN</dt> 196 * <dd>commit raised heuristics</dd> 197 * <dt>NO_TRANSACTION</dt> 198 * <dd>cannot get the status value from JTM</dd> 199 * </dl> 200 * 201 * @serial 202 */ 203 private int status = Status.STATUS_ACTIVE; 204 205 private Exception rollbackCause; 206 /** 207 * @serial 208 */ 209 private boolean beforeCompletionDone = false; 210 211 static final String JOTM_RD_ONLY = "JOTM_RD_ONLY"; 212 213 static final byte[] RT1 = "RR1".getBytes(); 214 static final byte[] RT2 = "RR2".getBytes(); 215 static final byte[] JOTMDONE = "RR3JOTMDONE".getBytes(); 216 217 // ------------------------------------------------------------------ 218 // Constructors 219 // ------------------------------------------------------------------ 220 221 /** 222 * constructor used by TransactionImpl 223 */ 224 SubCoordinator(TransactionImpl tx, Xid xid) throws RemoteException { 225 226 if (TraceTm.jta.isDebugEnabled()) { 227 TraceTm.jta.debug("tx=" + tx + ", xid=" + xid); 228 } 229 230 this.tx = tx; 231 this.xid = xid; 232 this.tm = Current.getTransactionManager(); 233 234 beforeCompletionDone = false; 235 } 236 237 // ------------------------------------------------------------------ 238 // Resource interface 239 // ------------------------------------------------------------------ 240 241 /** 242 * phase 1 of the 2PC. 243 * 244 * @return int vote commit, rollback, or readonly. 245 */ 246 @Override 247 public int prepare() throws RemoteException { 248 249 if (TraceTm.jta.isDebugEnabled()) { 250 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 251 } 252 253 try { 254 tx.doDetach(XAResource.TMSUCCESS); 255 } catch (SystemException e) { 256 257 if (TraceTm.jta.isDebugEnabled()) { 258 String error = "Error when detaching XAResource:" + e + "--" + e.getMessage(); 259 TraceTm.jta.debug(error); 260 } 261 } 262 263 switch (status) { 264 case Status.STATUS_MARKED_ROLLBACK: 265 // Doing rollback now may be an issue, because the following rollback 266 // will not work (this object do not exist any longer) 267 // doBeforeCompletion(false); 268 // doRollback(); 269 status = Status.STATUS_ROLLING_BACK; 270 return Resource.VOTE_ROLLBACK; 271 case Status.STATUS_COMMITTED: 272 return Resource.VOTE_COMMIT; 273 default: 274 doBeforeCompletion(true); 275 break; 276 } 277 278 // Recheck Status after doBeforeCompletion 279 280 if (status == Status.STATUS_MARKED_ROLLBACK) { 281 TraceTm.jotm.info("Rollback during beforeCompletion in SubCoordinator.prepare"); 282 // doRollback(); 283 status = Status.STATUS_ROLLING_BACK; 284 return Resource.VOTE_ROLLBACK; 285 } 286 287 int ret = doPrepare(); 288 289 if (ret == Resource.VOTE_READONLY) { 290 // Transaction completed for this Resource 291 doAfterCompletion(); 292 } 293 294 if (TraceTm.jta.isDebugEnabled()) { 295 TraceTm.jta.debug("vote = " + ret); 296 } 297 298 return ret; 299 } 300 301 /** 302 * rollback transaction 303 */ 304 305 @Override 306 public void rollback() throws RemoteException { 307 308 if (TraceTm.jta.isDebugEnabled()) { 309 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 310 } 311 312 try { 313 tx.doDetach(XAResource.TMSUCCESS); 314 } catch (SystemException e) { 315 if (TraceTm.jta.isDebugEnabled()) { 316 String error = "Error when detaching XAResource:" + e + "--" + e.getMessage(); 317 TraceTm.jta.debug(error); 318 } 319 } 320 321 switch (status) { 322 case Status.STATUS_ACTIVE: 323 case Status.STATUS_MARKED_ROLLBACK: 324 case Status.STATUS_ROLLING_BACK: 325 if (TraceTm.jotm.isDebugEnabled()) { 326 TraceTm.jotm.debug("transaction rolling back"); 327 } 328 break; 329 case Status.STATUS_PREPARED: 330 if (TraceTm.jotm.isDebugEnabled()) { 331 TraceTm.jotm.debug("should not rollback a prepared transaction"); 332 } 333 break; 334 case Status.STATUS_ROLLEDBACK: 335 if (TraceTm.jotm.isDebugEnabled()) { 336 TraceTm.jotm.debug("already rolledback"); 337 } 338 return; 339 default: 340 TraceTm.jotm.error("rollback: bad status: " + StatusHelper.getStatusName(status)); 341 return; 342 } 343 doBeforeCompletion(false); 344 doRollback(); 345 } 346 347 /** 348 * phase 2 of the 2PC. 349 */ 350 @Override 351 public void commit() throws RemoteException { 352 353 if (TraceTm.jta.isDebugEnabled()) { 354 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 355 } 356 357 switch (status) { 358 case Status.STATUS_PREPARED: 359 break; 360 default: 361 TraceTm.jotm.error("commit: bad status: " + StatusHelper.getStatusName(status)); 362 return; 363 } 364 doCommit(); 365 } 366 367 /** 368 * commit 1 phase. Called either from JTM (distributed transaction) or from Transaction.commit (local transaction). 369 */ 370 371 @Override 372 public void commit_one_phase() throws RemoteException { 373 374 if (TraceTm.jta.isDebugEnabled()) { 375 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 376 } 377 378 switch (status) { 379 case Status.STATUS_ROLLEDBACK: 380 try { 381 tx.doDetach(XAResource.TMSUCCESS); 382 } catch (SystemException e) { 383 384 if (TraceTm.jta.isDebugEnabled()) { 385 String error = "Error when detaching XAResource:" + e + "--" + e.getMessage(); 386 TraceTm.jta.debug(error); 387 } 388 } 389 // KULRICE-9919 : Updated to include the rollback cause 390 throw new JotmTransactionRolledbackException(rollbackCause); 391 // END KULRICE-9919 392 case Status.STATUS_MARKED_ROLLBACK: 393 doBeforeCompletion(false); 394 try { 395 tx.doDetach(XAResource.TMSUCCESS); 396 } catch (SystemException e) { 397 398 if (TraceTm.jta.isDebugEnabled()) { 399 String error = "Error when detaching XAResource:" + e + "--" + e.getMessage(); 400 TraceTm.jta.debug(error); 401 } 402 } 403 doRollback(); 404 // KULRICE-9919 : Updated to include the rollback cause 405 throw new JotmTransactionRolledbackException(rollbackCause); 406 // END KULRICE-9919 407 case Status.STATUS_COMMITTED: 408 try { 409 tx.doDetach(XAResource.TMSUCCESS); 410 } catch (SystemException e) { 411 412 if (TraceTm.jta.isDebugEnabled()) { 413 String error = "Error when detaching XAResource:" + e + "--" + e.getMessage(); 414 TraceTm.jta.debug(error); 415 } 416 } 417 return; 418 default: 419 doBeforeCompletion(true); 420 try { 421 tx.doDetach(XAResource.TMSUCCESS); 422 } catch (SystemException e) { 423 424 if (TraceTm.jta.isDebugEnabled()) { 425 String error = "Error when detaching XAResource:" + e + "--" + e.getMessage(); 426 TraceTm.jta.debug(error); 427 } 428 } 429 break; 430 } 431 432 // status many have changed in doBeforeCompletion 433 434 if (TraceTm.jta.isDebugEnabled()) { 435 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 436 } 437 438 // Recheck Status after doBeforeCompletion 439 440 if (status == Status.STATUS_MARKED_ROLLBACK) { 441 TraceTm.jotm.info("Rollback during beforeCompletion in SubCoordinator.commit_one_phase"); 442 doRollback(); 443 // KULRICE-9919 : Updated to include the rollback cause 444 throw new JotmTransactionRolledbackException(rollbackCause); 445 // END KULRICE-9919 446 } 447 448 // only 1 Resource => 1 phase commit 449 450 if (resourceList.size() == 1) { 451 doOnePhaseCommit(); 452 return; 453 } 454 455 // 2 phase commit 456 457 int vote = doPrepare(); 458 459 switch (vote) { 460 case Resource.VOTE_COMMIT: 461 doCommit(); 462 break; 463 case Resource.VOTE_READONLY: 464 doAfterCompletion(); 465 break; 466 case Resource.VOTE_ROLLBACK: 467 doRollback(); 468 // KULRICE-9919 : Updated to include the rollback cause 469 throw new JotmTransactionRolledbackException(rollbackCause); 470 // END KULRICE-9919 471 } 472 } 473 474 /** 475 * forget heuristics about this transaction. 476 */ 477 @Override 478 public void forget() throws RemoteException { 479 if (TraceTm.jta.isDebugEnabled()) { 480 TraceTm.jta.debug("SubCoordinator.forget()"); 481 } 482 doForget(); 483 } 484 485 // ------------------------------------------------------------------ 486 // Other public methods (interface exposed to Transaction object) 487 // ------------------------------------------------------------------ 488 489 /** 490 * add a Synchronization to the list 491 * 492 * @param synchro 493 * The javax.transaction.Synchronization object for the transaction associated with the target object 494 * 495 * @exception RollbackException 496 * Thrown to indicate that the transaction has been marked for rollback only. 497 * 498 * @exception IllegalStateException 499 * Thrown if the transaction in the target object is in prepared state or the transaction is 500 * inactive. 501 * 502 */ 503 public void addSynchronization(Synchronization synchro) throws RollbackException, IllegalStateException { 504 505 if (TraceTm.jta.isDebugEnabled()) { 506 TraceTm.jta.debug("synchro=" + synchro); 507 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 508 } 509 510 // check status: should be ACTIVE. 511 boolean markedRollback = false; 512 513 switch (status) { 514 case Status.STATUS_MARKED_ROLLBACK: 515 case Status.STATUS_ROLLEDBACK: 516 markedRollback = true; 517 break; 518 case Status.STATUS_ACTIVE: 519 break; 520 default: 521 String errorMsg = "addSynchronization: bad status = " + StatusHelper.getStatusName(status); 522 TraceTm.jotm.error(errorMsg); 523 throw new IllegalStateException(errorMsg); 524 } 525 526 // Add synchro to the list of local synchros 527 synchroList.addElement(synchro); 528 529 // If transaction marked rollback only, we add synchro but we throw 530 // the correct exception because nobody can presume what will be done in the 531 // Synchronization object, so we must send it beforeCompletion and afterCompletion 532 // even in case of rollback. 533 534 if (markedRollback) { 535 if (TraceTm.jta.isDebugEnabled()) { 536 TraceTm.jta.debug("SubCoordinator.addSynchronization: transaction rollback only"); 537 } 538 throw new RollbackException(); 539 } 540 } 541 542 /** 543 * add a XAResource to the list 544 * 545 * @param xares 546 * XAResource to register 547 * 548 * @return true if this datasource was already known 549 * 550 * @exception IllegalStateException 551 * Thrown if the transaction in the target object is in prepared state or the transaction is 552 * inactive. 553 */ 554 public synchronized boolean addResource(XAResource xares) throws IllegalStateException { 555 556 if (TraceTm.jta.isDebugEnabled()) { 557 TraceTm.jta.debug("xares=" + xares); 558 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 559 } 560 561 // check status: should be ACTIVE. 562 boolean markedRollback = false; 563 564 switch (status) { 565 case Status.STATUS_MARKED_ROLLBACK: 566 markedRollback = true; 567 break; 568 case Status.STATUS_ACTIVE: 569 break; 570 default: 571 String errorMsg = "SubCoordinator.addResource: bad status= " + StatusHelper.getStatusName(status); 572 TraceTm.jotm.error(errorMsg); 573 throw new IllegalStateException(errorMsg); 574 } 575 576 // Check if this DataSource is already known 577 // -> we must register only ONE resource per DataSource. 578 579 boolean found = false; 580 581 for (int i = 0; i < resourceList.size(); i++) { 582 XAResource res = (XAResource) resourceList.elementAt(i); 583 584 try { 585 if (res.isSameRM(xares)) { 586 found = true; 587 break; 588 } 589 } catch (XAException e) { 590 String error = "Cannot send res.isSameRM:" + e + " (error code = " + e.errorCode + ") --" 591 + e.getMessage(); 592 TraceTm.jotm.error("Exception on resource.isSameRM: " + error); 593 } 594 } 595 596 // add this XAResource to the list 597 598 if (found == false) { 599 if (TraceTm.jta.isDebugEnabled()) { 600 TraceTm.jta.debug("new XAResource added to the list"); 601 } 602 resourceList.addElement(xares); 603 } 604 605 // If transaction marked rollback only, we enlist resource but we throw 606 // the correct exception. It is important to enlist the Resource because 607 // if we don't, the database would be updated although transaction has been 608 // rolled back. 609 610 if (markedRollback) { 611 TraceTm.jta.debug("SubCoordinator.addResource: transaction set rollback only"); 612 } 613 614 return found; 615 } 616 617 /** 618 * add this javaxxid to the List 619 * 620 * @param javaxxid 621 */ 622 public synchronized void addJavaxXid(javax.transaction.xa.Xid javaxxid) { 623 if (TraceTm.jta.isDebugEnabled()) { 624 TraceTm.jta.debug("addJavaxXid javaxxid=" + javaxxid); 625 } 626 627 // add this javaxxid to the List at the index location of XAResource 628 javaxxidList.addElement(javaxxid); 629 if (TraceTm.jta.isDebugEnabled()) { 630 TraceTm.jta.debug("new JavaxXid added to the list"); 631 } 632 } 633 634 /** 635 * Get the javaxxid at specified index in the list 636 * 637 * @param xaresindex 638 * @return javaxxid 639 */ 640 public javax.transaction.xa.Xid getJavaxXid(int xaresindex) { 641 javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(xaresindex); 642 return myjavaxxid; 643 } 644 645 /** 646 * return the status of this transaction 647 */ 648 public int getStatus() { 649 if (TraceTm.jta.isDebugEnabled()) { 650 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 651 } 652 return status; 653 } 654 655 /** 656 * set the transaction "rollback only" 657 */ 658 public void setRollbackOnly() { 659 660 if (TraceTm.jta.isDebugEnabled()) { 661 TraceTm.jta.debug("status=" + StatusHelper.getStatusName(status)); 662 } 663 664 switch (status) { 665 case Status.STATUS_ACTIVE: 666 case Status.STATUS_UNKNOWN: 667 case Status.STATUS_PREPARING: 668 status = Status.STATUS_MARKED_ROLLBACK; 669 break; 670 case Status.STATUS_MARKED_ROLLBACK: 671 case Status.STATUS_ROLLING_BACK: 672 break; 673 case Status.STATUS_PREPARED: 674 case Status.STATUS_COMMITTED: 675 case Status.STATUS_ROLLEDBACK: 676 case Status.STATUS_NO_TRANSACTION: 677 case Status.STATUS_COMMITTING: 678 TraceTm.jotm.error("Cannot set transaction as rollback only"); 679 TraceTm.jotm.error("Bad status=" + StatusHelper.getStatusName(status)); 680 break; 681 } 682 } 683 684 // ------------------------------------------------------------------ 685 // private methods 686 // ------------------------------------------------------------------ 687 688 /** 689 * forget Transaction 690 */ 691 private void doForget() throws RemoteException { 692 if (TraceTm.jta.isDebugEnabled()) { 693 TraceTm.jta.debug("SubCoordinator.doForget()"); 694 } 695 696 boolean exception = false; 697 698 for (int i = 0; i < resourceList.size(); i++) { 699 // Tell this Resource to forget the transaction 700 // Remove from the list ? 701 XAResource xar = (XAResource) resourceList.elementAt(i); 702 703 javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i); 704 705 if (TraceTm.jta.isDebugEnabled()) { 706 TraceTm.jta.debug("myjavaxxid= " + myjavaxxid); 707 TraceTm.jta.debug("forgotten with resource= " + xar); 708 } 709 710 try { 711 xar.forget(myjavaxxid); 712 } catch (XAException e) { 713 String error = "Cannot send xar.forget:" + e + " (error code = " + e.errorCode + ") --" 714 + e.getMessage(); 715 TraceTm.jotm.error("Got XAException from xar.forget: " + error); 716 exception = true; 717 } 718 } 719 720 if (exception) { 721 throw new RemoteException("XAException on forget"); 722 } 723 724 unexportObject(this); 725 } 726 727 /** 728 * 2PC phase 1 (prepare) Basically, send prepare on each XAResource and collect the results. 729 */ 730 private synchronized int doPrepare() throws RemoteException { 731 if (TraceTm.jta.isDebugEnabled()) { 732 TraceTm.jta.debug("SubCoordinator.doPrepare()"); 733 } 734 735 int ret = VOTE_READONLY; 736 int errors = 0; 737 738 // No resource 739 740 if (resourceList.size() == 0) { 741 // increment counter for management 742 Current.getCurrent().incrementCommitCounter(); 743 status = Status.STATUS_COMMITTED; 744 return ret; 745 } 746 747 // Creates a log for that transaction, where we will add all the 748 // resources that replied VOTE_COMMIT to prepare. 749 // Do not flush the log on disk before decision to commit. 750 751 log = new SLog(); 752 753 // Sends prepare to each resource while no error 754 // In case of prepare on sub-coord. we may have only 1 resource. 755 756 status = Status.STATUS_PREPARING; 757 758 for (int i = 0; i < resourceList.size(); i++) { 759 XAResource res = (XAResource) resourceList.elementAt(i); 760 javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i); 761 762 Xid xid = new XidImpl(this.xid, i); 763 764 if (errors > 0) { 765 766 if (TraceTm.jta.isDebugEnabled()) { 767 TraceTm.jta.debug("xid= " + xid); 768 TraceTm.jta.debug("myjavaxxid= " + myjavaxxid); 769 TraceTm.jta.debug("rolled back with resource= " + res); 770 } 771 772 try { 773 res.rollback(myjavaxxid); 774 // res.rollback(xid); 775 } catch (XAException e) { 776 String error = "Cannot send res.rollback:" + e + " (error code = " + e.errorCode + ") --" 777 + e.getMessage(); 778 TraceTm.jotm.error("Got XAException from res.rollback: " + error); 779 // Nothing to do ? 780 } 781 } else { 782 if (TraceTm.jta.isDebugEnabled()) { 783 TraceTm.jta.debug("xid= " + xid); 784 TraceTm.jta.debug("myjavaxxid= " + myjavaxxid); 785 TraceTm.jta.debug("prepared with resource= " + res); 786 } 787 788 try { 789 switch (res.prepare(myjavaxxid)) { 790 // switch (res.prepare(xid)) { 791 case XAResource.XA_OK: 792 log.addResource(res, xid, myjavaxxid); 793 ret = VOTE_COMMIT; 794 if (TraceTm.jta.isDebugEnabled()) { 795 TraceTm.jta.debug("Prepare= XA_OK"); 796 } 797 break; 798 case XAResource.XA_RDONLY: 799 if (TraceTm.jta.isDebugEnabled()) { 800 TraceTm.jta.debug("Prepare= XA_RDONLY"); 801 } 802 break; 803 } 804 } catch (XAException e) { 805 String error = "Cannot send res.prepare:" + e + " (error code = " + e.errorCode + ") --" 806 + e.getMessage(); 807 TraceTm.jotm.error("Got XAException from res.prepare: " + error); 808 ret = VOTE_ROLLBACK; 809 errors++; 810 } 811 } 812 } 813 814 // Update the status, depending on vote result 815 // If all resources returned READ_ONLY, we can forget the transaction 816 817 switch (ret) { 818 case VOTE_READONLY: 819 // increment counter for management 820 Current.getCurrent().incrementCommitCounter(); 821 status = Status.STATUS_COMMITTED; 822 if (TraceTm.jta.isDebugEnabled()) { 823 TraceTm.jta.debug("VOTE_READONLY"); 824 } 825 break; 826 case VOTE_COMMIT: 827 status = Status.STATUS_PREPARED; 828 if (TraceTm.jta.isDebugEnabled()) { 829 TraceTm.jta.debug("VOTE_COMMIT"); 830 } 831 break; 832 case VOTE_ROLLBACK: 833 status = Status.STATUS_ROLLING_BACK; 834 if (TraceTm.jta.isDebugEnabled()) { 835 TraceTm.jta.debug("VOTE_ROLLBACK"); 836 } 837 break; 838 } 839 840 // return the global vote 841 return ret; 842 } 843 844 /** 845 * 2PC - phase 2 (commit) See JTM for heuristics management 846 */ 847 private synchronized int doCommit() throws RemoteException { 848 if (TraceTm.jta.isDebugEnabled()) { 849 TraceTm.jta.debug("SubCoordinator.doCommit()"); 850 } 851 852 // We build the Recovery Record in doCommit just in case of a system crash 853 // Store the Recovery Record using HOWL so it can manage for us. 854 // 855 // The Recovery Record consists of two record types: 856 // 1. XA Transaction recovery record 857 // 2. XA Resource recovery record 858 // 859 // The XA Transaction recovery record format: 860 // recovery record type1 (byte[3]) - 'RR1' 861 // recovery record stored date-time (long) - 8 bytes 862 // format id (int) - 4 bytes 863 // length of transaction's gtrid (int) - 4 bytes 864 // global transaction id (byte []) - txxidgti.length bytes 865 // length of transaction's bqual (int) - 4 bytes 866 // bqual (byte []) - txxidbq.length bytes 867 // length of transactions store date-time (int) - 4 bytes 868 // transactions created date-time (byte []) - Date.length bytes 869 // count of XA resources assigned to the transaction (int) - 4 bytes 870 // 871 // The XA Resource recovery record format: 872 // recovery record type2 (byte[3]) = 'RR2' 873 // resource index (int) 874 // length of XA resource (int) - 4 bytes 875 // XA resource (byte []) - xares.length bytes 876 // length of XA resource class name (int) - 4 bytes 877 // XA resource class name (byte []) - resclassname.length bytes 878 // XID.formatid (int) - 4 bytes 879 // length of XID.gtrid assigned to XA resource (int) - 4 bytes 880 // XID.gtrid assigned to XA resource (byte []) - xidgti.length bytes 881 // length of XID.bqual assigned to XA resource (int) - 4 bytes 882 // XID.bqual assigned to XA resource (byte []) - xidbq.length bytes 883 // XID status-state (int) = 4 bytes 884 // 885 // The JOTM Done recovery record format: 886 // recovery record type3 (byte[3]) = 'RR3' 887 // JOTM done value (byte[8]) = 'JOTMDONE' 888 889 // First check that a log is initialized 890 891 if (log == null) { 892 TraceTm.jotm.error("doCommit: no log"); 893 return -1; 894 } 895 896 boolean successfulcommit = true; 897 int commitnb = 0; 898 int heuristicnb = 0; 899 900 List loggedResources = log.getLoggedResources(); 901 List byteloggedResources = log.getByteLoggedResources(); 902 List loggedXids = log.getLoggedXids(); 903 List loggedJavaxXids = log.getLoggedJavaxXids(); 904 905 XACommittingTx xaCommitTx = null; 906 XACommittingTx xaCommitTxRewrite = null; 907 908 byte[][] recoveryBuffer = new byte[loggedResources.size() + 1][]; // loggedResources + 1 (recoveryRecord1) 909 910 byte[] recoveryRecord1 = null; 911 byte[] recoveryRecord2 = null; 912 ByteBuffer rr1 = null; 913 ByteBuffer rr2 = null; 914 915 byte[][] jotmDoneRecord = new byte[1][11]; 916 917 if (Current.getDefaultRecovery()) { 918 Xid txxid = tx.getXid(); 919 920 int txxidfi = txxid.getFormatId(); 921 byte[] txxidgti = txxid.getGlobalTransactionId(); 922 byte[] txxidbq = txxid.getBranchQualifier(); 923 int txxidlength = 4 + txxidgti.length + txxidbq.length; 924 925 // formatid(int) + gridlen(int) + grid + bqlen(int) + bqual 926 int gridlen = txxidgti.length; 927 int bqlen = txxidbq.length; 928 929 String txdate = tx.getTxDate().toString(); 930 int txdatelength = txdate.length(); 931 long rcdate = System.currentTimeMillis(); 932 933 recoveryRecord1 = new byte[3 + 8 + 4 + 4 + gridlen + 4 + bqlen + 4 + txdatelength + 4]; 934 935 rr1 = ByteBuffer.wrap(recoveryRecord1); 936 rr1.put(RT1); 937 rr1.putLong(rcdate); 938 rr1.putInt(txxidfi); 939 rr1.putInt(txxidgti.length); 940 rr1.put(txxidgti); 941 rr1.putInt(txxidbq.length); 942 rr1.put(txxidbq); 943 rr1.putInt(txdatelength); 944 rr1.put(txdate.getBytes()); 945 rr1.putInt(loggedResources.size()); 946 947 recoveryBuffer[0] = rr1.array(); 948 949 for (int i = 0; i < loggedResources.size(); i++) { 950 XAResource res = (XAResource) loggedResources.get(i); 951 byte[] resname = (byte[]) byteloggedResources.get(i); 952 953 Xid xid = (Xid) loggedXids.get(i); 954 int rmindex = 99; // Store 99, at recovery we will store the correct index 955 956 if (TraceTm.recovery.isDebugEnabled()) { 957 TraceTm.recovery.debug("recovery xid= " + xid); 958 TraceTm.recovery.debug("recovery resource= " + res); 959 } 960 961 int resnamelength = resname.length; 962 String resclassname = res.getClass().getName(); 963 int resclassnamelength = resclassname.length(); 964 965 int xidfi = xid.getFormatId(); 966 byte[] xidgti = xid.getGlobalTransactionId(); 967 byte[] xidbq = xid.getBranchQualifier(); 968 int rr2gtilen = xidgti.length; 969 int rr2bqlen = xidbq.length; 970 971 // formatid + : + gridlen + : + bqlen + : + xidgti + : + xidbq 972 973 recoveryRecord2 = new byte[3 + 4 + 4 + resnamelength + 4 + resclassnamelength + 4 + 4 + rr2gtilen + 4 974 + rr2bqlen + 4]; 975 976 rr2 = ByteBuffer.wrap(recoveryRecord2); 977 rr2.put(RT2); 978 rr2.putInt(rmindex); 979 rr2.putInt(resnamelength); 980 rr2.put(resname); 981 rr2.putInt(resclassnamelength); 982 rr2.put(resclassname.getBytes()); 983 rr2.putInt(xidfi); 984 rr2.putInt(xidgti.length); 985 rr2.put(xidgti); 986 rr2.putInt(xidbq.length); 987 rr2.put(xidbq); 988 rr2.putInt(status); 989 990 if (TraceTm.recovery.isDebugEnabled()) { 991 TraceTm.recovery.debug("Prepare Init RR2 to Recovery Buffer"); 992 } 993 recoveryBuffer[i + 1] = rr2.array(); // First record (0) is always rr1 994 } 995 996 try { 997 xaCommitTx = TransactionRecoveryImpl.getTransactionRecovery().howlCommitLog(recoveryBuffer); 998 } catch (Exception e) { 999 // If we cannot write the Log, we cannot perform recovery, rollback transaction 1000 status = Status.STATUS_ROLLEDBACK; 1001 1002 String howlerror = "Cannot howlCommitLog:" + e + " --" + e.getMessage(); 1003 TraceTm.jotm.error("Got LogException from howlCommitLog: " + howlerror); 1004 xaCommitTx = null; 1005 doAfterCompletion(); 1006 log.forgetLog(); 1007 1008 // KULRICE-9919 : Updated to include the rollback cause 1009 throw new JotmTransactionRolledbackException(e); 1010 // END KULRICE-9919 1011 } 1012 } 1013 1014 // Status Transaction = committing 1015 1016 status = Status.STATUS_COMMITTING; 1017 if (TraceTm.recovery.isDebugEnabled()) { 1018 TraceTm.recovery.debug("Status Committing"); 1019 } 1020 1021 // Send commit to each resource prepared 1022 1023 for (int i = 0; i < loggedResources.size(); i++) { 1024 XAResource res = (XAResource) loggedResources.get(i); 1025 1026 // javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i); 1027 javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) loggedJavaxXids.get(i); 1028 Xid xid = (Xid) loggedXids.get(i); 1029 1030 // Commit every resource that have been logged even if any of 1031 // the commit resources fail. During recovery, the administrator 1032 // will resolve any incomplete transactions. 1033 1034 if (TraceTm.jta.isDebugEnabled()) { 1035 TraceTm.jta.debug("xid= " + xid); 1036 TraceTm.jta.debug("myjavaxxid= " + myjavaxxid); 1037 TraceTm.jta.debug("attempting commit with resource= " + res); 1038 } 1039 1040 if (Current.getDefaultRecovery()) { 1041 int rmindex = 99; // Store 99, at recovery we will store the correct index 1042 byte[] resname = (byte[]) byteloggedResources.get(i); 1043 int resnamelength = resname.length; 1044 1045 String resclassname = res.getClass().getName(); 1046 int resclassnamelength = resclassname.length(); 1047 int xidfi = xid.getFormatId(); 1048 byte[] xidgti = xid.getGlobalTransactionId(); 1049 byte[] xidbq = xid.getBranchQualifier(); 1050 int rr2gtilen = xidgti.length; 1051 int rr2bqlen = xidbq.length; 1052 1053 // formatid + : + gridlen + : + bqlen + : + xidgti + : + xidbq 1054 1055 recoveryRecord2 = new byte[3 + 4 + 4 + resnamelength + 4 + resclassnamelength + 4 + 4 + rr2gtilen + 4 1056 + rr2bqlen + 4]; 1057 1058 rr2 = ByteBuffer.wrap(recoveryRecord2); 1059 1060 rr2.put(RT2); 1061 rr2.putInt(rmindex); 1062 rr2.putInt(resnamelength); 1063 rr2.put(resname); 1064 rr2.putInt(resclassnamelength); 1065 rr2.put(resclassname.getBytes()); 1066 rr2.putInt(xidfi); 1067 rr2.putInt(xidgti.length); 1068 rr2.put(xidgti); 1069 rr2.putInt(xidbq.length); 1070 rr2.put(xidbq); 1071 } 1072 1073 // commit resource 1074 1075 try { 1076 res.commit(myjavaxxid, false); 1077 // res.commit(xid, false); 1078 1079 if (Current.getDefaultRecovery()) { 1080 rr2.putInt(Status.STATUS_COMMITTED); 1081 if (TraceTm.recovery.isDebugEnabled()) { 1082 TraceTm.recovery.debug("Status Committed"); 1083 } 1084 } 1085 1086 commitnb++; // an XAresource was committed 1087 } catch (XAException e) { 1088 switch (e.errorCode) { 1089 case XAException.XA_HEURHAZ: 1090 case XAException.XA_HEURCOM: 1091 case XAException.XA_HEURRB: 1092 case XAException.XA_HEURMIX: 1093 if (TraceTm.jta.isDebugEnabled()) { 1094 TraceTm.jta.debug("Heuristic condition= " + e.getMessage()); 1095 } 1096 1097 if (Current.getDefaultRecovery()) { 1098 rr2.putInt(Status.STATUS_UNKNOWN); 1099 if (TraceTm.recovery.isDebugEnabled()) { 1100 TraceTm.recovery.debug("Status Unknown"); 1101 } 1102 } 1103 break; 1104 case XAException.XAER_RMERR: 1105 case XAException.XAER_NOTA: 1106 case XAException.XAER_INVAL: 1107 case XAException.XAER_PROTO: 1108 case XAException.XAER_RMFAIL: 1109 if (TraceTm.jta.isDebugEnabled()) { 1110 TraceTm.jta.debug("RM error= " + e.getMessage()); 1111 } 1112 1113 if (Current.getDefaultRecovery()) { 1114 rr2.putInt(Status.STATUS_COMMITTING); 1115 if (TraceTm.recovery.isDebugEnabled()) { 1116 TraceTm.recovery.debug("Status Committing"); 1117 } 1118 } 1119 break; 1120 default: 1121 if (TraceTm.jta.isDebugEnabled()) { 1122 TraceTm.jta.debug("Default error= " + e.getMessage()); 1123 } 1124 1125 if (Current.getDefaultRecovery()) { 1126 rr2.putInt(Status.STATUS_ROLLEDBACK); 1127 if (TraceTm.recovery.isDebugEnabled()) { 1128 TraceTm.recovery.debug("Status Rolledback"); 1129 } 1130 } 1131 } 1132 1133 String error = "Cannot send res.commit:" + e + " (error code = " + e.errorCode + ") --" 1134 + e.getMessage(); 1135 TraceTm.jotm.error("Got XAException from res.commit: " + error); 1136 1137 successfulcommit = false; 1138 if (commitnb > 0) { 1139 heuristicnb++; 1140 } 1141 } 1142 1143 if (Current.getDefaultRecovery()) { 1144 if (TraceTm.recovery.isDebugEnabled()) { 1145 TraceTm.recovery.debug("Prepare New RR2 to Recovery Buffer"); 1146 } 1147 recoveryBuffer[i + 1] = rr2.array(); // First record (0) is always rr1 1148 } 1149 } 1150 1151 if (successfulcommit) { 1152 // increment counter for management 1153 Current.getCurrent().incrementCommitCounter(); 1154 // Everything's fine. 1155 status = Status.STATUS_COMMITTED; 1156 1157 if (TraceTm.recovery.isDebugEnabled()) { 1158 TraceTm.recovery.debug("Status Committed"); 1159 } 1160 1161 if (Current.getDefaultRecovery()) { 1162 try { 1163 if (TraceTm.recovery.isDebugEnabled()) { 1164 TraceTm.recovery.debug("Done howl log, all okay"); 1165 } 1166 1167 jotmDoneRecord[0] = JOTMDONE; 1168 TransactionRecoveryImpl.getTransactionRecovery().howlDoneLog(jotmDoneRecord, xaCommitTx); 1169 } catch (Exception f) { 1170 String howlerror = "Cannot howlDoneLog:" + f + "--" + f.getMessage(); 1171 TraceTm.jotm.error("Got LogException from howlDoneLog: " + howlerror); 1172 } 1173 } 1174 1175 doAfterCompletion(); 1176 log.forgetLog(); 1177 1178 if (TraceTm.jta.isDebugEnabled()) { 1179 TraceTm.jta.debug("SubCoordinator.doCommit(): EXIT 0"); 1180 } 1181 1182 return 0; 1183 } 1184 1185 /***** 1186 * if (heuristicnb == 0) { // commits on all XAResources failed, just rollback // Transaction has been 1187 * eventually rolled back status = Status.STATUS_ROLLEDBACK; 1188 * 1189 * if (Current.getDefaultRecovery()) { try { jotmDoneRecord [0] = JOTMDONE; 1190 * TransactionRecoveryImpl.getTransactionRecovery().howlDoneLog(jotmDoneRecord, xaCommitTx); } catch (Exception 1191 * f) { String howlerror = "Cannot howlDoneLog" + f + "--" + f.getMessage(); 1192 * TraceTm.jotm.error("Got LogException from howlDoneLog: "+ howlerror); } } 1193 * 1194 * doAfterCompletion(); log.forgetLog(); 1195 * 1196 * throw new TransactionRolledbackException(); } 1197 *****/ 1198 1199 // Log heuristics if errors 1200 1201 // if (heuristicnb != 0) { // some XAResource commits succeeded, others failed, heuristicmixed 1202 1203 if (Current.getDefaultRecovery()) { 1204 try { 1205 if (TraceTm.recovery.isDebugEnabled()) { 1206 TraceTm.recovery.debug("Rewrite HowlCommitLog"); 1207 } 1208 xaCommitTxRewrite = TransactionRecoveryImpl.getTransactionRecovery().howlCommitLog(recoveryBuffer); 1209 } catch (Exception e) { 1210 // If we cannot write the Log, we cannot perform recovery, rollback transaction 1211 status = Status.STATUS_UNKNOWN; 1212 if (TraceTm.recovery.isDebugEnabled()) { 1213 TraceTm.recovery.debug("Status Unknown"); 1214 } 1215 1216 String howlerror = "Cannot howlCommitLog:" + e + " --" + e.getMessage(); 1217 TraceTm.jotm.error("Got LogException from howlCommitLog: " + howlerror); 1218 xaCommitTx = null; 1219 1220 doAfterCompletion(); 1221 log.forgetLog(); 1222 1223 throw new JotmTransactionRolledbackException(e); 1224 } 1225 1226 // Transaction state is unknown, now job for administrator 1227 // status = Status.STATUS_UNKNOWN; 1228 1229 try { 1230 jotmDoneRecord[0] = JOTMDONE; 1231 TransactionRecoveryImpl.getTransactionRecovery().howlDoneLog(jotmDoneRecord, xaCommitTx); 1232 } catch (Exception f) { 1233 String howlerror = "Cannot howlDoneLog" + f + "--" + f.getMessage(); 1234 TraceTm.jotm.error("Got LogException from howlDoneLog: " + howlerror); 1235 } 1236 } 1237 // } 1238 1239 status = Status.STATUS_UNKNOWN; 1240 1241 if (TraceTm.recovery.isDebugEnabled()) { 1242 TraceTm.recovery.debug("Status Unknown"); 1243 } 1244 1245 doAfterCompletion(); 1246 1247 if (TraceTm.jta.isDebugEnabled()) { 1248 TraceTm.jta.debug("SubCoordinator.doCommit(): Exit -1"); 1249 } 1250 1251 return -1; 1252 } 1253 1254 /** 1255 * 1PC (commit one phase) 1256 */ 1257 private synchronized void doOnePhaseCommit() throws RemoteException { 1258 if (TraceTm.jta.isDebugEnabled()) { 1259 TraceTm.jta.debug("SubCoordinator.doOnePhaseCommit()"); 1260 } 1261 1262 // Only 1 resource: commit with onePhase=true 1263 1264 status = Status.STATUS_COMMITTING; 1265 1266 XAResource res = (XAResource) resourceList.elementAt(0); 1267 javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(0); 1268 1269 if (TraceTm.jta.isDebugEnabled()) { 1270 TraceTm.jta.debug("myjavaxxid= " + myjavaxxid); 1271 TraceTm.jta.debug("one phase commit with resource= " + res); 1272 } 1273 1274 try { 1275 res.commit(myjavaxxid, true); 1276 1277 // increment counter for management 1278 Current.getCurrent().incrementCommitCounter(); 1279 status = Status.STATUS_COMMITTED; 1280 } catch (XAException e) { 1281 status = Status.STATUS_UNKNOWN; 1282 String error = "Cannot send res.commit:" + e + " (error code = " + e.errorCode + ") --" + e.getMessage(); 1283 TraceTm.jotm.error("Got XAException from res.commit: " + error); 1284 1285 if (e.errorCode == XAException.XA_RBROLLBACK) { 1286 // KULRICE-9919 : Updated to include the rollback cause 1287 throw new JotmTransactionRolledbackException("XAException:" + error, e); 1288 // END KULRICE-9919 1289 } 1290 throw new RemoteException("XAException:" + error); 1291 } finally { 1292 doAfterCompletion(); 1293 } 1294 } 1295 1296 /** 1297 * Rollback every resource involved 1298 */ 1299 1300 private synchronized void doRollback() throws RemoteException { 1301 if (TraceTm.jta.isDebugEnabled()) { 1302 TraceTm.jta.debug("SubCoordinator.doRollback()"); 1303 } 1304 1305 status = Status.STATUS_ROLLEDBACK; 1306 boolean heurroll = false; 1307 String heuristic = null; 1308 1309 // roll back each resource 1310 for (int i = 0; i < resourceList.size(); i++) { 1311 1312 XAResource res = (XAResource) resourceList.elementAt(i); 1313 javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList.elementAt(i); 1314 1315 if (TraceTm.jta.isDebugEnabled()) { 1316 TraceTm.jta.debug("myjavaxxid= " + myjavaxxid); 1317 TraceTm.jta.debug("rolled back with resource= " + res); 1318 } 1319 1320 // Rollback every resource that have been logged even if any of 1321 // the rollback resources fail. During recovery, the administrator 1322 // will resolve any incomplete transactions. 1323 1324 try { 1325 res.rollback(myjavaxxid); 1326 } catch (XAException e) { 1327 switch (e.errorCode) { 1328 case XAException.XA_HEURHAZ: 1329 case XAException.XA_HEURCOM: 1330 case XAException.XA_HEURRB: 1331 case XAException.XA_HEURMIX: 1332 heuristic = "Heuristic condition= " + e.errorCode + "--" + e.getMessage(); 1333 TraceTm.jta.warn(heuristic); 1334 heurroll = true; 1335 break; 1336 case XAException.XAER_RMERR: 1337 case XAException.XAER_NOTA: 1338 case XAException.XAER_INVAL: 1339 case XAException.XAER_PROTO: 1340 case XAException.XAER_RMFAIL: 1341 if (TraceTm.jta.isDebugEnabled()) { 1342 TraceTm.jta.debug("RM error= " + e.errorCode + "--" + e.getMessage()); 1343 } 1344 break; 1345 default: 1346 if (TraceTm.jta.isDebugEnabled()) { 1347 TraceTm.jta.debug("Default error= " + e.errorCode + "--" + e.getMessage()); 1348 } 1349 } 1350 } 1351 } 1352 1353 // raise Heuristic exception if XAResource returned heuristic 1354 // no need to throw an exception for other XAResource errors, 1355 // the resource (xid) has been rolled back anyway 1356 1357 if (heurroll) { 1358 // pass the last heuristic error 1359 throw new HeuristicRollback(heuristic); 1360 } 1361 1362 // increment counter for management 1363 1364 Current.getCurrent().incrementRollbackCounter(); 1365 1366 doAfterCompletion(); 1367 } 1368 1369 /** 1370 * before completion 1371 * 1372 * @param boolean true if completion ok, false if rollback 1373 */ 1374 private void doBeforeCompletion(boolean committing) { 1375 if (TraceTm.jta.isDebugEnabled()) { 1376 TraceTm.jta.debug("doBeforeCompletion committing= " + committing); 1377 } 1378 if (beforeCompletionDone) { 1379 return; 1380 } 1381 1382 // Unset the timer for this transaction, if any 1383 tx.unsetTimer(); 1384 1385 // For each synchro, send beforeCompletion (not if rollback) 1386 1387 if (committing && synchroList.size() > 0) { 1388 // We must be in the correct transaction context 1389 // See JTA spec. page 13 (3.3.2) 1390 // because at beforeCompletion, the bean will write its cache 1391 1392 // Check the trivial case where we already have the correct tx context 1393 1394 Transaction mytx = null; 1395 boolean suspended = false; 1396 boolean resumed = false; 1397 1398 try { 1399 mytx = tm.getTransaction(); 1400 } catch (SystemException e) { 1401 if (TraceTm.jta.isDebugEnabled()) { 1402 String error = "Cannot get transaction:" + e + "--" + e.getMessage(); 1403 TraceTm.jta.debug(error); 1404 } 1405 } 1406 1407 // Suspend if another tx context 1408 1409 if (mytx != null && mytx.equals(tx) == false) { 1410 try { 1411 tm.suspend(); 1412 suspended = true; 1413 } catch (SystemException e) { 1414 if (TraceTm.jta.isDebugEnabled()) { 1415 String error = "Cannot suspend transaction:" + e + "--" + e.getMessage(); 1416 TraceTm.jta.debug(error); 1417 } 1418 } 1419 } 1420 1421 // Resume the good tx context 1422 1423 if (mytx == null || suspended) { 1424 try { 1425 tm.resume(tx); 1426 resumed = true; 1427 } catch (SystemException e) { 1428 if (TraceTm.jta.isDebugEnabled()) { 1429 String error = "Cannot resume transaction:" + e + "--" + e.getMessage(); 1430 TraceTm.jta.debug(error); 1431 } 1432 } catch (InvalidTransactionException e) { 1433 if (TraceTm.jta.isDebugEnabled()) { 1434 String error = "Cannot resume transaction:" + e + "--" + e.getMessage(); 1435 TraceTm.jta.debug(error); 1436 } 1437 } catch (IllegalStateException e) { 1438 if (TraceTm.jta.isDebugEnabled()) { 1439 String error = "Cannot resume transaction:" + e + "--" + e.getMessage(); 1440 TraceTm.jta.debug(error); 1441 } 1442 } 1443 } 1444 1445 // Call the synchronizations 1446 // beforeCompletion may set the TX rollbackonly if something goes wrong 1447 1448 if (TraceTm.jta.isDebugEnabled()) { 1449 TraceTm.jta.debug("sychronization list size= " + synchroList.size()); 1450 } 1451 1452 for (int i = 0; i < synchroList.size(); i++) { 1453 Synchronization sync = (Synchronization) synchroList.elementAt(i); 1454 1455 if (TraceTm.jta.isDebugEnabled()) { 1456 TraceTm.jta.debug("Calling Synchro " + sync); 1457 } 1458 1459 try { 1460 // This can register a new synchro, modifying synchroList! 1461 // TODO add a synchronized on list, and call beforeCompletion 1462 // outside the block. 1463 sync.beforeCompletion(); 1464 } catch (RuntimeException e) { 1465 status = Status.STATUS_MARKED_ROLLBACK; 1466 rollbackCause = e; 1467 if (TraceTm.jta.isDebugEnabled()) { 1468 String error = "Cannot sync.beforeCompletion:" + e + "--" + e.getMessage(); 1469 TraceTm.jta.debug(error); 1470 } 1471 } 1472 } 1473 1474 // reset context as it was before 1475 1476 if (resumed) { 1477 try { 1478 tm.suspend(); 1479 } catch (SystemException e) { 1480 if (TraceTm.jta.isDebugEnabled()) { 1481 String error = "Cannot suspend transaction:" + e + "--" + e.getMessage(); 1482 TraceTm.jta.debug(error); 1483 } 1484 } 1485 } 1486 1487 if (suspended) { 1488 try { 1489 tm.resume(mytx); 1490 resumed = true; 1491 } catch (SystemException e) { 1492 if (TraceTm.jta.isDebugEnabled()) { 1493 String error = "Cannot resume transaction:" + e + "--" + e.getMessage(); 1494 TraceTm.jta.debug(error); 1495 } 1496 } catch (InvalidTransactionException e) { 1497 if (TraceTm.jta.isDebugEnabled()) { 1498 String error = "Cannot resume transaction:" + e + "--" + e.getMessage(); 1499 TraceTm.jta.debug(error); 1500 } 1501 } catch (IllegalStateException e) { 1502 if (TraceTm.jta.isDebugEnabled()) { 1503 String error = "Cannot resume transaction:" + e + "--" + e.getMessage(); 1504 TraceTm.jta.debug(error); 1505 } 1506 } 1507 } 1508 } 1509 1510 beforeCompletionDone = true; 1511 } 1512 1513 /** 1514 * after completion 1515 */ 1516 private void doAfterCompletion() { 1517 if (TraceTm.jta.isDebugEnabled()) { 1518 TraceTm.jta.debug("doAfterCompletion()"); 1519 } 1520 1521 // For each synchro, send afterCompletion 1522 // / CompletedTransactionListener has been replaced by Synchronization 1523 1524 if (TraceTm.jta.isDebugEnabled()) { 1525 TraceTm.jta.debug("sychronization list size= " + synchroList.size()); 1526 } 1527 1528 for (int i = 0; i < synchroList.size(); i++) { 1529 Synchronization sync = (Synchronization) synchroList.elementAt(i); 1530 1531 if (TraceTm.jta.isDebugEnabled()) { 1532 TraceTm.jta.debug("Synchronization sync= " + sync); 1533 TraceTm.jta.debug("sync.afterCompletion status= " + StatusHelper.getStatusName(status)); 1534 } 1535 1536 sync.afterCompletion(status); 1537 } 1538 1539 // Forget this transaction. 1540 // LATER: 1541 // - Should not forget it in case of heuristics (for recovery) 1542 // - May be this could be deferred in case of retry from a client: use a timer. 1543 Current.getCurrent().forgetTx(tx.getXid()); 1544 } 1545 1546 /** 1547 * return index in resourceList of this XAResource 1548 * 1549 * @param xares 1550 * the XAResource 1551 * @return index 1552 */ 1553 public int getXaresIndex(XAResource xares) { 1554 1555 if (TraceTm.jta.isDebugEnabled()) { 1556 TraceTm.jta.debug("getXaresIndex xares= " + xares); 1557 TraceTm.jta.debug("resourceList.size= " + resourceList.size()); 1558 } 1559 1560 int xaresIndex = -1; 1561 1562 // first, search for an XAResource with the same object reference 1563 if (TraceTm.jta.isDebugEnabled()) { 1564 TraceTm.jta.debug("search xares with same obj ref"); 1565 } 1566 1567 for (int i = 0; i < resourceList.size(); i++) { 1568 XAResource res = (XAResource) resourceList.elementAt(i); 1569 1570 if (TraceTm.jta.isDebugEnabled()) { 1571 TraceTm.jta.debug("res= " + res); 1572 } 1573 1574 if (res.equals(xares)) { 1575 xaresIndex = i; 1576 break; 1577 } 1578 } 1579 1580 // if not found, search for a xares with the same RM 1581 if (xaresIndex == -1) { 1582 if (TraceTm.jta.isDebugEnabled()) { 1583 TraceTm.jta.debug("not found -> search for xares with same RM"); 1584 } 1585 1586 for (int i = 0; i < resourceList.size(); i++) { 1587 XAResource res = (XAResource) resourceList.elementAt(i); 1588 1589 if (TraceTm.jta.isDebugEnabled()) { 1590 TraceTm.jta.debug("res= " + res); 1591 } 1592 1593 try { 1594 if (res.isSameRM(xares)) { 1595 xaresIndex = i; 1596 break; 1597 } 1598 } catch (XAException e) { 1599 if (TraceTm.jta.isDebugEnabled()) { 1600 String error = "res.isSameRm exception:" + e + "--" + e.getMessage(); 1601 TraceTm.jta.debug(error); 1602 } 1603 } 1604 } 1605 } 1606 1607 if (TraceTm.jta.isDebugEnabled()) { 1608 TraceTm.jta.debug("xaresIndex= " + xaresIndex); 1609 } 1610 1611 return xaresIndex; 1612 } 1613 1614 }