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