0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.transaction;
0031:
0032: import com.caucho.jca.UserTransactionImpl;
0033: import com.caucho.jca.UserTransactionSuspendState;
0034: import com.caucho.log.Log;
0035: import com.caucho.transaction.xalog.AbstractXALogStream;
0036: import com.caucho.util.Alarm;
0037: import com.caucho.util.AlarmListener;
0038: import com.caucho.util.CharBuffer;
0039: import com.caucho.util.L10N;
0040:
0041: import javax.transaction.*;
0042: import javax.transaction.xa.XAException;
0043: import javax.transaction.xa.XAResource;
0044: import javax.transaction.xa.Xid;
0045: import java.util.ArrayList;
0046: import java.util.HashMap;
0047: import java.util.logging.Level;
0048: import java.util.logging.Logger;
0049:
0050: /**
0051: * Implementation of the transaction. Transactions are normally
0052: * associated with a single thread.
0053: */
0054: public class TransactionImpl implements Transaction, AlarmListener {
0055: private static final Logger log = Log.open(TransactionImpl.class);
0056: private static final L10N L = new L10N(TransactionImpl.class);
0057:
0058: private final static long DEFAULT_TIMEOUT = 60000;
0059: private final static long EXTRA_TIMEOUT = 60000;
0060: private final static long MAX_TIMEOUT = 24 * 3600 * 1000L;
0061:
0062: // flag when the resource is active (between getConnection() and close())
0063: private final static int RES_ACTIVE = 0x1;
0064: // flag when the resource shares another resource manager
0065: private final static int RES_SHARED_RM = 0x2;
0066: // flag when the resource is suspended
0067: private final static int RES_SUSPENDED = 0x4;
0068: // flag when the resource wants to commit
0069: private final static int RES_COMMIT = 0x8;
0070:
0071: private TransactionManagerImpl _manager;
0072:
0073: // state of the transaction
0074: private int _status;
0075:
0076: // The transaction id for the resource
0077: private XidImpl _xid;
0078:
0079: // true if the transaction is suspended.
0080: private boolean _isSuspended;
0081: private boolean _isDead;
0082:
0083: // How many resources are available
0084: private int _resourceCount;
0085: // The current resources in the transaction
0086: private XAResource[] _resources;
0087: // The xids for the resources
0088: private XidImpl[] _resourceXid;
0089: // Whether the resources are active (between begin/end) or not
0090: private int[] _resourceState;
0091:
0092: private UserTransactionImpl _userTransaction;
0093: private UserTransactionSuspendState _suspendState;
0094:
0095: private HashMap<String, Object> _props;
0096:
0097: private ArrayList<Synchronization> _syncList;
0098:
0099: private Throwable _rollbackException;
0100:
0101: private AbstractXALogStream _xaLog;
0102:
0103: private long _timeout = 0;
0104: private Alarm _alarm;
0105:
0106: /**
0107: * Creates a new transaction.
0108: *
0109: * @param manager the owning transaction manager
0110: */
0111: TransactionImpl(TransactionManagerImpl manager) {
0112: _manager = manager;
0113: _timeout = _manager.getTimeout();
0114: _status = Status.STATUS_NO_TRANSACTION;
0115: _alarm = new Alarm("xa-timeout", this , ClassLoader
0116: .getSystemClassLoader());
0117: }
0118:
0119: /**
0120: * Sets the user transaction.
0121: */
0122: public void setUserTransaction(UserTransactionImpl ut) {
0123: _userTransaction = ut;
0124: }
0125:
0126: /**
0127: * Returns true if the transaction has any associated resources.
0128: */
0129: boolean hasResources() {
0130: return _resourceCount > 0;
0131: }
0132:
0133: /**
0134: * Returns true if the transaction is currently suspended.
0135: */
0136: boolean isSuspended() {
0137: return _isSuspended;
0138: }
0139:
0140: /**
0141: * Returns true if the transaction is dead, i.e. failed for some reason.
0142: */
0143: boolean isDead() {
0144: return _isDead;
0145: }
0146:
0147: /**
0148: * Return true if the transaction has no resources.
0149: */
0150: public boolean isEmpty() {
0151: if (_isDead)
0152: return true;
0153: else if (_resourceCount > 0)
0154: return false;
0155: // XXX: ejb/3692 because TransactionContext adds itself
0156: else if (_syncList != null && _syncList.size() > 1)
0157: return false;
0158: else
0159: return true;
0160: }
0161:
0162: /**
0163: * Start a transaction.
0164: */
0165: void begin() throws SystemException, NotSupportedException {
0166: if (_status != Status.STATUS_NO_TRANSACTION) {
0167: int status = _status;
0168:
0169: try {
0170: rollback();
0171: } catch (Throwable e) {
0172: log.log(Level.WARNING, e.toString(), e);
0173: }
0174:
0175: throw new NotSupportedException(
0176: L
0177: .l(
0178: "Nested transactions are not supported. The previous transaction for this thread did not commit() or rollback(). Check that every UserTransaction.begin() has its commit() or rollback() in a finally block.\nStatus was {0}.",
0179: xaState(status)));
0180: }
0181:
0182: if (_isDead)
0183: throw new IllegalStateException(L
0184: .l("Error trying to use dead transaction."));
0185:
0186: _status = Status.STATUS_ACTIVE;
0187:
0188: _rollbackException = null;
0189:
0190: if (_xid == null)
0191: _xid = _manager.createXID();
0192:
0193: if (log.isLoggable(Level.FINE))
0194: log.fine(this + " begin");
0195:
0196: if (_timeout > 0) {
0197: _alarm.queue(_timeout + EXTRA_TIMEOUT);
0198: }
0199: }
0200:
0201: /**
0202: * Enlists a resource with the current transaction. Example
0203: * resources are database or JMS connections.
0204: *
0205: * @return true if successful
0206: */
0207: public boolean enlistResource(XAResource resource)
0208: throws RollbackException, SystemException {
0209: if (resource == null)
0210: throw new IllegalArgumentException(L
0211: .l("Resource must not be null in enlistResource"));
0212:
0213: if (_isSuspended)
0214: throw new IllegalStateException(L
0215: .l("can't enlist with suspended transaction"));
0216: if (_status == Status.STATUS_ACTIVE) {
0217: // normal
0218: } else {
0219: // validate the status
0220: if (_status != Status.STATUS_MARKED_ROLLBACK) {
0221: } else if (_rollbackException != null)
0222: throw RollbackExceptionWrapper
0223: .create(_rollbackException);
0224: else
0225: throw new RollbackException(
0226: L
0227: .l("can't enlist with rollback-only transaction"));
0228:
0229: if (_status == Status.STATUS_NO_TRANSACTION)
0230: throw new IllegalStateException(L
0231: .l("can't enlist with inactive transaction"));
0232:
0233: throw new IllegalStateException(L.l(
0234: "can't enlist with transaction in '{0}' state",
0235: xaState(_status)));
0236: }
0237:
0238: // creates enough space in the arrays for the resource
0239: if (_resources == null) {
0240: _resources = new XAResource[16];
0241: _resourceState = new int[16];
0242: _resourceXid = new XidImpl[16];
0243: } else if (_resources.length <= _resourceCount) {
0244: int oldLength = _resources.length;
0245: int newLength = 2 * oldLength;
0246:
0247: XAResource[] resources = new XAResource[newLength];
0248: int[] resourceState = new int[newLength];
0249: XidImpl[] resourceXid = new XidImpl[newLength];
0250:
0251: System.arraycopy(_resources, 0, resources, 0, oldLength);
0252: System.arraycopy(_resourceState, 0, resourceState, 0,
0253: oldLength);
0254: System
0255: .arraycopy(_resourceXid, 0, resourceXid, 0,
0256: oldLength);
0257:
0258: _resources = resources;
0259: _resourceState = resourceState;
0260: _resourceXid = resourceXid;
0261: }
0262:
0263: int flags = XAResource.TMNOFLAGS;
0264:
0265: // Active transaction will call the XAResource.start() method
0266: // to let the resource manager know that the resource is managed.
0267: //
0268: // If the resource uses the same resource manager as one of the
0269: // current resources, issue a TMJOIN message.
0270: XidImpl xid = _xid;
0271: boolean hasNewResource = true;
0272:
0273: for (int i = 0; i < _resourceCount; i++) {
0274: if (_resources[i] != resource) {
0275: } else if ((_resourceState[i] & RES_ACTIVE) != 0) {
0276: IllegalStateException exn;
0277: exn = new IllegalStateException(
0278: L
0279: .l("Can't enlist same resource twice. Delist is required before calling enlist with an old resource."));
0280:
0281: setRollbackOnly(exn);
0282: throw exn;
0283: }
0284:
0285: try {
0286: if (_resources[i].isSameRM(resource)) {
0287: flags = XAResource.TMJOIN;
0288: xid = _resourceXid[i];
0289:
0290: if ((_resourceState[i] & RES_ACTIVE) == 0) {
0291: _resources[i] = resource;
0292: _resourceState[i] |= RES_ACTIVE;
0293: hasNewResource = false;
0294: }
0295:
0296: break;
0297: }
0298: } catch (Exception e) {
0299: log.log(Level.FINE, e.toString(), e);
0300: }
0301: }
0302:
0303: if (_resourceCount > 0 && flags != XAResource.TMJOIN)
0304: xid = new XidImpl(_xid, _resourceCount + 1);
0305:
0306: try {
0307: if (_timeout > 0)
0308: resource
0309: .setTransactionTimeout((int) (_timeout / 1000L));
0310:
0311: if (log.isLoggable(Level.FINER)) {
0312: if (flags == XAResource.TMJOIN)
0313: log.finer(this + " join-XA " + resource);
0314: else
0315: log.finer(this + " start-XA " + resource);
0316: }
0317:
0318: resource.start(xid, flags);
0319: } catch (XAException e) {
0320: setRollbackOnly(e);
0321: throw new SystemException(e);
0322: }
0323:
0324: if (hasNewResource) {
0325: _resources[_resourceCount] = resource;
0326: _resourceState[_resourceCount] = RES_ACTIVE;
0327:
0328: if (flags == XAResource.TMJOIN)
0329: _resourceState[_resourceCount] |= RES_SHARED_RM;
0330:
0331: _resourceXid[_resourceCount] = xid;
0332: _resourceCount++;
0333: }
0334:
0335: return true;
0336: }
0337:
0338: /**
0339: * Returns true if the local transaction optimization would be allowed.
0340: */
0341: public boolean allowLocalTransactionOptimization() {
0342: // XXX: can also check if all are non-local
0343: return _resourceCount == 0;
0344: }
0345:
0346: /**
0347: * Returns the current number of resources.
0348: */
0349: public int getEnlistedResourceCount() {
0350: return _resourceCount;
0351: }
0352:
0353: /**
0354: * delists a resource from the current transaction
0355: *
0356: * @param resource the resource to delist
0357: * @param flag XXX: ???
0358: *
0359: * @return true if successful
0360: */
0361: public boolean delistResource(XAResource resource, int flag)
0362: throws SystemException {
0363: if (_isSuspended)
0364: throw new IllegalStateException(L
0365: .l("transaction is suspended"));
0366:
0367: if (_resourceCount == 0)
0368: return true;
0369:
0370: int index;
0371:
0372: for (index = _resourceCount - 1; index >= 0; index--) {
0373: if (_resources[index].equals(resource))
0374: break;
0375: }
0376:
0377: if (index < 0)
0378: return false;
0379:
0380: // If there is no current transaction,
0381: // remove it from the resource list entirely.
0382: if (_status == Status.STATUS_NO_TRANSACTION) {
0383: for (; index + 1 < _resourceCount; index++) {
0384: _resources[index] = _resources[index + 1];
0385: _resourceState[index] = _resourceState[index + 1];
0386: _resourceXid[index] = _resourceXid[index + 1];
0387: }
0388:
0389: _resourceCount--;
0390:
0391: return true;
0392: }
0393:
0394: if (_status == Status.STATUS_MARKED_ROLLBACK)
0395: flag = XAResource.TMFAIL;
0396:
0397: _resourceState[index] &= ~RES_ACTIVE;
0398:
0399: try {
0400: resource.end(_resourceXid[index], flag);
0401: } catch (XAException e) {
0402: setRollbackOnly(e);
0403: throw new SystemException(e);
0404: }
0405:
0406: return true;
0407: }
0408:
0409: /**
0410: * Adds an attribute.
0411: */
0412: public void setAttribute(String var, Object value) {
0413: if (_props == null)
0414: _props = new HashMap<String, Object>();
0415:
0416: _props.put(var, value);
0417: }
0418:
0419: /**
0420: * Gets an attribute.
0421: */
0422: public Object getAttribute(String var) {
0423: if (_props != null)
0424: return _props.get(var);
0425: else
0426: return null;
0427: }
0428:
0429: public Xid getXid() {
0430: return _xid;
0431: }
0432:
0433: /**
0434: * Returns the status of this transaction
0435: */
0436: public int getStatus() {
0437: return _status;
0438: }
0439:
0440: /**
0441: * Suspend the transaction. The timeouts are stopped.
0442: */
0443: void suspend() throws SystemException {
0444: if (_isSuspended)
0445: throw new IllegalStateException(L
0446: .l("can't suspend already-suspended transaction"));
0447:
0448: // _alarm.dequeue();
0449:
0450: _isSuspended = true;
0451:
0452: for (int i = _resourceCount - 1; i >= 0; i--) {
0453: if ((_resourceState[i] & (RES_ACTIVE | RES_SUSPENDED)) == RES_ACTIVE) {
0454: try {
0455: XAResource resource = _resources[i];
0456:
0457: resource.end(_resourceXid[i], XAResource.TMSUSPEND);
0458: } catch (Exception e) {
0459: setRollbackOnly(e);
0460: }
0461: }
0462: }
0463:
0464: if (_userTransaction != null)
0465: _suspendState = _userTransaction.userSuspend();
0466:
0467: if (log.isLoggable(Level.FINER))
0468: log.fine(this + " suspended");
0469: }
0470:
0471: /**
0472: * Resume the transaction and requeue the timeout.
0473: */
0474: void resume() throws SystemException {
0475: if (!_isSuspended)
0476: throw new IllegalStateException(L
0477: .l("can't resume non-suspended transaction"));
0478:
0479: _alarm.queue(_timeout + EXTRA_TIMEOUT);
0480:
0481: for (int i = _resourceCount - 1; i >= 0; i--) {
0482: if ((_resourceState[i] & (RES_ACTIVE | RES_SUSPENDED)) == RES_ACTIVE) {
0483: try {
0484: XAResource resource = _resources[i];
0485:
0486: resource
0487: .start(_resourceXid[i], XAResource.TMRESUME);
0488: } catch (Exception e) {
0489: setRollbackOnly(e);
0490: }
0491: }
0492: }
0493:
0494: if (_userTransaction != null)
0495: _userTransaction.userResume(_suspendState);
0496:
0497: _isSuspended = false;
0498:
0499: if (log.isLoggable(Level.FINE))
0500: log.fine(this + " resume");
0501: }
0502:
0503: /**
0504: * Register a synchronization callback
0505: */
0506: public void registerSynchronization(Synchronization sync) {
0507: if (_syncList == null)
0508: _syncList = new ArrayList<Synchronization>();
0509:
0510: _syncList.add(sync);
0511: }
0512:
0513: /**
0514: * Force any completion to be a rollback.
0515: */
0516: public void setRollbackOnly() throws SystemException {
0517: if (_status != Status.STATUS_ACTIVE
0518: && _status != Status.STATUS_MARKED_ROLLBACK)
0519: throw new IllegalStateException(L
0520: .l("can't set rollback-only"));
0521:
0522: _status = Status.STATUS_MARKED_ROLLBACK;
0523:
0524: _timeout = 0;
0525:
0526: if (log.isLoggable(Level.FINE))
0527: log.fine(this + " rollback-only");
0528: }
0529:
0530: /**
0531: * Force any completion to be a rollback.
0532: */
0533: public void setRollbackOnly(Throwable exn) {
0534: if (_status != Status.STATUS_ACTIVE
0535: && _status != Status.STATUS_MARKED_ROLLBACK)
0536: throw new IllegalStateException(L
0537: .l("can't set rollback-only"));
0538:
0539: _status = Status.STATUS_MARKED_ROLLBACK;
0540:
0541: if (_rollbackException == null)
0542: _rollbackException = exn;
0543:
0544: if (log.isLoggable(Level.FINE))
0545: log.fine(this + " rollback-only: " + exn.toString());
0546: }
0547:
0548: /**
0549: * Commit the transaction.
0550: */
0551: public void commit() throws RollbackException,
0552: HeuristicMixedException, HeuristicRollbackException,
0553: SystemException {
0554: _alarm.dequeue();
0555:
0556: Exception heuristicExn = null;
0557:
0558: try {
0559: callBeforeCompletion();
0560: } catch (RollbackException e) {
0561: callAfterCompletion();
0562:
0563: throw e;
0564: } catch (Throwable e) {
0565: setRollbackOnly(e);
0566: }
0567:
0568: try {
0569: if (_status != Status.STATUS_ACTIVE) {
0570: switch (_status) {
0571: case Status.STATUS_MARKED_ROLLBACK:
0572: rollbackInt();
0573: if (_rollbackException != null)
0574: throw new RollbackExceptionWrapper(
0575: _rollbackException);
0576: else
0577: throw new RollbackException(
0578: L
0579: .l("Transaction has been marked rolled back."));
0580:
0581: case Status.STATUS_NO_TRANSACTION:
0582: throw new IllegalStateException(
0583: L
0584: .l("Can't commit outside of a transaction. Either the UserTransaction.begin() is missing or the transaction has already been committed or rolled back."));
0585:
0586: default:
0587: rollbackInt();
0588: throw new IllegalStateException(L
0589: .l("can't commit {0}", String
0590: .valueOf(_status)));
0591: }
0592: }
0593:
0594: if (log.isLoggable(Level.FINE))
0595: log.fine(this + " committing");
0596:
0597: if (_resourceCount > 0) {
0598: _status = Status.STATUS_PREPARING;
0599:
0600: AbstractXALogStream xaLog = _manager.getXALogStream();
0601: boolean hasPrepare = false;
0602: boolean allowSinglePhase = false;
0603:
0604: for (int i = _resourceCount - 1; i >= 0; i--) {
0605: XAResource resource = (XAResource) _resources[i];
0606:
0607: if (i == 0 && (xaLog == null || !hasPrepare)) {
0608: // server/1601
0609: _resourceState[0] |= RES_COMMIT;
0610:
0611: allowSinglePhase = true;
0612: break;
0613: }
0614:
0615: if ((_resourceState[i] & RES_SHARED_RM) == 0) {
0616: try {
0617: int prepare = resource
0618: .prepare(_resourceXid[i]);
0619:
0620: if (prepare == XAResource.XA_RDONLY) {
0621: } else if (prepare == XAResource.XA_OK) {
0622: hasPrepare = true;
0623: _resourceState[i] |= RES_COMMIT;
0624: } else {
0625: log.finer(this
0626: + " unexpected prepare result "
0627: + prepare);
0628: rollbackInt();
0629: }
0630: } catch (XAException e) {
0631: heuristicExn = heuristicException(
0632: heuristicExn, e);
0633: rollbackInt();
0634: throw new RollbackExceptionWrapper(L
0635: .l("all commits rolled back"),
0636: heuristicExn);
0637: }
0638: }
0639: }
0640:
0641: if (hasPrepare && xaLog != null) {
0642: _xaLog = xaLog;
0643:
0644: xaLog.writeTMCommit(_xid);
0645: }
0646:
0647: _status = Status.STATUS_COMMITTING;
0648:
0649: if (allowSinglePhase) {
0650: try {
0651: XAResource resource = (XAResource) _resources[0];
0652:
0653: if ((_resourceState[0] & RES_COMMIT) != 0)
0654: resource.commit(_xid, true);
0655:
0656: if (_timeout > 0)
0657: resource.setTransactionTimeout(0);
0658: } catch (XAException e) {
0659: log.log(Level.FINE, e.toString(), e);
0660:
0661: heuristicExn = heuristicException(heuristicExn,
0662: e);
0663: }
0664: }
0665:
0666: for (int i = 0; i < _resourceCount; i++) {
0667: XAResource resource = (XAResource) _resources[i];
0668:
0669: if (i == 0 && allowSinglePhase)
0670: continue;
0671:
0672: if ((_resourceState[i] & RES_SHARED_RM) != 0)
0673: continue;
0674: if ((_resourceState[i] & RES_COMMIT) == 0)
0675: continue;
0676:
0677: if (heuristicExn == null) {
0678: try {
0679: resource.commit(_resourceXid[i], false);
0680:
0681: if (_timeout > 0)
0682: resource.setTransactionTimeout(0);
0683: } catch (XAException e) {
0684: heuristicExn = e;
0685: log.log(Level.FINE, e.toString(), e);
0686: }
0687: } else {
0688: try {
0689: resource.rollback(_resourceXid[i]);
0690:
0691: if (_timeout > 0)
0692: resource.setTransactionTimeout(0);
0693: } catch (XAException e) {
0694: log.log(Level.FINE, e.toString(), e);
0695: }
0696: }
0697: }
0698: }
0699:
0700: if (heuristicExn != null && log.isLoggable(Level.FINE))
0701: log.fine(this + " " + heuristicExn);
0702:
0703: if (heuristicExn == null)
0704: _status = Status.STATUS_COMMITTED;
0705: else if (heuristicExn instanceof RollbackException) {
0706: _status = Status.STATUS_ROLLEDBACK;
0707: throw (RollbackException) heuristicExn;
0708: } else if (heuristicExn instanceof HeuristicMixedException) {
0709: _status = Status.STATUS_ROLLEDBACK;
0710: throw (HeuristicMixedException) heuristicExn;
0711: } else if (heuristicExn instanceof HeuristicRollbackException) {
0712: _status = Status.STATUS_ROLLEDBACK;
0713: throw (HeuristicRollbackException) heuristicExn;
0714: } else if (heuristicExn instanceof SystemException) {
0715: _status = Status.STATUS_ROLLEDBACK;
0716: throw (SystemException) heuristicExn;
0717: } else {
0718: _status = Status.STATUS_ROLLEDBACK;
0719: throw RollbackExceptionWrapper.create(heuristicExn);
0720: }
0721: } finally {
0722: callAfterCompletion();
0723: }
0724: }
0725:
0726: /**
0727: * Rollback the transaction.
0728: */
0729: public void rollback() {
0730: _alarm.dequeue();
0731:
0732: try {
0733: callBeforeCompletion();
0734: } catch (Throwable e) {
0735: setRollbackOnly(e);
0736: }
0737:
0738: try {
0739: switch (_status) {
0740: case Status.STATUS_ACTIVE:
0741: case Status.STATUS_MARKED_ROLLBACK:
0742: // fall through to normal completion
0743: break;
0744:
0745: case Status.STATUS_NO_TRANSACTION:
0746: throw new IllegalStateException(
0747: L
0748: .l("Can't rollback outside of a transaction. Either the UserTransaction.begin() is missing or the transaction has already been committed or rolled back."));
0749:
0750: default:
0751: rollbackInt();
0752: throw new IllegalStateException(L.l(
0753: "Can't rollback in state: {0}", String
0754: .valueOf(_status)));
0755: }
0756:
0757: _status = Status.STATUS_MARKED_ROLLBACK;
0758:
0759: rollbackInt();
0760: } finally {
0761: callAfterCompletion();
0762: }
0763: }
0764:
0765: /**
0766: * Calculates the heuristic exception based on the resource manager's
0767: * exception.
0768: */
0769: private Exception heuristicException(Exception oldException,
0770: XAException xaException) {
0771: switch (xaException.errorCode) {
0772: case XAException.XA_HEURHAZ:
0773: case XAException.XA_HEURCOM:
0774: return oldException;
0775:
0776: case XAException.XA_HEURRB:
0777: if (oldException instanceof HeuristicMixedException)
0778: return oldException;
0779: else if (oldException instanceof HeuristicRollbackException)
0780: return oldException;
0781: else if (oldException instanceof RollbackException)
0782: return oldException;
0783: else
0784: return new HeuristicRollbackException(
0785: getXAMessage(xaException));
0786:
0787: default:
0788: if (oldException instanceof SystemException)
0789: return oldException;
0790: else
0791: return new SystemExceptionWrapper(
0792: getXAMessage(xaException), xaException);
0793: }
0794: }
0795:
0796: /**
0797: * Rollback the transaction.
0798: */
0799: private void rollbackInt() {
0800: _status = Status.STATUS_ROLLING_BACK;
0801:
0802: if (log.isLoggable(Level.FINE))
0803: log.fine(this + " rollback");
0804:
0805: for (int i = 0; i < _resourceCount; i++) {
0806: XAResource resource = (XAResource) _resources[i];
0807:
0808: if ((_resourceState[i] & RES_SHARED_RM) != 0)
0809: continue;
0810:
0811: try {
0812: resource.rollback(_resourceXid[i]);
0813:
0814: if (_timeout > 0)
0815: resource.setTransactionTimeout(0);
0816: } catch (Throwable e) {
0817: log.log(Level.FINE, e.toString(), e);
0818: }
0819: }
0820:
0821: _status = Status.STATUS_ROLLEDBACK;
0822: }
0823:
0824: /**
0825: * Call all the Synchronization listeners before the commit()/rollback()
0826: * starts.
0827: */
0828: private void callBeforeCompletion() throws RollbackException {
0829: int length = _syncList == null ? 0 : _syncList.size();
0830:
0831: for (int i = 0; i < length; i++) {
0832: Synchronization sync = _syncList.get(i);
0833:
0834: try {
0835: sync.beforeCompletion();
0836: } catch (RuntimeException e) {
0837: throw new RollbackException(e);
0838: } catch (Throwable e) {
0839: log.log(Level.FINE, e.toString(), e);
0840: }
0841: }
0842:
0843: // tell the resources everything's done
0844: for (int i = _resourceCount - 1; i >= 0; i--) {
0845: XAResource resource = _resources[i];
0846:
0847: int flag;
0848:
0849: if (_status == Status.STATUS_MARKED_ROLLBACK)
0850: flag = XAResource.TMFAIL;
0851: else
0852: flag = XAResource.TMSUCCESS;
0853:
0854: try {
0855: if ((_resourceState[i] & RES_ACTIVE) != 0)
0856: resource.end(_resourceXid[i], flag);
0857: } catch (Throwable e) {
0858: log.log(Level.FINE, e.toString(), e);
0859: setRollbackOnly(e);
0860: }
0861: }
0862: }
0863:
0864: /**
0865: * Call all the Synchronization listeners after the commit()/rollback()
0866: * complete.
0867: */
0868: private void callAfterCompletion() {
0869: ArrayList<Synchronization> syncList = _syncList;
0870: _syncList = null;
0871:
0872: _userTransaction = null;
0873:
0874: XidImpl xid = _xid;
0875: _xid = null;
0876:
0877: int status = _status;
0878: _status = Status.STATUS_NO_TRANSACTION;
0879:
0880: _rollbackException = null;
0881:
0882: // remove the resources which have officially delisted
0883: for (int i = _resourceCount - 1; i >= 0; i--)
0884: _resources[i] = null;
0885:
0886: _resourceCount = 0;
0887:
0888: AbstractXALogStream xaLog = _xaLog;
0889: _xaLog = null;
0890:
0891: if (xaLog != null) {
0892: try {
0893: xaLog.writeTMFinish(xid);
0894: } catch (Throwable e) {
0895: log.log(Level.FINER, e.toString(), e);
0896: }
0897: }
0898:
0899: int length = syncList == null ? 0 : syncList.size();
0900: for (int i = 0; i < length; i++) {
0901: Synchronization sync = (Synchronization) syncList.get(i);
0902:
0903: try {
0904: sync.afterCompletion(status);
0905: } catch (Throwable e) {
0906: log.log(Level.FINE, e.toString(), e);
0907: }
0908: }
0909:
0910: if (_props != null)
0911: _props.clear();
0912: }
0913:
0914: /**
0915: * sets the timeout for the transaction
0916: */
0917: public void setTransactionTimeout(int seconds)
0918: throws SystemException {
0919: if (seconds == 0)
0920: _timeout = _manager.getTimeout();
0921: else if (seconds < 0)
0922: _timeout = MAX_TIMEOUT;
0923: else {
0924: _timeout = 1000L * (long) seconds;
0925: }
0926: }
0927:
0928: /**
0929: * sets the timeout for the transaction
0930: */
0931: public int getTransactionTimeout() throws SystemException {
0932: if (_timeout < 0)
0933: return -1;
0934: else
0935: return (int) (_timeout / 1000L);
0936: }
0937:
0938: public void handleAlarm(Alarm alarm) {
0939: try {
0940: log.warning(L.l("{0}: timed out after {1} seconds.", this ,
0941: String.valueOf(getTransactionTimeout())));
0942:
0943: setRollbackOnly();
0944:
0945: // should not close at this point because there could be following
0946: // statements that also need to be rolled back
0947: // server/16a7
0948: // close();
0949: } catch (Throwable e) {
0950: log.log(Level.FINE, e.toString(), e);
0951: }
0952: }
0953:
0954: /**
0955: * Close the transaction, rolling back everything and removing all
0956: * enlisted resources.
0957: */
0958: public void close() {
0959: _isDead = true;
0960: _alarm.dequeue();
0961:
0962: try {
0963: if (_status != Status.STATUS_NO_TRANSACTION)
0964: rollback();
0965: } catch (Throwable e) {
0966: log.log(Level.FINE, e.toString(), e);
0967: }
0968:
0969: if (_syncList != null)
0970: _syncList.clear();
0971:
0972: for (int i = _resourceCount - 1; i >= 0; i--)
0973: _resources[i] = null;
0974:
0975: _resourceCount = 0;
0976:
0977: _xid = null;
0978: }
0979:
0980: /**
0981: * Printable version of the transaction.
0982: */
0983: public String toString() {
0984: if (_xid == null)
0985: return "Transaction[]";
0986:
0987: CharBuffer cb = new CharBuffer();
0988:
0989: cb.append("Transaction[");
0990:
0991: byte[] branch = _xid.getBranchQualifier();
0992:
0993: addByte(cb, branch[0]);
0994:
0995: cb.append(":");
0996:
0997: byte[] global = _xid.getGlobalTransactionId();
0998: for (int i = 24; i < 28; i++)
0999: addByte(cb, global[i]);
1000:
1001: cb.append("]");
1002:
1003: return cb.toString();
1004: }
1005:
1006: /**
1007: * Adds hex for debug
1008: */
1009: private void addByte(CharBuffer cb, int b) {
1010: int h = (b / 16) & 0xf;
1011: int l = b & 0xf;
1012:
1013: if (h >= 10)
1014: cb.append((char) ('a' + h - 10));
1015: else
1016: cb.append((char) ('0' + h));
1017:
1018: if (l >= 10)
1019: cb.append((char) ('a' + l - 10));
1020: else
1021: cb.append((char) ('0' + l));
1022: }
1023:
1024: /**
1025: * Converts XA error code to a message.
1026: */
1027: private static String getXAMessage(XAException xaException) {
1028: if (xaException.getMessage() != null
1029: && !xaException.getMessage().equals(""))
1030: return xaException.getMessage();
1031: else
1032: return (xaName(xaException.errorCode) + ": " + xaMessage(xaException.errorCode));
1033: }
1034:
1035: /**
1036: * Converts XA state code to string.
1037: */
1038: private static String xaState(int xaState) {
1039: switch (xaState) {
1040: case Status.STATUS_ACTIVE:
1041: return "ACTIVE";
1042: case Status.STATUS_MARKED_ROLLBACK:
1043: return "MARKED-ROLLBACK";
1044: case Status.STATUS_PREPARED:
1045: return "PREPARED";
1046: case Status.STATUS_COMMITTED:
1047: return "COMITTED";
1048: case Status.STATUS_ROLLEDBACK:
1049: return "ROLLEDBACK";
1050: case Status.STATUS_PREPARING:
1051: return "PREPARING";
1052: case Status.STATUS_COMMITTING:
1053: return "COMMITTING";
1054: case Status.STATUS_ROLLING_BACK:
1055: return "ROLLING_BACK";
1056: default:
1057: return "XA-STATE(" + xaState + ")";
1058: }
1059: }
1060:
1061: /**
1062: * Converts XA error code to string.
1063: */
1064: private static String xaName(int xaCode) {
1065: switch (xaCode) {
1066: // rollback codes
1067: case XAException.XA_RBROLLBACK:
1068: return "XA_RBROLLBACK";
1069: case XAException.XA_RBCOMMFAIL:
1070: return "XA_RBCOMMFAIL";
1071: case XAException.XA_RBDEADLOCK:
1072: return "XA_RBDEADLOCK";
1073: case XAException.XA_RBINTEGRITY:
1074: return "XA_RBINTEGRITY";
1075: case XAException.XA_RBOTHER:
1076: return "XA_RBOTHER";
1077: case XAException.XA_RBPROTO:
1078: return "XA_RBPROTO";
1079: case XAException.XA_RBTIMEOUT:
1080: return "XA_RBTIMEOUT";
1081: case XAException.XA_RBTRANSIENT:
1082: return "XA_RBTRANSIENT";
1083:
1084: // suspension code
1085: case XAException.XA_NOMIGRATE:
1086: return "XA_NOMIGRATE";
1087:
1088: // heuristic completion codes
1089: case XAException.XA_HEURHAZ:
1090: return "XA_HEURHAZ";
1091: case XAException.XA_HEURCOM:
1092: return "XA_HEURCOM";
1093: case XAException.XA_HEURRB:
1094: return "XA_HEURRB";
1095: case XAException.XA_HEURMIX:
1096: return "XA_HEURMIX";
1097: case XAException.XA_RDONLY:
1098: return "XA_RDONLY";
1099:
1100: // errors
1101: case XAException.XAER_RMERR:
1102: return "XA_RMERR";
1103: case XAException.XAER_NOTA:
1104: return "XA_NOTA";
1105: case XAException.XAER_INVAL:
1106: return "XA_INVAL";
1107: case XAException.XAER_PROTO:
1108: return "XA_PROTO";
1109: case XAException.XAER_RMFAIL:
1110: return "XA_RMFAIL";
1111: case XAException.XAER_DUPID:
1112: return "XA_DUPID";
1113: case XAException.XAER_OUTSIDE:
1114: return "XA_OUTSIDE";
1115:
1116: default:
1117: return "XA(" + xaCode + ")";
1118: }
1119: }
1120:
1121: /**
1122: * Converts XA error code to a message.
1123: */
1124: private static String xaMessage(int xaCode) {
1125: switch (xaCode) {
1126: // rollback codes
1127: case XAException.XA_RBROLLBACK:
1128: case XAException.XA_RBOTHER:
1129: return L
1130: .l("Resource rolled back for an unspecified reason.");
1131: case XAException.XA_RBCOMMFAIL:
1132: return L
1133: .l("Resource rolled back because of a communication failure.");
1134: case XAException.XA_RBDEADLOCK:
1135: return L.l("Resource rolled back because of a deadlock.");
1136: case XAException.XA_RBINTEGRITY:
1137: return L
1138: .l("Resource rolled back because of an integrity check failure.");
1139: case XAException.XA_RBPROTO:
1140: return L
1141: .l("Resource rolled back because of a protocol error in the resource manager.");
1142: case XAException.XA_RBTIMEOUT:
1143: return L.l("Resource rolled back because of a timeout.");
1144: case XAException.XA_RBTRANSIENT:
1145: return L
1146: .l("Resource rolled back because of transient error.");
1147:
1148: // suspension code
1149: case XAException.XA_NOMIGRATE:
1150: return L
1151: .l("Resumption must occur where the suspension occurred.");
1152:
1153: // heuristic completion codes
1154: case XAException.XA_HEURHAZ:
1155: return L
1156: .l("Resource may have been heuristically completed.");
1157: case XAException.XA_HEURCOM:
1158: return L.l("Resource has been heuristically committed.");
1159: case XAException.XA_HEURRB:
1160: return L.l("Resource has been heuristically rolled back.");
1161: case XAException.XA_HEURMIX:
1162: return L
1163: .l("Resource has been heuristically committed and rolled back.");
1164: case XAException.XA_RDONLY:
1165: return L
1166: .l("Resource was read-only and has been heuristically committed.");
1167:
1168: // errors
1169: case XAException.XAER_RMERR:
1170: return L.l("Resource manager error.");
1171: case XAException.XAER_NOTA:
1172: return L.l("The XID (transaction identifier) was invalid.");
1173: case XAException.XAER_INVAL:
1174: return L.l("Invalid arguments were given.");
1175: case XAException.XAER_PROTO:
1176: return L.l("Method called in an invalid context.");
1177: case XAException.XAER_RMFAIL:
1178: return L.l("Resource manager is unavailable.");
1179: case XAException.XAER_DUPID:
1180: return L.l("Duplicate XID (transaction identifier).");
1181: case XAException.XAER_OUTSIDE:
1182: return L
1183: .l("Resource manager called outside of transaction.");
1184:
1185: default:
1186: return "";
1187: }
1188: }
1189: }
|