0001: /*
0002: * @(#) Current.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: * The original code and portions created by Bull SA are
0013: * Copyright (c) 1999 BULL SA
0014: * All rights reserved.
0015: *
0016: * Redistribution and use in source and binary forms, with or without
0017: * modification, are permitted provided that the following conditions are met:
0018: *
0019: * -Redistributions of source code must retain the above copyright notice, this
0020: * list of conditions and the following disclaimer.
0021: *
0022: * -Redistributions in binary form must reproduce the above copyright notice,
0023: * this list of conditions and the following disclaimer in the documentation
0024: * and/or other materials provided with the distribution.
0025: *
0026: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0027: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0028: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0029: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0030: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0031: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0032: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0033: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0034: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0035: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0036: * POSSIBILITY OF SUCH DAMAGE.
0037: * --------------------------------------------------------------------------
0038: *
0039: * Contributor(s):
0040: * 01/11/06 Christophe Ney cney@batisseurs.com
0041: * Added ResourceManagerListener mechanism to remove ThreadData
0042: * dependency.
0043: * 01/12/03 Dean Jennings - synchronizedMap for txXids
0044: *
0045: * --------------------------------------------------------------------------
0046: * $Id: Current.java,v 1.54 2005/05/12 14:06:26 tonyortiz Exp $
0047: * --------------------------------------------------------------------------
0048: */
0049: package org.objectweb.jotm;
0050:
0051: import java.io.Serializable;
0052: import java.util.Collections;
0053: import java.util.Date;
0054: import java.util.EmptyStackException;
0055: import java.util.HashMap;
0056: import java.util.Iterator;
0057: import java.util.List;
0058: import java.util.Map;
0059: import java.util.Set;
0060: import java.util.Stack;
0061: import java.util.Vector;
0062:
0063: import javax.naming.NamingException;
0064: import javax.naming.Reference;
0065: import javax.naming.Referenceable;
0066: import javax.naming.StringRefAddr;
0067: import javax.resource.spi.XATerminator;
0068: import javax.transaction.HeuristicMixedException;
0069: import javax.transaction.HeuristicRollbackException;
0070: import javax.transaction.InvalidTransactionException;
0071: import javax.transaction.NotSupportedException;
0072: import javax.transaction.RollbackException;
0073: import javax.transaction.Status;
0074: import javax.transaction.SystemException;
0075: import javax.transaction.Transaction;
0076: import javax.transaction.UserTransaction;
0077: import javax.transaction.xa.XAException;
0078: import javax.transaction.xa.XAResource;
0079:
0080: import org.objectweb.transaction.jta.ResourceManagerEvent;
0081: import org.objectweb.transaction.jta.TransactionManager;
0082: import org.objectweb.howl.log.xa.XACommittingTx;
0083:
0084: /**
0085: * <code>Current</code> is the common implementation for both
0086: * <code>UserTransaction</code> and <code>TransactionManager</code>.
0087: * <ul>
0088: * <li><code>UserTransaction</code> is used by clients that want to demarcate
0089: * transactions themselves. It is referenceable through JNDI.</li>
0090: * <li><code>TransactionManager</code> is used by an application server.</li>
0091: * </ul>
0092: *
0093: * <p>This object is unique in a VM, i. e. each application server has
0094: * <em>ONE</em> <code>Current</code> object and each client program should
0095: * normally issue only <em>ONE</em> lookup on JNDI.</p>
0096: *
0097: * <p><code>Current</code> also implements <code>Referenceable</code> and
0098: * <code>Serializable</code> because of JNDI.</p>
0099: */
0100:
0101: public class Current implements UserTransaction, TransactionManager,
0102: Referenceable, Serializable {
0103:
0104: // transaction associated to the thread
0105: // Thread specific data
0106: private transient static ThreadLocal threadTx = new ThreadLocal();
0107:
0108: // ------------------------------------------------------------------
0109: // Private data
0110: // ------------------------------------------------------------------
0111:
0112: // Static hashtable: Xid ---> transaction
0113: private transient static Map txXids = Collections
0114: .synchronizedMap(new HashMap());
0115:
0116: // Must be init at null, for clients that do not get UserTransaction
0117: private transient static Current unique = null;
0118:
0119: // private info
0120: private transient static TimerManager timermgr = null; // local
0121: private transient static TransactionFactory tm = null; // local or remote
0122: private transient static TransactionRecovery tr = null; // transaction recovery object
0123:
0124: private static final String JOTM_VERSION = "JOTM 2.0.10";
0125: private static final int DEFAULT_TIMEOUT = 60;
0126: private int defaultTimeout = DEFAULT_TIMEOUT;
0127: private int transactionTimeout = DEFAULT_TIMEOUT;
0128: private static final boolean DEFAULT_RECOVERY = false;
0129: private static boolean transactionRecovery = DEFAULT_RECOVERY;
0130:
0131: // management counter
0132: private transient int nb_bg_tx = 0;
0133: private transient int nb_rb_tx = 0;
0134: private transient int nb_cm_tx = 0;
0135: private transient int nb_to = 0;
0136:
0137: // ------------------------------------------------------------------
0138: // Constructors
0139: // ------------------------------------------------------------------
0140:
0141: /**
0142: * Default constructor.
0143: * A client does not need the TMFactory.
0144: */
0145:
0146: public Current() {
0147: TraceTm.jta.info(JOTM_VERSION);
0148:
0149: if (TraceTm.jta.isDebugEnabled()) {
0150: TraceTm.jta.debug("no args constructor");
0151: }
0152:
0153: unique = this ;
0154: timermgr = TimerManager.getInstance();
0155:
0156: try {
0157: tr = new TransactionRecoveryImpl();
0158: } catch (Exception e) {
0159: setDefaultRecovery(false);
0160: TraceTm.recovery.error("Cannot open Howl Log");
0161: TraceTm.recovery.error("JOTM Recovery is being disabled");
0162: }
0163: }
0164:
0165: /**
0166: * Constructor for an application server.
0167: *
0168: * The TM factory is passed as an argument. Note that the TM factory can be
0169: * either local or remote.
0170: *
0171: * @param tmfact TM Factory to use
0172: */
0173:
0174: public Current(TransactionFactory tmfact) {
0175: TraceTm.jta.info(JOTM_VERSION);
0176:
0177: if (TraceTm.jta.isDebugEnabled()) {
0178: TraceTm.jta.debug("TransactionFactory=" + tmfact);
0179: }
0180:
0181: unique = this ;
0182: tm = tmfact;
0183:
0184: timermgr = TimerManager.getInstance();
0185:
0186: try {
0187: tr = new TransactionRecoveryImpl();
0188: } catch (Exception e) {
0189: setDefaultRecovery(false);
0190: TraceTm.recovery.error("Cannot open Howl Log");
0191: TraceTm.recovery.error("JOTM Recovery is being disabled");
0192: }
0193: }
0194:
0195: /**
0196: * Gets the <code>TransactionManager</code> instance.
0197: *
0198: * @return TransactionManager
0199: */
0200:
0201: public static TransactionManager getTransactionManager() {
0202: return (TransactionManager) unique;
0203: }
0204:
0205: // ------------------------------------------------------------------
0206: // UserTransaction implementation
0207: // ------------------------------------------------------------------
0208:
0209: /**
0210: * Creates a new transaction and associate it with the current thread.
0211: *
0212: * @exception NotSupportedException Thrown if the thread is already
0213: * associated with a transaction. (nested transaction are not
0214: * supported)
0215: *
0216: * @exception SystemException Thrown if the transaction manager
0217: * encounters an unexpected error condition
0218: */
0219:
0220: public void begin() throws NotSupportedException, SystemException {
0221:
0222: if (TraceTm.jta.isDebugEnabled()) {
0223: TraceTm.jta.debug("begin transaction");
0224: }
0225:
0226: // checks that no transaction is already associated with this thread.
0227: TransactionImpl tx = (TransactionImpl) threadTx.get();
0228:
0229: if (TraceTm.jta.isDebugEnabled()) {
0230: TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
0231: }
0232:
0233: if (tx != null) {
0234: if (txXids.containsValue(tx)) {
0235: throw new NotSupportedException(
0236: "Nested transactions not supported");
0237: } else {
0238: if (TraceTm.jta.isDebugEnabled()) {
0239: TraceTm.jta.debug("Resetting current tx = " + tx
0240: + " since it is already completed.");
0241: }
0242: }
0243: }
0244:
0245: // builds a new Xid
0246: // - should pass servername + ip addr. (LATER)
0247: XidImpl otid = new XidImpl();
0248:
0249: // creates a new TransactionImpl object
0250: // - May raise SystemException
0251: tx = new TransactionImpl(otid, transactionTimeout);
0252:
0253: if (TraceTm.jta.isDebugEnabled()) {
0254: TraceTm.jta.debug("tx=" + tx);
0255: }
0256:
0257: /// clear suspended transaction.
0258: try {
0259: tx.doAttach(XAResource.TMJOIN);
0260: } catch (RollbackException e) {
0261: // never.
0262: TraceTm.jotm.error("doAttach: RollbackException");
0263: throw new SystemException(
0264: "RollbackException in occured in begin() "
0265: + e.getMessage());
0266: }
0267:
0268: // associates transaction with current thread
0269: threadTx.set(tx);
0270:
0271: if (TraceTm.jta.isDebugEnabled()) {
0272: TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
0273: }
0274:
0275: // associates this Tx with the Xid
0276: putTxXid(otid, tx);
0277:
0278: // sets a timer for the transaction
0279: if (timermgr != null) {
0280: tx.setTimer(timermgr.addTimer(tx, transactionTimeout, null,
0281: false));
0282: }
0283:
0284: // sets the time stamp for the transaction
0285: Date myDate = new Date();
0286: tx.setTxDate(myDate.toString());
0287:
0288: // enlist all connections using the event callback this should be done in UserTransaction
0289: // was done in doAttach
0290: Stack curStack = (Stack) eventListStack.get();
0291:
0292: if (curStack != null) { // if there is opened connections
0293:
0294: try {
0295: List list = (List) curStack.peek();
0296:
0297: if (list != null) {
0298:
0299: for (Iterator it = list.iterator(); it.hasNext();) {
0300: ((ResourceManagerEvent) it.next())
0301: .enlistConnection(tx);
0302: }
0303: } else {
0304:
0305: if (TraceTm.jta.isDebugEnabled()) {
0306: TraceTm.jta
0307: .debug("Current.begin called with null list");
0308: }
0309: }
0310: } catch (EmptyStackException e) {
0311: if (TraceTm.jta.isDebugEnabled()) {
0312: TraceTm.jta
0313: .debug("Current.begin called with empty stack");
0314: }
0315: }
0316: }
0317: }
0318:
0319: // ------------------------------------------------------------------
0320: // Inflow Transaction implementation
0321: // ------------------------------------------------------------------
0322:
0323: /**
0324: * Creates a new inflow transaction and associates it with the current thread.
0325: *
0326: * @param passxid <code>Xid</code> of the inflow transaction.
0327: *
0328: * @exception NotSupportedException Thrown if the thread is already
0329: * associated with a transaction. (nested transaction are not
0330: * supported)
0331: *
0332: * @exception SystemException Thrown if the transaction manager
0333: * encounters an unexpected error condition
0334: */
0335:
0336: public void begin(javax.transaction.xa.Xid passxid)
0337: throws NotSupportedException, SystemException {
0338: begin(passxid, (long) transactionTimeout);
0339: }
0340:
0341: /**
0342: * Creates a new inflow transaction and associates it with the current thread.
0343: *
0344: * @param passxid <code>Xid</code> of the inflow transaction.
0345: *
0346: * @param timeout value of the timeout (in seconds). If the value is less than
0347: * or equal to zero, the value will be set to the default value.
0348: *
0349: * @exception NotSupportedException Thrown if the thread is already
0350: * associated with a transaction. (nested transaction are not
0351: * supported)
0352: *
0353: * @exception SystemException Thrown if the transaction manager
0354: * encounters an unexpected error condition
0355: */
0356:
0357: public void begin(javax.transaction.xa.Xid passxid, long timeout)
0358: throws NotSupportedException, SystemException {
0359:
0360: if (TraceTm.jta.isDebugEnabled()) {
0361: TraceTm.jta.debug("begin inflow transaction, timeout = "
0362: + timeout);
0363: }
0364:
0365: if (timeout <= 0) {
0366: timeout = defaultTimeout;
0367: }
0368:
0369: // checks that no transaction is already associated with this thread.
0370: TransactionImpl tx = (TransactionImpl) threadTx.get();
0371:
0372: if (TraceTm.jta.isDebugEnabled()) {
0373: TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
0374: }
0375:
0376: if (tx != null) {
0377: if (txXids.containsValue(tx)) {
0378: throw new NotSupportedException(
0379: "Nested transactions not supported");
0380: } else {
0381: if (TraceTm.jta.isDebugEnabled()) {
0382: TraceTm.jta.debug("Resetting current tx = " + tx
0383: + " since it is already completed.");
0384: }
0385: }
0386: }
0387:
0388: // stores the passed xid components
0389: XidImpl pxid = new XidImpl(passxid);
0390:
0391: // creates a new TransactionImpl object
0392: // - May raise SystemException
0393: tx = new TransactionImpl(pxid, (int) timeout);
0394:
0395: if (TraceTm.jta.isDebugEnabled()) {
0396: TraceTm.jta.debug("tx=" + tx);
0397: }
0398:
0399: // associates transaction with current thread
0400: threadTx.set(tx);
0401:
0402: if (TraceTm.jta.isDebugEnabled()) {
0403: TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
0404: }
0405:
0406: // associates this Tx with the Xid
0407: putTxXid(pxid, tx);
0408:
0409: // sets a timer for the transaction
0410: if (timermgr != null) {
0411: tx.setTimer(timermgr.addTimer(tx, (int) timeout, null,
0412: false));
0413: }
0414:
0415: // sets the time stamp for the transaction
0416: Date myDate = new Date();
0417: tx.setTxDate(myDate.toString());
0418: }
0419:
0420: /**
0421: * Gets the inflow transaction object that represents the transaction context of
0422: * the calling thread.
0423: *
0424: * @return the XATerminator object representing the inflow transaction
0425: * associated with the calling thread. If the calling thread is
0426: * not associated with an inflow transaction, a null object reference
0427: * is returned.
0428: *
0429: * @exception XAException Thrown if the transaction manager
0430: * encounters an unexpected error condition
0431: */
0432:
0433: public XATerminator getXATerminator() throws XAException {
0434:
0435: XATerminator xaterm = null;
0436:
0437: try {
0438: xaterm = new XATerminatorImpl();
0439: } catch (XAException e) {
0440: if (TraceTm.jta.isDebugEnabled()) {
0441: TraceTm.jta.debug("Cannot create XATerminatorImpl" + e);
0442: }
0443: }
0444:
0445: return xaterm;
0446: }
0447:
0448: // ------------------------------------------------------------------
0449: // End of Inflow Transaction implementation
0450: // ------------------------------------------------------------------
0451:
0452: /**
0453: * Commits the transaction associated with the current thread. When this
0454: * method completes, the thread becomes associated with no transaction.
0455: *
0456: * @exception RollbackException Thrown to indicate that the transaction
0457: * has been rolled back rather than committed.
0458: *
0459: * @exception HeuristicMixedException Thrown to indicate that a heuristic
0460: * decision was made and that some relevant updates have been committed
0461: * while others have been rolled back.
0462: *
0463: * @exception HeuristicRollbackException Thrown to indicate that a
0464: * heuristic decision was made and that some relevant updates have been
0465: * rolled back.
0466: *
0467: * @exception SecurityException Thrown to indicate that the thread is not
0468: * allowed to commit the transaction.
0469: *
0470: * @exception IllegalStateException Thrown if the current thread is not
0471: * associated with a transaction.
0472: *
0473: * @exception SystemException Thrown if the transaction manager encounters
0474: * an unexpected error condition
0475: */
0476:
0477: public void commit() throws RollbackException,
0478: HeuristicMixedException, HeuristicRollbackException,
0479: SecurityException, IllegalStateException, SystemException {
0480:
0481: if (TraceTm.jta.isDebugEnabled()) {
0482: TraceTm.jta.debug("commit transaction ");
0483: }
0484:
0485: // Get Transaction
0486: TransactionImpl tx = (TransactionImpl) getTransaction();
0487:
0488: if (tx == null) {
0489: throw new IllegalStateException(
0490: "Cannot get Transaction for commit");
0491: }
0492:
0493: if (TraceTm.jta.isDebugEnabled()) {
0494: TraceTm.jta.debug("tx=" + tx);
0495: }
0496:
0497: // Commit Transaction. Exceptions may be raised!
0498: try {
0499: tx.commit();
0500: } finally {
0501: // Dissociates the transaction from current thread
0502: // Has not been done in doDetach because we need to
0503: // be in the transactional context for beforeCompletion
0504: threadTx.set(null);
0505:
0506: // Reset the timeout to the default timeout
0507: transactionTimeout = defaultTimeout;
0508:
0509: if (TraceTm.jta.isDebugEnabled()) {
0510: TraceTm.jta.debug("threadTx.set= null");
0511: TraceTm.jta.debug("reset timeout= " + defaultTimeout);
0512: }
0513: }
0514: }
0515:
0516: /**
0517: * Rolls back the transaction associated with the current thread. When this
0518: * method completes, the thread becomes associated with no transaction.
0519: *
0520: * @exception SecurityException Thrown to indicate that the thread is not
0521: * allowed to roll back the transaction.
0522: *
0523: * @exception IllegalStateException Thrown if the current thread is not
0524: * associated with a transaction.
0525: *
0526: * @exception SystemException Thrown if the transaction manager
0527: * encounters an unexpected error condition
0528: */
0529: public void rollback() throws IllegalStateException,
0530: SecurityException, SystemException {
0531:
0532: if (TraceTm.jta.isDebugEnabled()) {
0533: TraceTm.jta.debug("Current.rollback()");
0534: }
0535:
0536: // Get Transaction
0537: TransactionImpl tx = (TransactionImpl) getTransaction();
0538:
0539: if (tx == null) {
0540: throw new IllegalStateException(
0541: "Cannot get Transaction for rollback");
0542: }
0543:
0544: threadTx.set(null);
0545:
0546: if (TraceTm.jta.isDebugEnabled()) {
0547: TraceTm.jta.debug("threadTx.set= null");
0548: }
0549:
0550: // Roll back the transaction. Exceptions may be raised!
0551: tx.rollback();
0552:
0553: if (TraceTm.jta.isDebugEnabled()) {
0554: TraceTm.jta.debug("reset timeout= " + defaultTimeout);
0555: }
0556:
0557: transactionTimeout = defaultTimeout;
0558:
0559: }
0560:
0561: /**
0562: * Modify the transaction associated with the current thread such that the
0563: * only possible outcome of the transaction is to roll back the transaction.
0564: *
0565: * @exception IllegalStateException Thrown if the current thread is not
0566: * associated with a transaction.
0567: *
0568: * @exception SystemException Thrown if the transaction manager
0569: * encounters an unexpected error condition
0570: */
0571:
0572: public void setRollbackOnly() throws IllegalStateException,
0573: SystemException {
0574:
0575: if (TraceTm.jta.isDebugEnabled()) {
0576: TraceTm.jta.debug("Current.setRollbackOnly()");
0577: }
0578: // Get Transaction
0579: TransactionImpl tx = (TransactionImpl) getTransaction();
0580:
0581: if (tx == null) {
0582: throw new IllegalStateException(
0583: "Cannot get Transaction for setRollbackOnly");
0584: }
0585:
0586: // Set transaction rollback only. Exceptions may be raised!
0587: tx.setRollbackOnly();
0588: }
0589:
0590: /**
0591: * Returns the status of the transaction associated with the current
0592: * thread.
0593: *
0594: * @return transaction status. If no transaction is associated with the
0595: * current thread, this method returns the Status.NoTransaction value.
0596: *
0597: * @exception SystemException Thrown if the transaction manager
0598: * encounters an unexpected error condition
0599: */
0600:
0601: public int getStatus() throws SystemException {
0602:
0603: if (TraceTm.jta.isDebugEnabled()) {
0604: TraceTm.jta.debug("Current.getStatus()");
0605: }
0606:
0607: // Get Transaction
0608: TransactionImpl tx = (TransactionImpl) getTransaction();
0609:
0610: if (tx == null) {
0611: return Status.STATUS_NO_TRANSACTION;
0612: }
0613:
0614: // Get TX status. Exceptions may be raised!
0615: return tx.getStatus();
0616: }
0617:
0618: /**
0619: * Modifies the value of the timeout value that is associated with the
0620: * transactions started by the current thread with the begin method.
0621: *
0622: * If an application has not called this method, the transaction
0623: * service uses some default value for the transaction timeout.
0624: *
0625: * @param timeout value of the timeout (in seconds). If the value is zero,
0626: * the transaction service restores the default value.
0627: *
0628: * @exception SystemException Thrown if the transaction manager
0629: * encounters an unexpected error condition
0630: */
0631:
0632: public void setTransactionTimeout(int timeout)
0633: throws SystemException {
0634:
0635: if (TraceTm.jta.isDebugEnabled()) {
0636: TraceTm.jta.debug("timeout= " + timeout);
0637: }
0638:
0639: // checks that no transaction is already associated with this thread.
0640: // If one is, then we have a running transaction (ut.begin) and we must
0641: // wait until transaction completes (ut.commit or ut.rollback)
0642:
0643: TransactionImpl tx = (TransactionImpl) threadTx.get();
0644:
0645: if (tx != null) {
0646: if (txXids.containsValue(tx)) {
0647: if (TraceTm.jta.isDebugEnabled()) {
0648: TraceTm.jta
0649: .debug("Cannot reset transaction timeout, tx in execution");
0650: }
0651: // Cannot reset transaction timeout, tx (ut.begin)in execution, ignore.
0652: return;
0653: }
0654: }
0655:
0656: if (timeout > 0) {
0657: transactionTimeout = timeout;
0658: } else {
0659: transactionTimeout = defaultTimeout;
0660: }
0661:
0662: if (TraceTm.jta.isDebugEnabled()) {
0663: TraceTm.jta.debug("Resetting transaction timeout= "
0664: + transactionTimeout);
0665: }
0666: }
0667:
0668: /**
0669: * Modifies the value of the recovery value that is associated with the
0670: * transactions started by the current thread with the begin method.
0671: *
0672: * If an application has not called this method, the transaction
0673: * service uses the default value of 'false' for recovery.
0674: *
0675: * @param recovery value of the recovery (true or faluse). If the value is
0676: * false, recovery of transactions is disabled.
0677: *
0678: * @exception SystemException Thrown if the transaction manager
0679: * encounters an unexpected error condition
0680: */
0681:
0682: public void setTransactionRecovery(boolean recovery)
0683: throws SystemException {
0684:
0685: if (TraceTm.recovery.isDebugEnabled()) {
0686: TraceTm.recovery.debug("recovery=" + recovery);
0687: }
0688:
0689: if (recovery) {
0690: transactionRecovery = recovery;
0691: } else {
0692: transactionRecovery = DEFAULT_RECOVERY;
0693: }
0694: }
0695:
0696: // ------------------------------------------------------------------
0697: // TransactionManager implementation
0698: // (only the methods that are not already in UserTransaction)
0699: // ------------------------------------------------------------------
0700:
0701: /**
0702: * Gets the transaction object that represents the transaction context of
0703: * the calling thread.
0704: *
0705: * @return the Transaction object representing the transaction
0706: * associated with the calling thread. If the calling thread is
0707: * not associated with a transaction, a null object reference
0708: * is returned.
0709: *
0710: * @exception SystemException Thrown if the transaction manager
0711: * encounters an unexpected error condition
0712: */
0713:
0714: public Transaction getTransaction() throws SystemException {
0715: Transaction ret = (Transaction) threadTx.get();
0716:
0717: if (TraceTm.jta.isDebugEnabled()) {
0718: TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
0719: TraceTm.jta.debug("Transaction ret= " + ret);
0720: }
0721:
0722: return ret;
0723: }
0724:
0725: /**
0726: * Resumes the transaction context association of the calling thread with
0727: * the transaction represented by the supplied Transaction object. When this
0728: * method returns, the calling thread is associated with the transaction
0729: * context specified.
0730: * <p><em>Warning</em>: No XA start is done here. We suppose it is already
0731: * done after a <code>getConnection()</code>.
0732: * </p>
0733: * The supposed programming model is: <ol>
0734: * <li><code>getConnection()</code></li>
0735: * <li>SQL code</li>
0736: * <li><code>connection.close()</code</li>
0737: * </ol>
0738: *
0739: * @param tobj The <code>Transaction</code> object that represents the
0740: * transaction to be resumed.
0741: *
0742: * @exception InvalidTransactionException Thrown if the parameter
0743: * transaction object contains an invalid transaction
0744: *
0745: * @exception IllegalStateException Thrown if the thread is already
0746: * associated with another transaction.
0747: *
0748: * @exception SystemException Thrown if the transaction manager
0749: * encounters an unexpected error condition
0750: */
0751:
0752: public void resume(Transaction tobj)
0753: throws InvalidTransactionException, IllegalStateException,
0754: SystemException {
0755:
0756: if (TraceTm.jta.isDebugEnabled()) {
0757: TraceTm.jta.debug("resume transaction");
0758: }
0759:
0760: // invalid Transaction
0761: if (tobj == null) {
0762: TraceTm.jotm.error("resume: null arg.");
0763: throw new InvalidTransactionException(
0764: "resume(null) is not valid");
0765: }
0766:
0767: if (TraceTm.jta.isDebugEnabled()) {
0768: TraceTm.jta.debug("tx=" + tobj);
0769: }
0770:
0771: // Checks that the thread is not already associated to ANOTHER transaction
0772: Transaction mytx = (Transaction) threadTx.get();
0773:
0774: if (TraceTm.jta.isDebugEnabled()) {
0775: TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
0776: }
0777:
0778: if (mytx != null) {
0779: if (mytx.equals(tobj)) {
0780: if (TraceTm.jta.isDebugEnabled()) {
0781: TraceTm.jta.debug("nothing to do");
0782: }
0783: return;
0784: }
0785: TraceTm.jotm
0786: .error("resume: already associated with another transaction.");
0787: throw new IllegalStateException(
0788: "the thread is already associated with another transaction.");
0789: }
0790:
0791: // test for type before cast
0792: if (!(tobj instanceof TransactionImpl)) {
0793: TraceTm.jotm.error("resume: non TransactionImpl arg.");
0794: throw new InvalidTransactionException("resume("
0795: + tobj.getClass().getName() + ") is not valid");
0796: }
0797:
0798: // Associates this Tx with the current thread
0799: threadTx.set(tobj);
0800:
0801: if (TraceTm.jta.isDebugEnabled()) {
0802: TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
0803: }
0804:
0805: // attach suspended resources
0806: try {
0807: ((TransactionImpl) tobj).doAttach(XAResource.TMRESUME);
0808: } catch (RollbackException e) {
0809: // never.
0810: TraceTm.jotm.error("RollbackException occured in resume()");
0811: throw new SystemException(
0812: "RollbackException in occured in resume() "
0813: + e.getMessage());
0814: }
0815: }
0816:
0817: /**
0818: * Suspends the transaction currently associated with the calling thread
0819: * and return a <code>Transaction</code> object that represents the
0820: * transaction context being suspended.
0821: * If the calling thread is not
0822: * associated with a transaction, the method returns
0823: * <code>null</code>. When this method returns, the calling thread is
0824: * associated with no transaction.
0825: <p><em>Warning</em>: No XA start is done here. We suppose it is already
0826: done after a <code>getConnection()</code>.
0827: * </p>
0828: * The supposed programming model is: <ol>
0829: * <li><code>getConnection()</code></li>
0830: * <li>SQL code</li>
0831: * <li><code>connection.close()</code</li>
0832: * </ol>
0833: *
0834: * @return Transaction object representing the suspended transaction.
0835: *
0836: * @exception SystemException Thrown if the transaction manager
0837: * encounters an unexpected error condition
0838: *
0839: * @exception SystemException Thrown if the transaction manager
0840: * encounters an unexpected error condition
0841: */
0842:
0843: public Transaction suspend() throws SystemException {
0844:
0845: if (TraceTm.jta.isDebugEnabled()) {
0846: TraceTm.jta.debug("suspend transaction");
0847: }
0848:
0849: /// TMSUSPEND should be defined in Transaction.
0850: TransactionImpl tx = (TransactionImpl) threadTx.get();
0851:
0852: if (TraceTm.jta.isDebugEnabled()) {
0853: TraceTm.jta.debug("threadTx.get= " + threadTx.toString());
0854: }
0855:
0856: if (tx != null) {
0857:
0858: if (TraceTm.jta.isDebugEnabled()) {
0859: TraceTm.jta.debug("tx=" + tx);
0860: }
0861:
0862: tx.doDetach(XAResource.TMSUSPEND);
0863: threadTx.set(null);
0864:
0865: if (TraceTm.jta.isDebugEnabled()) {
0866: TraceTm.jta.debug("threadTx.set= null");
0867: }
0868: }
0869:
0870: return tx;
0871: }
0872:
0873: // ------------------------------------------------------------------
0874: // ResourceManagerEventListener implementation
0875: // Should be in UserTransaction only.
0876: // ------------------------------------------------------------------
0877:
0878: /**
0879: * @see org.objectweb.transaction.jta.ResourceManagerEventListener#connectionOpened(org.objectweb.transaction.jta.ResourceManagerEvent)
0880: */
0881:
0882: public void connectionOpened(ResourceManagerEvent event) {
0883:
0884: if (TraceTm.jta.isDebugEnabled()) {
0885: TraceTm.jta.debug("Current.connectionOpened " + this );
0886: }
0887:
0888: List list = null;
0889: Stack curStack = (Stack) eventListStack.get();
0890: // get thread local stack
0891:
0892: if (curStack == null) {
0893: // no stack yet : create one
0894: eventListStack.set(curStack = new Stack());
0895: } else { // pop list from current stack
0896: try {
0897: list = (List) curStack.pop();
0898: } catch (EmptyStackException e) {
0899: // ignore: might happen if thread used by session bean
0900: }
0901: }
0902:
0903: // no list yet: create one
0904: if (list == null)
0905: list = new Vector(1);
0906:
0907: // add event to list
0908: list.add(event);
0909:
0910: if (TraceTm.jta.isDebugEnabled()) {
0911: TraceTm.jta.debug("list.add(event) = " + event);
0912: }
0913:
0914: // push list into stack
0915: curStack.push(list);
0916: }
0917:
0918: /**
0919: * @see org.objectweb.transaction.jta.ResourceManagerEventListener#connectionClosed(org.objectweb.transaction.jta.ResourceManagerEvent)
0920: */
0921:
0922: public void connectionClosed(ResourceManagerEvent event) {
0923:
0924: if (TraceTm.jta.isDebugEnabled()) {
0925: TraceTm.jta.debug("Current.connectionClosed, remove");
0926: }
0927:
0928: // remove event from list
0929: removeFromCurrentStack(event);
0930: }
0931:
0932: /**
0933: * @see org.objectweb.transaction.jta.ResourceManagerEventListener#connectionErrorOccured(org.objectweb.transaction.jta.ResourceManagerEvent)
0934: */
0935:
0936: public void connectionErrorOccured(ResourceManagerEvent event) {
0937:
0938: if (TraceTm.jta.isDebugEnabled()) {
0939: TraceTm.jta.debug("Current.connectionErrorOccured");
0940: }
0941:
0942: removeFromCurrentStack(event); // remove event from list
0943: }
0944:
0945: private void removeFromCurrentStack(ResourceManagerEvent event) {
0946:
0947: if (TraceTm.jta.isDebugEnabled()) {
0948: TraceTm.jta
0949: .debug("Current.removeFromCurrentStack " + event);
0950: TraceTm.jta.debug("Current = " + this );
0951: }
0952:
0953: Stack curStack = (Stack) eventListStack.get();
0954: // get thread local stack
0955:
0956: if (curStack == null) {
0957: // if no transaction currently executing (no begin), no curStack
0958: // has been put on the eventListStack so just return
0959: return;
0960: }
0961:
0962: try {
0963:
0964: List list = (List) curStack.peek();
0965:
0966: if (list != null) {
0967: // get list from stack top
0968:
0969: list.remove(event);
0970: }
0971: } catch (EmptyStackException e) {
0972: // no list of RM Events are known, just return
0973: }
0974: }
0975:
0976: //
0977: // objectweb transaction manager implementation
0978: //
0979:
0980: // thread local variable that stores all RM Events
0981:
0982: private transient ThreadLocal eventListStack = new ThreadLocal();
0983:
0984: /**
0985: * @see org.objectweb.transaction.jta.TransactionManager#pushThreadLocalRMEventList(java.util.List)
0986: */
0987:
0988: public void pushThreadLocalRMEventList(List eventList) {
0989:
0990: if (TraceTm.jta.isDebugEnabled()) {
0991: TraceTm.jta.debug("Current.pushThreadLocalRMEventList");
0992: }
0993:
0994: Stack curStack = (Stack) eventListStack.get();
0995:
0996: if (curStack == null) {
0997: eventListStack.set(curStack = new Stack());
0998: }
0999:
1000: curStack.push(eventList);
1001: }
1002:
1003: /**
1004: * @see org.objectweb.transaction.jta.TransactionManager#popThreadLocalRMEventList()
1005: */
1006:
1007: public List popThreadLocalRMEventList() {
1008:
1009: if (TraceTm.jta.isDebugEnabled()) {
1010: TraceTm.jta.debug("Current.popThreadLocalRMEventList");
1011: }
1012:
1013: Stack curStack = (Stack) eventListStack.get();
1014: return (List) curStack.pop();
1015: }
1016:
1017: // ------------------------------------------------------------------
1018: // Referenceable implementation
1019: // ------------------------------------------------------------------
1020:
1021: /**
1022: * Retrieves the <code>Reference</code> of this object.
1023: *
1024: * @return The non-null <code>Reference</code> of this object.
1025: *
1026: * @exception NamingException If a naming exception was encountered while retrieving the reference.
1027: */
1028:
1029: public Reference getReference() throws NamingException {
1030:
1031: if (TraceTm.jta.isDebugEnabled()) {
1032: TraceTm.jta.debug("Current.getReference()");
1033: }
1034:
1035: // create the reference
1036: Reference ref = new Reference(this .getClass().getName(),
1037: "org.objectweb.jotm.UserTransactionFactory", null);
1038: Integer i = new Integer(transactionTimeout);
1039: ref.add(new StringRefAddr("jotm.timeout", i.toString()));
1040: return ref;
1041: }
1042:
1043: // ------------------------------------------------------------------
1044: // Other public methods
1045: // ------------------------------------------------------------------
1046:
1047: /**
1048: * Returns the unique instance of the class or <code>null</code> if not
1049: * initialized in case of plain client.
1050: *
1051: * @return The <code>Current</code> object created
1052: */
1053:
1054: public static Current getCurrent() {
1055:
1056: return unique;
1057: }
1058:
1059: /**
1060: * Returns the TMFactory (in JTM)
1061: *
1062: * @return TransactionFactory
1063: */
1064:
1065: public static TransactionFactory getJTM() {
1066:
1067: if (tm == null) {
1068: TraceTm.jotm.error("Current: TMFactory is null!");
1069: }
1070:
1071: return tm;
1072: }
1073:
1074: /**
1075: * Returns the Transaction Recovery object
1076: *
1077: * @return TransactionRecovery
1078: */
1079:
1080: public static TransactionRecovery getTransactionRecovery() {
1081:
1082: if (tr == null) {
1083: TraceTm.jotm
1084: .error("Current: Transaction Recovery is null!");
1085: }
1086:
1087: return tr;
1088: }
1089:
1090: /**
1091: * Sets the default timeout value
1092: *
1093: * @param timeout timeout value (in seconds)
1094: */
1095:
1096: public void setDefaultTimeout(int timeout) {
1097:
1098: if (TraceTm.jta.isDebugEnabled()) {
1099: TraceTm.jta.debug("timeout= " + timeout);
1100: }
1101:
1102: if (timeout != 0) {
1103: defaultTimeout = timeout;
1104: }
1105:
1106: if (TraceTm.jta.isDebugEnabled()) {
1107: TraceTm.jta.debug("default timeout= " + defaultTimeout);
1108: }
1109:
1110: }
1111:
1112: /**
1113: * Gets the default timeout value
1114: *
1115: * @return default timeout value (in seconds)
1116: */
1117:
1118: public int getDefaultTimeout() {
1119:
1120: return defaultTimeout;
1121: }
1122:
1123: /**
1124: * Sets the default recovery value
1125: *
1126: * @param recovery recovery value (true or false)
1127: */
1128:
1129: public static void setDefaultRecovery(boolean recovery) {
1130:
1131: TraceTm.recovery.info("Jotm Recovery= " + recovery);
1132:
1133: transactionRecovery = recovery;
1134: }
1135:
1136: /**
1137: * Gets the default recovery value
1138: *
1139: * @return default recovery value (true or false)
1140: */
1141:
1142: public static boolean getDefaultRecovery() {
1143:
1144: return transactionRecovery;
1145: }
1146:
1147: /**
1148: * Associate to the current thread a transaction represented by its
1149: * transaction context.
1150: * This is used internally by the implicit propagation of the
1151: * transactional context:
1152: * <ul>
1153: * <li>in the skeleton, before calling the request (<code>isReply =
1154: * false</code>)</li>
1155: * <li>in the stub, after receiving the reply (<code>isReply =
1156: * true</code>)</li>
1157: * </ul>
1158: *
1159: * @param pctx TransactionContext
1160: * @param isReply <code>true</code> before calling a request,
1161: * <code>false</code> after receiving a reply
1162: */
1163:
1164: public void setPropagationContext(TransactionContext pctx,
1165: boolean isReply) {
1166:
1167: if (TraceTm.jotm.isDebugEnabled()) {
1168: TraceTm.jotm.debug("pctx=" + pctx + ", isReply=" + isReply);
1169: }
1170:
1171: if (pctx == null) { // never isReply when pctx=null
1172:
1173: if (TraceTm.jta.isDebugEnabled()) {
1174: TraceTm.jta.debug("detach tx");
1175: }
1176:
1177: TransactionImpl tx = (TransactionImpl) threadTx.get();
1178:
1179: if (TraceTm.jta.isDebugEnabled()) {
1180: TraceTm.jta.debug("threadTx.get= "
1181: + threadTx.toString());
1182: }
1183:
1184: if (tx != null) {
1185: // Free TransactionImpl, if not used only!!!
1186: // This is to avoid memory leaks.
1187:
1188: if (tx.isRemovable()) {
1189: forgetTx(tx.getXid());
1190: }
1191:
1192: // Detaches thread from any transaction
1193: threadTx.set(null);
1194:
1195: if (TraceTm.jta.isDebugEnabled()) {
1196: TraceTm.jta.debug("threadTx.set= null");
1197: }
1198: }
1199:
1200: return;
1201: }
1202:
1203: // Get the Transaction matching this Xid if it exists
1204: Xid xid = pctx.getXid();
1205: TransactionImpl tx = getTxXid(xid);
1206:
1207: if (tx == null) {
1208:
1209: if (!isReply) { // create a Transaction object
1210:
1211: if (TraceTm.jta.isDebugEnabled()) {
1212: TraceTm.jta.debug("new Tx");
1213: }
1214:
1215: tx = new TransactionImpl(pctx);
1216: putTxXid(xid, tx);
1217:
1218: // sets the time stamp for the transaction
1219: Date myDate = new Date();
1220: tx.setTxDate(myDate.toString());
1221:
1222: } else {
1223:
1224: // This may happen with Jeremie and distributed transactions with
1225: // no dammage.
1226:
1227: if (TraceTm.jta.isDebugEnabled()) {
1228: TraceTm.jta.debug("unknown tx:" + xid);
1229: }
1230: }
1231:
1232: } else {
1233:
1234: if (isReply) {
1235: // This thread is already associated to a transaction.
1236: // A proper PropagationContext exists yet, but it may have been changed
1237: // in case of distributed transaction.
1238:
1239: if (TraceTm.jta.isDebugEnabled()) {
1240: TraceTm.jta.debug("updating Xid=" + xid);
1241: }
1242:
1243: tx.updatePropagationContext(pctx);
1244: } else {
1245:
1246: if (TraceTm.jta.isDebugEnabled()) {
1247: TraceTm.jta.debug("transaction already known:"
1248: + xid);
1249: }
1250: }
1251: }
1252:
1253: // Associates this Tx with the current thread (= resume)
1254: // In case of Jeremy, if isReply, we are not in the correct thread, so
1255: // never touch threadTx in that case!!!
1256:
1257: if (!isReply) {
1258: threadTx.set(tx);
1259:
1260: if (TraceTm.jta.isDebugEnabled()) {
1261: TraceTm.jta.debug("threadTx.set= "
1262: + threadTx.toString());
1263: }
1264: }
1265: }
1266:
1267: /**
1268: * Get the transaction context associated with the current thread or null
1269: * if the thread is not involved in a transaction.
1270: */
1271:
1272: public TransactionContext getPropagationContext(boolean hold) {
1273:
1274: if (TraceTm.jotm.isDebugEnabled()) {
1275: TraceTm.jotm.debug("hold=" + hold);
1276: }
1277:
1278: try {
1279: TransactionImpl tx = (TransactionImpl) getTransaction();
1280:
1281: if (tx != null) { // will return null if Tx not valid.
1282: return tx.getPropagationContext(hold);
1283: }
1284: } catch (SystemException e) {
1285: TraceTm.jotm.error(
1286: "getPropagationContext system exception:", e);
1287: }
1288:
1289: return null;
1290: }
1291:
1292: /**
1293: * Forget all about this transaction.
1294: * References to <code>TransactionImpl</code> must be destroyed to allow
1295: * the garbage collector to free memory allocated to this transaction.
1296: *
1297: * @param xid <code>Xid</code> of the transaction
1298: */
1299:
1300: public void forgetTx(Xid xid) {
1301:
1302: // Only clear threadTx if this thread is working on its Xid
1303:
1304: TransactionImpl txCur = (TransactionImpl) txXids.get(xid);
1305:
1306: if (txCur != null
1307: && txCur.equals((TransactionImpl) threadTx.get())) {
1308: threadTx.set(null);
1309:
1310: if (TraceTm.jta.isDebugEnabled()) {
1311: TraceTm.jta.debug("threadTx.set = null");
1312: }
1313: }
1314: removeTxXid(xid);
1315: }
1316:
1317: /**
1318: * Get the transaction referenced by Xid.
1319: *
1320: * @param xid <code>Xid</code> of the transaction
1321: */
1322:
1323: public TransactionImpl getTxByXid(Xid xid) {
1324:
1325: TransactionImpl tx = (TransactionImpl) txXids.get(xid);
1326: return tx;
1327: }
1328:
1329: /**
1330: * Get the Xid's of all prepared transactions.
1331: *
1332: * @return array of all Xids in the prepared state
1333: */
1334:
1335: public javax.transaction.xa.Xid[] getPreparedHeuristicXid() {
1336:
1337: Vector xidlist;
1338: int xidcount;
1339: javax.transaction.xa.Xid xid = null;
1340:
1341: xidcount = txXids.size(); /* Xid count */
1342:
1343: if (xidcount == 0) {
1344: return null;
1345: }
1346:
1347: xidlist = new Vector();
1348:
1349: Set txXidsSet = txXids.keySet();
1350:
1351: synchronized (txXids) {
1352:
1353: Iterator txXidsIterator = txXidsSet.iterator();
1354:
1355: while (txXidsIterator.hasNext()) {
1356: TransactionImpl tx = (TransactionImpl) txXids
1357: .get(txXidsIterator.next());
1358:
1359: try {
1360:
1361: if (tx.getStatus() == Status.STATUS_PREPARED) {
1362: xid = (javax.transaction.xa.Xid) tx.getXid();
1363: xidlist.add(xid);
1364: }
1365: } catch (SystemException e) {
1366: TraceTm.jotm
1367: .error(
1368: "getPreparedHeuristicsXid system exception:",
1369: e);
1370: }
1371: }
1372: }
1373:
1374: Vector mytxrecovered = JotmRecovery.getTxRecovered();
1375:
1376: return (javax.transaction.xa.Xid[]) xidlist.toArray();
1377: }
1378:
1379: /**
1380: * Get all Xid's associated with this transaction.
1381: *
1382: * @return array of all Xids
1383: */
1384:
1385: public javax.transaction.xa.Xid[] getAllXid() {
1386:
1387: Vector xidlist;
1388: int xidcount;
1389: javax.transaction.xa.Xid xid = null;
1390:
1391: xidcount = txXids.size(); /* Xid count */
1392:
1393: if (xidcount == 0) {
1394: return null;
1395: }
1396:
1397: xidlist = new Vector();
1398:
1399: Set txXidsSet = txXids.keySet();
1400:
1401: synchronized (txXids) {
1402:
1403: Iterator txXidsIterator = txXidsSet.iterator();
1404:
1405: while (txXidsIterator.hasNext()) {
1406: TransactionImpl mytx = (TransactionImpl) txXids
1407: .get(txXidsIterator.next());
1408: xid = (javax.transaction.xa.Xid) mytx.getXid();
1409: xidlist.add(xid);
1410: }
1411: }
1412:
1413: return (javax.transaction.xa.Xid[]) xidlist.toArray();
1414: }
1415:
1416: /**
1417: * Get all executing transactions.
1418: *
1419: * @return array of all Transactions in execution
1420: */
1421:
1422: public String[] getAllTx() {
1423:
1424: Vector txList;
1425: List txResourceList;
1426: int txCount;
1427: int txResourceCount;
1428: String txStatusName;
1429:
1430: txCount = txXids.size(); /* transaction count */
1431:
1432: if (txCount == 0) {
1433: return null;
1434: }
1435:
1436: txList = new Vector();
1437:
1438: Set txXidsSet = txXids.keySet();
1439:
1440: synchronized (txXids) {
1441:
1442: Iterator txXidsIterator = txXidsSet.iterator();
1443:
1444: while (txXidsIterator.hasNext()) {
1445: TransactionImpl mytx = (TransactionImpl) txXids
1446: .get(txXidsIterator.next());
1447:
1448: try {
1449: txStatusName = StatusHelper.getStatusName(mytx
1450: .getStatus());
1451: } catch (SystemException e) {
1452: txStatusName = "No State Defined";
1453: }
1454:
1455: txResourceList = mytx.getEnlistedXAResource();
1456: txResourceCount = txResourceList.size();
1457:
1458: if (txResourceCount == 0) {
1459: txList.add(mytx.getTxDate() + "????"
1460: + mytx.toString() + "????"
1461: + "NO Resource Defined" + "????"
1462: + txStatusName);
1463: } else {
1464:
1465: for (int i = 0; i < txResourceCount; i++) {
1466: txList.add(mytx.getTxDate() + "????"
1467: + mytx.toString() + "????"
1468: + txResourceList.get(i).toString()
1469: + "????" + txStatusName);
1470: }
1471: }
1472: }
1473: }
1474:
1475: String[] myTxString = new String[txCount];
1476:
1477: for (int i = 0; i < txCount; i++) {
1478: myTxString[i] = txList.get(i).toString();
1479: }
1480:
1481: return myTxString;
1482: }
1483:
1484: /**
1485: * Get all Transactions that may require recovery.
1486: *
1487: * @return array of all Transactions that may require recovery
1488: */
1489:
1490: public String[] getAllRcTx() {
1491:
1492: Vector vTxRecovered = null;
1493: Vector txList;
1494: int txCount;
1495:
1496: JotmRecovery myjr = null;
1497:
1498: if (tr == null) {
1499: if (TraceTm.recovery.isDebugEnabled()) {
1500: TraceTm.recovery.debug("tr= null");
1501: }
1502: return null;
1503: }
1504:
1505: myjr = tr.getJotmRecovery();
1506:
1507: if (myjr == null) {
1508: return null;
1509: }
1510:
1511: vTxRecovered = JotmRecovery.getTxRecovered();
1512:
1513: txCount = vTxRecovered.size();
1514:
1515: if (TraceTm.recovery.isDebugEnabled()) {
1516: TraceTm.recovery.debug("txcount= " + txCount);
1517: }
1518:
1519: if (txCount == 0) {
1520: return null;
1521: }
1522:
1523: txList = new Vector();
1524: TxRecovered mytxRecovered = null;
1525:
1526: for (int i = 0; i < txCount; i++) {
1527: mytxRecovered = (TxRecovered) vTxRecovered.elementAt(i);
1528:
1529: Xid temptxxid = new XidImpl(mytxRecovered.gettxxid());
1530:
1531: // We must send the complete transaction Xid string along
1532: // with the truncated. The complete will be used for further
1533: // searching while the truncated will be used in the display
1534: // of the JSP.
1535:
1536: txList.add(new String(mytxRecovered.gettxxid()) + "????"
1537: + temptxxid.toString() + "????"
1538: + mytxRecovered.gettxdatetime() + "????"
1539: + mytxRecovered.getxidcount());
1540: }
1541:
1542: String[] myTxString = new String[txCount];
1543:
1544: for (int i = 0; i < txCount; i++) {
1545: myTxString[i] = txList.get(i).toString();
1546: }
1547:
1548: return myTxString;
1549: }
1550:
1551: /**
1552: * Get all XAResources that may require recovery.
1553: *
1554: * @return array of all XAResources that may require recovery
1555: */
1556:
1557: public String[] getAllXaTx(String stx) {
1558:
1559: Vector xaList;
1560: Vector rmList;
1561: int txCount;
1562: int rmiCount;
1563: int myxacount = 0;
1564: boolean mytxfound = false;
1565: boolean myrmifound = false;
1566: boolean myrmregistered = false;
1567: boolean myxaresfound = false;
1568: String mytxstatusname;
1569:
1570: JotmRecovery myjr = null;
1571:
1572: if (tr == null) {
1573: return null;
1574: }
1575:
1576: myjr = tr.getJotmRecovery();
1577:
1578: if (myjr == null) {
1579: if (TraceTm.recovery.isDebugEnabled()) {
1580: TraceTm.recovery.debug("myjr= null");
1581: }
1582: return null;
1583: }
1584:
1585: xaList = new Vector();
1586: TxxidRecovered mytxxidRecovered = null;
1587: TxRecovered mytxRecovered = null;
1588: Vector vTxRecovered = null;
1589: Vector vRecoverRmInfo = null;
1590: RmRegistration myRmRegistration = null;
1591: String myxares = null;
1592:
1593: vTxRecovered = JotmRecovery.getTxRecovered();
1594:
1595: txCount = vTxRecovered.size();
1596:
1597: if (TraceTm.recovery.isDebugEnabled()) {
1598: TraceTm.recovery.debug("txcount= " + txCount);
1599: }
1600:
1601: for (int i = 0; i < txCount; i++) {
1602: mytxRecovered = (TxRecovered) vTxRecovered.elementAt(i);
1603:
1604: if (TraceTm.recovery.isDebugEnabled()) {
1605: TraceTm.recovery.debug("gettxxid= "
1606: + new String(mytxRecovered.gettxxid()));
1607: TraceTm.recovery.debug("stx=" + stx);
1608: }
1609:
1610: if (new String(mytxRecovered.gettxxid()).equals(stx)) {
1611: mytxfound = true;
1612: break;
1613: }
1614: }
1615:
1616: rmList = new Vector();
1617: RecoverRmInfo rmInfo = null;
1618: vRecoverRmInfo = JotmRecovery.getRecoverRmInfo();
1619: String myrm = null;
1620: Vector vRmRegistration = tr.getRmRegistration();
1621:
1622: if (mytxfound) {
1623: myxacount = mytxRecovered.getxidcount();
1624:
1625: if (TraceTm.recovery.isDebugEnabled()) {
1626: TraceTm.recovery.debug("myxacount= " + myxacount);
1627: }
1628:
1629: for (int i = 0; i < myxacount; i++) {
1630: myrmregistered = false;
1631: myrmifound = false;
1632: myxaresfound = false;
1633: mytxxidRecovered = mytxRecovered.getRecoverTxXidInfo(i);
1634:
1635: if (mytxxidRecovered == null) {
1636: xaList.add("NotFound" + "????" + "NotFound"
1637: + "????" + "NotFound" + "????" + "NotFound"
1638: + "????" + "NotFound");
1639: } else {
1640: rmiCount = vRecoverRmInfo.size();
1641:
1642: if (TraceTm.recovery.isDebugEnabled()) {
1643: TraceTm.recovery.debug("rmiCount= " + rmiCount);
1644: }
1645:
1646: for (int j = 0; j < rmiCount; j++) {
1647: rmInfo = (RecoverRmInfo) vRecoverRmInfo
1648: .elementAt(j);
1649:
1650: if (TraceTm.recovery.isDebugEnabled()) {
1651: TraceTm.recovery
1652: .debug("getRecoverXaResName()= "
1653: + rmInfo
1654: .getRecoverXaResName());
1655: TraceTm.recovery
1656: .debug("getRecoverxaresname()="
1657: + mytxxidRecovered
1658: .getRecoverxaresname());
1659: TraceTm.recovery
1660: .debug("getRecoverXaRes()= "
1661: + new String(rmInfo
1662: .getRecoverXaRes()));
1663: TraceTm.recovery.debug("getRecoverxares()="
1664: + new String(mytxxidRecovered
1665: .getRecoverxares()));
1666: }
1667:
1668: // if (rmInfo.getRecoverXaResName().equals(mytxxidRecovered.getRecoverxaresname())) {
1669: if ((new String(rmInfo.getRecoverXaRes()))
1670: .equals(new String(mytxxidRecovered
1671: .getRecoverxares()))) {
1672: myrm = rmInfo.getRecoverRm();
1673: myrmifound = true;
1674: myxares = new String(mytxxidRecovered
1675: .getRecoverxares());
1676:
1677: if (TraceTm.recovery.isDebugEnabled()) {
1678: TraceTm.recovery.debug("myrm= " + myrm);
1679: TraceTm.recovery.debug("myxares= "
1680: + myxares);
1681: }
1682: break;
1683: }
1684: }
1685:
1686: if (!myrmifound) {
1687: myrm = "NotFound";
1688: }
1689:
1690: if (vRmRegistration == null) {
1691: if (TraceTm.recovery.isDebugEnabled()) {
1692: TraceTm.recovery
1693: .debug("vRmRegistration is null");
1694: }
1695: myxares = "NotRegistered";
1696: } else {
1697: int rmregcount = vRmRegistration.size();
1698:
1699: if (TraceTm.recovery.isDebugEnabled()) {
1700: TraceTm.recovery.debug("rmregcount= "
1701: + rmregcount);
1702: }
1703:
1704: for (int j = 0; j < rmregcount; j++) {
1705: myRmRegistration = (RmRegistration) vRmRegistration
1706: .elementAt(j);
1707:
1708: if (TraceTm.recovery.isDebugEnabled()) {
1709: TraceTm.recovery.debug("myrm= " + myrm);
1710: TraceTm.recovery.debug("rmGetName= "
1711: + myRmRegistration.rmGetName());
1712: }
1713:
1714: if (myrm.equals(myRmRegistration
1715: .rmGetName())) {
1716: myrmregistered = true;
1717:
1718: if (myRmRegistration.rmGetXaRes() == null) {
1719: myxares = "IsNull";
1720: } else {
1721: myxares = myRmRegistration
1722: .rmGetXaRes().toString();
1723: myxaresfound = true;
1724:
1725: if (TraceTm.recovery
1726: .isDebugEnabled()) {
1727: TraceTm.recovery
1728: .debug("myxares= "
1729: + myxares);
1730: }
1731: }
1732: break;
1733: }
1734: }
1735:
1736: if (!myrmregistered) {
1737: myxares = "NotRegistered";
1738: }
1739:
1740: if (TraceTm.recovery.isDebugEnabled()) {
1741: TraceTm.recovery.debug("myxares= "
1742: + myxares);
1743: }
1744: }
1745:
1746: Xid tempxid = new XidImpl(mytxxidRecovered
1747: .getRecoverxid());
1748:
1749: // We must send the complete Xid string along with the truncated.
1750: // The complete will be used for further searching while the
1751: // truncated will be used in the display of the JSP.
1752: xaList.add(myrm
1753: + "????"
1754: + myxares
1755: + "????"
1756: + new String(mytxxidRecovered
1757: .getRecoverxid())
1758: + "????"
1759: + new String(tempxid.toString())
1760: + "????"
1761: + StatusHelper
1762: .getStatusName(mytxxidRecovered
1763: .getRecoverstatus()));
1764: }
1765: }
1766: }
1767:
1768: String[] myTxString = new String[myxacount];
1769:
1770: for (int i = 0; i < myxacount; i++) {
1771: myTxString[i] = xaList.get(i).toString();
1772: }
1773:
1774: return myTxString;
1775: }
1776:
1777: /**
1778: * @return Returns all XAResources that require administrator recovery action.
1779: */
1780: public int actionXAResource(String xaAction, String xatx) {
1781: int commiterror;
1782:
1783: Vector vRmRegistration = null;
1784: Vector vRecoverRmInfo = null;
1785: int rmregcount;
1786: boolean mytxfound = false;
1787: boolean myrmifound = false;
1788: String mytxstatusname;
1789: JotmRecovery myjr = null;
1790: RmRegistration myRmRegistration = null;
1791: XAResource xaresource = null;
1792:
1793: if (tr == null) {
1794: return 0;
1795: }
1796:
1797: myjr = tr.getJotmRecovery();
1798:
1799: if (myjr == null) {
1800: return 0;
1801: }
1802:
1803: vRmRegistration = tr.getRmRegistration();
1804:
1805: if (vRmRegistration == null) {
1806: return 0;
1807: }
1808:
1809: // Given the passed Resource Manager, find it in the RMRegistered vector.
1810: // When the Resource Manager is found, use the XAResource associated with
1811: // the RMRegistered Resource Manager to perform a XAResource.recover call
1812: // to return all XIDs that require recovery.
1813: // With the list of XIDs to be recovered, verify that the Xid passes in
1814: // the list. If the Xid passed is in the recover list, then we can attempt
1815: // the commit of the Xid (i.e., XAResource.Commit(Xid)).
1816: // When the commit is completed, we can remove the TxXidRecovered array
1817: // entry associated with the Xid and decrement the count in its respective
1818: // TxRecovered vector entry. If the count in the TxRecovered vector entry
1819: // is zero, we can then remove the TxRecovered entry from the vector.
1820:
1821: String mys = xatx;
1822: int myix1 = mys.indexOf('\n');
1823: String sResmgr = mys.substring(0, myix1);
1824: int myix2 = mys.indexOf('\n', myix1 + 1);
1825: String sResource = mys.substring(myix1 + 1, myix2);
1826: int myix3 = mys.indexOf('\n', myix2 + 1);
1827: String sFullXid = mys.substring(myix2 + 1, myix3);
1828: String sXidstate = mys.substring(myix3 + 1);
1829:
1830: rmregcount = vRmRegistration.size();
1831:
1832: for (int i = 0; i < rmregcount; i++) {
1833: myRmRegistration = (RmRegistration) vRmRegistration
1834: .elementAt(i);
1835:
1836: if (sResmgr.equals(myRmRegistration.rmGetName())) {
1837: xaresource = myRmRegistration.rmGetXaRes();
1838: break;
1839: }
1840: }
1841:
1842: int rcflag = 0;
1843:
1844: javax.transaction.xa.Xid javaxid[] = new javax.transaction.xa.Xid[100];
1845:
1846: try {
1847: // recover returns javax.transaction.xa.Xid objects
1848: // we must cast to org.objectweb.jotm.Xid objects
1849: javaxid = xaresource.recover(rcflag);
1850: } catch (XAException e) {
1851: if (TraceTm.jta.isDebugEnabled()) {
1852: TraceTm.recovery
1853: .debug("xaResource.recover call failed during recovery "
1854: + e.getMessage());
1855: }
1856: }
1857:
1858: if (javaxid == null) {
1859:
1860: if (TraceTm.recovery.isDebugEnabled()) {
1861: TraceTm.recovery
1862: .debug("No XIDs to recover for Xares javaxid is null");
1863: }
1864:
1865: cleanuptxrecovery(sFullXid);
1866: return 0;
1867: }
1868:
1869: if (javaxid.length == 0) {
1870:
1871: if (TraceTm.recovery.isDebugEnabled()) {
1872: TraceTm.recovery.debug("No XIDs to recover for Xares= "
1873: + xaresource);
1874: }
1875:
1876: cleanuptxrecovery(sFullXid);
1877: return 0;
1878: }
1879:
1880: Xid rcxid[] = new Xid[javaxid.length];
1881:
1882: for (int xx = 0; xx < javaxid.length; xx++) {
1883:
1884: if (javaxid[xx] == null)
1885: break;
1886:
1887: if (new String(javaxid[xx].toString()).equals(sFullXid)) {
1888:
1889: if (xaAction == "commit") {
1890: try {
1891: xaresource.commit(javaxid[xx], false);
1892: } catch (XAException e) {
1893: TraceTm.recovery
1894: .error("Unable to commit Xid during Admin Recovery "
1895: + e.getMessage());
1896: }
1897: } else {
1898: if (xaAction == "rollback") {
1899: try {
1900: xaresource.rollback(javaxid[xx]);
1901: } catch (XAException e) {
1902: TraceTm.recovery
1903: .error("Unable to rollback Xid during Admin Recovery "
1904: + e.getMessage());
1905: }
1906: } else {
1907: if (xaAction == "forget") {
1908: try {
1909: xaresource.rollback(javaxid[xx]);
1910: } catch (XAException e) {
1911: TraceTm.recovery
1912: .error("Unable to rollback Xid during Admin Recovery "
1913: + e.getMessage());
1914: }
1915: }
1916: }
1917: }
1918: }
1919: break;
1920: }
1921:
1922: cleanuptxrecovery(sFullXid);
1923: return 0;
1924: }
1925:
1926: private void cleanuptxrecovery(String pFullXid) {
1927:
1928: // We can now remove the TxXidRecovered array entry associated with the
1929: // Xid and decrement the count in its respective TxRecovered vector entry.
1930: // If the count in the TxRecovered vector entry is zero, we can then remove
1931: // the TxRecovered entry from the vector.
1932:
1933: Vector vTxRecovered = null;
1934: TxRecovered mytxRecovered = null;
1935: TxxidRecovered mytxxidRecovered = null;
1936: int txCount;
1937: int myxacount = 0;
1938: boolean mytxxidrecoveredfound = false;
1939:
1940: byte[] jotmDone = new byte[11];
1941: byte[][] jotmDoneRecord = new byte[1][11];
1942:
1943: jotmDone = "RR3JOTMDONE".getBytes();
1944:
1945: vTxRecovered = JotmRecovery.getTxRecovered();
1946: txCount = vTxRecovered.size();
1947:
1948: for (int i = 0; i < txCount; i++) {
1949: mytxRecovered = (TxRecovered) vTxRecovered.elementAt(i);
1950: myxacount = mytxRecovered.getxidcount();
1951:
1952: for (int j = 0; j < myxacount; j++) {
1953: mytxxidRecovered = mytxRecovered.getRecoverTxXidInfo(j);
1954:
1955: if (mytxxidRecovered != null) {
1956:
1957: if (pFullXid.equals(new String(mytxxidRecovered
1958: .getRecoverxid()))) {
1959: // mytxxidRecovered.setRecoverstatus(Status.STATUS_NO_TRANSACTION);
1960: mytxxidRecovered
1961: .setRecoverstatus(Status.STATUS_COMMITTED);
1962: mytxxidrecoveredfound = true;
1963: break;
1964: }
1965: }
1966: }
1967:
1968: boolean allcompleted = true;
1969:
1970: for (int j = 0; j < myxacount; j++) {
1971: mytxxidRecovered = mytxRecovered.getRecoverTxXidInfo(j);
1972:
1973: // if (mytxxidRecovered.getRecoverstatus() != Status.STATUS_NO_TRANSACTION) { // something other than completed
1974: if (mytxxidRecovered.getRecoverstatus() != Status.STATUS_COMMITTED) { // something other than completed
1975: allcompleted = false;
1976: break;
1977: }
1978: }
1979:
1980: if (allcompleted) {
1981: XACommittingTx xaCommitTx = mytxRecovered
1982: .getXACommittingTx();
1983: jotmDoneRecord[0] = jotmDone;
1984:
1985: if (Current.getDefaultRecovery()) {
1986: try {
1987: if (TraceTm.recovery.isDebugEnabled()) {
1988: TraceTm.recovery
1989: .debug("Done howl log, after admin action");
1990: }
1991:
1992: TransactionRecoveryImpl
1993: .getTransactionRecovery().howlDoneLog(
1994: jotmDoneRecord, xaCommitTx);
1995: } catch (Exception f) {
1996: String howlerror = "Cannot howlDoneLog:" + f
1997: + "--" + f.getMessage();
1998: TraceTm.jotm
1999: .error("Got LogException from howlDoneLog: "
2000: + howlerror);
2001: }
2002: }
2003:
2004: vTxRecovered.remove(i);
2005: break;
2006: }
2007:
2008: if (mytxxidrecoveredfound)
2009: return;
2010: }
2011: }
2012:
2013: /**
2014: * Associate Thread to this transaction.
2015: * (used by iiop interceptor)
2016: */
2017:
2018: public void associateThreadTx(Xid xid) {
2019:
2020: TransactionImpl tx = getTxXid(xid);
2021: threadTx.set(tx);
2022:
2023: if (TraceTm.jta.isDebugEnabled()) {
2024: TraceTm.jta.debug("threadTx.set= " + threadTx.toString());
2025: }
2026: }
2027:
2028: /**
2029: * Clear transaction from this thread if not known.
2030: * Useful when another thread completes the current thread's transaction
2031: */
2032: public void clearThreadTx() {
2033:
2034: TransactionImpl tx = (TransactionImpl) threadTx.get();
2035:
2036: if (tx != null) {
2037: threadTx.set(null);
2038: if (TraceTm.jta.isDebugEnabled()) {
2039: TraceTm.jta.debug("threadTx.set=null");
2040: }
2041: }
2042: }
2043:
2044: // ------------------------------------------------------------------
2045: // private methods
2046: // ------------------------------------------------------------------
2047:
2048: /*
2049: * put the Tx/Xid mapping into the hashtable.
2050: */
2051:
2052: private void putTxXid(Xid xid, TransactionImpl tx) {
2053:
2054: if (TraceTm.jta.isDebugEnabled()) {
2055: TraceTm.jta.debug("Associate tx to xid (xid=" + xid + ")");
2056: }
2057:
2058: txXids.put(xid, tx);
2059: }
2060:
2061: /*
2062: * given the Xid, get the corresponding Tx from the hashtable.
2063: */
2064:
2065: private TransactionImpl getTxXid(Xid xid) {
2066:
2067: if (TraceTm.jta.isDebugEnabled()) {
2068: TraceTm.jta.debug("get tx from xid (xid=" + xid + ")");
2069: }
2070:
2071: TransactionImpl tx = (TransactionImpl) txXids.get(xid);
2072: return tx;
2073: }
2074:
2075: /*
2076: * removeTxXid method.
2077: */
2078:
2079: private void removeTxXid(Xid xid) {
2080:
2081: if (TraceTm.jta.isDebugEnabled()) {
2082: TraceTm.jta.debug("remove tx from xid (xid=" + xid + ")");
2083: }
2084:
2085: txXids.remove(xid);
2086: }
2087:
2088: /**
2089: * remove the Transaction from the ThreadLocal
2090: */
2091:
2092: void forget() throws Exception {
2093: threadTx.set(null);
2094:
2095: if (TraceTm.jta.isDebugEnabled()) {
2096: TraceTm.jta.debug("threadTx.set= null");
2097: }
2098: }
2099:
2100: /* Management methods */
2101:
2102: /**
2103: * Returns the current number of transactions.
2104: *
2105: * @return current number of transaction
2106: */
2107:
2108: public int getTotalCurrentTransactions() {
2109: return txXids.size();
2110: }
2111:
2112: /**
2113: * Increments number of begun transactions by one.
2114: */
2115:
2116: synchronized void incrementBeginCounter() {
2117: nb_bg_tx++;
2118: }
2119:
2120: /**
2121: * Returns the total number of begun transactions.
2122: *
2123: * @return total number of begun transactions
2124: */
2125:
2126: public int getTotalBegunTransactions() {
2127: return nb_bg_tx;
2128: }
2129:
2130: /**
2131: * Increments the number of rolled back transaction by one.
2132: */
2133:
2134: synchronized void incrementRollbackCounter() {
2135: nb_rb_tx++;
2136: }
2137:
2138: /**
2139: * Returns the total number of rolled back transactions.
2140: *
2141: * @return total number of rolled back transactions
2142: */
2143:
2144: public int getTotalRolledbackTransactions() {
2145: return nb_rb_tx;
2146: }
2147:
2148: /**
2149: * Increments the number of of committed transactions by one.
2150: */
2151:
2152: synchronized void incrementCommitCounter() {
2153: nb_cm_tx++;
2154: }
2155:
2156: /**
2157: * Returns the total number of committed transactions.
2158: *
2159: * @return total number of commited transactions
2160: */
2161:
2162: public int getTotalCommittedTransactions() {
2163: return nb_cm_tx;
2164: }
2165:
2166: /**
2167: * Resets total number of transactions.
2168: */
2169:
2170: public synchronized void resetAllTxTotalCounters() {
2171: nb_bg_tx = 0;
2172: nb_cm_tx = 0;
2173: nb_rb_tx = 0;
2174: nb_to = 0;
2175: }
2176:
2177: /**
2178: * Increments number of rolled back transactions due to timeout by one.
2179: */
2180:
2181: synchronized void incrementExpiredCounter() {
2182: nb_to++;
2183: }
2184:
2185: /**
2186: * Returns the total number of rolled back transactions due to timeout.
2187: *
2188: * @return number of rolled back transactions due to timeout
2189: */
2190:
2191: public int getTotalExpiredTransactions() {
2192: return nb_to;
2193: }
2194:
2195: /**
2196: * Returns all counters.
2197: *
2198: * @return an array of all counters (current tx, begun tx, committed tx,
2199: * rolled back tx, timeouted tx)
2200: */
2201:
2202: public synchronized Integer[] getTransactionCounters() {
2203: Integer[] result = new Integer[5];
2204: result[0] = new Integer(txXids.size());
2205: result[1] = new Integer(nb_bg_tx);
2206: result[2] = new Integer(nb_cm_tx);
2207: result[3] = new Integer(nb_rb_tx);
2208: result[4] = new Integer(nb_to);
2209: return result;
2210: }
2211:
2212: }
|