User: osh
Date: 00/09/28 14:10:12
Modified: src/main/org/jboss/tm TransactionImpl.java TxCapsule.java
TxManager.java XidImpl.java
Log:
Transaction manager performance enhancements
Revision Changes Path
1.9 +149 -80 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.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- TransactionImpl.java 2000/09/08 05:22:29 1.8
+++ TransactionImpl.java 2000/09/28 21:10:11 1.9
@@ -25,15 +25,17 @@
import javax.transaction.xa.XAException;
/**
- * A light weight transaction.
+ * A light weight transaction.
*
- * It is the public face of the TxCapsule. Many of these "transactions" can
coexist representing the TxCap
- * Access to the underlying txCap is done through the TransactionManager
+ * It is the public face of the TxCapsule.
+ * Many of these "transactions" can coexist representing the TxCap.
+ * Access to the underlying txCap is done through the TransactionManager.
*
- * @see <related>
- * @author Rickard �berg ([EMAIL PROTECTED])
+ * @see TxCapsule
+ * @author Rickard �berg ([EMAIL PROTECTED])
* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
- * @version $Revision: 1.8 $
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
+ * @version $Revision: 1.9 $
*/
public class TransactionImpl
implements Transaction, Serializable
@@ -41,54 +43,63 @@
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
- int hash;
- Xid xid; // XA legacy
-
- // Static --------------------------------------------------------
- public transient TxManager tm;
-
- static String hostName;
+ XidImpl xid; // Transaction ID.
+
// Constructors --------------------------------------------------
- public TransactionImpl(TxManager tm,int hash, Xid xid)
+ TransactionImpl(TxCapsule txCapsule, XidImpl xid)
{
- this.tm = tm;
- this.hash = hash;
+ this.txCapsule = txCapsule;
this.xid = xid;
+ travelled = false;
}
- /*
- * setTxManager()
- *
- * used for propagated Tx
- */
- public void setTxManager(TxManager tm) {
-
- this.tm= tm;
- }
-
-
-
-
// Public --------------------------------------------------------
+
+ // In the following methods we synchronize to avoid races with transaction
+ // termination. The travelled flag is not checked, as we assume that the
+ // transaction has already been imported.
+
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
{
-
- tm.commit(this);
+ synchronized (this) {
+ if (done)
+ throw new IllegalStateException("No transaction.");
+
+ txCapsule.commit();
+ }
}
+ public void rollback()
+ throws java.lang.IllegalStateException,
+ java.lang.SecurityException,
+ SystemException
+ {
+ synchronized (this) {
+ if (done)
+ throw new IllegalStateException("No transaction.");
+
+ txCapsule.rollback();
+ }
+ }
+
public boolean delistResource(XAResource xaRes, int flag)
throws java.lang.IllegalStateException,
SystemException
{
- return tm.delistResource(this, xaRes, flag);
+ synchronized (this) {
+ if (done)
+ throw new IllegalStateException("No transaction.");
+
+ return txCapsule.delistResource(xaRes, flag);
+ }
}
public boolean enlistResource(XAResource xaRes)
@@ -96,73 +107,131 @@
java.lang.IllegalStateException,
SystemException
{
- return tm.enlistResource(this, xaRes);
+ synchronized (this) {
+ if (done)
+ throw new IllegalStateException("No transaction.");
+
+ return txCapsule.enlistResource(xaRes);
+ }
}
public int getStatus()
- throws SystemException
+ throws SystemException
{
- return tm.getStatus(this);
+ synchronized (this) {
+ if (done)
+ return Status.STATUS_NO_TRANSACTION;
+
+ return txCapsule.getStatus();
+ }
}
public void registerSynchronization(Synchronization s)
- throws RollbackException,
- java.lang.IllegalStateException,
- SystemException
+ throws RollbackException,
+ java.lang.IllegalStateException,
+ SystemException
{
- tm.registerSynchronization(this, s);
+ synchronized (this) {
+ if (done)
+ throw new IllegalStateException("No transaction.");
+
+ txCapsule.registerSynchronization(s);
+ }
}
- public void rollback()
- throws java.lang.IllegalStateException,
- java.lang.SecurityException,
- SystemException
+ public void setRollbackOnly()
+ throws java.lang.IllegalStateException,
+ SystemException
{
-
- tm.rollback(this);
+ synchronized (this) {
+ if (done)
+ throw new IllegalStateException("No transaction.");
+
+ txCapsule.setRollbackOnly();
+ }
}
- public void setRollbackOnly()
- throws java.lang.IllegalStateException,
- SystemException
+ public int hashCode()
{
-
- tm.setRollbackOnly(this);
- }
+ return xid.hash;
+ }
- public boolean equals(Object obj)
+ public String toString()
{
- return ((TransactionImpl)obj).hash == hash;
+ return "TransactionImpl:" + xid.toString();
}
- public int hashCode()
+ public boolean equals(Object obj)
{
- return hash;
+ if (obj != null && obj instanceof TransactionImpl)
+ return xid.equals(((TransactionImpl)obj).xid);
+ return false;
}
// Package protected ---------------------------------------------
- // Protected -----------------------------------------------------
- protected String getHostName()
+ /**
+ * Setter for property txCapsule.
+ *
+ * This is needed when a propagated transaction is imported into the
+ * current transaction manager.
+ */
+ synchronized void setTxCapsule(TxCapsule txCapsule)
+ {
+ if (done)
+ // Shouldn't happen.
+ throw new IllegalStateException("Transaction " + toString() +
+ " is done.");
+ this.txCapsule = txCapsule;
+ travelled = false;
+ }
+
+ /**
+ * Setter for property done.
+ * No argument for this mutator; we can only set to false.
+ * This will also clear the txCapsule reference.
+ */
+ synchronized void setDone()
+ {
+ done = true;
+ txCapsule = null;
+ }
+
+ /**
+ * Getter for property done.
+ */
+ boolean isDone()
+ {
+ return done;
+ }
+
+ /**
+ * Returns true iff this transaction needs to be imported into the
+ * local transaction manager.
+ */
+ boolean importNeeded()
{
- if (hostName == null)
- {
- try
- {
- hostName = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e)
- {
- hostName = "localhost";
- }
- }
-
- return hostName;
- }
-
- public String toString() {
- return "tx:Xid:"+hash;
- }
+ return !done && travelled;
+ }
// Private -------------------------------------------------------
+
+ private transient TxCapsule txCapsule; // The real implementation.
+ private boolean done; // Flags that the transaction has terminated.
+ transient boolean travelled; // Flags that the transaction has travelled.
+
+ private void writeObject(java.io.ObjectOutputStream stream)
+ throws java.io.IOException
+ {
+ stream.defaultWriteObject();
+ }
+
+ private void readObject(java.io.ObjectInputStream stream)
+ throws java.io.IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ travelled = true;
+ }
+
// Inner classes -------------------------------------------------
}
1.9 +453 -164 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.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- TxCapsule.java 2000/09/28 01:17:11 1.8
+++ TxCapsule.java 2000/09/28 21:10:11 1.9
@@ -7,8 +7,6 @@
package org.jboss.tm;
import java.io.Serializable;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
@@ -37,7 +35,6 @@
* 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
@@ -46,23 +43,21 @@
* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
*
- * @version $Revision: 1.8 $
+ * @version $Revision: 1.9 $
*/
class TxCapsule implements TimeoutTarget
{
// Constants -----------------------------------------------------
+ // Trace enabled flag
+ static private final boolean trace = true;
+
// Code meaning "no heuristics seen", must not be XAException.XA_HEURxxx
static private final int HEUR_NONE = XAException.XA_RETRY;
// Attributes ----------------------------------------------------
-
- // Static --------------------------------------------------------
- static private int nextId = 0;
- static private synchronized int getNextId() { return nextId++; }
-
- private static String hostName;
+ // Static --------------------------------------------------------
// Constructors --------------------------------------------------
@@ -90,12 +85,41 @@
*/
private TxCapsule(TxManager tm)
{
- int hashCode = getNextId();
- xid = new XidImpl((getHostName()+"/"+hashCode).getBytes(), null);
- transaction = new TransactionImpl(tm, hashCode, xid);
+ xid = new XidImpl();
this.tm = tm;
}
+ /**
+ * Create a new front for this transaction.
+ */
+ TransactionImpl createTransactionImpl()
+ {
+ TransactionImpl tx = new TransactionImpl(this, xid);
+ addTransaction(tx);
+
+ return tx;
+ }
+
+ /**
+ * Prepare this instance for reuse.
+ */
+ void reUse(int timeout)
+ {
+ if (!done)
+ throw new IllegalStateException();
+
+ done = false;
+ resourcesEnded = false;
+
+ xid = new XidImpl();
+
+ status = Status.STATUS_ACTIVE;
+ heuristicCode = HEUR_NONE;
+
+ start = System.currentTimeMillis();
+ this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
+ }
+
// Public --------------------------------------------------------
/**
@@ -139,7 +163,7 @@
status = Status.STATUS_MARKED_ROLLBACK;
// fall through..
case Status.STATUS_MARKED_ROLLBACK:
- suspendedResourcesDone();
+ endResources();
rollbackResources();
doAfterCompletion();
gotHeuristic(null, XAException.XA_HEURRB);
@@ -166,10 +190,17 @@
// Package protected ---------------------------------------------
/**
- * Return the transaction encapsulated here.
+ * Import a transaction encapsulated here.
*/
- Transaction getTransaction() {
- return transaction;
+ void importTransaction(TransactionImpl tx) {
+ try {
+ lock();
+
+ tx.setTxCapsule(this);
+ addTransaction(tx);
+ } finally {
+ unlock();
+ }
}
/**
@@ -186,10 +217,12 @@
SystemException
{
try {
- //DEBUG Logger.debug("TxCapsule before lock");
- lock();
- //DEBUG Logger.debug("TxCapsule after lock status is
"+getStringStatus(status));
-
+ lock();
+
+ if (trace)
+ Logger.debug("TxCapsule.commit(): Entered, status=" +
+ getStringStatus(status));
+
switch (status) {
case Status.STATUS_PREPARING:
throw new IllegalStateException("Already started preparing.");
@@ -198,11 +231,13 @@
case Status.STATUS_ROLLING_BACK:
throw new IllegalStateException("Already started rolling back.");
case Status.STATUS_ROLLEDBACK:
+ instanceDone();
checkHeuristics();
throw new IllegalStateException("Already rolled back.");
case Status.STATUS_COMMITTING:
throw new IllegalStateException("Already started committing.");
case Status.STATUS_COMMITTED:
+ instanceDone();
checkHeuristics();
throw new IllegalStateException("Already committed.");
case Status.STATUS_NO_TRANSACTION:
@@ -210,37 +245,42 @@
case Status.STATUS_UNKNOWN:
throw new IllegalStateException("Unknown state");
case Status.STATUS_MARKED_ROLLBACK:
- suspendedResourcesDone();
+ endResources();
rollbackResources();
doAfterCompletion();
+ cancelTimeout();
+ instanceDone();
+ checkHeuristics();
throw new RollbackException("Already marked for rollback");
case Status.STATUS_ACTIVE:
- //DEBUG Logger.debug("Commiting tx with status Active");
break;
default:
throw new IllegalStateException("Illegal status: " + status);
}
- suspendedResourcesDone();
-
doBeforeCompletion();
+
+ if (trace)
+ Logger.debug("TxCapsule.commit(): Before completion done, " +
+ "status=" + getStringStatus(status));
- Logger.debug("Before completion is done status is
"+getStringStatus(status));
-
+ endResources();
+
if (status == Status.STATUS_ACTIVE) {
- if (resources.size() == 0) {
- //DEBUG Logger.debug("no resources 0 phi commit");
+ if (resourceCount == 0) {
// Zero phase commit is really fast ;-)
+ if (trace)
+ Logger.debug("TxCapsule.commit(): No resources.");
status = Status.STATUS_COMMITTED;
- } else if (resources.size() == 1) {
- // DEBUG Logger.debug("1 resource 1 phi commit");
- // One phase commit
-
+ } else if (resourceCount == 1) {
+ // One phase commit
+ if (trace)
+ Logger.debug("TxCapsule.commit(): One resource.");
commitResources(true);
} else {
-
- // DEBUG Logger.debug("many resources 2 phi commit");
// Two phase commit
+ if (trace)
+ Logger.debug("TxCapsule.commit(): Many resources.");
if (!prepareResources()) {
boolean commitDecision =
@@ -262,48 +302,24 @@
rollbackResources();
doAfterCompletion();
cancelTimeout();
- throw new RollbackException("Unable to commit transaction has status.
"+getStringStatus(status));
+ instanceDone();
+ throw new RollbackException("Unable to commit, status=" +
+ getStringStatus(status));
}
cancelTimeout();
-
doAfterCompletion();
-
+ instanceDone();
checkHeuristics();
+
+ if (trace)
+ Logger.debug("TxCapsule.commit(): Committed OK.");
+
} finally {
unlock();
}
}
- private String getStringStatus(int status) {
-
- switch (status) {
- case Status.STATUS_PREPARING:
- return "STATUS_PREPARING";
- case Status.STATUS_PREPARED:
- return "STATUS_PREPARED";
- case Status.STATUS_ROLLING_BACK:
- return "STATUS_ROLLING_BACK";
- case Status.STATUS_ROLLEDBACK:
- return "STATUS_ROLLEDBACK";
- case Status.STATUS_COMMITTING:
- return "STATUS_COMMITING";
- case Status.STATUS_COMMITTED:
- return "STATUS_COMMITED";
- case Status.STATUS_NO_TRANSACTION:
- return "STATUS_NO_TRANSACTION";
- case Status.STATUS_UNKNOWN:
- return "STATUS_UNKNOWN";
- case Status.STATUS_MARKED_ROLLBACK:
- return "STATUS_MARKED_ROLLBACK";
- case Status.STATUS_ACTIVE:
- return "STATUS_ACTIVE";
-
- default:
- return "STATUS_UNKNOWN";
- }
-
- }
/**
* Rollback the transaction encapsulated here.
* Should not be called directly, use <code>TxManager.rollback()</code>
@@ -317,13 +333,18 @@
try {
lock();
+ if (trace)
+ Logger.debug("TxCapsule.rollback(): Entered, status=" +
+ getStringStatus(status));
+
switch (status) {
case Status.STATUS_ACTIVE:
case Status.STATUS_MARKED_ROLLBACK:
- suspendedResourcesDone();
+ endResources();
rollbackResources();
cancelTimeout();
doAfterCompletion();
+ instanceDone();
// Cannot throw heuristic exception, so we just have to
// clear the heuristics without reporting.
heuristicCode = HEUR_NONE;
@@ -333,7 +354,8 @@
status = Status.STATUS_MARKED_ROLLBACK;
return; // commit() will do rollback.
default:
- throw new IllegalStateException("Cannot rollback()");
+ throw new IllegalStateException("Cannot rollback(), status=" +
+ getStringStatus(status));
}
} finally {
unlock();
@@ -353,6 +375,10 @@
try {
lock();
+ if (trace)
+ Logger.debug("TxCapsule.setRollbackOnly(): Entered, status=" +
+ getStringStatus(status));
+
switch (status) {
case Status.STATUS_ACTIVE:
case Status.STATUS_PREPARING:
@@ -404,7 +430,13 @@
try {
lock();
- if (!resources.contains(xaRes))
+ if (trace)
+ Logger.debug("TxCapsule.delistResource(): Entered, status=" +
+ getStringStatus(status));
+
+ int idx = findResource(xaRes);
+
+ if (idx == -1)
throw new IllegalArgumentException("xaRes not enlisted");
switch (status) {
@@ -434,9 +466,12 @@
try {
endResource(xaRes, flag);
if (flag == XAResource.TMSUSPEND)
- suspendedResources.add(xaRes);
- else if (flag == XAResource.TMFAIL)
- status = Status.STATUS_MARKED_ROLLBACK;
+ resourceState[idx] = RS_SUSPENDED;
+ else {
+ if (flag == XAResource.TMFAIL)
+ status = Status.STATUS_MARKED_ROLLBACK;
+ resourceState[idx] = RS_ENDED;
+ }
return true;
} catch(XAException e) {
Logger.exception(e);
@@ -466,6 +501,10 @@
try {
lock();
+ if (trace)
+ Logger.debug("TxCapsule.enlistResource(): Entered, status=" +
+ getStringStatus(status));
+
switch (status) {
case Status.STATUS_ACTIVE:
case Status.STATUS_PREPARING:
@@ -490,17 +529,29 @@
throw new IllegalStateException("Illegal status: " + status);
}
+ if (resourcesEnded)
+ throw new IllegalStateException("Too late to enlist resources");
+
// Add resource
try {
- if (suspendedResources.contains(xaRes)) {
- startResource(xaRes, XAResource.TMRESUME);
- suspendedResources.remove(xaRes);
- return true;
+ int idx = findResource(xaRes);
+
+ if (idx != -1) {
+ if (resourceState[idx] == RS_SUSPENDED) {
+ startResource(xaRes, XAResource.TMRESUME);
+ resourceState[idx] = RS_ENLISTED;
+ return true;
+ } else if (resourceState[idx] == RS_ENDED) {
+ startResource(xaRes, XAResource.TMJOIN);
+ resourceState[idx] = RS_ENLISTED;
+ return true;
+ } else
+ return false; // already enlisted
}
- for (int i = 0; i < resources.size(); ++i) {
- if (xaRes.isSameRM((XAResource)resources.get(i))) {
+ for (int i = 0; i < resourceCount; ++i) {
+ if (xaRes.isSameRM(resources[i])) {
startResource(xaRes, XAResource.TMJOIN);
- resources.add(xaRes);
+ addResource(xaRes);
return true;
}
}
@@ -508,7 +559,7 @@
// According to the JTA spec we should create a new
// transaction branch here.
startResource(xaRes, XAResource.TMNOFLAGS);
- resources.add(xaRes);
+ addResource(xaRes);
return true;
} catch(XAException e) {
Logger.exception(e);
@@ -543,6 +594,10 @@
try {
lock();
+ if (trace)
+ Logger.debug("TxCapsule.registerSynchronization(): Entered, " +
+ "status=" + getStringStatus(status));
+
switch (status) {
case Status.STATUS_ACTIVE:
case Status.STATUS_PREPARING:
@@ -570,7 +625,15 @@
throw new IllegalStateException("Illegal status: " + status);
}
- sync.add(s);
+ if (syncCount == syncAllocSize) {
+ // expand table
+ syncAllocSize = 2 * syncAllocSize;
+
+ Synchronization[] sy = new Synchronization[syncAllocSize];
+ System.arraycopy(sync, 0, sy, 0, syncCount);
+ sync = sy;
+ }
+ sync[syncCount++] = s;
} finally {
unlock();
}
@@ -578,65 +641,175 @@
// Protected -----------------------------------------------------
+
+ // Private -------------------------------------------------------
+
/**
- * 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.
+ * The public faces of this capsule are JTA Transaction implementations.
*/
- protected String getHostName()
- {
- if (hostName == null) {
- try {
- hostName = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- hostName = "localhost";
- }
- }
+ private TransactionImpl[] transactions = new TransactionImpl[1];
- return hostName;
- }
+ /**
+ * Size of allocated transaction frontend array.
+ */
+ private int transactionAllocSize = 1;
- // Private -------------------------------------------------------
+ /**
+ * Count of transaction frontends for this transaction.
+ */
+ private int transactionCount = 0;
- // 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();
+ /**
+ * The synchronizations to call back.
+ */
+ private Synchronization[] sync = new Synchronization[1];
- // Suspended XAResources are in this set
- private Set suspendedResources = new HashSet();
+ /**
+ * Size of allocated synchronization array.
+ */
+ private int syncAllocSize = 1;
+
+ /**
+ * Count of ynchronizations for this transaction.
+ */
+ private int syncCount = 0;
- private Xid xid; // XA legacy
- private int status; // status code
- private int heuristicCode = HEUR_NONE; // heuristics status
+
+ /**
+ * A list of the XARessources that have participated in this transaction.
+ */
+ private XAResource[] resources = new XAResource[1];
+
+ /**
+ * The state of the resources.
+ */
+ private int[] resourceState = new int[1];
+
+ private final static int RS_ENLISTED = 1; // enlisted
+ private final static int RS_SUSPENDED = 2; // suspended
+ private final static int RS_ENDED = 3; // not associated
+ private final static int RS_VOTE_READONLY = 4; // voted read-only
+ private final static int RS_VOTE_OK = 5; // voted ok
+
+ /**
+ * Size of allocated resource arrays.
+ */
+ private int resourceAllocSize = 1;
+
+ /**
+ * Count of resources that have participated in this transaction.
+ */
+ private int resourceCount = 0;
+
+
+ /**
+ * Flags that it is too late to enlist new resources.
+ */
+ private boolean resourcesEnded = false;
+
+ /**
+ * The ID of this transaction.
+ */
+ private XidImpl xid; // Transaction id
+
+ /**
+ * Status of this transaction.
+ */
+ private int status;
+
+ /**
+ * The heuristics status of this transaction.
+ */
+ private int heuristicCode = HEUR_NONE;
+
+ /**
+ * The time when this transaction was started.
+ */
private long start;
+
+ /**
+ * The timeout handle for this transaction.
+ */
private Timeout timeout;
- // The public face of the capsule a JTA implementation
- private Transaction transaction;
+ /**
+ * The incarnation count of this transaction. This is incremented
+ * whenever this instance is done so that nobody is waiting for the
+ * lock across incarnations.
+ */
+ private long incarnationCount = 1;
- // My manager
+ /**
+ * The transaction manager for this transaction.
+ */
private TxManager tm;
- // Mutex for thread-safety
+ /**
+ * Mutex for thread-safety. This should only be changed in the
+ * <code>lock()</code> and <code>unlock()</code> methods.
+ */
private boolean locked = false;
/**
+ * Flags that we are done with this transaction and that it can be reused.
+ */
+ private boolean done = false;
+
+
+ /**
+ * Return a string representation of the given status code.
+ */
+ private String getStringStatus(int status) {
+ switch (status) {
+ case Status.STATUS_PREPARING:
+ return "STATUS_PREPARING";
+ case Status.STATUS_PREPARED:
+ return "STATUS_PREPARED";
+ case Status.STATUS_ROLLING_BACK:
+ return "STATUS_ROLLING_BACK";
+ case Status.STATUS_ROLLEDBACK:
+ return "STATUS_ROLLEDBACK";
+ case Status.STATUS_COMMITTING:
+ return "STATUS_COMMITING";
+ case Status.STATUS_COMMITTED:
+ return "STATUS_COMMITED";
+ case Status.STATUS_NO_TRANSACTION:
+ return "STATUS_NO_TRANSACTION";
+ case Status.STATUS_UNKNOWN:
+ return "STATUS_UNKNOWN";
+ case Status.STATUS_MARKED_ROLLBACK:
+ return "STATUS_MARKED_ROLLBACK";
+ case Status.STATUS_ACTIVE:
+ return "STATUS_ACTIVE";
+
+ default:
+ return "STATUS_UNKNOWN(" + status + ")";
+ }
+ }
+
+ /**
* Lock this instance.
*/
private synchronized void lock()
{
+ if (done)
+ throw new IllegalStateException("No transaction");
+
if (locked) {
Logger.warning("TxCapsule: Lock contention."); // Good for debugging.
Thread.currentThread().dumpStack();
- }
- while (locked) {
- try {
- wait();
- } catch (InterruptedException ex) {}
+ long myIncarnation = incarnationCount;
+
+ while (locked) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {}
+
+ if (done || myIncarnation != incarnationCount)
+ throw new IllegalStateException("No transaction");
+ }
}
locked = true;
@@ -673,17 +846,73 @@
}
/**
+ * Return index of XAResource, or <code>-1</code> if not found.
+ */
+ private int findResource(XAResource xaRes)
+ {
+ for (int i = 0; i < resourceCount; ++i)
+ if (resources[i] == xaRes)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Add a resource, expanding tables if needed.
+ */
+ private void addResource(XAResource xaRes)
+ {
+ if (resourceCount == resourceAllocSize) {
+ // expand tables
+ resourceAllocSize = 2 * resourceAllocSize;
+
+ XAResource[] res = new XAResource[resourceAllocSize];
+ System.arraycopy(resources, 0, res, 0, resourceCount);
+ resources = res;
+
+ int[] stat = new int[resourceAllocSize];
+ System.arraycopy(resourceState, 0, stat, 0, resourceCount);
+ resourceState = stat;
+ }
+ resources[resourceCount] = xaRes;
+ resourceState[resourceCount] = RS_ENLISTED;
+ ++resourceCount;
+ }
+
+ /**
+ * Add a transaction frontend, expanding the table if needed.
+ */
+ private void addTransaction(TransactionImpl tx)
+ {
+ if (transactionCount == transactionAllocSize) {
+ // expand table
+ transactionAllocSize = 2 * transactionAllocSize;
+
+ TransactionImpl[] tr = new TransactionImpl[transactionAllocSize];
+ System.arraycopy(transactions, 0, tr, 0, transactionCount);
+ transactions = tr;
+ }
+ transactions[transactionCount++] = tx;
+ }
+
+ /**
* Call <code>start()</code> on the XAResource.
* This will release the lock while calling out.
*/
private void startResource(XAResource xaRes, int flags)
throws XAException
{
+System.err.println("TxCapsule.startResource(" + xid.toString() +
+ ") entered: " + xaRes.toString() +
+ " flags=" + flags);
unlock();
try {
xaRes.start(xid, flags);
} finally {
lock();
+System.err.println("TxCapsule.startResource(" + xid.toString() +
+ ") leaving: " + xaRes.toString() +
+ " flags=" + flags);
}
}
@@ -694,38 +923,59 @@
private void endResource(XAResource xaRes, int flag)
throws XAException
{
+System.err.println("TxCapsule.endResource(" + xid.toString() +
+ ") entered: " + xaRes.toString() +
+ " flag=" + flag);
unlock();
try {
xaRes.end(xid, flag);
} finally {
lock();
+System.err.println("TxCapsule.endResource(" + xid.toString() +
+ ") leaving: " + xaRes.toString() +
+ " flag=" + flag);
}
}
/**
- * End Tx association for all suspended resources.
+ * End Tx association for all resources.
*/
- private void suspendedResourcesDone()
+ private void endResources()
{
- 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) { }
+ for (int i = 0; i < resourceCount; i++) {
+ try {
+ if (resourceState[i] == RS_SUSPENDED) {
+ // This is mad, but JTA 1.0.1 spec says on page 41:
+ // "If TMSUSPEND is specified in flags, the transaction
+ // branch is temporarily suspended in incomplete state.
+ // The transaction context is in suspened state and must
+ // be resumed via start with TMRESUME specified."
+ // Note the _must_ above: It does not say _may_.
+ // The above citation also seem to contradict the XA resource
+ // state table on pages 17-18 where it is legal to do both
+ // end(TMSUCCESS) and end(TMFAIL) when the resource is in
+ // a suspended state.
+ // But the Minerva XA pool does not like that we call end()
+ // two times in a row, so we resume before ending.
+ startResource(resources[i], XAResource.TMRESUME);
+ resourceState[i] = RS_ENLISTED;
+ }
+ if (resourceState[i] == RS_ENLISTED) {
+System.err.println("endresources("+i+"): state="+resourceState[i]);
+ endResource(resources[i], XAResource.TMSUCCESS);
+ resourceState[i] = RS_ENDED;
+ }
+ } catch(XAException e) {
+System.err.println("endresources: XAException: " + e);
+System.err.println("endresources: XAException: errorCode=" + e.errorCode);
+ Logger.exception(e);
+ status = Status.STATUS_MARKED_ROLLBACK;
}
- } catch(XAException e) {
- Logger.exception(e);
- status = Status.STATUS_MARKED_ROLLBACK;
}
+ resourcesEnded = true; // Too late to enlist new resources.
}
+
/**
* Call synchronization <code>beforeCompletion()</code>.
* This will release the lock while calling out.
@@ -734,12 +984,8 @@
{
unlock();
try {
- for (int i = 0; i < sync.size(); i++) {
- //DEBUG Logger.debug("calling beforeCompletion on synch status
is "+getStringStatus(status));
- ((Synchronization)sync.get(i)).beforeCompletion();
- //DEBUG Logger.debug("Done calling beforeCompletion on synch
status is "+getStringStatus(status));
-
- }
+ for (int i = 0; i < syncCount; i++)
+ sync[i].beforeCompletion();
} finally {
lock();
}
@@ -754,8 +1000,8 @@
// 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);
+ for (int i = 0; i < syncCount; i++)
+ sync[i].afterCompletion(status);
} finally {
lock();
}
@@ -821,9 +1067,15 @@
case XAException.XA_HEURHAZ:
case XAException.XA_HEURMIX:
heuristicCode = HEUR_NONE;
+ if (trace)
+ Logger.debug("TxCapsule: Throwing HeuristicMixedException, " +
+ "status=" + getStringStatus(status));
throw new HeuristicMixedException();
case XAException.XA_HEURRB:
heuristicCode = HEUR_NONE;
+ if (trace)
+ Logger.debug("TxCapsule: Throwing HeuristicRollbackException, " +
+ "status=" + getStringStatus(status));
throw new HeuristicRollbackException();
case XAException.XA_HEURCOM:
heuristicCode = HEUR_NONE;
@@ -831,11 +1083,44 @@
// And why define something that is not used ?
// For now we just have to ignore this failure, even if it happened
// on rollback.
+ if (trace)
+ Logger.debug("TxCapsule: NOT Throwing HeuristicCommitException, " +
+ "status=" + getStringStatus(status));
return;
}
}
/**
+ * Prepare this instance for reuse.
+ */
+ private void instanceDone()
+ {
+ synchronized (this) {
+ // Done with this incarnation.
+ ++incarnationCount;
+
+ // Set done flag so we get no more frontends waiting for
+ // the lock.
+ done = true;
+
+ // Wake up anybody waiting for the lock.
+ notifyAll();
+ }
+
+ // Notify transaction fronts that we are done.
+ for (int i = 0; i < transactionCount; ++i)
+ transactions[i].setDone();
+
+ // Clear content of collections.
+ syncCount = 0;
+ transactionCount = 0;
+ resourceCount = 0;
+
+ // This instance is now ready for reuse (when we release the lock).
+ tm.releaseTxCapsule(this);
+ }
+
+ /**
* Prepare all enlisted resources.
* If the first phase of the commit process results in a decision
* to commit the <code>status</code> will be
@@ -851,46 +1136,47 @@
boolean readOnly = true;
status = Status.STATUS_PREPARING;
- Logger.debug("Status Preparing: "+status);
- for (int i = 0; i < resources.size(); i++) {
+ for (int i = 0; i < resourceCount; i++) {
// Abort prepare on state change.
if (status != Status.STATUS_PREPARING)
return false;
- XAResource resource = (XAResource)resources.get(i);
+ XAResource resource = resources[i];
try {
int vote;
unlock();
try {
- vote = resource.prepare(xid);
-
- Logger.debug("resource vote is "+vote);
-
+ vote = resources[i].prepare(xid);
} finally {
lock();
}
- if (vote != XAResource.XA_RDONLY)
+ if (vote == XAResource.XA_OK) {
readOnly = false;
+ resourceState[i] = RS_VOTE_OK;
+ } else if (vote == XAResource.XA_RDONLY)
+ resourceState[i] = RS_VOTE_READONLY;
else {
- // Resource voted read-only: Can forget about resource.
- resources.remove(i);
- --i; // undo future increment
+ // Illegal vote: rollback.
+ status = Status.STATUS_MARKED_ROLLBACK;
+ return false;
}
} catch (XAException e) {
+ readOnly = false;
+
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);
+ gotHeuristic(resources[i], e.errorCode);
break;
case XAException.XA_HEURRB:
case XAException.XA_HEURMIX:
case XAException.XA_HEURHAZ:
- gotHeuristic(resource, e.errorCode);
+ gotHeuristic(resources[i], e.errorCode);
if (status == Status.STATUS_PREPARING)
status = Status.STATUS_MARKED_ROLLBACK;
break;
@@ -916,18 +1202,20 @@
private void commitResources(boolean onePhase)
{
status = Status.STATUS_COMMITTING;
+
+ for (int i = 0; i < resourceCount; i++) {
+System.err.println("TxCapsule.commitResources():
resourceStates["+i+"]="+resourceState[i]);
+ if (!onePhase && resourceState[i] != RS_VOTE_OK)
+ continue;
- 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);
+ resources[i].commit(xid, onePhase);
} finally {
lock();
}
@@ -937,7 +1225,7 @@
case XAException.XA_HEURCOM:
case XAException.XA_HEURMIX:
case XAException.XA_HEURHAZ:
- gotHeuristic(resource, e.errorCode);
+ gotHeuristic(resources[i], e.errorCode);
break;
default:
Logger.exception(e);
@@ -958,13 +1246,14 @@
{
status = Status.STATUS_ROLLING_BACK;
- for (int i = 0; i < resources.size(); i++) {
- XAResource resource = (XAResource)resources.get(i);
+ for (int i = 0; i < resourceCount; i++) {
+ if (resourceState[i] == RS_VOTE_READONLY)
+ continue;
try {
unlock();
try {
- resource.rollback(xid);
+ resources[i].rollback(xid);
} finally {
lock();
}
@@ -972,12 +1261,12 @@
switch (e.errorCode) {
case XAException.XA_HEURRB:
// Heuristic rollback is not that bad when rolling back.
- gotHeuristic(resource, e.errorCode);
+ gotHeuristic(resources[i], e.errorCode);
break;
case XAException.XA_HEURCOM:
case XAException.XA_HEURMIX:
case XAException.XA_HEURHAZ:
- gotHeuristic(resource, e.errorCode);
+ gotHeuristic(resources[i], e.errorCode);
break;
default:
Logger.exception(e);
1.18 +242 -246 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.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- TxManager.java 2000/09/28 01:17:11 1.17
+++ TxManager.java 2000/09/28 21:10:11 1.18
@@ -6,7 +6,12 @@
*/
package org.jboss.tm;
+import java.lang.ref.SoftReference;
+
import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.HashMap;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
@@ -26,274 +31,265 @@
import org.jboss.logging.Logger;
/**
-* <description>
-*
-* @see <related>
-* @author Rickard �berg ([EMAIL PROTECTED])
-* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
-* @version $Revision: 1.17 $
-*/
+ * Our TransactionManager implementation.
+ *
+ * @see <related>
+ * @author Rickard �berg ([EMAIL PROTECTED])
+ * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
+ * @version $Revision: 1.18 $
+ */
public class TxManager
implements TransactionManager
{
- // Constants -----------------------------------------------------
-
- // Attributes ----------------------------------------------------
- // threadTx keeps track of a thread local association of tx
- ThreadLocal threadTx = new ThreadLocal();
- // transactions maps
- Hashtable txCapsules = new Hashtable();
-
- int timeOut = 60*1000; // Timeout in milliseconds
-
- // Static --------------------------------------------------------
-
- // Constructors --------------------------------------------------
+ // Constants -----------------------------------------------------
- // Public --------------------------------------------------------
- public void begin()
- throws NotSupportedException,
- SystemException
- {
- try {
- Logger.debug("begin tx");
-
- // create tx capsule
- TxCapsule txCap = new TxCapsule(this, timeOut);
-
- // Store it
- txCapsules.put(txCap.getTransaction(), txCap);
-
- // 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
- {
- getTransaction().commit();
- }
+ // Attributes ----------------------------------------------------
+
+ /**
+ * Default timeout in milliseconds.
+ */
+ int timeOut = 60*1000; // Default timeout in milliseconds
- public int getStatus()
- throws SystemException
- {
- // Get the txCapsule running now with the thread
- Object current = threadTx.get();
- if (current != null) {
- TxCapsule txCap = (TxCapsule) txCapsules.get(current);
-
- if (txCap == null)
- return Status.STATUS_NO_TRANSACTION;
- else
- return txCap.getStatus();
- } else {
- return Status.STATUS_NO_TRANSACTION;
- }
- }
+ // Static --------------------------------------------------------
- public Transaction getTransaction()
- throws SystemException
- {
- return (Transaction)threadTx.get();
- }
+ // Constructors --------------------------------------------------
- public void resume(Transaction tobj)
- throws InvalidTransactionException,
- java.lang.IllegalStateException,
- SystemException
- {
+ // Public --------------------------------------------------------
+
+ public void begin()
+ throws NotSupportedException,
+ SystemException
+ {
+ Transaction current = (Transaction)threadTx.get();
+
+ if (current != null &&
+ (!(current instanceof TransactionImpl) ||
+ !((TransactionImpl)current).isDone()))
+ throw new NotSupportedException("Transaction already active, " +
+ "cannot nest transactions.");
+
+ TxCapsule txCapsule = null;
+ while (inactiveCapsules.size() > 0) {
+ SoftReference ref = (SoftReference)inactiveCapsules.removeFirst();
+ txCapsule = (TxCapsule)ref.get();
+ if (txCapsule != null) {
+ txCapsule.reUse(timeOut);
+ break;
+ }
+ }
+ if (txCapsule == null)
+ txCapsule = new TxCapsule(this, timeOut);
+ TransactionImpl tx = txCapsule.createTransactionImpl();
+ threadTx.set(tx);
+ activeCapsules.put(tx.xid, txCapsule);
+ }
+
+ /**
+ * Commit the transaction associated with the currently running thread.
+ */
+ public void commit()
+ throws RollbackException,
+ HeuristicMixedException,
+ HeuristicRollbackException,
+ java.lang.SecurityException,
+ java.lang.IllegalStateException,
+ SystemException
+ {
+ Transaction current = (Transaction)threadTx.get();
+
+ if (current != null) {
+ current.commit();
+ threadTx.set(null);
+ } else
+ throw new IllegalStateException("No transaction.");
+ }
+
+ /**
+ * Return the status of the transaction associated with the currently
+ * running thread, or <code>Status.STATUS_NO_TRANSACTION</code> if no
+ * active transaction is currently associated.
+ */
+ public int getStatus()
+ throws SystemException
+ {
+ Transaction current = (Transaction)threadTx.get();
+
+ if (current != null)
+ return current.getStatus();
+ else
+ return Status.STATUS_NO_TRANSACTION;
+ }
+
+ /**
+ * Return the transaction currently associated with the invoking thread,
+ * or <code>null</code> if no active transaction is currently associated.
+ */
+ public Transaction getTransaction()
+ throws SystemException
+ {
+ Transaction current = (Transaction)threadTx.get();
+
+ if (current != null && current instanceof TransactionImpl &&
+ ((TransactionImpl)current).isDone()) {
+ threadTx.set(null);
+ return null;
+ }
+ return current;
+ }
+
+ public void resume(Transaction tobj)
+ throws InvalidTransactionException,
+ java.lang.IllegalStateException,
+ SystemException
+ {
//Useless
//throw new Exception("txMan.resume() NYI");
- }
-
-
- public Transaction suspend()
- throws SystemException
- {
- // Logger.debug("suspend tx");
+ }
+
+ public Transaction suspend()
+ throws SystemException
+ {
+ // Logger.log("suspend tx");
// Useless
return null;
//throw new Exception("txMan.suspend() NYI");
- }
-
-
- public void rollback()
- throws java.lang.IllegalStateException,
- java.lang.SecurityException,
- SystemException
- {
- getTransaction().rollback();
- }
-
- public void setRollbackOnly()
- throws java.lang.IllegalStateException,
- SystemException
- {
- // Logger.debug("set rollback only tx");
- getTransaction().setRollbackOnly();
- }
-
- public void setTransactionTimeout(int seconds)
- throws SystemException
- {
- timeOut = seconds;
- }
-
- /*
- * The following 2 methods are here to provide association and disassociation of
the thread
+ }
+
+ /**
+ * Roll back the transaction associated with the currently running thread.
*/
- public Transaction disassociateThread() {
- Transaction current = (Transaction) threadTx.get();
-
- threadTx.set(null);
-
- //DEBUG Logger.debug("DisassociateThread " + ((current==null) ? "null"
: Integer.toString(current.hashCode())));
- Logger.debug("disassociateThread " + ((current==null) ? "null" :
Integer.toString(current.hashCode())));
-
- return current;
- }
-
- public void associateThread(Transaction transaction) {
- // If the tx has traveled it needs the TxManager
- ((TransactionImpl) transaction).setTxManager(this);
+ public void rollback()
+ throws java.lang.IllegalStateException,
+ java.lang.SecurityException,
+ SystemException
+ {
+ Transaction current = (Transaction)threadTx.get();
+
+ if (current != null) {
+ current.rollback();
+ threadTx.set(null);
+ } else
+ throw new IllegalStateException("No transaction.");
+ }
+
+ /**
+ * Mark the transaction associated with the currently running thread
+ * so that the only possible outcome is a rollback.
+ */
+ public void setRollbackOnly()
+ throws java.lang.IllegalStateException,
+ SystemException
+ {
+ Transaction current = (Transaction)threadTx.get();
+
+ if (current != null)
+ current.setRollbackOnly();
+ else
+ throw new IllegalStateException("No transaction.");
+ }
+
+ /**
+ * Set the transaction timeout for new transactions started here.
+ */
+ public void setTransactionTimeout(int seconds)
+ throws SystemException
+ {
+ timeOut = seconds;
+ }
+
+ /*
+ * The following 2 methods are here to provide association and
+ * disassociation of the thread.
+ */
+ public Transaction disassociateThread()
+ {
+ Transaction current = (Transaction)threadTx.get();
- // Associate with the thread
- threadTx.set(transaction);
-
- //DEBUG Logger.debug("DisassociateThread " + ((transaction==null) ?
"null" : Integer.toString(transaction.hashCode())));
- Logger.debug("associateThread " + ((transaction==null) ? "null" :
Integer.toString(transaction.hashCode())));
+ threadTx.set(null);
- }
+ return current;
+ }
+ public void associateThread(Transaction transaction)
+ {
+ // If the transaction has travelled, we have to import it.
+ if (transaction != null && transaction instanceof TransactionImpl) {
+ TransactionImpl tx = (TransactionImpl)transaction;
+
+ if (tx.importNeeded()) {
+ synchronized(tx) {
+ // Recheck with synchronization.
+ if (tx.importNeeded()) {
+ TxCapsule txCapsule = (TxCapsule)activeCapsules.get(tx.xid);
+ if (txCapsule != null)
+ txCapsule.importTransaction(tx);
+ else
+ Logger.warning("Cannot import transaction: " +
+ tx.toString());
+ }
+ }
+ }
+ }
+
+ // Associate with the thread
+ threadTx.set(transaction);
+ }
- // Package protected ---------------------------------------------
- // There has got to be something better :)
- static TxManager getTransactionManager() {
- try {
-
- javax.naming.InitialContext context = new javax.naming.InitialContext();
+ // Package protected ---------------------------------------------
+
+ // There has got to be something better :)
+ static TxManager getTransactionManager()
+ {
+ try {
+ javax.naming.InitialContext context = new javax.naming.InitialContext();
- //One tx in naming
- Logger.debug("Calling get manager from JNDI");
- TxManager manager = (TxManager) context.lookup("TransactionManager");
- Logger.debug("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;
-
- } catch (Exception e ) { return null;}
- }
-
- int getTransactionTimeout()
- {
- return timeOut;
- }
-
-
- // Public --------------------------------------------------------
-
- public void commit(Transaction tx)
- throws RollbackException,
- HeuristicMixedException,
- HeuristicRollbackException,
- java.lang.SecurityException,
- java.lang.IllegalStateException,
- SystemException
- {
- Logger.debug("txManager commit tx "+tx.hashCode());
- try {
- // Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).commit();
- }
- finally {
- // Disassociation
- threadTx.set(null);
-
- //Remove from the internal maps, txCapsule should be GC'ed
- txCapsules.remove(tx);
- }
- }
-
- 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);
- }
-
- public boolean enlistResource(Transaction tx, XAResource xaRes)
- throws RollbackException,
- java.lang.IllegalStateException,
- SystemException
- {
- // Look up the txCapsule and delegate
- return ((TxCapsule) txCapsules.get(tx)).enlistResource(xaRes);
- }
-
- public int getStatus(Transaction tx)
- throws SystemException
- {
- // Look up the txCapsule and delegate
- TxCapsule txCap = ((TxCapsule) txCapsules.get(tx));
- return txCap == null ? Status.STATUS_NO_TRANSACTION : txCap.getStatus();
- }
+ return manager;
+ } catch (Exception e ) {
+ return null;
+ }
+ }
- 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);
- }
-
- public void rollback(Transaction tx)
- throws java.lang.IllegalStateException,
- java.lang.SecurityException,
- SystemException
- {
- Logger.debug("rollback tx "+tx.hashCode());
-
- try {
- // Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).rollback();
- }
- finally {
- // Disassociation
- threadTx.set(null);
-
- //Remove from the internal maps, txCapsule should be GC'ed
- txCapsules.remove(tx);
- }
- }
-
- public void setRollbackOnly(Transaction tx)
- throws java.lang.IllegalStateException,
- SystemException
- {
- // Look up the txCapsule and delegate
- ((TxCapsule) txCapsules.get(tx)).setRollbackOnly();
- }
-
-
-
- // Protected -----------------------------------------------------
-
- // Private -------------------------------------------------------
-
- // Inner classes -------------------------------------------------
+ /**
+ * Release the given txCapsule for reuse.
+ */
+ void releaseTxCapsule(TxCapsule txCapsule)
+ {
+ activeCapsules.remove(txCapsule);
+ inactiveCapsules.add(new SoftReference(txCapsule));
+ }
+
+
+ // Protected -----------------------------------------------------
+
+ // Private -------------------------------------------------------
+
+ /**
+ * This keeps track of the transaction association with threads.
+ * In some cases terminated transactions may not be cleared here.
+ */
+ private ThreadLocal threadTx = new ThreadLocal();
+
+ /**
+ * This map contains the active txCapsules as values.
+ * The keys are the <code>Xid</code> of the txCapsules.
+ */
+ private Map activeCapsules = new HashMap();
+
+ /**
+ * This collection contains the inactive txCapsules.
+ * We keep these for reuse.
+ */
+ private LinkedList inactiveCapsules = new LinkedList();
+
+ // Inner classes -------------------------------------------------
}
1.4 +156 -31 jboss/src/main/org/jboss/tm/XidImpl.java
Index: XidImpl.java
===================================================================
RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/XidImpl.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- XidImpl.java 2000/07/27 22:24:52 1.3
+++ XidImpl.java 2000/09/28 21:10:11 1.4
@@ -6,73 +6,198 @@
*/
package org.jboss.tm;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
import javax.transaction.xa.Xid;
+
/**
- * <description>
- *
- * @see <related>
- * @author Rickard �berg ([EMAIL PROTECTED])
- * @version $Revision: 1.3 $
+ * This object encapsulates the ID of a transaction.
+ *
+ * @see TransactionImpl
+ * @author Rickard �berg ([EMAIL PROTECTED])
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
+ * @version $Revision: 1.4 $
*/
-public class XidImpl
+class XidImpl
implements Xid, java.io.Serializable
{
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
+
+ /**
+ * Hash code of this instance. This is really a sequence number.
+ */
+ int hash;
+
+ /**
+ * Global transaction id of this instance.
+ */
byte[] globalId;
+
+ /**
+ * Branch qualifier of this instance.
+ * This identifies the branch of a transaction.
+ */
byte[] branchId;
// Static --------------------------------------------------------
-
+
+ /**
+ * A cache for the <code>getHostName()</code> function.
+ */
+ private static String hostName;
+
+ /**
+ * Return the host name of this host, followed by a slash.
+ *
+ * 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.
+ */
+ static private String getHostName()
+ {
+ if (hostName == null) {
+ try {
+ hostName = InetAddress.getLocalHost().getHostName() + "/";
+ } catch (UnknownHostException e) {
+ hostName = "localhost/";
+ }
+ }
+
+ return hostName;
+ }
+
+ /**
+ * The next transaction id to use on this host.
+ */
+ static private int nextId = 0;
+
+ /**
+ * Return a new unique transaction id to use on this host.
+ */
+ static private synchronized int getNextId()
+ {
+ return nextId++;
+ }
+
+ /**
+ * Singleton for no branch qualifier.
+ */
+ static private byte[] noBranchQualifier = new byte[0];
+
// Constructors --------------------------------------------------
- public XidImpl(byte[] globalId, byte[] branchId)
+
+ /**
+ * Create a new unique branch qualifier.
+ */
+ public XidImpl()
{
- this.globalId = globalId;
+ hash = getNextId();
+ this.globalId = getGlobalIdString().getBytes();
+ this.branchId = noBranchQualifier;
+ }
+
+ /**
+ * Create a new branch of an existing global transaction id.
+ */
+ public XidImpl(XidImpl xid, byte[] branchId)
+ {
+ hash = xid.hash;
+ this.globalId = xid.globalId;
this.branchId = branchId;
}
// Public --------------------------------------------------------
// Xid implementation --------------------------------------------
+
+ /**
+ * Return the global transaction id of this transaction.
+ */
public byte[] getGlobalTransactionId()
{
- return globalId;
+ return (byte[])globalId.clone();
}
+ /**
+ * Return the branch qualifier of this transaction.
+ */
+ public byte[] getBranchQualifier()
+ {
+ if (branchId.length == 0)
+ return branchId; // Zero length arrays are immutable.
+ else
+ return (byte[])branchId.clone();
+ }
+
+ /**
+ * Return the format identifier of this transaction.
+ *
+ * The format identifier augments the global id and specifies
+ * how the global id and branch qualifier should be interpreted.
+ */
public int getFormatId()
{
- return 1; // TODO: what should be here?
+ // The id we return here should be different from all other transaction
+ // implementations.
+ // Known IDs are:
+ // -1: Sometimes used to denote a null transaction id.
+ // 0: OSI TP (javadoc states OSI CCR, but that is a bit misleading
+ // as OSI CCR doesn't even have ACID properties. But OSI CCR and
+ // OSI TP do have the same id format.)
+ // 0xBB14: Used by JONAS
+ // 0xBB20: Used by JONAS
+ return 1;
}
-
- public byte[] getBranchQualifier()
+
+ /**
+ * Compare for equality.
+ *
+ * This checks the format id and the global transaction ID, but
+ * ignores the branch qualifier.
+ */
+ public boolean equals(Object obj)
{
- return branchId;
+ // OSH: Should we also compare the branch ID ?
+
+ if (obj != null && obj instanceof XidImpl) {
+ XidImpl other = (XidImpl)obj;
+
+ if (globalId.length != other.globalId.length)
+ return false;
+
+ for (int i = 0; i < globalId.length; ++i)
+ if (globalId[i] != other.globalId[i])
+ return false;
+
+ return true;
+ }
+ return false;
}
-
- /*
- * equals works on all the bytes of the Xid
- */
- public boolean equals(Object obj) {
-
- byte[] otherGlobalId = ((XidImpl) obj).globalId;
-
- for (int i = 0 ; i<globalId.length ; i++) {
-
- if (otherGlobalId[i] != globalId[i]) {
-
- return false;
- }
- }
-
- return true;
+
+ public int hashCode()
+ {
+ return hash;
+ }
+
+ public String toString()
+ {
+ return "XidImpl:" + getGlobalIdString();
}
+
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
+
+ private String getGlobalIdString()
+ {
+ return getHostName() + hash;
+ }
// Inner classes -------------------------------------------------
}