0001: /*
0002: * @(#) TransactionImpl.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/01/15 Dean Jennings - List instead of Vector for enlistedXARes delistedXARes
0046: *
0047: * 02/06/18 Marek Prochazka - in timeoutExpired() removed code
0048: * preventing deadlock by rolling back transaction
0049: * --------------------------------------------------------------------------
0050: * $Id: TransactionImpl.java,v 1.45 2005/05/10 22:52:35 tonyortiz Exp $
0051: * --------------------------------------------------------------------------
0052: */
0053: package org.objectweb.jotm;
0054:
0055: import java.rmi.RemoteException;
0056: import java.util.ArrayList;
0057: import java.util.Collections;
0058: import java.util.List;
0059: import javax.transaction.*;
0060: import javax.transaction.xa.XAException;
0061: import javax.transaction.xa.XAResource;
0062:
0063: /**
0064: * TransactionImpl is the implementation of the Transaction interface,
0065: * defined in JTA specifications. This object is intended to be used
0066: * by the EJBServer for transaction management. It is used indirectly
0067: * by the UserTransaction implementation too, i.e. the Current object.
0068: * The goal is to use the JTA interface to hide the JTM interface to
0069: * the caller (EJBServer, Bean or Client).
0070: */
0071:
0072: public class TransactionImpl implements Transaction, TimerEventListener {
0073:
0074: // ------------------------------------------------------------------
0075: // Private data
0076: // ------------------------------------------------------------------
0077: private SubCoordinator subcoord = null;
0078: private TransactionContext myCtx = null;
0079: private Xid myXid = null;
0080: private boolean genXidhashcode = false;
0081: private boolean genXidtostring = false;
0082: private int myXidhashcode = 0;
0083: private String myXidtostring = null;
0084: private String txDate = null;
0085: private boolean interpose = false;
0086: private int ucount = 0; // count of users (0 => can be freed)
0087: private TimerEvent timer = null; // keep this to unvalidate timer
0088: private RecoveryCoordinator recoveryCoord = null;
0089: /// store enlisted resources
0090: private List enlistedXARes = Collections
0091: .synchronizedList(new ArrayList());
0092: /// store suspended resources
0093: private List delistedXARes = null;
0094: // propagate context
0095: private boolean propagateCtx = true;
0096: private List enlistedJavaxXid = Collections
0097: .synchronizedList(new ArrayList());
0098:
0099: // ------------------------------------------------------------------
0100: // Constructors
0101: // ------------------------------------------------------------------
0102:
0103: /**
0104: * New transaction (begin).
0105: *
0106: * @param timeout The value of the timeout in seconds.
0107: *
0108: */
0109:
0110: public TransactionImpl(Xid xid, int timeout) throws SystemException {
0111: if (TraceTm.jta.isDebugEnabled()) {
0112: TraceTm.jta.debug("xid= " + xid);
0113: TraceTm.jta.debug("timeout= " + timeout);
0114: }
0115:
0116: // Build a propagation context local (no ref to JTM yet)
0117: myXid = xid;
0118: // myXidhashcode = myXid.hashCode();
0119: // myXidtostring = myXid.toString();
0120: myCtx = new InternalTransactionContext(timeout, null,
0121: (javax.transaction.xa.Xid) xid);
0122: }
0123:
0124: /**
0125: * New Transaction for this thread (setPropagationContext)
0126: *
0127: * @param pctx propagation context
0128: *
0129: */
0130:
0131: public TransactionImpl(TransactionContext pctx) {
0132:
0133: if (pctx == null) {
0134: TraceTm.jotm
0135: .error("TransactionImpl: null PropagationContext");
0136: }
0137: myCtx = pctx;
0138: myXid = pctx.getXid();
0139: // myXidhashcode = myXid.hashCode();
0140: // myXidtostring = myXid.toString();
0141: // interposition will be done later, only if necessary.
0142: interpose = true;
0143: }
0144:
0145: // ------------------------------------------------------------------
0146: // Transaction implementation
0147: // ------------------------------------------------------------------
0148:
0149: /**
0150: * Complete the transaction represented by this Transaction object
0151: * The calling thread is not required to have the same transaction
0152: * associated with the thread. (JTA 3.3.3)
0153: *
0154: * @exception RollbackException Thrown to indicate that
0155: * the transaction has been rolled back rather than committed.
0156: *
0157: * @exception HeuristicMixedException Thrown to indicate that a heuristic
0158: * decision was made and that some relevant updates have been committed
0159: * while others have been rolled back.
0160: *
0161: * @exception HeuristicRollbackException Thrown to indicate that a
0162: * heuristic decision was made and that some relevant updates have been
0163: * rolled back.
0164: *
0165: * @exception SecurityException Thrown to indicate that the thread is
0166: * not allowed to commit the transaction.
0167: *
0168: * @exception IllegalStateException Thrown if the current thread is
0169: * not associated with a transaction.
0170: *
0171: * @exception SystemException Thrown if the transaction manager
0172: * encounters an unexpected error condition
0173: */
0174:
0175: public void commit() throws RollbackException,
0176: HeuristicMixedException, HeuristicRollbackException,
0177: SecurityException, SystemException {
0178:
0179: if (TraceTm.jta.isDebugEnabled()) {
0180: TraceTm.jta.debug("TransactionImpl.commit (tx= " + this
0181: + ")");
0182: }
0183:
0184: // *** Distributed transaction.
0185: Terminator term = myCtx.getTerminator();
0186:
0187: if (term != null) {
0188: // Commits the Transaction, with heuristic report
0189: try {
0190: propagateCtx = false;
0191: term.commit(true);
0192: propagateCtx = true;
0193: } catch (TransactionRolledbackException e) {
0194: Current.getCurrent().forgetTx(getXid());
0195: if (TraceTm.jta.isDebugEnabled()) {
0196: TraceTm.jta
0197: .debug("Commit distributed transaction -> rolled back!");
0198: }
0199: throw new RollbackException();
0200: } catch (RemoteException e) {
0201:
0202: if (TraceTm.jta.isWarnEnabled()) {
0203: TraceTm.jta.warn("got a RemoteException", e);
0204: }
0205:
0206: if (e.detail instanceof TransactionRolledbackException) {
0207: Current.getCurrent().forgetTx(getXid());
0208: if (TraceTm.jta.isDebugEnabled()) {
0209: TraceTm.jta
0210: .debug("Commit distributed transaction -> rolled back!");
0211: }
0212: throw new RollbackException();
0213: }
0214:
0215: if (e.detail instanceof HeuristicMixed) {
0216: TraceTm.jotm
0217: .info("Commit distributed transaction -> Heuristic mixed!");
0218: throw new HeuristicMixedException();
0219: } else {
0220: throw new SystemException(
0221: "Unexpected RemoteException on commit:"
0222: + e.detail.getMessage());
0223: }
0224: } catch (Exception e) {
0225: TraceTm.jotm
0226: .error("Unexpected Exception on commit:", e);
0227: throw new SystemException(
0228: "Unexpected Exception on commit");
0229: }
0230:
0231: if (subcoord == null) {
0232: // if no coordinator, timer will not be unset by JTM.
0233: unsetTimer();
0234: }
0235:
0236: Current.getCurrent().forgetTx(getXid());
0237: return;
0238: }
0239:
0240: // *** Local transaction
0241: // commit_one_phase may raise remote exceptions. We must rethrow local exceptions.
0242:
0243: if (subcoord != null) {
0244: try {
0245: subcoord.commit_one_phase();
0246: } catch (TransactionRolledbackException e) {
0247: if (TraceTm.jta.isDebugEnabled()) {
0248: TraceTm.jta
0249: .debug("Commit local transaction -> rolled back!");
0250: }
0251: Current.getCurrent().forgetTx(getXid());
0252: throw new RollbackException();
0253: } catch (RemoteException e) {
0254: TraceTm.jotm.error(
0255: "Unexpected Exception on commit_one_phase:", e);
0256: Current.getCurrent().forgetTx(getXid());
0257: throw new SystemException(
0258: "Unexpected Exception on commit_one_phase");
0259: }
0260: } else {
0261: // if no coordinator, just unset the timer and release this object.
0262: unsetTimer();
0263: Current.getCurrent().forgetTx(getXid());
0264: }
0265: }
0266:
0267: /**
0268: * Delist the resource specified from the current transaction
0269: * associated with the calling thread.
0270: *
0271: * @param xares The XAResource object representing the resource to delist
0272: *
0273: * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL.
0274: *
0275: * @exception IllegalStateException Thrown if the transaction in the
0276: * target object is inactive.
0277: *
0278: * @exception SystemException Thrown if the transaction manager
0279: * encounters an unexpected error condition
0280: *
0281: * @return true if the dissociation of the Resource is successful;
0282: * false otherwise.
0283: */
0284:
0285: public boolean delistResource(XAResource xares, int flag)
0286: throws IllegalStateException, SystemException {
0287:
0288: if (TraceTm.jta.isDebugEnabled()) {
0289: TraceTm.jta.debug("TransactionImpl.delistResource");
0290: TraceTm.jta.debug("xares= " + xares + ", flag= " + flag);
0291: }
0292:
0293: if (enlistedXARes == null) {
0294: if (TraceTm.jta.isDebugEnabled()) {
0295: TraceTm.jta.error("No XA resources enlisted by JOTM");
0296: }
0297: return false;
0298: }
0299:
0300: // Verify that the XAResource to be delisted was enlisted earlier.
0301:
0302: if (!enlistedXARes.contains(xares)) {
0303: if (TraceTm.jta.isDebugEnabled()) {
0304: TraceTm.jta.error("XAResouce " + xares
0305: + " not enlisted by JOTM");
0306: }
0307: return false;
0308: }
0309:
0310: Xid resXid = new XidImpl(getXid(), subcoord
0311: .getXaresIndex(xares));
0312:
0313: javax.transaction.xa.Xid javaxxid = subcoord
0314: .getJavaxXid(subcoord.getXaresIndex(xares));
0315:
0316: if (!enlistedJavaxXid.contains(javaxxid)) {
0317: if (TraceTm.jta.isDebugEnabled()) {
0318: TraceTm.jta.error("XAResouce " + xares
0319: + " not enlisted by JOTM");
0320: }
0321: return false;
0322: }
0323:
0324: int javaxxidindex = enlistedJavaxXid.indexOf(javaxxid);
0325: javax.transaction.xa.Xid myjavaxxid = (javax.transaction.xa.Xid) enlistedJavaxXid
0326: .get(javaxxidindex);
0327:
0328: if (TraceTm.jta.isDebugEnabled()) {
0329: TraceTm.jta.debug("resXid= " + resXid);
0330: TraceTm.jta.debug("delisted with resource= " + xares);
0331: TraceTm.jta.debug("end myjavaxxid= " + myjavaxxid);
0332: }
0333:
0334: // Send the XA end to the XAResource
0335: try {
0336: xares.end(myjavaxxid, flag);
0337: // xares.end( (javax.transaction.xa.Xid) resXid, flag );
0338: } catch (XAException e) {
0339: String error = "Cannot send XA end:" + e
0340: + " (error code = " + e.errorCode + ") --"
0341: + e.getMessage();
0342: TraceTm.jotm.error(error);
0343: if (TraceTm.jta.isDebugEnabled()) {
0344: TraceTm.jotm.debug("xares.end= " + xares);
0345: }
0346: throw new SystemException(error);
0347: }
0348:
0349: if (TraceTm.jta.isDebugEnabled()) {
0350: TraceTm.jta.debug("enlistedXAres.remove xares= " + xares);
0351: }
0352:
0353: /// remove from enlisted list
0354: enlistedXARes.remove(xares);
0355: enlistedJavaxXid.remove(javaxxid);
0356: return true;
0357: }
0358:
0359: /**
0360: * Enlist the resource specified with the current transaction
0361: * context of the calling thread
0362: *
0363: * @param xares The XAResource object representing the resource to enlist
0364: *
0365: * @return <i>true</i> if the resource was enlisted successfully; otherwise
0366: * false.
0367: *
0368: * @exception RollbackException Thrown to indicate that
0369: * the transaction has been marked for rollback only.
0370: *
0371: * @exception IllegalStateException Thrown if the transaction in the
0372: * target object is in prepared state or the transaction is inactive.
0373: *
0374: * @exception SystemException Thrown if the transaction manager
0375: * encounters an unexpected error condition
0376: *
0377: */
0378:
0379: public boolean enlistResource(XAResource xares)
0380: throws RollbackException, IllegalStateException,
0381: SystemException {
0382:
0383: if (TraceTm.jta.isDebugEnabled()) {
0384: TraceTm.jta.debug("TransactionImpl.enlistResource");
0385: TraceTm.jta.debug("xares= " + xares);
0386: }
0387:
0388: // Check trivial cases
0389: if (xares == null) {
0390: TraceTm.jotm.error("enlistResource: null argument");
0391: throw new SystemException("enlistResource: null argument");
0392: }
0393:
0394: if (myCtx == null) {
0395: throw new SystemException(
0396: "enlistResource: no Transactional Context");
0397: }
0398:
0399: // make a subCoordinator object if not existing yet
0400: if (subcoord == null) {
0401: makeSubCoord();
0402: if (subcoord == null) {
0403: TraceTm.jotm
0404: .error("enlistResource: could not create subcoordinator");
0405: throw new SystemException(
0406: "enlistResource: could not create subcoordinator");
0407: }
0408: }
0409:
0410: boolean found = false;
0411:
0412: try {
0413: found = subcoord.addResource(xares);
0414: } catch (IllegalStateException e) {
0415: throw new IllegalStateException(
0416: "enlistResource: could not addResource " + xares);
0417: }
0418:
0419: boolean rollbackOnly = false;
0420:
0421: // Send the XA start to the XAResource
0422: // A new Xid branch should be generated in case of new RM (if !found)
0423: // See JTA Specifications, page 12/13.
0424: int flag = found ? XAResource.TMJOIN : XAResource.TMNOFLAGS;
0425:
0426: if ((delistedXARes != null) && delistedXARes.contains(xares)) {
0427: flag = XAResource.TMRESUME;
0428: }
0429:
0430: Xid resXid = new XidImpl(getXid(), subcoord
0431: .getXaresIndex(xares));
0432: javax.transaction.xa.Xid javaxxid = new JavaXidImpl(resXid);
0433:
0434: if (TraceTm.jta.isDebugEnabled()) {
0435: TraceTm.jta.debug("resXid= " + resXid);
0436: TraceTm.jta.debug("enlisted with resource= " + xares);
0437: TraceTm.jta.debug("start javaxxid= " + javaxxid);
0438: }
0439:
0440: if (!found) {
0441: subcoord.addJavaxXid(javaxxid);
0442: }
0443:
0444: try {
0445: xares.start(javaxxid, flag);
0446: // xares.start( (javax.transaction.xa.Xid) resXid, flag );
0447: } catch (XAException e) {
0448: String error = "Cannot send XA(" + xares + ") start:" + e
0449: + " (error code = " + e.errorCode + ") --"
0450: + e.getMessage();
0451: TraceTm.jotm.error(error);
0452: throw new SystemException(error);
0453: }
0454:
0455: if (rollbackOnly) {
0456: throw new RollbackException();
0457: }
0458:
0459: if (!enlistedXARes.contains(xares)) {
0460: /// add to enlisted list
0461: enlistedXARes.add(xares);
0462: enlistedJavaxXid.add(javaxxid);
0463: }
0464:
0465: int status = this .getStatus();
0466:
0467: switch (status) {
0468: case Status.STATUS_ACTIVE:
0469: case Status.STATUS_PREPARING:
0470: break;
0471: case Status.STATUS_PREPARED:
0472: throw new IllegalStateException(
0473: "Transaction already prepared.");
0474: case Status.STATUS_COMMITTING:
0475: // throw new IllegalStateException("Transaction already started committing.");
0476: break;
0477: case Status.STATUS_COMMITTED:
0478: throw new IllegalStateException(
0479: "Transaction already committed.");
0480: case Status.STATUS_MARKED_ROLLBACK:
0481: throw new RollbackException(
0482: "Transaction already marked for rollback");
0483: case Status.STATUS_ROLLING_BACK:
0484: throw new RollbackException(
0485: "Transaction already started rolling back.");
0486: case Status.STATUS_ROLLEDBACK:
0487: throw new RollbackException(
0488: "Transaction already rolled back.");
0489: case Status.STATUS_NO_TRANSACTION:
0490: throw new IllegalStateException("No current transaction.");
0491: case Status.STATUS_UNKNOWN:
0492: throw new IllegalStateException(
0493: "Unknown transaction status");
0494: default:
0495: throw new IllegalStateException(
0496: "Illegal transaction status: " + status);
0497: }
0498:
0499: return true;
0500: }
0501:
0502: /// delist all enlisted resources and move to suspended
0503:
0504: public void doDetach(int flag) throws SystemException {
0505: if (TraceTm.jta.isDebugEnabled()) {
0506: TraceTm.jta.debug("TransactionImpl.doDetach flag= "
0507: + XAResourceHelper.getFlagName(flag));
0508: TraceTm.jta.debug("number of enlisted= "
0509: + enlistedXARes.size());
0510: }
0511:
0512: // always copy enlisted to suspended resource list
0513: // since jonas may resume the transaction in beforecompletion
0514:
0515: delistedXARes = new ArrayList(enlistedXARes);
0516:
0517: for (int i = 0; i < delistedXARes.size(); i++) {
0518: delistResource((XAResource) delistedXARes.get(i), flag);
0519: }
0520: }
0521:
0522: /// enlist/clear all suspended resource
0523:
0524: public void doAttach(int flag) throws SystemException,
0525: RollbackException {
0526: if (TraceTm.jta.isDebugEnabled()) {
0527: TraceTm.jta.debug("TransactionImpl.doAttach flag= "
0528: + XAResourceHelper.getFlagName(flag));
0529: TraceTm.jta.debug("number of enlisted= "
0530: + enlistedXARes.size());
0531: }
0532:
0533: // we attach suspended transactions
0534:
0535: if (flag == XAResource.TMRESUME) {
0536: // we may be calling resume from beforecompletion on transaction that are not suspended!
0537:
0538: for (int i = 0; (delistedXARes != null)
0539: && (i < delistedXARes.size()); i++) {
0540:
0541: enlistResource((XAResource) delistedXARes.get(i));
0542: }
0543: }
0544:
0545: delistedXARes = null;
0546: }
0547:
0548: /// get a copy of the list of currently enlisted resource
0549:
0550: public List getEnlistedXAResource() {
0551: if (TraceTm.jta.isDebugEnabled()) {
0552: TraceTm.jta.debug("getEnlistedXAResource size= "
0553: + enlistedXARes.size());
0554: }
0555: return new ArrayList(enlistedXARes);
0556: }
0557:
0558: /**
0559: * Obtain the status of the transaction associated with the current thread.
0560: *
0561: * @return The transaction status. If no transaction is associated with
0562: * the current thread, this method returns the Status.NoTransaction
0563: * value.
0564: *
0565: * @exception SystemException Thrown if the transaction manager
0566: * encounters an unexpected error condition
0567: *
0568: */
0569:
0570: public int getStatus() throws SystemException {
0571: if (TraceTm.jta.isDebugEnabled()) {
0572: TraceTm.jta.debug("TransactionImpl.getStatus()");
0573: }
0574:
0575: // *** Distributed transaction
0576: Coordinator coord = myCtx.getCoordinator();
0577:
0578: if (coord != null) {
0579: // Ask the transaction status to JTM
0580: int ret;
0581: try {
0582: ret = coord.get_status();
0583: } catch (Exception e) {
0584: TraceTm.jotm.error("cannot reach JTM:", e);
0585: return Status.STATUS_NO_TRANSACTION;
0586: }
0587: return ret;
0588: }
0589:
0590: // *** Local transaction
0591: // The status is kept in the subcoordinator
0592:
0593: if (subcoord == null) {
0594: // The transaction has just been started
0595: // No resource or synchro are registered yet.
0596: return Status.STATUS_ACTIVE;
0597: }
0598:
0599: return subcoord.getStatus();
0600: }
0601:
0602: /**
0603: * Register a synchronization object for the transaction currently
0604: * associated with the calling thread. The transction manager invokes
0605: * the beforeCompletion method prior to starting the transaction
0606: * commit process. After the transaction is completed, the transaction
0607: * manager invokes the afterCompletion method.
0608: *
0609: * @param sync The javax.transaction.Synchronization object for the
0610: * transaction associated with the target object
0611: *
0612: * @exception RollbackException Thrown to indicate that
0613: * the transaction has been marked for rollback only.
0614: *
0615: * @exception IllegalStateException Thrown if the transaction in the
0616: * target object is in prepared state or the transaction is inactive.
0617: *
0618: * @exception SystemException Thrown if the transaction manager
0619: * encounters an unexpected error condition
0620: */
0621:
0622: public void registerSynchronization(Synchronization sync)
0623: throws RollbackException, IllegalStateException,
0624: SystemException {
0625: if (TraceTm.jta.isDebugEnabled()) {
0626: TraceTm.jta
0627: .debug("TransactionImpl.registerSynchronization(Synchronization sync)");
0628: }
0629:
0630: // It's time to make the subcoordinator, if not existing yet.
0631: if (subcoord == null) {
0632: makeSubCoord();
0633: }
0634:
0635: // Add Synchronization to the list.
0636: // may raise exceptions
0637: subcoord.addSynchronization(sync);
0638: }
0639:
0640: /**
0641: * Rollback the transaction represented by this Transaction object.
0642: *
0643: * @exception IllegalStateException Thrown if the transaction in the
0644: * target object is in prepared state or the transaction is inactive.
0645: *
0646: * @exception SystemException Thrown if the transaction manager
0647: * encounters an unexpected error condition
0648: *
0649: */
0650:
0651: public void rollback() throws IllegalStateException,
0652: SystemException {
0653: if (TraceTm.jta.isDebugEnabled()) {
0654: TraceTm.jta.debug("TransactionImpl.rollback(tx= " + this
0655: + ")");
0656: }
0657:
0658: // *** Distributed transaction.
0659: Terminator term = myCtx.getTerminator();
0660:
0661: if (term != null) {
0662: // Rollback the Transaction
0663: try {
0664: propagateCtx = false;
0665: term.rollback();
0666: propagateCtx = true;
0667: } catch (java.rmi.ServerException e) {
0668: // HeuristicCommit ????
0669: throw new IllegalStateException(
0670: "Exception on rollback:" + e.detail);
0671: } catch (Exception e) {
0672: Current.getCurrent().forgetTx(getXid());
0673: throw new SystemException(
0674: "Unexpected Exception on rollback");
0675: }
0676:
0677: if (subcoord == null) {
0678: // if no coordinator, timer will not be unset by JTM.
0679: unsetTimer();
0680: }
0681:
0682: // release this object.
0683: Current.getCurrent().forgetTx(getXid());
0684: return;
0685: }
0686:
0687: // *** Local transaction.
0688: // if no coordinator, nothing to do.
0689:
0690: if (subcoord != null) {
0691: try {
0692: subcoord.rollback();
0693: } catch (RemoteException e) {
0694: Current.getCurrent().forgetTx(getXid());
0695: throw new IllegalStateException(
0696: "Exception on rollback:" + e);
0697: }
0698:
0699: } else {
0700: // if no coordinator, just unset the timer.
0701: unsetTimer();
0702: }
0703:
0704: // release this object.
0705: Current.getCurrent().forgetTx(getXid());
0706: }
0707:
0708: /**
0709: * Prepare the transaction represented by this Transaction object.
0710: *
0711: * @exception IllegalStateException Thrown if the transaction in the
0712: * target object is in prepared state or the transaction is inactive.
0713: *
0714: * @exception SystemException Thrown if the transaction manager
0715: * encounters an unexpected error condition
0716: *
0717: */
0718:
0719: public int prepare() throws IllegalStateException, SystemException {
0720: if (TraceTm.jta.isDebugEnabled()) {
0721: TraceTm.jta.debug("TransactionImpl.prepare(tx= " + this
0722: + ")");
0723: }
0724:
0725: int ret = 0;
0726: if (subcoord != null) {
0727: try {
0728: ret = subcoord.prepare();
0729: } catch (RemoteException e) {
0730: TraceTm.jotm.error("Unexpected Exception on prepare:",
0731: e);
0732: throw new SystemException(
0733: "Unexpected Exception on prepare");
0734: }
0735: }
0736:
0737: return ret;
0738: }
0739:
0740: /**
0741: * Modify the transaction associated with the current thread such that
0742: * the only possible outcome of the transaction is to roll back the
0743: * transaction.
0744: *
0745: * @exception IllegalStateException Thrown if the current thread is
0746: * not associated with any transaction.
0747: *
0748: * @exception SystemException Thrown if the transaction manager
0749: * encounters an unexpected error condition
0750: *
0751: */
0752:
0753: public void setRollbackOnly() throws IllegalStateException,
0754: SystemException {
0755: if (TraceTm.jta.isDebugEnabled()) {
0756: TraceTm.jta.debug("TransactionImpl.setRollbackOnly(tx= "
0757: + this + ")");
0758: }
0759:
0760: // *** Distributed transaction
0761: Coordinator coord = myCtx.getCoordinator();
0762:
0763: if (coord != null) {
0764: try {
0765: coord.rollback_only();
0766: } catch (RemoteException e) {
0767: TraceTm.jotm.error(
0768: "Cannot perform coordinator rollback only", e);
0769: }
0770: }
0771:
0772: // perform the if within this else, resolves bugs 300077, 300078
0773: // subbcoord.setRollbackOnly sets the 'status' variable to
0774: // "rollbackonly", checked in SubCoordinator.commit_one_phase
0775: // } else {
0776: // *** Local transaction
0777: // The status is kept in the subcoordinator
0778:
0779: if (subcoord == null) {
0780: // make a subCoordinator object if not existing yet
0781: makeSubCoord();
0782: }
0783:
0784: subcoord.setRollbackOnly();
0785: // }
0786: }
0787:
0788: // ------------------------------------------------------------------
0789: // TimerEventListener implementation
0790: // ------------------------------------------------------------------
0791:
0792: /**
0793: * timeout for that transaction has expired
0794: */
0795:
0796: public void timeoutExpired(Object arg) {
0797: if (TraceTm.jta.isDebugEnabled()) {
0798: TraceTm.jta.debug("TransactionImpl.timeoutExpired");
0799: }
0800:
0801: // increment counter for management
0802: Current.getCurrent().incrementExpiredCounter();
0803:
0804: // make the subcoordinator object, if not existing yet.
0805:
0806: if (subcoord == null) {
0807: // if this is a proxy, just forget this object. The JTM will
0808: // rollback transaction with its own timer.
0809: Terminator term = myCtx.getTerminator();
0810:
0811: if (term != null) {
0812: TraceTm.jotm.info("forget tx (tx=" + this + ")");
0813: Current.getCurrent().forgetTx(getXid());
0814: return;
0815: }
0816:
0817: makeSubCoord();
0818: }
0819:
0820: // Try to set it "rollback only"
0821: // avoids a rollback while SQL requests are in progress
0822: TraceTm.jotm.info("set rollback only (tx=" + this + ")");
0823:
0824: try {
0825: subcoord.setRollbackOnly();
0826: } catch (Exception e) {
0827: TraceTm.jotm.error("cannot rollbackonly:" + e);
0828: return;
0829: }
0830: }
0831:
0832: // ------------------------------------------------------------------
0833: // This object is used as an HashTable index
0834: // ------------------------------------------------------------------
0835:
0836: /**
0837: * return true if objects are identical
0838: */
0839:
0840: public boolean equals(Object obj2) {
0841: TransactionImpl tx2 = (TransactionImpl) obj2;
0842:
0843: // trivial cases
0844: if (tx2 == this ) {
0845: return true;
0846: }
0847: if (tx2 == null) {
0848: return false;
0849: }
0850:
0851: // compare otids
0852: return getXid().equals(tx2.getXid());
0853: }
0854:
0855: /**
0856: * return a hashcode value for this object
0857: */
0858:
0859: public int hashCode() {
0860: if (!genXidhashcode) {
0861: genXidhashcode = true;
0862: myXidhashcode = getXid().hashCode();
0863: }
0864:
0865: return myXidhashcode;
0866: // return getXid().hashCode();
0867: }
0868:
0869: // ------------------------------------------------------------------
0870: // Other methods
0871: // ------------------------------------------------------------------
0872:
0873: /**
0874: * string form
0875: */
0876:
0877: public String toString() {
0878: if (!genXidtostring) {
0879: genXidtostring = true;
0880: myXidtostring = getXid().toString();
0881: }
0882:
0883: return myXidtostring;
0884: // return getXid().toString();
0885: }
0886:
0887: /**
0888: * Return associated PropagationContext
0889: * Used for implicit Context propagation.
0890: *
0891: * @return PropagationContext associated with the transaction.
0892: */
0893:
0894: public synchronized TransactionContext getPropagationContext(
0895: boolean hold) {
0896:
0897: if (propagateCtx) {
0898: if (hold) {
0899: ucount++;
0900: }
0901: return myCtx;
0902: } else {
0903: return null;
0904: }
0905: }
0906:
0907: /**
0908: * set a timer for the transaction
0909: */
0910:
0911: public void setTimer(TimerEvent timer) {
0912: if (TraceTm.jta.isDebugEnabled()) {
0913: TraceTm.jta.debug("set timer for tx (timer=" + timer
0914: + ", tx=" + this + ")");
0915: }
0916: this .timer = timer;
0917: }
0918:
0919: /**
0920: * unset the timer
0921: */
0922:
0923: public void unsetTimer() {
0924: if (TraceTm.jta.isDebugEnabled()) {
0925: TraceTm.jta.debug("unset timer for tx (timer=" + timer
0926: + ", tx=" + this + ")");
0927: }
0928: if (timer != null) {
0929: timer.unset();
0930: timer = null;
0931: }
0932: }
0933:
0934: /**
0935: * set the date time stamp for the transaction
0936: */
0937:
0938: public void setTxDate(String date) {
0939: if (TraceTm.jta.isDebugEnabled()) {
0940: TraceTm.jta.debug("set date for tx (data=" + date + ", tx="
0941: + this + ")");
0942: }
0943: txDate = date;
0944: }
0945:
0946: /**
0947: * get the date time stamp for the transaction
0948: */
0949:
0950: public String getTxDate() {
0951: if (TraceTm.jta.isDebugEnabled()) {
0952: TraceTm.jta.debug("get date for tx (date=" + txDate
0953: + ", tx=" + this + ")");
0954: }
0955: return txDate;
0956: }
0957:
0958: /**
0959: * update the propagation context
0960: * We should be inside the reply of a request involved in a tx here!
0961: */
0962:
0963: public synchronized void updatePropagationContext(
0964: TransactionContext pctx) {
0965: if (TraceTm.jta.isDebugEnabled()) {
0966: TraceTm.jta
0967: .debug("TransactionImpl.updatePropagationContext");
0968: }
0969:
0970: Coordinator remoteCoord = pctx.getCoordinator();
0971:
0972: if (remoteCoord == null && myCtx.getCoordinator() != null) {
0973: TraceTm.jotm
0974: .error("setPropagationContext: Bad Coordinator");
0975: TraceTm.jotm.error("remoteCoord = " + remoteCoord);
0976: TraceTm.jotm.error("myCtx.getCoordinator()= "
0977: + myCtx.getCoordinator());
0978: return;
0979: }
0980:
0981: // release count of users
0982: ucount--;
0983:
0984: // Interpose subCoordinator if newly distributed Tx
0985: if (remoteCoord != null && myCtx.getCoordinator() == null) {
0986: myCtx.setCoordinator(pctx.getCoordinator());
0987:
0988: if (subcoord != null) {
0989: // register the subCoordinator as a Resource.
0990: try {
0991: recoveryCoord = remoteCoord
0992: .register_resource(subcoord);
0993: } catch (RemoteException e) {
0994: TraceTm.jotm.error("Cannot make interposition:", e);
0995: return;
0996: }
0997: }
0998: }
0999:
1000: if (pctx.getTerminator() != null) {
1001: myCtx.setTerminator(pctx.getTerminator());
1002: }
1003: }
1004:
1005: /**
1006: * Get the Xid of the transaction
1007: */
1008:
1009: public Xid getXid() {
1010: return myXid;
1011: }
1012:
1013: /**
1014: * make a SubCoordinator for this Transaction object
1015: */
1016:
1017: private void makeSubCoord() {
1018: if (TraceTm.jta.isDebugEnabled()) {
1019: TraceTm.jta.debug("make subcoordinator");
1020: }
1021:
1022: // Build the SubCoordinator object
1023: try {
1024: subcoord = new SubCoordinator(this , getXid());
1025: } catch (RemoteException e) {
1026: // should never go here.
1027: TraceTm.jotm.error("new SubCoordinator raised exception: ",
1028: e);
1029: return;
1030: }
1031:
1032: // If interposition must be done: do it now!
1033: // Each time we have a remoteCoord + a subCoord, we must interpose.
1034: Coordinator remoteCoord = myCtx.getCoordinator();
1035:
1036: // First of all, create the Control object on JTM
1037: // if it was not created before, if interpose flag is set.
1038:
1039: if (interpose && remoteCoord == null) {
1040: try {
1041: // XXX Note that the JTM will build another Xid
1042: // This must be clarified
1043: if (TraceTm.jta.isDebugEnabled()) {
1044: TraceTm.jta
1045: .debug("Creating a remote Control on JTM for a distributed transaction");
1046: }
1047:
1048: remoteCoord = (Coordinator) javax.rmi.PortableRemoteObject
1049: .narrow(Current.getJTM().create(
1050: myCtx.getTimeout()), Coordinator.class);
1051:
1052: } catch (RemoteException e) {
1053: TraceTm.jotm.error(
1054: "Cannot create distributed transaction:", e);
1055: return;
1056: }
1057:
1058: myCtx.setCoordinator(remoteCoord);
1059:
1060: // fix for transaction context propagation with
1061: // the Jeremie protocol
1062:
1063: if (myCtx.getTerminator() == null) {
1064: myCtx.setTerminator((Terminator) remoteCoord);
1065: }
1066: }
1067:
1068: // Achieve interposition if not already done:
1069: // - register the subCoordinator as a Resource.
1070: if (remoteCoord != null && recoveryCoord == null) {
1071: try {
1072: recoveryCoord = remoteCoord.register_resource(subcoord);
1073: } catch (RemoteException e) {
1074: TraceTm.jotm.error("Cannot make interposition:", e);
1075: return;
1076: }
1077: }
1078: }
1079:
1080: /**
1081: * return true if object is no more used (= removable)
1082: */
1083:
1084: public boolean isRemovable() {
1085: return (ucount == 0 && subcoord == null);
1086: }
1087:
1088: }
|