0001: /*
0002: * @(#) SubCoordinator.java
0003: *
0004: * JOTM: Java Open Transaction Manager
0005: *
0006: *
0007: * This module was originally developed by
0008: *
0009: * - Bull S.A. as part of the JOnAS application server code released in
0010: * July 1999 (www.bull.com)
0011: *
0012: * --------------------------------------------------------------------------
0013: * The original code and portions created by Bull SA are
0014: * Copyright (c) 1999 BULL SA
0015: * All rights reserved.
0016: *
0017: * Redistribution and use in source and binary forms, with or without
0018: * modification, are permitted provided that the following conditions are met:
0019: *
0020: * -Redistributions of source code must retain the above copyright notice, this
0021: * list of conditions and the following disclaimer.
0022: *
0023: * -Redistributions in binary form must reproduce the above copyright notice,
0024: * this list of conditions and the following disclaimer in the documentation
0025: * and/or other materials provided with the distribution.
0026: *
0027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0028: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0029: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0030: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0031: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0032: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0033: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0034: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0035: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0036: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0037: * POSSIBILITY OF SUCH DAMAGE.
0038: *
0039: * --------------------------------------------------------------------------
0040: * Contributor(s):
0041: * 01/11/06 Christophe Ney cney@batisseurs.com
0042: * Added ResourceManagerListener mechanism to remove ThreadData
0043: * dependency.
0044: *
0045: * 02/06/18 Marek Prochazka
0046: * In addSynchronization() synchronizations are processed the
0047: * same way as resources. We add synchro although transaction is
0048: * marked rollback-only to be able to send beforeCompletion
0049: *
0050: * --------------------------------------------------------------------------
0051: * $Id: SubCoordinator.java,v 1.37 2005/05/10 22:50:54 tonyortiz Exp $
0052: * --------------------------------------------------------------------------
0053: */
0054:
0055: package org.objectweb.jotm;
0056:
0057: import java.rmi.RemoteException;
0058:
0059: import java.util.List;
0060: import java.util.Vector;
0061: import java.nio.ByteBuffer;
0062: import javax.transaction.*;
0063: import javax.transaction.xa.XAException;
0064: import javax.transaction.xa.XAResource; // import javax.transaction.xa.Xid;
0065: import javax.rmi.PortableRemoteObject;
0066:
0067: import org.objectweb.howl.log.xa.XACommittingTx;
0068:
0069: /**
0070: * Log associated to this transaction coordinator
0071: */
0072: class SLog {
0073:
0074: private Vector loggedResources = new Vector();
0075:
0076: private Vector loggedXids = new Vector();
0077:
0078: int decision;
0079: static final int DECISION_TO_COMMIT = 1;
0080: static final int DECISION_TO_ROLLBACK = 2;
0081:
0082: public void addResource(XAResource res, Xid xid) {
0083: if (TraceTm.jta.isDebugEnabled()) {
0084: TraceTm.jta.debug("res= " + res);
0085: TraceTm.jta.debug("xid= " + xid);
0086: }
0087: loggedResources.addElement(res);
0088: loggedXids.addElement(xid);
0089: }
0090:
0091: /**
0092: * @return a List of logged Resources
0093: */
0094: public List getLoggedResources() {
0095: if (TraceTm.jta.isDebugEnabled()) {
0096: TraceTm.jta.debug("logged resources=" + loggedResources);
0097: }
0098: return loggedResources;
0099: }
0100:
0101: public List getLoggedXids() {
0102: return loggedXids;
0103: }
0104:
0105: public void flushLog(int decide) {
0106: if (TraceTm.jta.isDebugEnabled()) {
0107: TraceTm.jta.debug("decide=" + decide);
0108: }
0109: decision = decide;
0110:
0111: // XXX serialize log on disk
0112: }
0113:
0114: public void forgetLog() {
0115: if (TraceTm.jta.isDebugEnabled()) {
0116: TraceTm.jta.debug("forget log");
0117: }
0118: // XXX remove file on disk
0119: }
0120: }
0121:
0122: /**
0123: * This object is the local coordinator. It may be registered as
0124: * sub-coordinator in case of distributed transaction, so it must
0125: * be callable remotely and implement Resource
0126: */
0127: public class SubCoordinator extends PortableRemoteObject implements
0128: Resource {
0129:
0130: // ------------------------------------------------------------------
0131: // Object state
0132: // ------------------------------------------------------------------
0133:
0134: /**
0135: * @serial
0136: */
0137: private TransactionImpl tx = null;
0138:
0139: /**
0140: * List of Synchronization objects
0141: * @serial
0142: */
0143: private Vector synchroList = new Vector();
0144:
0145: /**
0146: * List of XAResource objects
0147: * @serial
0148: */
0149: private Vector resourceList = new Vector();
0150: private Vector javaxxidList = new Vector();
0151:
0152: /**
0153: * Keep a reference on TransactionManager
0154: * @serial
0155: */
0156: private TransactionManager tm;
0157:
0158: /**
0159: * @serial
0160: */
0161: private Xid xid = null;
0162:
0163: /**
0164: * @serial
0165: */
0166: private SLog log = null;
0167:
0168: /**
0169: * javax.transaction.Status
0170: * <dl>
0171: * <dt>ACTIVE</dt><dd>transaction started, commit phase not started</dd>
0172: * <dt>PREPARING</dt><dd>prepare is being sent to resources</dd>
0173: * <dt>PREPARED</dt><dd>prepare is done. Must commit now</dd>
0174: * <dt>COMMITTING</dt><dd>commit is being sent to resources</dd>
0175: * <dt>COMMITTED</dt><dd>commit was successful</dd>
0176: * <dt>ROLLING_BACK</dt><dd>not used</dd>
0177: * <dt>MARKED_ROLLBACK</dt><dd>setRollbackOnly has been called</dd>
0178: * <dt>ROLLEDBACK</dt>
0179: * <dd>transaction has been rolled back (or prepare returned "vote_rollback")</dd>
0180: * <dt>UNKNOWN</dt><dd>commit raised heuristics</dd>
0181: * <dt>NO_TRANSACTION</dt><dd>cannot get the status value from JTM</dd>
0182: * </dl>
0183: * @serial
0184: */
0185: private int status = Status.STATUS_ACTIVE;
0186:
0187: /**
0188: * @serial
0189: */
0190: private boolean beforeCompletionDone = false;
0191:
0192: // ------------------------------------------------------------------
0193: // Constructors
0194: // ------------------------------------------------------------------
0195:
0196: /**
0197: * constructor used by TransactionImpl
0198: */
0199:
0200: SubCoordinator(TransactionImpl tx, Xid xid) throws RemoteException {
0201:
0202: if (TraceTm.jta.isDebugEnabled()) {
0203: TraceTm.jta.debug("tx=" + tx + ", xid=" + xid);
0204: }
0205:
0206: this .tx = tx;
0207: this .xid = xid;
0208: this .tm = Current.getTransactionManager();
0209:
0210: // increment counter for management
0211: Current.getCurrent().incrementBeginCounter();
0212: beforeCompletionDone = false;
0213: }
0214:
0215: // ------------------------------------------------------------------
0216: // Resource interface
0217: // ------------------------------------------------------------------
0218:
0219: /**
0220: * phase 1 of the 2PC.
0221: *
0222: * @return int vote commit, rollback, or readonly.
0223: */
0224:
0225: public int prepare() throws RemoteException {
0226:
0227: if (TraceTm.jta.isDebugEnabled()) {
0228: TraceTm.jta.debug("status="
0229: + StatusHelper.getStatusName(status));
0230: }
0231:
0232: try {
0233: tx.doDetach(XAResource.TMSUCCESS);
0234: } catch (SystemException e) {
0235:
0236: if (TraceTm.jta.isDebugEnabled()) {
0237: String error = "Error when detaching XAResource:" + e
0238: + "--" + e.getMessage();
0239: TraceTm.jta.debug(error);
0240: }
0241: }
0242:
0243: switch (status) {
0244: case Status.STATUS_MARKED_ROLLBACK:
0245: doBeforeCompletion(false);
0246: doRollback();
0247: return Resource.VOTE_ROLLBACK;
0248: case Status.STATUS_COMMITTED:
0249: return Resource.VOTE_COMMIT;
0250: default:
0251: doBeforeCompletion(true);
0252: break;
0253: }
0254:
0255: // Recheck Status after doBeforeCompletion
0256:
0257: if (status == Status.STATUS_MARKED_ROLLBACK) {
0258: TraceTm.jotm
0259: .info("Rollback during beforeCompletion in SubCoordinator.prepare");
0260: doRollback();
0261: return Resource.VOTE_ROLLBACK;
0262: }
0263:
0264: int ret = doPrepare();
0265:
0266: if (ret == Resource.VOTE_READONLY) {
0267: // Transaction completed for this Resource
0268: doAfterCompletion();
0269: }
0270:
0271: if (TraceTm.jta.isDebugEnabled()) {
0272: TraceTm.jta.debug("vote = " + ret);
0273: }
0274:
0275: return ret;
0276: }
0277:
0278: /**
0279: * rollback transaction
0280: */
0281:
0282: public void rollback() throws RemoteException {
0283:
0284: if (TraceTm.jta.isDebugEnabled()) {
0285: TraceTm.jta.debug("status="
0286: + StatusHelper.getStatusName(status));
0287: }
0288:
0289: try {
0290: tx.doDetach(XAResource.TMSUCCESS);
0291: } catch (SystemException e) {
0292: if (TraceTm.jta.isDebugEnabled()) {
0293: String error = "Error when detaching XAResource:" + e
0294: + "--" + e.getMessage();
0295: TraceTm.jta.debug(error);
0296: }
0297: }
0298:
0299: switch (status) {
0300: case Status.STATUS_ACTIVE:
0301: case Status.STATUS_MARKED_ROLLBACK:
0302: case Status.STATUS_ROLLING_BACK:
0303: if (TraceTm.jotm.isDebugEnabled()) {
0304: TraceTm.jotm.debug("transaction rolling back");
0305: }
0306: break;
0307: case Status.STATUS_PREPARED:
0308: if (TraceTm.jotm.isDebugEnabled()) {
0309: TraceTm.jotm
0310: .debug("should not rollback a prepared transaction");
0311: }
0312: break;
0313: case Status.STATUS_ROLLEDBACK:
0314: if (TraceTm.jotm.isDebugEnabled()) {
0315: TraceTm.jotm.debug("already rolledback");
0316: }
0317: return;
0318: default:
0319: TraceTm.jotm.error("rollback: bad status: "
0320: + StatusHelper.getStatusName(status));
0321: return;
0322: }
0323:
0324: doBeforeCompletion(false);
0325: doRollback();
0326: }
0327:
0328: /**
0329: * phase 2 of the 2PC.
0330: */
0331:
0332: public void commit() throws RemoteException {
0333:
0334: if (TraceTm.jta.isDebugEnabled()) {
0335: TraceTm.jta.debug("status="
0336: + StatusHelper.getStatusName(status));
0337: }
0338:
0339: switch (status) {
0340: case Status.STATUS_PREPARED:
0341: break;
0342: default:
0343: TraceTm.jotm.error("commit: bad status: "
0344: + StatusHelper.getStatusName(status));
0345: return;
0346: }
0347:
0348: doCommit();
0349: }
0350:
0351: /**
0352: * commit 1 phase. Called either from JTM (distributed transaction) or
0353: * from Transaction.commit (local transaction).
0354: */
0355:
0356: public void commit_one_phase() throws RemoteException {
0357:
0358: if (TraceTm.jta.isDebugEnabled()) {
0359: TraceTm.jta.debug("status="
0360: + StatusHelper.getStatusName(status));
0361: }
0362:
0363: switch (status) {
0364: case Status.STATUS_ROLLEDBACK:
0365: try {
0366: tx.doDetach(XAResource.TMSUCCESS);
0367: } catch (SystemException e) {
0368:
0369: if (TraceTm.jta.isDebugEnabled()) {
0370: String error = "Error when detaching XAResource:"
0371: + e + "--" + e.getMessage();
0372: TraceTm.jta.debug(error);
0373: }
0374: }
0375: throw new TransactionRolledbackException();
0376: case Status.STATUS_MARKED_ROLLBACK:
0377: doBeforeCompletion(false);
0378: try {
0379: tx.doDetach(XAResource.TMSUCCESS);
0380: } catch (SystemException e) {
0381:
0382: if (TraceTm.jta.isDebugEnabled()) {
0383: String error = "Error when detaching XAResource:"
0384: + e + "--" + e.getMessage();
0385: TraceTm.jta.debug(error);
0386: }
0387: }
0388: doRollback();
0389: throw new TransactionRolledbackException();
0390: case Status.STATUS_COMMITTED:
0391: try {
0392: tx.doDetach(XAResource.TMSUCCESS);
0393: } catch (SystemException e) {
0394:
0395: if (TraceTm.jta.isDebugEnabled()) {
0396: String error = "Error when detaching XAResource:"
0397: + e + "--" + e.getMessage();
0398: TraceTm.jta.debug(error);
0399: }
0400: }
0401: return;
0402: default:
0403: doBeforeCompletion(true);
0404: try {
0405: tx.doDetach(XAResource.TMSUCCESS);
0406: } catch (SystemException e) {
0407:
0408: if (TraceTm.jta.isDebugEnabled()) {
0409: String error = "Error when detaching XAResource:"
0410: + e + "--" + e.getMessage();
0411: TraceTm.jta.debug(error);
0412: }
0413: }
0414: break;
0415: }
0416:
0417: // status many have changed in doBeforeCompletion
0418:
0419: if (TraceTm.jta.isDebugEnabled()) {
0420: TraceTm.jta.debug("status="
0421: + StatusHelper.getStatusName(status));
0422: }
0423:
0424: // Recheck Status after doBeforeCompletion
0425:
0426: if (status == Status.STATUS_MARKED_ROLLBACK) {
0427: TraceTm.jotm
0428: .info("Rollback during beforeCompletion in SubCoordinator.commit_one_phase");
0429: doRollback();
0430: throw new TransactionRolledbackException();
0431: }
0432:
0433: // only 1 Resource => 1 phase commit
0434:
0435: if (resourceList.size() == 1) {
0436: doOnePhaseCommit();
0437: return;
0438: }
0439:
0440: // 2 phase commit
0441:
0442: int vote = doPrepare();
0443:
0444: switch (vote) {
0445: case Resource.VOTE_COMMIT:
0446: doCommit();
0447: break;
0448: case Resource.VOTE_READONLY:
0449: doAfterCompletion();
0450: break;
0451: case Resource.VOTE_ROLLBACK:
0452: doRollback();
0453: throw new TransactionRolledbackException();
0454: }
0455: }
0456:
0457: /**
0458: * forget heuristics about this transaction.
0459: */
0460:
0461: public void forget() throws RemoteException {
0462: if (TraceTm.jta.isDebugEnabled()) {
0463: TraceTm.jta.debug("SubCoordinator.forget()");
0464: }
0465: doForget();
0466: }
0467:
0468: // ------------------------------------------------------------------
0469: // Other public methods (interface exposed to Transaction object)
0470: // ------------------------------------------------------------------
0471:
0472: /**
0473: * add a Synchronization to the list
0474: *
0475: * @param synchro The javax.transaction.Synchronization object for the
0476: * transaction associated with the target object
0477: *
0478: * @exception RollbackException Thrown to indicate that
0479: * the transaction has been marked for rollback only.
0480: *
0481: * @exception IllegalStateException Thrown if the transaction in the
0482: * target object is in prepared state or the transaction is inactive.
0483: *
0484: */
0485:
0486: public void addSynchronization(Synchronization synchro)
0487: throws RollbackException, IllegalStateException {
0488:
0489: if (TraceTm.jta.isDebugEnabled()) {
0490: TraceTm.jta.debug("synchro=" + synchro);
0491: TraceTm.jta.debug("status="
0492: + StatusHelper.getStatusName(status));
0493: }
0494:
0495: // check status: should be ACTIVE.
0496:
0497: boolean markedRollback = false;
0498:
0499: switch (status) {
0500: case Status.STATUS_MARKED_ROLLBACK:
0501: case Status.STATUS_ROLLEDBACK:
0502: markedRollback = true;
0503: break;
0504: case Status.STATUS_ACTIVE:
0505: break;
0506: default:
0507: String errorMsg = "addSynchronization: bad status = "
0508: + StatusHelper.getStatusName(status);
0509: TraceTm.jotm.error(errorMsg);
0510: throw new IllegalStateException(errorMsg);
0511: }
0512:
0513: // Add synchro to the list of local synchros
0514:
0515: synchroList.addElement(synchro);
0516:
0517: // If transaction marked rollback only, we add synchro but we throw
0518: // the correct exception because nobody can presume what will be done in the
0519: // Synchronization object, so we must send it beforeCompletion and afterCompletion
0520: // even in case of rollback.
0521:
0522: if (markedRollback) {
0523: if (TraceTm.jta.isDebugEnabled()) {
0524: TraceTm.jta
0525: .debug("SubCoordinator.addSynchronization: transaction rollback only");
0526: }
0527: throw new RollbackException();
0528: }
0529:
0530: }
0531:
0532: /**
0533: * add a XAResource to the list
0534: *
0535: * @param xares XAResource to register
0536: *
0537: * @return true if this datasource was already known
0538: *
0539: * @exception IllegalStateException Thrown if the transaction in the
0540: * target object is in prepared state or the transaction is inactive.
0541: */
0542:
0543: public synchronized boolean addResource(XAResource xares)
0544: throws IllegalStateException {
0545:
0546: if (TraceTm.jta.isDebugEnabled()) {
0547: TraceTm.jta.debug("xares=" + xares);
0548: TraceTm.jta.debug("status="
0549: + StatusHelper.getStatusName(status));
0550: }
0551:
0552: // check status: should be ACTIVE.
0553:
0554: boolean markedRollback = false;
0555:
0556: switch (status) {
0557: case Status.STATUS_MARKED_ROLLBACK:
0558: markedRollback = true;
0559: break;
0560: case Status.STATUS_ACTIVE:
0561: break;
0562: default:
0563: String errorMsg = "SubCoordinator.addResource: bad status= "
0564: + StatusHelper.getStatusName(status);
0565: TraceTm.jotm.error(errorMsg);
0566: throw new IllegalStateException(errorMsg);
0567: }
0568:
0569: // Check if this DataSource is already known
0570: // -> we must register only ONE resource per DataSource.
0571:
0572: boolean found = false;
0573:
0574: for (int i = 0; i < resourceList.size(); i++) {
0575: XAResource res = (XAResource) resourceList.elementAt(i);
0576:
0577: try {
0578: if (res.isSameRM(xares)) {
0579: found = true;
0580: break;
0581: }
0582: } catch (XAException e) {
0583: String error = "Cannot send res.isSameRM:" + e
0584: + " (error code = " + e.errorCode + ") --"
0585: + e.getMessage();
0586: TraceTm.jotm.error("Exception on resource.isSameRM: "
0587: + error);
0588: }
0589: }
0590:
0591: // add this XAResource to the list
0592:
0593: if (found == false) {
0594: if (TraceTm.jta.isDebugEnabled()) {
0595: TraceTm.jta.debug("new XAResource added to the list");
0596: }
0597: resourceList.addElement(xares);
0598: }
0599:
0600: // If transaction marked rollback only, we enlist resource but we throw
0601: // the correct exception. It is important to enlist the Resource because
0602: // if we don't, the database would be updated although transaction has been
0603: // rolled back.
0604:
0605: if (markedRollback) {
0606: if (TraceTm.jta.isDebugEnabled()) {
0607: TraceTm.jta
0608: .debug("SubCoordinator.addResource: transaction set rollback only");
0609: }
0610: }
0611:
0612: return found;
0613: }
0614:
0615: public synchronized void addJavaxXid(
0616: javax.transaction.xa.Xid javaxxid) {
0617:
0618: if (TraceTm.jta.isDebugEnabled()) {
0619: TraceTm.jta.debug("addJavaxXid javaxxid=" + javaxxid);
0620: }
0621:
0622: // add this javaxxid to the List at the index location of XAResource
0623:
0624: javaxxidList.addElement(javaxxid);
0625:
0626: if (TraceTm.jta.isDebugEnabled()) {
0627: TraceTm.jta.debug("new JavaxXid added to the list");
0628: }
0629:
0630: }
0631:
0632: /**
0633: * return the status of this transaction
0634: */
0635:
0636: public int getStatus() {
0637:
0638: if (TraceTm.jta.isDebugEnabled()) {
0639: TraceTm.jta.debug("status="
0640: + StatusHelper.getStatusName(status));
0641: }
0642:
0643: return status;
0644: }
0645:
0646: public javax.transaction.xa.Xid getJavaxXid(int xaresindex) {
0647: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList
0648: .elementAt(xaresindex);
0649: return myjavaxxid;
0650: }
0651:
0652: /**
0653: * set the transaction "rollback only"
0654: */
0655:
0656: public void setRollbackOnly() {
0657:
0658: if (TraceTm.jta.isDebugEnabled()) {
0659: TraceTm.jta.debug("status="
0660: + StatusHelper.getStatusName(status));
0661: }
0662:
0663: switch (status) {
0664: case Status.STATUS_ACTIVE:
0665: case Status.STATUS_UNKNOWN:
0666: case Status.STATUS_PREPARING:
0667: status = Status.STATUS_MARKED_ROLLBACK;
0668: break;
0669: case Status.STATUS_MARKED_ROLLBACK:
0670: case Status.STATUS_ROLLING_BACK:
0671: break;
0672: case Status.STATUS_PREPARED:
0673: case Status.STATUS_COMMITTED:
0674: case Status.STATUS_ROLLEDBACK:
0675: case Status.STATUS_NO_TRANSACTION:
0676: case Status.STATUS_COMMITTING:
0677: TraceTm.jotm
0678: .error("Cannot set transaction as rollback only");
0679: TraceTm.jotm.error("Bad status="
0680: + StatusHelper.getStatusName(status));
0681: break;
0682: }
0683: }
0684:
0685: // ------------------------------------------------------------------
0686: // private methods
0687: // ------------------------------------------------------------------
0688:
0689: /**
0690: * forget Transaction
0691: */
0692:
0693: private void doForget() throws RemoteException {
0694: if (TraceTm.jta.isDebugEnabled()) {
0695: TraceTm.jta.debug("SubCoordinator.doForget()");
0696: }
0697:
0698: boolean exception = false;
0699:
0700: for (int i = 0; i < resourceList.size(); i++) {
0701: // Tell this Resource to forget the transaction
0702: // Remove from the list ?
0703: XAResource xar = (XAResource) resourceList.elementAt(i);
0704:
0705: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList
0706: .elementAt(i);
0707: Xid xid = new XidImpl(this .xid, i);
0708:
0709: if (TraceTm.jta.isDebugEnabled()) {
0710: TraceTm.jta.debug("xid= " + xid);
0711: TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
0712: TraceTm.jta.debug("forgotten with resource= " + xar);
0713: }
0714:
0715: try {
0716: xar.forget(myjavaxxid);
0717: // xar.forget(xid);
0718: } catch (XAException e) {
0719: String error = "Cannot send xar.forget:" + e
0720: + " (error code = " + e.errorCode + ") --"
0721: + e.getMessage();
0722: TraceTm.jotm.error("Got XAException from xar.forget: "
0723: + error);
0724: exception = true;
0725: }
0726: }
0727:
0728: if (exception) {
0729: throw new RemoteException("XAException on forget");
0730: }
0731:
0732: unexportObject(this );
0733: }
0734:
0735: /**
0736: * 2PC phase 1 (prepare)
0737: * Basically, send prepare on each XAResource and
0738: * collect the results.
0739: */
0740:
0741: private synchronized int doPrepare() throws RemoteException {
0742: if (TraceTm.jta.isDebugEnabled()) {
0743: TraceTm.jta.debug("SubCoordinator.doPrepare()");
0744: }
0745:
0746: String jotmPrepareRecord = "PREPARE";
0747:
0748: int ret = VOTE_READONLY;
0749: int errors = 0;
0750:
0751: // No resource
0752:
0753: if (resourceList.size() == 0) {
0754: // increment counter for management
0755: Current.getCurrent().incrementCommitCounter();
0756: status = Status.STATUS_COMMITTED;
0757: return ret;
0758: }
0759:
0760: // Creates a log for that transaction, where we will add all the
0761: // resources that replied VOTE_COMMIT to prepare.
0762: // Do not flush the log on disk before decision to commit.
0763:
0764: log = new SLog();
0765:
0766: // Sends prepare to each resource while no error
0767: // In case of prepare on sub-coord. we may have only 1 resource.
0768:
0769: status = Status.STATUS_PREPARING;
0770:
0771: for (int i = 0; i < resourceList.size(); i++) {
0772: XAResource res = (XAResource) resourceList.elementAt(i);
0773: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList
0774: .elementAt(i);
0775:
0776: Xid xid = new XidImpl(this .xid, i);
0777:
0778: if (errors > 0) {
0779:
0780: if (TraceTm.jta.isDebugEnabled()) {
0781: TraceTm.jta.debug("xid= " + xid);
0782: TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
0783: TraceTm.jta.debug("rolled back with resource= "
0784: + res);
0785: }
0786:
0787: try {
0788: res.rollback(myjavaxxid);
0789: // res.rollback(xid);
0790: } catch (XAException e) {
0791: String error = "Cannot send res.rollback:" + e
0792: + " (error code = " + e.errorCode + ") --"
0793: + e.getMessage();
0794: TraceTm.jotm
0795: .error("Got XAException from res.rollback: "
0796: + error);
0797: // Nothing to do ?
0798: }
0799: } else {
0800: if (TraceTm.jta.isDebugEnabled()) {
0801: TraceTm.jta.debug("xid= " + xid);
0802: TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
0803: TraceTm.jta.debug("prepared with resource= " + res);
0804: }
0805:
0806: try {
0807: switch (res.prepare(myjavaxxid)) {
0808: // switch (res.prepare(xid)) {
0809: case XAResource.XA_OK:
0810: log.addResource(res, xid);
0811: ret = VOTE_COMMIT;
0812: break;
0813: case XAResource.XA_RDONLY:
0814: break;
0815: }
0816: } catch (XAException e) {
0817: String error = "Cannot send res.prepare:" + e
0818: + " (error code = " + e.errorCode + ") --"
0819: + e.getMessage();
0820: TraceTm.jotm
0821: .error("Got XAException from res.prepare: "
0822: + error);
0823: ret = VOTE_ROLLBACK;
0824: errors++;
0825: }
0826: }
0827: }
0828:
0829: // Update the status, depending on vote result
0830: // If all resources returned READ_ONLY, we can forget the transaction
0831:
0832: switch (ret) {
0833: case VOTE_READONLY:
0834: // increment counter for management
0835: Current.getCurrent().incrementCommitCounter();
0836: status = Status.STATUS_COMMITTED;
0837: break;
0838: case VOTE_COMMIT:
0839: status = Status.STATUS_PREPARED;
0840: break;
0841: case VOTE_ROLLBACK:
0842: status = Status.STATUS_ROLLING_BACK;
0843: break;
0844: }
0845:
0846: // return the global vote
0847: return ret;
0848: }
0849:
0850: /**
0851: * 2PC - phase 2 (commit)
0852: * See JTM for heuristics management
0853: */
0854:
0855: private synchronized int doCommit() throws RemoteException {
0856: if (TraceTm.jta.isDebugEnabled()) {
0857: TraceTm.jta.debug("SubCoordinator.doCommit()");
0858: }
0859:
0860: // We build the Recovery Record in doCommit just in case of a system crash
0861: // Store the Recovery Record using HOWL so it can manage for us.
0862: //
0863: // The Recovery Record consists of two record types:
0864: // 1. XA Transaction recovery record
0865: // 2. XA Resource recovery record
0866: //
0867: // The XA Transaction recovery record format:
0868: // recovery record type1 (byte[3]) - 'RR1'
0869: // recovery record stored date-time (long) - 8 bytes
0870: // length of transaction's xid (int) - 4 bytes
0871: // transaction's xid (byte []) - txxid.length bytes
0872: // length of transactions store date-time (int) - 4 bytes
0873: // transactions created date-time (byte []) - Date.length bytes
0874: // count of XA resources assigned to the transaction (int) - 4 bytes
0875: //
0876: // The XA Resource recovery record format:
0877: // recovery record type2 (byte[3]) = 'RR2'
0878: // length of XA resource (int) - 4 bytes
0879: // XA resource (byte []) - xares.length bytes
0880: // length of XID assigned to XA resource (int) - 4 bytes
0881: // XID assigned to XA resource (byte []) - xid.length bytes
0882: // XID status-state (int) = 4 bytes
0883: //
0884: // The JOTM Done recovery record format:
0885: // recovery record type3 (byte[3]) = 'RR3'
0886: // JOTM done value (byte[8]) = 'JOTMDONE'
0887:
0888: // First check that a log is initialized
0889:
0890: if (log == null) {
0891: TraceTm.jotm.error("doCommit: no log");
0892: return -1;
0893: }
0894:
0895: int errors = 0;
0896: int commitnb = 0;
0897: int heuristicnb = 0;
0898:
0899: List loggedResources = log.getLoggedResources();
0900: List loggedXids = log.getLoggedXids();
0901:
0902: XACommittingTx xaCommitTx = null;
0903: XACommittingTx xaCommitTxRewrite = null;
0904:
0905: byte[][] recoveryBuffer = new byte[loggedResources.size() + 1][]; // loggedResources + 1 (recoveryRecord1)
0906:
0907: byte[] recoveryRecord1 = null;
0908: byte[] recoveryRecord2 = null;
0909: ByteBuffer rr1 = null;
0910: ByteBuffer rr2 = null;
0911:
0912: String rt1 = "RR1";
0913: String rt2 = "RR2";
0914: byte[] jotmDone = new byte[11];
0915: byte[][] jotmDoneRecord = new byte[1][11];
0916:
0917: if (Current.getDefaultRecovery()) {
0918: Xid txxid = tx.getXid();
0919: String stxxid = txxid.toString(true);
0920: int txxidlength = stxxid.length();
0921: String txdate = tx.getTxDate();
0922: int txdatelength = txdate.length();
0923: long rcdate = System.currentTimeMillis();
0924:
0925: recoveryRecord1 = new byte[3 + 8 + 4 + txxidlength + 4
0926: + txdatelength + 4];
0927:
0928: rr1 = ByteBuffer.wrap(recoveryRecord1);
0929: rr1.put(rt1.getBytes());
0930: rr1.putLong(rcdate);
0931: rr1.putInt(txxidlength);
0932: rr1.put(stxxid.getBytes());
0933: rr1.putInt(txdatelength);
0934: rr1.put(txdate.getBytes());
0935: rr1.putInt(loggedResources.size());
0936:
0937: recoveryBuffer[0] = rr1.array();
0938:
0939: jotmDone = "RR3JOTMDONE".getBytes();
0940:
0941: for (int i = 0; i < loggedResources.size(); i++) {
0942: XAResource res = (XAResource) loggedResources.get(i);
0943: Xid xid = (Xid) loggedXids.get(i);
0944: int rmindex = 99; // Store 99, at recovery we will store the correct index
0945:
0946: if (TraceTm.recovery.isDebugEnabled()) {
0947: TraceTm.recovery.debug("recovery xid= " + xid);
0948: TraceTm.recovery.debug("recovery resource= " + res);
0949: }
0950:
0951: int reslength = res.toString().length();
0952: int resnamelength = res.getClass().getName().length();
0953: int xidlength = xid.toString(true).length();
0954:
0955: recoveryRecord2 = new byte[3 + 4 + 4 + reslength + 4
0956: + resnamelength + 4 + xidlength + 4];
0957:
0958: // ByteBuffer rr2 = ByteBuffer.wrap(recoveryRecord2);
0959: rr2 = ByteBuffer.wrap(recoveryRecord2);
0960: rr2.put(rt2.getBytes());
0961: rr2.putInt(rmindex);
0962: rr2.putInt(reslength);
0963: rr2.put(res.toString().getBytes());
0964: rr2.putInt(resnamelength);
0965: rr2.put(res.getClass().getName().getBytes());
0966: rr2.putInt(xidlength);
0967: rr2.put(xid.toString(true).getBytes());
0968: rr2.putInt(status);
0969:
0970: if (TraceTm.recovery.isDebugEnabled()) {
0971: TraceTm.recovery
0972: .debug("Prepare Init RR2 to Recovery Buffer");
0973: }
0974: recoveryBuffer[i + 1] = rr2.array(); // First record (0) is always rr1
0975: }
0976:
0977: try {
0978: xaCommitTx = TransactionRecoveryImpl
0979: .getTransactionRecovery().howlCommitLog(
0980: recoveryBuffer);
0981: } catch (Exception e) {
0982: // If we cannot write the Log, we cannot perform recovery, rollback transaction
0983: status = Status.STATUS_ROLLEDBACK;
0984:
0985: String howlerror = "Cannot howlCommitLog:" + e + " --"
0986: + e.getMessage();
0987: TraceTm.jotm
0988: .error("Got LogException from howlCommitLog: "
0989: + howlerror);
0990: xaCommitTx = null;
0991: doAfterCompletion();
0992: log.forgetLog();
0993:
0994: throw new TransactionRolledbackException();
0995: }
0996: }
0997:
0998: // Status Transaction = committing
0999:
1000: status = Status.STATUS_COMMITTING;
1001:
1002: // Send commit to each resource prepared
1003:
1004: for (int i = 0; i < loggedResources.size(); i++) {
1005: XAResource res = (XAResource) loggedResources.get(i);
1006:
1007: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList
1008: .elementAt(i);
1009: Xid xid = (Xid) loggedXids.get(i);
1010:
1011: // Commit every resource that have been logged even if any of
1012: // the commit resources fail. During recovery, the administrator
1013: // will resolve any incomplete transactions.
1014:
1015: if (TraceTm.jta.isDebugEnabled()) {
1016: TraceTm.jta.debug("xid= " + xid);
1017: TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
1018: TraceTm.jta.debug("attempting commit with resource= "
1019: + res);
1020: }
1021:
1022: if (Current.getDefaultRecovery()) {
1023: int rmindex = 99; // Store 99, at recovery we will store the correct index
1024: int reslength = res.toString().length();
1025: int resnamelength = res.getClass().getName().length();
1026: int xidlength = xid.toString(true).length();
1027:
1028: recoveryRecord2 = new byte[3 + 4 + 4 + reslength + 4
1029: + resnamelength + 4 + xidlength + 4];
1030:
1031: rr2 = ByteBuffer.wrap(recoveryRecord2);
1032:
1033: rr2.put(rt2.getBytes());
1034: rr2.putInt(rmindex);
1035: rr2.putInt(reslength);
1036: rr2.put(res.toString().getBytes());
1037: rr2.putInt(resnamelength);
1038: rr2.put(res.getClass().getName().getBytes());
1039: rr2.putInt(xidlength);
1040: rr2.put(xid.toString(true).getBytes());
1041: }
1042:
1043: // commit resource
1044:
1045: try {
1046: res.commit(myjavaxxid, false);
1047: // res.commit(xid, false);
1048:
1049: if (Current.getDefaultRecovery()) {
1050: rr2.putInt(Status.STATUS_COMMITTED);
1051: }
1052:
1053: commitnb++; // an XAresource was committed
1054: } catch (XAException e) {
1055: switch (e.errorCode) {
1056: case XAException.XA_HEURHAZ:
1057: case XAException.XA_HEURCOM:
1058: case XAException.XA_HEURRB:
1059: case XAException.XA_HEURMIX:
1060: if (TraceTm.jta.isDebugEnabled()) {
1061: TraceTm.jta.debug("Heuristic condition= "
1062: + e.getMessage());
1063: }
1064:
1065: if (Current.getDefaultRecovery()) {
1066: rr2.putInt(Status.STATUS_UNKNOWN);
1067: }
1068: // break;
1069: case XAException.XAER_RMERR:
1070: case XAException.XAER_NOTA:
1071: case XAException.XAER_INVAL:
1072: case XAException.XAER_PROTO:
1073: case XAException.XAER_RMFAIL:
1074: if (TraceTm.jta.isDebugEnabled()) {
1075: TraceTm.jta
1076: .debug("RM error= " + e.getMessage());
1077: }
1078:
1079: if (Current.getDefaultRecovery()) {
1080: rr2.putInt(Status.STATUS_COMMITTING);
1081: }
1082: // break;
1083: default:
1084: if (TraceTm.jta.isDebugEnabled()) {
1085: TraceTm.jta.debug("Default error= "
1086: + e.getMessage());
1087: }
1088:
1089: if (Current.getDefaultRecovery()) {
1090: rr2.putInt(Status.STATUS_ROLLEDBACK);
1091: }
1092: }
1093:
1094: String error = "Cannot send res.commit:" + e
1095: + " (error code = " + e.errorCode + ") --"
1096: + e.getMessage();
1097: TraceTm.jotm.error("Got XAException from res.commit: "
1098: + error);
1099:
1100: errors++;
1101: if (commitnb > 0)
1102: heuristicnb++;
1103: }
1104:
1105: if (Current.getDefaultRecovery()) {
1106: if (TraceTm.recovery.isDebugEnabled()) {
1107: TraceTm.recovery
1108: .debug("Prepare New RR2 to Recovery Buffer");
1109: }
1110: recoveryBuffer[i + 1] = rr2.array(); // First record (0) is always rr1
1111: }
1112: }
1113:
1114: if (errors == 0) {
1115: // increment counter for management
1116: Current.getCurrent().incrementCommitCounter();
1117: // Everything's fine.
1118: status = Status.STATUS_COMMITTED;
1119:
1120: if (Current.getDefaultRecovery()) {
1121: try {
1122: if (TraceTm.recovery.isDebugEnabled()) {
1123: TraceTm.recovery
1124: .debug("Done howl log, all okay");
1125: }
1126:
1127: jotmDoneRecord[0] = jotmDone;
1128: TransactionRecoveryImpl.getTransactionRecovery()
1129: .howlDoneLog(jotmDoneRecord, xaCommitTx);
1130: } catch (Exception f) {
1131: String howlerror = "Cannot howlDoneLog:" + f + "--"
1132: + f.getMessage();
1133: TraceTm.jotm
1134: .error("Got LogException from howlDoneLog: "
1135: + howlerror);
1136: }
1137: }
1138:
1139: doAfterCompletion();
1140: log.forgetLog();
1141:
1142: return 0;
1143: }
1144:
1145: if (heuristicnb == 0) { // commits on all XAResources failed, just rollback
1146: // Transaction has been eventually rolled back
1147: status = Status.STATUS_ROLLEDBACK;
1148:
1149: if (Current.getDefaultRecovery()) {
1150: try {
1151: jotmDoneRecord[0] = jotmDone;
1152: TransactionRecoveryImpl.getTransactionRecovery()
1153: .howlDoneLog(jotmDoneRecord, xaCommitTx);
1154: } catch (Exception f) {
1155: String howlerror = "Cannot howlDoneLog" + f + "--"
1156: + f.getMessage();
1157: TraceTm.jotm
1158: .error("Got LogException from howlDoneLog: "
1159: + howlerror);
1160: }
1161: }
1162:
1163: doAfterCompletion();
1164: log.forgetLog();
1165:
1166: throw new TransactionRolledbackException();
1167: }
1168:
1169: // Log heuristics if errors
1170:
1171: if (heuristicnb != 0) { // some XAResource commits succeeded, others failed, heuristicmixed
1172:
1173: if (Current.getDefaultRecovery()) {
1174: try {
1175: if (TraceTm.recovery.isDebugEnabled()) {
1176: TraceTm.recovery.debug("Rewrite HowlCommitLog");
1177: }
1178: xaCommitTxRewrite = TransactionRecoveryImpl
1179: .getTransactionRecovery().howlCommitLog(
1180: recoveryBuffer);
1181: } catch (Exception e) {
1182: // If we cannot write the Log, we cannot perform recovery, rollback transaction
1183: status = Status.STATUS_UNKNOWN;
1184:
1185: String howlerror = "Cannot howlCommitLog:" + e
1186: + " --" + e.getMessage();
1187: TraceTm.jotm
1188: .error("Got LogException from howlCommitLog: "
1189: + howlerror);
1190: xaCommitTx = null;
1191:
1192: doAfterCompletion();
1193: log.forgetLog();
1194:
1195: throw new TransactionRolledbackException();
1196: }
1197:
1198: // Transaction state is unknown, now job for administrator
1199: status = Status.STATUS_UNKNOWN;
1200:
1201: try {
1202: jotmDoneRecord[0] = jotmDone;
1203: TransactionRecoveryImpl.getTransactionRecovery()
1204: .howlDoneLog(jotmDoneRecord, xaCommitTx);
1205: } catch (Exception f) {
1206: String howlerror = "Cannot howlDoneLog" + f + "--"
1207: + f.getMessage();
1208: TraceTm.jotm
1209: .error("Got LogException from howlDoneLog: "
1210: + howlerror);
1211: }
1212: }
1213: }
1214:
1215: status = Status.STATUS_UNKNOWN;
1216:
1217: doAfterCompletion();
1218: return -1;
1219: }
1220:
1221: /**
1222: * 1PC (commit one phase)
1223: */
1224:
1225: private synchronized void doOnePhaseCommit() throws RemoteException {
1226: if (TraceTm.jta.isDebugEnabled()) {
1227: TraceTm.jta.debug("SubCoordinator.doOnePhaseCommit()");
1228: }
1229:
1230: // Only 1 resource: commit with onePhase=true
1231:
1232: status = Status.STATUS_COMMITTING;
1233:
1234: XAResource res = (XAResource) resourceList.elementAt(0);
1235: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList
1236: .elementAt(0);
1237: Xid xid = new XidImpl(this .xid, 0);
1238:
1239: if (TraceTm.jta.isDebugEnabled()) {
1240: TraceTm.jta.debug("xid= " + xid);
1241: TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
1242: TraceTm.jta.debug("one phase commit with resource= " + res);
1243: }
1244:
1245: try {
1246: res.commit(myjavaxxid, true);
1247: // res.commit(xid, true);
1248:
1249: // increment counter for management
1250: Current.getCurrent().incrementCommitCounter();
1251: status = Status.STATUS_COMMITTED;
1252: } catch (XAException e) {
1253: status = Status.STATUS_UNKNOWN;
1254: String error = "Cannot send res.commit:" + e
1255: + " (error code = " + e.errorCode + ") --"
1256: + e.getMessage();
1257: TraceTm.jotm.error("Got XAException from res.commit: "
1258: + error);
1259:
1260: if (e.errorCode == XAException.XA_RBROLLBACK) {
1261: throw new TransactionRolledbackException("XAException:"
1262: + error);
1263: }
1264: throw new RemoteException("XAException:" + error);
1265: } finally {
1266: doAfterCompletion();
1267: }
1268: }
1269:
1270: /**
1271: * Rollback every resource involved
1272: */
1273:
1274: private synchronized void doRollback() throws RemoteException {
1275: if (TraceTm.jta.isDebugEnabled()) {
1276: TraceTm.jta.debug("SubCoordinator.doRollback()");
1277: }
1278:
1279: status = Status.STATUS_ROLLEDBACK;
1280: boolean heurroll = false;
1281: String rberror = null;
1282: int errors = 0;
1283:
1284: // roll back each resource
1285: for (int i = 0; i < resourceList.size(); i++) {
1286:
1287: XAResource res = (XAResource) resourceList.elementAt(i);
1288: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) javaxxidList
1289: .elementAt(i);
1290: Xid xid = new XidImpl(this .xid, i);
1291:
1292: if (TraceTm.jta.isDebugEnabled()) {
1293: TraceTm.jta.debug("xid= " + xid);
1294: TraceTm.jta.debug("myjavaxxid= " + myjavaxxid);
1295: TraceTm.jta.debug("rolled back with resource= " + res);
1296: }
1297:
1298: // Rollback every resource that have been logged even if any of
1299: // the rollback resources fail. During recovery, the administrator
1300: // will resolve any incomplete transactions.
1301:
1302: try {
1303: res.rollback(myjavaxxid);
1304: // res.rollback(xid);
1305: } catch (XAException e) {
1306: switch (e.errorCode) {
1307: case XAException.XA_HEURHAZ:
1308: case XAException.XA_HEURCOM:
1309: case XAException.XA_HEURRB:
1310: case XAException.XA_HEURMIX:
1311: if (TraceTm.jta.isDebugEnabled()) {
1312: TraceTm.jta.debug("Heuristic condition= "
1313: + e.getMessage());
1314: }
1315: heurroll = true;
1316: case XAException.XAER_RMERR:
1317: case XAException.XAER_NOTA:
1318: case XAException.XAER_INVAL:
1319: case XAException.XAER_PROTO:
1320: case XAException.XAER_RMFAIL:
1321: if (TraceTm.jta.isDebugEnabled()) {
1322: TraceTm.jta
1323: .debug("RM error= " + e.getMessage());
1324: }
1325: default:
1326: if (TraceTm.jta.isDebugEnabled()) {
1327: TraceTm.jta.debug("Default error= "
1328: + e.getMessage());
1329: }
1330: }
1331:
1332: rberror = "Cannot send res.rollback:" + e
1333: + " (error code = " + e.errorCode + ") --"
1334: + e.getMessage();
1335: TraceTm.jotm
1336: .error("Got XAException from res.rollback: "
1337: + rberror);
1338:
1339: errors++;
1340: }
1341: }
1342:
1343: // raise Heuristic exception if XAResource returned heuristic
1344:
1345: if (heurroll) {
1346: throw new HeuristicRollback();
1347: }
1348:
1349: // raise exception if error on a resource
1350:
1351: if (errors != 0) {
1352: throw new RemoteException(
1353: "rollback: Unexpected XAException:" + rberror);
1354: }
1355:
1356: // increment counter for management
1357:
1358: Current.getCurrent().incrementRollbackCounter();
1359:
1360: doAfterCompletion();
1361:
1362: }
1363:
1364: /**
1365: * before completion
1366: *
1367: * @param boolean true if completion ok, false if rollback
1368: */
1369:
1370: private void doBeforeCompletion(boolean committing) {
1371: if (TraceTm.jta.isDebugEnabled()) {
1372: TraceTm.jta.debug("doBeforeCompletion committing= "
1373: + committing);
1374: }
1375:
1376: if (beforeCompletionDone)
1377: return;
1378:
1379: // Unset the timer for this transaction, if any
1380:
1381: tx.unsetTimer();
1382:
1383: // For each synchro, send beforeCompletion (not if rollback)
1384:
1385: if (committing && synchroList.size() > 0) {
1386: // We must be in the correct transaction context
1387: // See JTA spec. page 13 (3.3.2)
1388: // because at beforeCompletion, the bean will write its cache
1389:
1390: // Check the trivial case where we already have the correct tx context
1391:
1392: Transaction mytx = null;
1393: boolean suspended = false;
1394: boolean resumed = false;
1395:
1396: try {
1397: mytx = tm.getTransaction();
1398: } catch (SystemException e) {
1399: if (TraceTm.jta.isDebugEnabled()) {
1400: String error = "Cannot get transaction:" + e + "--"
1401: + e.getMessage();
1402: TraceTm.jta.debug(error);
1403: }
1404: }
1405:
1406: // Suspend if another tx context
1407:
1408: if (mytx != null && mytx.equals(tx) == false) {
1409: try {
1410: tm.suspend();
1411: suspended = true;
1412: } catch (SystemException e) {
1413: if (TraceTm.jta.isDebugEnabled()) {
1414: String error = "Cannot suspend transaction:"
1415: + 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
1430: + "--" + e.getMessage();
1431: TraceTm.jta.debug(error);
1432: }
1433: } catch (InvalidTransactionException e) {
1434: if (TraceTm.jta.isDebugEnabled()) {
1435: String error = "Cannot resume transaction:" + e
1436: + "--" + e.getMessage();
1437: TraceTm.jta.debug(error);
1438: }
1439: } catch (IllegalStateException e) {
1440: if (TraceTm.jta.isDebugEnabled()) {
1441: String error = "Cannot resume transaction:" + e
1442: + "--" + e.getMessage();
1443: TraceTm.jta.debug(error);
1444: }
1445: }
1446: }
1447:
1448: // Call the synchronizations
1449: // beforeCompletion may set the TX rollbackonly if something goes wrong
1450:
1451: if (TraceTm.jta.isDebugEnabled()) {
1452: TraceTm.jta.debug("sychronization list size= "
1453: + synchroList.size());
1454: }
1455:
1456: for (int i = 0; i < synchroList.size(); i++) {
1457: Synchronization sync = (Synchronization) synchroList
1458: .elementAt(i);
1459:
1460: if (TraceTm.jta.isDebugEnabled()) {
1461: TraceTm.jta.debug("Synchronization sync= " + sync);
1462: }
1463:
1464: sync.beforeCompletion();
1465: }
1466:
1467: // reset context as it was before
1468:
1469: if (resumed) {
1470: try {
1471: tm.suspend();
1472: } catch (SystemException e) {
1473: if (TraceTm.jta.isDebugEnabled()) {
1474: String error = "Cannot suspend transaction:"
1475: + e + "--" + e.getMessage();
1476: TraceTm.jta.debug(error);
1477: }
1478: }
1479: }
1480:
1481: if (suspended) {
1482: try {
1483: tm.resume(mytx);
1484: resumed = true;
1485: } catch (SystemException e) {
1486: if (TraceTm.jta.isDebugEnabled()) {
1487: String error = "Cannot resume transaction:" + e
1488: + "--" + e.getMessage();
1489: TraceTm.jta.debug(error);
1490: }
1491: } catch (InvalidTransactionException e) {
1492: if (TraceTm.jta.isDebugEnabled()) {
1493: String error = "Cannot resume transaction:" + e
1494: + "--" + e.getMessage();
1495: TraceTm.jta.debug(error);
1496: }
1497: } catch (IllegalStateException e) {
1498: if (TraceTm.jta.isDebugEnabled()) {
1499: String error = "Cannot resume transaction:" + e
1500: + "--" + e.getMessage();
1501: TraceTm.jta.debug(error);
1502: }
1503: }
1504: }
1505: }
1506:
1507: beforeCompletionDone = true;
1508: }
1509:
1510: /**
1511: * after completion
1512: */
1513:
1514: private void doAfterCompletion() {
1515: if (TraceTm.jta.isDebugEnabled()) {
1516: TraceTm.jta.debug("doAfterCompletion()");
1517: }
1518:
1519: // For each synchro, send afterCompletion
1520: /// CompletedTransactionListener has been replaced by Synchronization
1521:
1522: if (TraceTm.jta.isDebugEnabled()) {
1523: TraceTm.jta.debug("sychronization list size= "
1524: + synchroList.size());
1525: }
1526:
1527: for (int i = 0; i < synchroList.size(); i++) {
1528: Synchronization sync = (Synchronization) synchroList
1529: .elementAt(i);
1530:
1531: if (TraceTm.jta.isDebugEnabled()) {
1532: TraceTm.jta.debug("Synchronization sync= " + sync);
1533: TraceTm.jta.debug("sync.afterCompletion status= "
1534: + StatusHelper.getStatusName(status));
1535: }
1536:
1537: sync.afterCompletion(status);
1538: }
1539:
1540: // Forget this transaction.
1541: // LATER:
1542: // - Should not forget it in case of heuristics (for recovery)
1543: // - May be this could be deferred in case of retry from a client: use a timer.
1544:
1545: Current.getCurrent().forgetTx(tx.getXid());
1546:
1547: if (TraceTm.jta.isDebugEnabled()) {
1548: TraceTm.jta.debug("SubCoordinator unexported [subcoord="
1549: + this + "]");
1550: }
1551:
1552: try {
1553: unexportObject(this );
1554: } catch (Exception e) {
1555: }
1556: }
1557:
1558: // ------------------------------------------------------------------
1559: // new method for TxImpl
1560: // ------------------------------------------------------------------
1561:
1562: public int getXaresIndex(XAResource xares) {
1563:
1564: if (TraceTm.jta.isDebugEnabled()) {
1565: TraceTm.jta.debug("getXaresIndex xares= " + xares);
1566: TraceTm.jta.debug("resourceList.size= "
1567: + resourceList.size());
1568: }
1569:
1570: int xaresIndex = -1;
1571:
1572: // first, search for an XAResource with the same object reference
1573: if (TraceTm.jta.isDebugEnabled()) {
1574: TraceTm.jta.debug("search xares with same obj ref");
1575: }
1576:
1577: for (int i = 0; i < resourceList.size(); i++) {
1578: XAResource res = (XAResource) resourceList.elementAt(i);
1579:
1580: if (TraceTm.jta.isDebugEnabled()) {
1581: TraceTm.jta.debug("res= " + res);
1582: }
1583:
1584: if (res.equals(xares)) {
1585: xaresIndex = i;
1586: break;
1587: }
1588: }
1589:
1590: // if not found, search for a xares with the same RM
1591: if (xaresIndex == -1) {
1592: if (TraceTm.jta.isDebugEnabled()) {
1593: TraceTm.jta
1594: .debug("not found -> search for xares with same RM");
1595: }
1596:
1597: for (int i = 0; i < resourceList.size(); i++) {
1598: XAResource res = (XAResource) resourceList.elementAt(i);
1599:
1600: if (TraceTm.jta.isDebugEnabled()) {
1601: TraceTm.jta.debug("res= " + res);
1602: }
1603:
1604: try {
1605: if (res.isSameRM(xares)) {
1606: xaresIndex = i;
1607: break;
1608: }
1609: } catch (XAException e) {
1610: if (TraceTm.jta.isDebugEnabled()) {
1611: String error = "res.isSameRm exception:" + e
1612: + "--" + e.getMessage();
1613: TraceTm.jta.debug(error);
1614: }
1615: }
1616: }
1617: }
1618:
1619: if (TraceTm.jta.isDebugEnabled()) {
1620: TraceTm.jta.debug("xaresIndex= " + xaresIndex);
1621: }
1622:
1623: return xaresIndex;
1624: }
1625:
1626: }
|