User: fleury
Date: 00/09/07 19:25:01
Modified: src/main/org/jboss/tm TxManager.java TxCapsule.java
TransactionImpl.java
Log:
Ole husgaard updates
Revision Changes Path
1.13 +111 -108 jboss/src/main/org/jboss/tm/TxManager.java
Index: TxManager.java
===================================================================
RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/TxManager.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- TxManager.java 2000/08/28 17:51:37 1.12
+++ TxManager.java 2000/09/08 02:25:00 1.13
@@ -31,7 +31,7 @@
* @see <related>
* @author Rickard �berg ([EMAIL PROTECTED])
* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
- * @version $Revision: 1.12 $
+ * @version $Revision: 1.13 $
*/
public class TxManager
implements TransactionManager
@@ -52,101 +52,105 @@
// Public --------------------------------------------------------
public void begin()
- throws NotSupportedException,SystemException
+ throws NotSupportedException,
+ SystemException
{
+try {
// Logger.log("begin tx");
- // create tx capsule
- TxCapsule txCap = new TxCapsule(this, timeOut);
+ // create tx capsule
+ TxCapsule txCap = new TxCapsule(this, timeOut);
- // Store it
- txCapsules.put(txCap.getTransaction(), txCap);
+ // Store it
+ txCapsules.put(txCap.getTransaction(), txCap);
- // Associate it with the Thread
- threadTx.set(txCap.getTransaction());
+ // Associate it with the Thread
+ threadTx.set(txCap.getTransaction());
+} catch (RuntimeException ex) {
+ System.err.println("Exception: " + ex);
+ ex.printStackTrace();
+ throw ex;
+}
}
public void commit()
- throws RollbackException,
- HeuristicMixedException,
- HeuristicRollbackException,
- java.lang.SecurityException,
- java.lang.IllegalStateException,
- SystemException
+ throws RollbackException,
+ HeuristicMixedException,
+ HeuristicRollbackException,
+ java.lang.SecurityException,
+ java.lang.IllegalStateException,
+ SystemException
{
// Logger.log("commit tx");
-
-
- getTransaction().commit();
+ getTransaction().commit();
}
public int getStatus()
- throws SystemException
+ throws SystemException
{
- // Get the txCapsule running now with the thread
+ // Get the txCapsule running now with the thread
Object current = threadTx.get();
- if(current != null) {
- TxCapsule txCap = (TxCapsule) txCapsules.get(current);
+ if (current != null) {
+ TxCapsule txCap = (TxCapsule) txCapsules.get(current);
- if (txCap == null)
- return Status.STATUS_NO_TRANSACTION;
- else
- return txCap.getStatus();
+ if (txCap == null)
+ return Status.STATUS_NO_TRANSACTION;
+ else
+ return txCap.getStatus();
} else {
return Status.STATUS_NO_TRANSACTION;
}
}
public Transaction getTransaction()
- throws SystemException
+ throws SystemException
{
return (Transaction)threadTx.get();
-
}
public void resume(Transaction tobj)
- throws InvalidTransactionException,
- java.lang.IllegalStateException,
- SystemException
+ throws InvalidTransactionException,
+ java.lang.IllegalStateException,
+ SystemException
{
- //Useless
+ //Useless
- //throw new Exception("txMan.resume() NYI");
+ //throw new Exception("txMan.resume() NYI");
}
public Transaction suspend()
- throws SystemException
+ throws SystemException
{
// Logger.log("suspend tx");
- // Useless
+ // Useless
- return null;
- //throw new Exception("txMan.suspend() NYI");
+ return null;
+ //throw new Exception("txMan.suspend() NYI");
}
public void rollback()
- throws java.lang.IllegalStateException,
- java.lang.SecurityException,
- SystemException
+ throws java.lang.IllegalStateException,
+ java.lang.SecurityException,
+ SystemException
{
// Logger.log("rollback tx");
getTransaction().rollback();
}
public void setRollbackOnly()
- throws java.lang.IllegalStateException,
- SystemException
+ throws java.lang.IllegalStateException,
+ SystemException
{
// Logger.log("set rollback only tx");
getTransaction().setRollbackOnly();
}
public void setTransactionTimeout(int seconds)
- throws SystemException
+ throws SystemException
{
timeOut = seconds;
}
@@ -155,21 +159,19 @@
* The following 2 methods are here to provide association and disassociation of
the thread
*/
public Transaction disassociateThread() {
+ Transaction current = (Transaction) threadTx.get();
- Transaction current = (Transaction) threadTx.get();
+ threadTx.set(null);
- threadTx.set(null);
-
- return current;
+ return current;
}
public void associateThread(Transaction transaction) {
-
- // If the tx has traveled it needs the TxManager
- ((TransactionImpl) transaction).setTxManager(this);
+ // If the tx has traveled it needs the TxManager
+ ((TransactionImpl) transaction).setTxManager(this);
- // Associate with the thread
- threadTx.set(transaction);
+ // Associate with the thread
+ threadTx.set(transaction);
}
@@ -177,64 +179,65 @@
// There has got to be something better :)
static TxManager getTransactionManager() {
+ try {
- try {
+ javax.naming.InitialContext context = new javax.naming.InitialContext();
- javax.naming.InitialContext context = new
javax.naming.InitialContext();
+ //One tx in naming
+ Logger.log("Calling get manager from JNDI");
+ TxManager manager = (TxManager) context.lookup("TransactionManager");
+ Logger.log("Returning TM "+manager.hashCode());
- //One tx in naming
- Logger.log("Calling get manager from JNDI");
- TxManager manager = (TxManager)
context.lookup("TransactionManager");
- Logger.log("Returning TM "+manager.hashCode());
+ return manager;
- return manager;
+ } catch (Exception e ) { return null;}
+ }
- } catch (Exception e ) { return null;}
- }
int getTransactionTimeout()
{
return timeOut;
}
-
+ // Public --------------------------------------------------------
- // Public --------------------------------------------------------
- public void commit(Transaction tx)
- throws RollbackException,
- HeuristicMixedException,
- HeuristicRollbackException,
- java.lang.SecurityException,
- java.lang.IllegalStateException,
- SystemException
- {
- try {
-
- // Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).commit();
- }
- finally {
-
- // Disassociation
- threadTx.set(null);
- }
- }
+ public void commit(Transaction tx)
+ throws RollbackException,
+ HeuristicMixedException,
+ HeuristicRollbackException,
+ java.lang.SecurityException,
+ java.lang.IllegalStateException,
+ SystemException
+ {
+ try {
+ // Look up the txCapsule and delegate
+ ((TxCapsule) txCapsules.get(tx)).commit();
+ }
+ finally {
+ // Disassociation
+ threadTx.set(null);
+ }
+ }
public boolean delistResource(Transaction tx, XAResource xaRes, int flag)
+ throws java.lang.IllegalStateException,
+ SystemException
{
- // Look up the txCapsule and delegate
- return ((TxCapsule) txCapsules.get(tx)).delistResource(xaRes, flag);
+ // Look up the txCapsule and delegate
+ return ((TxCapsule) txCapsules.get(tx)).delistResource(xaRes, flag);
}
public boolean enlistResource(Transaction tx, XAResource xaRes)
- throws RollbackException
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
{
- // Look up the txCapsule and delegate
- return ((TxCapsule) txCapsules.get(tx)).enlistResource(xaRes);
+ // Look up the txCapsule and delegate
+ return ((TxCapsule) txCapsules.get(tx)).enlistResource(xaRes);
}
public int getStatus(Transaction tx)
- throws SystemException
+ throws SystemException
{
// Look up the txCapsule and delegate
TxCapsule txCap = ((TxCapsule) txCapsules.get(tx));
@@ -242,35 +245,35 @@
}
public void registerSynchronization(Transaction tx, Synchronization s)
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
{
// Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).registerSynchronization(s);
+ ((TxCapsule) txCapsules.get(tx)).registerSynchronization(s);
}
public void rollback(Transaction tx)
- throws java.lang.IllegalStateException,
- java.lang.SecurityException,
- SystemException
- {
- try {
-
- // Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).rollback();
- }
- finally {
-
- // Disassociation
- threadTx.set(null);
- }
- }
+ throws java.lang.IllegalStateException,
+ java.lang.SecurityException,
+ SystemException
+ {
+ try {
+ // Look up the txCapsule and delegate
+ ((TxCapsule) txCapsules.get(tx)).rollback();
+ }
+ finally {
+ // Disassociation
+ threadTx.set(null);
+ }
+ }
public void setRollbackOnly(Transaction tx)
- throws java.lang.IllegalStateException,
- SystemException
+ throws java.lang.IllegalStateException,
+ SystemException
{
- // Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).setRollbackOnly();
-
+ // Look up the txCapsule and delegate
+ ((TxCapsule) txCapsules.get(tx)).setRollbackOnly();
}
1.3 +845 -190 jboss/src/main/org/jboss/tm/TxCapsule.java
Index: TxCapsule.java
===================================================================
RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/TxCapsule.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- TxCapsule.java 2000/08/18 03:21:12 1.2
+++ TxCapsule.java 2000/09/08 02:25:01 1.3
@@ -9,7 +9,11 @@
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ConcurrentModificationException;
import javax.transaction.Transaction;
import javax.transaction.Status;
@@ -23,254 +27,532 @@
import javax.transaction.xa.Xid;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.XAException;
+
import org.jboss.logging.Logger;
+import org.jboss.util.timeout.Timeout;
+import org.jboss.util.timeout.TimeoutTarget;
+import org.jboss.util.timeout.TimeoutFactory;
/**
- * TxCapsule
+ * TxCapsule holds all the information relevant to a transaction.
+ * Callbacks and synchronizations are held here.
+ *
+ * TODO: Implement timeouts.
+ * TODO: Implement persistent storage and recovery.
+ *
+ * @see TxManager
+ * @see TransactionImpl
*
- * TxCapsule holds all the information relevant to a transaction. Callbacks and
synchronizations are held here
+ * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
*
- * @see <related>
- * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
- * @version $Revision: 1.2 $
+ * @version $Revision: 1.3 $
*/
-public class TxCapsule
+class TxCapsule implements TimeoutTarget
{
// Constants -----------------------------------------------------
- // Attributes ----------------------------------------------------
- // A list of synchronizations to call back on commit (before and after)
- Vector sync;
-
- // A list of the XARessources to 2phi commit (prepare and commit)
- Vector resources;
+ // Code meaning "no heuristics seen", must not be XAException.XA_HEURxxx
+ static private final int HEUR_NONE = XAException.XA_RETRY;
- Xid xid; // XA legacy
- int hashCode;
- int status;
- long timeout;
- long start;
-
- // The public face of the capsule a JTA implementation
- Transaction transaction;
+ // Attributes ----------------------------------------------------
// Static --------------------------------------------------------
- static int nextId = 0;
- public int getNextId() { return nextId++; }
- public TxManager tm;
- static String hostName;
+ static private int nextId = 0;
+ static private synchronized int getNextId() { return nextId++; }
+ private static String hostName;
+
// Constructors --------------------------------------------------
- public TxCapsule(TxManager tm, int timeout)
+
+ /**
+ * Create a new TxCapsule.
+ *
+ * @param tm The transaction manager for this transaction.
+ * @param timeout The timeout for this transaction in milliseconds
+ * (timeouts are not yet implemented).
+ */
+ TxCapsule(TxManager tm, int timeout)
{
- this(tm);
+ this(tm);
- resources = new Vector();
- sync = new Vector();
status = Status.STATUS_ACTIVE;
- this.timeout = (long) timeout;
start = System.currentTimeMillis();
- }
+ this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
+ }
- public TxCapsule(TxManager tm)
+ /**
+ * Create a new TxCapsule.
+ *
+ * @param tm The transaction manager for this transaction.
+ */
+ private TxCapsule(TxManager tm)
{
- status = Status.STATUS_NO_TRANSACTION;
- hashCode = getNextId();
+ int hashCode = getNextId();
xid = new XidImpl((getHostName()+"/"+hashCode).getBytes(), null);
transaction = new TransactionImpl(tm, hashCode, xid);
this.tm = tm;
}
// Public --------------------------------------------------------
- public void commit()
- throws RollbackException,
- HeuristicMixedException,
- HeuristicRollbackException,
- java.lang.SecurityException,
- java.lang.IllegalStateException,
- SystemException
- {
- if (status == Status.STATUS_NO_TRANSACTION)
- throw new IllegalStateException("No transaction started");
- // Call Synchronization
- for (int i = 0; i < sync.size(); i++)
- {
- // Check rollback
- if (status == Status.STATUS_MARKED_ROLLBACK)
- {
+
+ /**
+ * Called when our timeout expires.
+ */
+ public void timedOut(Timeout timeout)
+ {
+ try {
+ lock();
+
+ Logger.warning("Transaction " + toString() + " timed out.");
+
+ if (this.timeout == null)
+ return; // Don't race with timeout cancellation.
+ this.timeout = null;
+
+ switch (status) {
+ case Status.STATUS_ROLLEDBACK:
+ case Status.STATUS_COMMITTED:
+ case Status.STATUS_NO_TRANSACTION:
+ return; // Transaction done.
+
+ case Status.STATUS_ROLLING_BACK:
+ return; // Will be done shortly.
+
+ case Status.STATUS_COMMITTING:
+ // This is _very_ bad:
+ // We are in the second commit phase, and have decided
+ // to commit, but now we get a timeout and should rollback.
+ // So we end up with a mixed decision.
+ suspendedResourcesDone();
+ rollbackResources();
+ doAfterCompletion();
+ gotHeuristic(null, XAException.XA_HEURMIX);
+ return;
+
+ case Status.STATUS_PREPARED:
+ // This is bad:
+ // We are done with the first phase, and are persistifying
+ // our decision. Fortunately this case is currently never
+ // hit, as we do not release the lock between the two phases.
+ case Status.STATUS_ACTIVE:
+ status = Status.STATUS_MARKED_ROLLBACK;
+ // fall through..
+ case Status.STATUS_MARKED_ROLLBACK:
+ suspendedResourcesDone();
+ rollbackResources();
+ doAfterCompletion();
+ gotHeuristic(null, XAException.XA_HEURRB);
+ return;
+
+ case Status.STATUS_PREPARING:
+ status = Status.STATUS_MARKED_ROLLBACK;
+ return; // commit will fail
+
+ default:
+ Logger.warning("TxCapsule: Unknown status at timeout.");
+ return;
+ }
+ } finally {
+ unlock();
+ }
+ }
+
+ public String toString()
+ {
+ return xid.toString();
+ }
+
+ // Package protected ---------------------------------------------
+
+ /**
+ * Return the transaction encapsulated here.
+ */
+ Transaction getTransaction() {
+ return transaction;
+ }
+
+ /**
+ * Commit the transaction encapsulated here.
+ * Should not be called directly, use <code>TxManager.commit()</code>
+ * instead.
+ */
+ void commit()
+ throws RollbackException,
+ HeuristicMixedException,
+ HeuristicRollbackException,
+ java.lang.SecurityException,
+ java.lang.IllegalStateException,
+ SystemException
+ {
+ try {
+ lock();
+
+ switch (status) {
+ case Status.STATUS_PREPARING:
+ throw new IllegalStateException("Already started preparing.");
+ case Status.STATUS_PREPARED:
+ throw new IllegalStateException("Already prepared.");
+ case Status.STATUS_ROLLING_BACK:
+ throw new IllegalStateException("Already started rolling back.");
+ case Status.STATUS_ROLLEDBACK:
+ checkHeuristics();
+ throw new IllegalStateException("Already rolled back.");
+ case Status.STATUS_COMMITTING:
+ throw new IllegalStateException("Already started committing.");
+ case Status.STATUS_COMMITTED:
+ checkHeuristics();
+ throw new IllegalStateException("Already committed.");
+ case Status.STATUS_NO_TRANSACTION:
+ throw new IllegalStateException("No transaction.");
+ case Status.STATUS_UNKNOWN:
+ throw new IllegalStateException("Unknown state");
+ case Status.STATUS_MARKED_ROLLBACK:
+ suspendedResourcesDone();
+ rollbackResources();
+ doAfterCompletion();
+ throw new RollbackException("Already marked for rollback");
+ case Status.STATUS_ACTIVE:
break;
+ default:
+ throw new IllegalStateException("Illegal status: " + status);
}
- ((Synchronization)sync.elementAt(i)).beforeCompletion();
- }
+ suspendedResourcesDone();
- // Check rollback
- if (status != Status.STATUS_MARKED_ROLLBACK)
- {
- // Prepare XAResources
- status = Status.STATUS_PREPARING;
- for (int i = 0; i < resources.size(); i++)
- {
- // Check rollback
- if (status == Status.STATUS_MARKED_ROLLBACK)
- {
- break;
- }
+ doBeforeCompletion();
- try
- {
- ((XAResource)resources.elementAt(i)).prepare(xid);
- } catch (XAException e)
- {
- if(e.errorCode != XAException.XA_HEURCOM)
- {
- Logger.exception(e);
- // Rollback
- setRollbackOnly();
- break;
- }
+ if (status == Status.STATUS_ACTIVE) {
+ if (resources.size() == 0) {
+ // Zero phase commit is really fast ;-)
+ status = Status.STATUS_COMMITTED;
+ } else if (resources.size() == 1) {
+ // One phase commit
+ commitResources(true);
+ } else {
+ // Two phase commit
+
+ if (!prepareResources()) {
+ boolean commitDecision =
+ status == Status.STATUS_PREPARED &&
+ (heuristicCode == HEUR_NONE ||
+ heuristicCode == XAException.XA_HEURCOM);
+
+ // TODO: Save decision to stable storage for recovery
+ // after system crash.
+
+ if (commitDecision)
+ commitResources(false);
+ } else
+ status = Status.STATUS_COMMITTED; // all was read-only
}
}
- // Check rollback
- if (status != Status.STATUS_MARKED_ROLLBACK)
- {
- status = Status.STATUS_PREPARED; // TODO: necessary to set?
-
- // Commit XAResources
- status = Status.STATUS_COMMITTING;
- for (int i = 0; i < resources.size(); i++)
- {
-
- try
- {
- ((XAResource)resources.elementAt(i)).commit(xid, false);
- } catch (XAException e)
- {
- try {
- ((XAResource)resources.elementAt(i)).forget(xid);
- } catch(XAException another) {}
- Logger.exception(e);
- // TODO: what to do here?
- }
- }
- status = Status.STATUS_COMMITTED;
+ if (status != Status.STATUS_COMMITTED) {
+ rollbackResources();
+ doAfterCompletion();
+ cancelTimeout();
+ throw new RollbackException("Unable to commit.");
}
+
+ cancelTimeout();
+
+ doAfterCompletion();
+
+ checkHeuristics();
+ } finally {
+ unlock();
}
+ }
- // Check rollback
- if (status == Status.STATUS_MARKED_ROLLBACK)
- {
- // Rollback XAResources
- status = Status.STATUS_ROLLING_BACK;
- for (int i = 0; i < resources.size(); i++)
- {
-
- try
- {
- ((XAResource)resources.elementAt(i)).rollback(xid);
- } catch (XAException e)
- {
- try {
- ((XAResource)resources.elementAt(i)).forget(xid);
- } catch(XAException another) {}
- // TODO: what to do here?
- }
+ /**
+ * Rollback the transaction encapsulated here.
+ * Should not be called directly, use <code>TxManager.rollback()</code>
+ * instead.
+ */
+ void rollback()
+ throws java.lang.IllegalStateException,
+ java.lang.SecurityException,
+ SystemException
+ {
+ try {
+ lock();
+
+ switch (status) {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_MARKED_ROLLBACK:
+ suspendedResourcesDone();
+ rollbackResources();
+ cancelTimeout();
+ doAfterCompletion();
+ // Cannot throw heuristic exception, so we just have to
+ // clear the heuristics without reporting.
+ heuristicCode = HEUR_NONE;
+ return;
+ case Status.STATUS_PREPARING:
+ // Set status to avoid race with prepareResources().
+ status = Status.STATUS_MARKED_ROLLBACK;
+ return; // commit() will do rollback.
+ default:
+ throw new IllegalStateException("Cannot rollback()");
}
- status = Status.STATUS_ROLLEDBACK;
+ } finally {
+ unlock();
}
+ }
- // Call Synchronization
- for (int i = 0; i < sync.size(); i++)
- {
- ((Synchronization)sync.elementAt(i)).afterCompletion(status);
+ /**
+ * Mark the transaction encapsulated here so that the only possible
+ * outcome is a rollback.
+ * Should not be called directly, use <code>TxManager.rollback()</code>
+ * instead.
+ */
+ void setRollbackOnly()
+ throws java.lang.IllegalStateException,
+ SystemException
+ {
+ try {
+ lock();
+
+ switch (status) {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_PREPARING:
+ case Status.STATUS_PREPARED:
+ status = Status.STATUS_MARKED_ROLLBACK;
+ // fall through..
+ case Status.STATUS_MARKED_ROLLBACK:
+ case Status.STATUS_ROLLING_BACK:
+ return;
+ case Status.STATUS_COMMITTING:
+ throw new IllegalStateException("Already started committing.");
+ case Status.STATUS_COMMITTED:
+ throw new IllegalStateException("Already committed.");
+ case Status.STATUS_ROLLEDBACK:
+ throw new IllegalStateException("Already rolled back.");
+ case Status.STATUS_NO_TRANSACTION:
+ throw new IllegalStateException("No transaction.");
+ case Status.STATUS_UNKNOWN:
+ throw new IllegalStateException("Unknown state");
+ default:
+ throw new IllegalStateException("Illegal status: " + status);
+ }
+ } finally {
+ unlock();
}
-
}
- public boolean delistResource(XAResource xaRes, int flag)
- {
- try {
- xaRes.end(xid, Status.STATUS_ACTIVE);
-// resources.removeElement(xaRes);
+ /**
+ * Delist a resource from the transaction encapsulated here.
+ *
+ * @param xaRes The resource to delist.
+ * @param flag One of <code>XAResource.TMSUCCESS</code>,
+ * <code>XAResource.TMSUSPEND</code>
+ * or <code>XAResource.TMFAIL</code>.
+ *
+ * @returns True iff the resource was successfully delisted.
+ */
+ boolean delistResource(XAResource xaRes, int flag)
+ throws java.lang.IllegalStateException,
+ SystemException
+ {
+ if (xaRes == null)
+ throw new IllegalArgumentException("null xaRes");
+ if (flag != XAResource.TMSUCCESS &&
+ flag != XAResource.TMSUSPEND &&
+ flag != XAResource.TMFAIL)
+ throw new IllegalArgumentException("Bad flag: " + flag);
+
+ try {
+ lock();
+
+ if (!resources.contains(xaRes))
+ throw new IllegalArgumentException("xaRes not enlisted");
+
+ switch (status) {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_MARKED_ROLLBACK:
+ break;
+ case Status.STATUS_PREPARING:
+ throw new IllegalStateException("Already started preparing.");
+ case Status.STATUS_ROLLING_BACK:
+ throw new IllegalStateException("Already started rolling back.");
+ case Status.STATUS_PREPARED:
+ throw new IllegalStateException("Already prepared.");
+ case Status.STATUS_COMMITTING:
+ throw new IllegalStateException("Already started committing.");
+ case Status.STATUS_COMMITTED:
+ throw new IllegalStateException("Already committed.");
+ case Status.STATUS_ROLLEDBACK:
+ throw new IllegalStateException("Already rolled back.");
+ case Status.STATUS_NO_TRANSACTION:
+ throw new IllegalStateException("No transaction.");
+ case Status.STATUS_UNKNOWN:
+ throw new IllegalStateException("Unknown state");
+ default:
+ throw new IllegalStateException("Illegal status: " + status);
+ }
+
+ try {
+ endResource(xaRes, flag);
+ if (flag == XAResource.TMSUSPEND)
+ suspendedResources.add(xaRes);
+ else if (flag == XAResource.TMFAIL)
+ status = Status.STATUS_MARKED_ROLLBACK;
return true;
- } catch(XAException e) {
+ } catch(XAException e) {
Logger.exception(e);
+ status = Status.STATUS_MARKED_ROLLBACK;
return false;
- }
+ }
+ } finally {
+ unlock();
+ }
}
- public boolean enlistResource(XAResource xaRes)
- throws RollbackException
- {
- // Check rollback only
- if (status == Status.STATUS_MARKED_ROLLBACK)
- {
- throw new RollbackException();
- }
+ /**
+ * Enlist a resource with the transaction encapsulated here.
+ *
+ * @param xaRes The resource to enlist.
+ *
+ * @returns True iff the resource was successfully enlisted.
+ */
+ boolean enlistResource(XAResource xaRes)
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
+ {
+ if (xaRes == null)
+ throw new IllegalArgumentException("null xaRes");
+
+ try {
+ lock();
+
+ switch (status) {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_PREPARING:
+ break;
+ case Status.STATUS_PREPARED:
+ throw new IllegalStateException("Already prepared.");
+ case Status.STATUS_COMMITTING:
+ throw new IllegalStateException("Already started committing.");
+ case Status.STATUS_COMMITTED:
+ throw new IllegalStateException("Already committed.");
+ case Status.STATUS_MARKED_ROLLBACK:
+ throw new RollbackException("Already marked for rollback");
+ case Status.STATUS_ROLLING_BACK:
+ throw new RollbackException("Already started rolling back.");
+ case Status.STATUS_ROLLEDBACK:
+ throw new RollbackException("Already rolled back.");
+ case Status.STATUS_NO_TRANSACTION:
+ throw new IllegalStateException("No transaction.");
+ case Status.STATUS_UNKNOWN:
+ throw new IllegalStateException("Unknown state");
+ default:
+ throw new IllegalStateException("Illegal status: " + status);
+ }
- // Add resource
- try {
- xaRes.start(xid, Status.STATUS_ACTIVE);
- resources.addElement(xaRes);
+ // Add resource
+ try {
+ if (suspendedResources.contains(xaRes)) {
+ startResource(xaRes, XAResource.TMRESUME);
+ suspendedResources.remove(xaRes);
+ return true;
+ }
+ for (int i = 0; i < resources.size(); ++i) {
+ if (xaRes.isSameRM((XAResource)resources.get(i))) {
+ startResource(xaRes, XAResource.TMJOIN);
+ resources.add(xaRes);
+ return true;
+ }
+ }
+ // New resource
+ // According to the JTA spec we should create a new
+ // transaction branch here.
+ startResource(xaRes, XAResource.TMNOFLAGS);
+ resources.add(xaRes);
return true;
- } catch(XAException e) {
+ } catch(XAException e) {
Logger.exception(e);
return false;
- }
+ }
+ } finally {
+ unlock();
+ }
}
- public int getStatus()
- throws SystemException
+ /**
+ * Return the status of the transaction encapsulated here.
+ */
+ int getStatus()
+ throws SystemException
{
return status;
}
-
- public void registerSynchronization(Synchronization s)
- {
- sync.addElement(s);
- }
-
- public void rollback()
- throws java.lang.IllegalStateException,
- java.lang.SecurityException,
- SystemException
- {
- if (status == Status.STATUS_NO_TRANSACTION)
- throw new IllegalStateException("No transaction started");
-
- //MF FIXME I don't get it what is the use of this call if the
"rollback is done in the commit
- }
- public void setRollbackOnly()
- throws java.lang.IllegalStateException,
- SystemException
- {
- if (status == Status.STATUS_NO_TRANSACTION)
- throw new IllegalStateException("No transaction started");
+ /**
+ * Register a new transaction synchronization for the transaction
+ * encapsulated here.
+ */
+ void registerSynchronization(Synchronization s)
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
+ {
+ if (s == null)
+ throw new IllegalArgumentException("Null synchronization");
+
+ try {
+ lock();
+
+ switch (status) {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_PREPARING:
+ break;
+ case Status.STATUS_PREPARED:
+ throw new IllegalStateException("Already prepared.");
+ case Status.STATUS_COMMITTING:
+ throw new IllegalStateException("Already started committing.");
+ case Status.STATUS_COMMITTED:
+ throw new IllegalStateException("Already committed.");
+ case Status.STATUS_MARKED_ROLLBACK:
+ //throw new RollbackException("Already marked for rollback");
+ // Workaround for EntitySynchronizationInterceptor bug.
+ Logger.warning("TxCapsule: Violating JTA by adding synchronization to a
transaction marked for rollback.");
+ break;
+ case Status.STATUS_ROLLING_BACK:
+ throw new RollbackException("Already started rolling back.");
+ case Status.STATUS_ROLLEDBACK:
+ throw new RollbackException("Already rolled back.");
+ case Status.STATUS_NO_TRANSACTION:
+ throw new IllegalStateException("No transaction.");
+ case Status.STATUS_UNKNOWN:
+ throw new IllegalStateException("Unknown state");
+ default:
+ throw new IllegalStateException("Illegal status: " + status);
+ }
- status = Status.STATUS_MARKED_ROLLBACK;
+ sync.add(s);
+ } finally {
+ unlock();
+ }
}
- // Package protected ---------------------------------------------
-
- Transaction getTransaction() {
-
- return transaction;
- }
-
// Protected -----------------------------------------------------
+
+ /**
+ * Return the host name of this host.
+ * This is used for building globally unique transaction identifiers.
+ * It would be safer to use the IP address, but a host name is better
+ * for humans to read and will do for now.
+ */
protected String getHostName()
{
- if (hostName == null)
- {
- try
- {
+ if (hostName == null) {
+ try {
hostName = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e)
- {
+ } catch (UnknownHostException e) {
hostName = "localhost";
}
}
@@ -278,16 +560,389 @@
return hostName;
}
- public String toString() {
- return xid.toString();
- }
-
// Private -------------------------------------------------------
-/* private Object writeReplace(java.io.ObjectOutputStream out)
- throws IOException
+
+ // A list of synchronizations to call back on commit (before and after)
+ private ArrayList sync = new ArrayList();
+
+ // A list of the XARessources to 2phi commit (prepare and commit)
+ private ArrayList resources = new ArrayList();
+
+ // Suspended XAResources are in this set
+ private Set suspendedResources = new HashSet();
+
+ private Xid xid; // XA legacy
+ private int status; // status code
+ private int heuristicCode = HEUR_NONE; // heuristics status
+ private long start;
+ private Timeout timeout;
+
+ // The public face of the capsule a JTA implementation
+ private Transaction transaction;
+
+ // My manager
+ private TxManager tm;
+
+ // Mutex for thread-safety
+ private boolean locked = false;
+
+ /**
+ * Lock this instance.
+ */
+ private synchronized void lock()
+ {
+ if (locked) {
+ Logger.warning("TxCapsule: Lock contention."); // Good for debugging.
+ Thread.currentThread().dumpStack();
+ }
+
+ while (locked) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {}
+ }
+
+ locked = true;
+ }
+
+ /**
+ * Unlock this instance.
+ */
+ private synchronized void unlock()
{
- return new TransactionProxy(this);
+ if (!locked)
+ Logger.warning("TxCapsule: Unlocking, but not locked.");
+
+ locked = false;
+
+ notify();
+ }
+
+ /**
+ * Cancel the timeout.
+ * This will release the lock while calling out.
+ */
+ private void cancelTimeout()
+ {
+ if (timeout != null) {
+ unlock();
+ try {
+ timeout.cancel();
+ } finally {
+ lock();
+ }
+ timeout = null;
+ }
+ }
+
+ /**
+ * Call <code>start()</code> on the XAResource.
+ * This will release the lock while calling out.
+ */
+ private void startResource(XAResource xaRes, int flags)
+ throws XAException
+ {
+ unlock();
+ try {
+ xaRes.start(xid, flags);
+ } finally {
+ lock();
+ }
+ }
+
+ /**
+ * Call <code>end()</code> on the XAResource.
+ * This will release the lock while calling out.
+ */
+ private void endResource(XAResource xaRes, int flag)
+ throws XAException
+ {
+ unlock();
+ try {
+ xaRes.end(xid, flag);
+ } finally {
+ lock();
+ }
+ }
+
+ /**
+ * End Tx association for all suspended resources.
+ */
+ private void suspendedResourcesDone()
+ {
+ try {
+ while (!suspendedResources.isEmpty()) {
+ Iterator iter = suspendedResources.iterator();
+
+ try {
+ while (iter.hasNext()) {
+ XAResource xaRes = (XAResource)iter.next();
+
+ iter.remove();
+ endResource(xaRes, XAResource.TMSUCCESS);
+ }
+ } catch (ConcurrentModificationException e) { }
+ }
+ } catch(XAException e) {
+ Logger.exception(e);
+ status = Status.STATUS_MARKED_ROLLBACK;
+ }
+ }
+
+ /**
+ * Call synchronization <code>beforeCompletion()</code>.
+ * This will release the lock while calling out.
+ */
+ private void doBeforeCompletion()
+ {
+ unlock();
+ try {
+ for (int i = 0; i < sync.size(); i++)
+ ((Synchronization)sync.get(i)).beforeCompletion();
+ } finally {
+ lock();
+ }
+ }
+
+ /**
+ * Call synchronization <code>afterCompletion()</code>.
+ * This will release the lock while calling out.
+ */
+ private void doAfterCompletion()
+ {
+ // Assert: Status indicates: Too late to add new synchronizations.
+ unlock();
+ try {
+ for (int i = 0; i < sync.size(); i++)
+ ((Synchronization)sync.get(i)).afterCompletion(status);
+ } finally {
+ lock();
+ }
+ }
+
+ /**
+ * We got another heuristic.
+ * Promote <code>heuristicCode</code> if needed and tell
+ * the resource to forget the heuristic.
+ * This will release the lock while calling out.
+ */
+ private void gotHeuristic(XAResource resource, int code)
+ {
+ switch (code) {
+ case XAException.XA_HEURMIX:
+ heuristicCode = XAException.XA_HEURMIX;
+ break;
+ case XAException.XA_HEURRB:
+ if (heuristicCode == HEUR_NONE)
+ heuristicCode = XAException.XA_HEURRB;
+ else if (heuristicCode == XAException.XA_HEURCOM ||
+ heuristicCode == XAException.XA_HEURHAZ)
+ heuristicCode = XAException.XA_HEURMIX;
+ break;
+ case XAException.XA_HEURCOM:
+ if (heuristicCode == HEUR_NONE)
+ heuristicCode = XAException.XA_HEURCOM;
+ else if (heuristicCode == XAException.XA_HEURRB ||
+ heuristicCode == XAException.XA_HEURHAZ)
+ heuristicCode = XAException.XA_HEURMIX;
+ break;
+ case XAException.XA_HEURHAZ:
+ if (heuristicCode == HEUR_NONE)
+ heuristicCode = XAException.XA_HEURHAZ;
+ else if (heuristicCode == XAException.XA_HEURCOM ||
+ heuristicCode == XAException.XA_HEURRB)
+ heuristicCode = XAException.XA_HEURMIX;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ if (resource != null) {
+ try {
+ unlock();
+ resource.forget(xid);
+ } catch (XAException e) {
+ Logger.exception(e);
+ } finally {
+ lock();
+ }
+ }
+ }
+
+ /**
+ * Check for heuristics, clear and throw exception if any found.
+ */
+ private void checkHeuristics()
+ throws HeuristicMixedException,
+ HeuristicRollbackException
+ {
+ switch (heuristicCode) {
+ case XAException.XA_HEURHAZ:
+ case XAException.XA_HEURMIX:
+ heuristicCode = HEUR_NONE;
+ throw new HeuristicMixedException();
+ case XAException.XA_HEURRB:
+ heuristicCode = HEUR_NONE;
+ throw new HeuristicRollbackException();
+ case XAException.XA_HEURCOM:
+ heuristicCode = HEUR_NONE;
+ // Why isn't HeuristicCommitException used in JTA ?
+ // And why define something that is not used ?
+ // For now we just have to ignore this failure, even if it happened
+ // on rollback.
+ return;
+ }
}
-*/
+
+ /**
+ * Prepare all enlisted resources.
+ * If the first phase of the commit process results in a decision
+ * to commit the <code>status</code> will be
+ * <code>Status.STATUS_PREPARED</code> on return.
+ * Otherwise the <code>status</code> will be
+ * <code>Status.STATUS_MARKED_ROLLBACK</code> on return.
+ * This will release the lock while calling out.
+ *
+ * @returns True iff all resources voted read-only.
+ */
+ private boolean prepareResources()
+ {
+ boolean readOnly = true;
+
+ status = Status.STATUS_PREPARING;
+
+ for (int i = 0; i < resources.size(); i++) {
+ // Abort prepare on state change.
+ if (status != Status.STATUS_PREPARING)
+ return false;
+
+ XAResource resource = (XAResource)resources.get(i);
+
+ try {
+ int vote;
+
+ unlock();
+ try {
+ vote = resource.prepare(xid);
+ } finally {
+ lock();
+ }
+
+ if (vote != XAResource.XA_RDONLY)
+ readOnly = false;
+ else {
+ // Resource voted read-only: Can forget about resource.
+ resources.remove(i);
+ --i; // undo future increment
+ }
+ } catch (XAException e) {
+ switch (e.errorCode) {
+ case XAException.XA_HEURCOM:
+ // Heuristic commit is not that bad when preparing.
+ // But it means trouble if we have to rollback.
+ gotHeuristic(resource, e.errorCode);
+ break;
+ case XAException.XA_HEURRB:
+ case XAException.XA_HEURMIX:
+ case XAException.XA_HEURHAZ:
+ gotHeuristic(resource, e.errorCode);
+ if (status == Status.STATUS_PREPARING)
+ status = Status.STATUS_MARKED_ROLLBACK;
+ break;
+ default:
+ Logger.exception(e);
+ if (status == Status.STATUS_PREPARING)
+ status = Status.STATUS_MARKED_ROLLBACK;
+ break;
+ }
+ }
+ }
+
+ if (status == Status.STATUS_PREPARING)
+ status = Status.STATUS_PREPARED;
+
+ return readOnly;
+ }
+
+ /**
+ * Commit all enlisted resources.
+ * This will release the lock while calling out.
+ */
+ private void commitResources(boolean onePhase)
+ {
+ status = Status.STATUS_COMMITTING;
+
+ for (int i = 0; i < resources.size(); i++) {
+ // Abort commit on state change.
+ if (status != Status.STATUS_COMMITTING)
+ return;
+
+ XAResource resource = (XAResource)resources.get(i);
+
+ try {
+ unlock();
+ try {
+ resource.commit(xid, onePhase);
+ } finally {
+ lock();
+ }
+ } catch (XAException e) {
+ switch (e.errorCode) {
+ case XAException.XA_HEURRB:
+ case XAException.XA_HEURCOM:
+ case XAException.XA_HEURMIX:
+ case XAException.XA_HEURHAZ:
+ gotHeuristic(resource, e.errorCode);
+ break;
+ default:
+ Logger.exception(e);
+ break;
+ }
+ }
+ }
+
+ if (status != Status.STATUS_COMMITTING)
+ status = Status.STATUS_COMMITTED;
+ }
+
+ /**
+ * Rollback all enlisted resources.
+ * This will release the lock while calling out.
+ */
+ private void rollbackResources()
+ {
+ status = Status.STATUS_ROLLING_BACK;
+
+ for (int i = 0; i < resources.size(); i++) {
+ XAResource resource = (XAResource)resources.get(i);
+
+ try {
+ unlock();
+ try {
+ resource.rollback(xid);
+ } finally {
+ lock();
+ }
+ } catch (XAException e) {
+ switch (e.errorCode) {
+ case XAException.XA_HEURRB:
+ // Heuristic rollback is not that bad when rolling back.
+ gotHeuristic(resource, e.errorCode);
+ break;
+ case XAException.XA_HEURCOM:
+ case XAException.XA_HEURMIX:
+ case XAException.XA_HEURHAZ:
+ gotHeuristic(resource, e.errorCode);
+ break;
+ default:
+ Logger.exception(e);
+ break;
+ }
+ }
+ }
+
+ status = Status.STATUS_ROLLEDBACK;
+ }
+
// Inner classes -------------------------------------------------
}
1.7 +19 -12 jboss/src/main/org/jboss/tm/TransactionImpl.java
Index: TransactionImpl.java
===================================================================
RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/TransactionImpl.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- TransactionImpl.java 2000/08/11 03:00:54 1.6
+++ TransactionImpl.java 2000/09/08 02:25:01 1.7
@@ -33,7 +33,7 @@
* @see <related>
* @author Rickard �berg ([EMAIL PROTECTED])
* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
- * @version $Revision: 1.6 $
+ * @version $Revision: 1.7 $
*/
public class TransactionImpl
implements Transaction, Serializable
@@ -54,8 +54,8 @@
public TransactionImpl(TxManager tm,int hash, Xid xid)
{
this.tm = tm;
- this.hash = hash;
- this.xid = xid;
+ this.hash = hash;
+ this.xid = xid;
}
/*
@@ -64,8 +64,8 @@
* used for propagated Tx
*/
public void setTxManager(TxManager tm) {
-
- this.tm= tm;
+
+ this.tm= tm;
}
@@ -84,14 +84,18 @@
}
public boolean delistResource(XAResource xaRes, int flag)
+ throws java.lang.IllegalStateException,
+ SystemException
{
return tm.delistResource(this, xaRes, flag);
}
public boolean enlistResource(XAResource xaRes)
- throws RollbackException
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
{
- return tm.enlistResource(this, xaRes);
+ return tm.enlistResource(this, xaRes);
}
public int getStatus()
@@ -101,8 +105,11 @@
}
public void registerSynchronization(Synchronization s)
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
{
- tm.registerSynchronization(this, s);
+ tm.registerSynchronization(this, s);
}
public void rollback()
@@ -110,7 +117,7 @@
java.lang.SecurityException,
SystemException
{
- tm.rollback(this);
+ tm.rollback(this);
}
public void setRollbackOnly()
@@ -118,12 +125,12 @@
SystemException
{
- tm.setRollbackOnly(this);
- }
+ tm.setRollbackOnly(this);
+ }
public boolean equals(Object obj)
{
- return ((TransactionImpl)obj).hash == hash;
+ return ((TransactionImpl)obj).hash == hash;
}
public int hashCode()