TopCoordinatorpublic class TopCoordinator extends CoordinatorImpl The TopCoordinator interface is our implementation of the standard
Coordinator interface that is used for top-level transactions. It allows
Resources to be registered for participation in a top-level transaction.
In addition the TopCoordinator recovery interface can be used if the
connection to a superior Coordinator is lost after a transaction is
prepared. As an instance of this class may be accessed from multiple
threads within a process, serialisation for thread-safety is necessary in
the implementation. The information managed should be reconstructible in
the case of a failure. |
Fields Summary |
---|
String | name | RegisteredResources | participants | RegisteredSyncs | synchronizations | SuperiorInfo | superInfo | NestingInfo | nestingInfo | TransactionState | tranState | CoordinatorLog | logRecord | CompletionHandler | terminator | boolean | registered | boolean | registeredSync | boolean | root | boolean | rollbackOnly | boolean | dying | boolean | temporary | int | hash | static Logger | _logger | Vector | recoveryCoordinatorList | CoordinatorSynchronizationImpl | coordSyncImpl | boolean | delegated | String | logPath | static String[] | resultName | private static Any | emptyData |
Constructors Summary |
---|
TopCoordinator()Default TopCoordinator constructor.
// No persistent reference is created in this case.
| TopCoordinator(int timeOut)Creates and initialises a new root TopCoordinator,
and returns the global identifier for the transaction.
The timeout value, if non-zero, is used
to establish a time-out for the transaction. A CoordinatorLog object is
created at this time if the log is available.
// If this execution of the process is recoverable, then create a
// CoordinatorLog object for the top-level transaction. Each of the
// implementation classes that use the CoordinatorLog have been written
// to be able to work with or without a CoordinatorLog reference.
if (Configuration.isRecoverable()) {
// get a CoordinatorLog object from the cache
// instead of instantiating a new one Arun 9/27/99
logRecord = CoordinatorLogPool.getCoordinatorLog();
} else {
logRecord = null;
}
// Allocate a new global identifier for the transaction.
tranState = new TransactionState(logRecord);
// Store information about the superior, ancestors and
// participants of the new transaction.
superInfo = new SuperiorInfo(tranState.localTID, tranState.globalTID,
null, logRecord);
// Cache the name - create a buffer and print the global XID into it.
// name = superInfo.globalTID.toString();
// Cache the hash value of the Coordinator.
hash = superInfo.globalTID.hashCode();
// Zero out the RegisteredResources, NestingInfo and RegisteredSyncs
// references. These will be created when they are required.
nestingInfo = null;
participants = null;
synchronizations = null;
// Set other instance variables.
root = true;
registered = true;
registeredSync = true;
rollbackOnly = false;
dying = false;
temporary = false;
terminator = null;
if (!tranState.setState(TransactionState.STATE_ACTIVE)) {
// Set the state of the transaction to active before making
// it visible to the TransactionManager.
LogicErrorException exc =
new LogicErrorException(
LogFormatter.getLocalizedMessage(_logger,
"jts.invalid_state_change"));
throw exc;
} else {
// Inform the RecoveryManager of the existence of this transaction.
if (!RecoveryManager.addCoordinator(tranState.globalTID,
tranState.localTID,
this,
timeOut)) {
LogicErrorException exc =
new LogicErrorException(
LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_id_already_in_use"));
throw exc;
}
}
| TopCoordinator(int timeOut, GlobalTID globalTID, Coordinator superior, boolean temporary)Creates and initialises a subordinate TopCoordinator, given the global
identifier and superior Coordinator reference, and returns the local
identifier for the transaction. The timeout value, if non-zero, is used
to establish a time-out for the subordinate transaction. The temporary
flag indicates whether the TopCoordinator was created as a temporary
ancestor.
// If this execution of the process is recoverable, then create a
// CoordinatorLog object for the top-level transaction. Each of the
// implementation classes that use the CoordinatorLog have been written
// to be able to work with or without a CoordinatorLog reference.
if (Configuration.isRecoverable()) {
// get a CoordinatorLog object from the cache
// instead of instantiating a new one Arun 9/27/99
logRecord = CoordinatorLogPool.getCoordinatorLog();
} else {
logRecord = null;
}
// Allocate a new local identifier for the transaction.
// If one cannot be allocated, raise an exception as the
// transaction cannot be started.
tranState = new TransactionState(globalTID,logRecord);
// Store information about the superior, ancestors and participants
// of the new subordinate transaction.
superInfo = new SuperiorInfo(tranState.localTID, tranState.globalTID,
superior, logRecord);
// Cache the name - create a buffer and print the global XID into it.
// name = superInfo.globalTID.toString();
// Cache the hash value of the Coordinator.
hash = superInfo.globalTID.hashCode();
// Zero out the RegisteredResources, NestingInfo and RegisteredSyncs
// references. These will be created when they are required.
nestingInfo = null;
participants = null;
synchronizations = null;
// Set other instance variables.
root = false;
registered = false;
registeredSync = false;
rollbackOnly = false;
dying = false;
this.temporary = temporary;
terminator = null;
// Set the state of the transaction to active before making it
// visible to the RecoveryManager.
if (!tranState.setState(TransactionState.STATE_ACTIVE)) {
LogicErrorException exc =
new LogicErrorException(
LogFormatter.getLocalizedMessage(_logger,
"jts.invalid_state_change"));
throw exc;
} else {
if (!RecoveryManager.addCoordinator(globalTID, tranState.localTID,
this, timeOut)) {
LogicErrorException exc =
new LogicErrorException(
LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_id_already_in_use"));
throw exc;
}
}
|
Methods Summary |
---|
synchronized boolean | addChild(CoordinatorImpl child)Adds the given Coordinator reference to the set of children of the
target TopCoordinator.
boolean result;
// Make sure the NestingInfo instance variables is set up
// before adding the child.
if (nestingInfo == null) {
nestingInfo = new NestingInfo();
}
result = nestingInfo.addChild(child);
return result;
| synchronized void | afterCompletion(Status status)Informs the TopCoordinator that the transaction has completed.
The TopCoordinator informs all Synchronization objects registered with
it that the transaction has completed. It does not need to wait for all
responses before returning.
// If the Coordinator is still active, set it to read only to prevent
// the Coordinator from actually rolling back when it is destroyed.
if (tranState.state == TransactionState.STATE_ACTIVE) {
tranState.setState(TransactionState.STATE_PREPARING);
tranState.setState(TransactionState.STATE_PREPARED_READONLY);
}
// If there are registered Synchronization objects,
// tell them the transaction has completed.
if (synchronizations != null) {
// Tell the RegisteredSyncs to distribute the after completion
// messages. If an exception occurs, just report it.
// synchronizations.distributeAfter(get_status());
synchronizations.distributeAfter(status);
}
// At this point, there is nothing left to do, so destroy ourselves
// before returning.
boolean aborted = true;
if (status == Status.StatusCommitted) {
aborted = false;
}
if (!delegated) {
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
aborted);
} else {
DelegatedRecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
aborted, logPath);
}
// memory leak fix (Ram J) - cleanup the Recovery Coordinator objs.
if (recoveryCoordinatorList != null) {
for (int i = 0; i < recoveryCoordinatorList.size(); i++) {
RecoveryCoordinatorImpl rcImpl = (RecoveryCoordinatorImpl)
recoveryCoordinatorList.elementAt(i);
rcImpl.destroy();
}
recoveryCoordinatorList = null;
}
// memory leak fix (Ram J)
// destroy the CoordinatorSynchronization object.
if (this.coordSyncImpl != null) {
this.coordSyncImpl.destroy();
}
this.synchronizations = null;
// destroy the coordinator object.
destroy();
| synchronized void | beforeCompletion()Informs the TopCoordinator that the transaction is about to complete.
The TopCoordinator informs all Synchronization objects registered with
it that the transaction is about to complete and waits for all of the
replies before this operation completes.
// First check for active children, before getting too far in.
// This is only done for the root Coordinator as for any
// others its too late.
if (root && nestingInfo != null && nestingInfo.numChildren() != 0) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.UnfinishedSubtransactions,
CompletionStatus.COMPLETED_NO);
throw exc;
}
// If there are registered Synchronization objects, tell them
// the transaction is about to prepare.
if (synchronizations != null) {
// Tell the RegisteredSyncs to distribute the before completion
// messages. If an exception is raised, then mark the transaction
// rollback-only.
if (!synchronizations.distributeBefore()) {
rollbackOnly = true;
}
}
| void | cleanUpEmpty(CoordinatorImpl parent)Cleans up an empty Coordinator.
// Roll the transaction back, ignoring any exceptions.
try {
rollback(true);
} catch( Throwable exc ) {}
| void | commit()Directs the TopCoordinator to commit the transaction.
The TopCoordinator directs all registered Resources to commit. If any
Resources raise Heuristic exceptions, the information is recorded,
and the Resources are directed to forget the transaction before the
Coordinator returns a heuristic exception to its caller.
// Until we actually distribute prepare flows, synchronize the method.
synchronized(this) {
if(_logger.isLoggable(Level.FINE))
{
_logger.logp(Level.FINE,"TopCoordinator","commit()",
"Within TopCoordinator.commit()"+"GTID is :"+
superInfo.globalTID.toString());
}
// If the TopCoordinator voted readonly,
// produce a warning and return.
if (tranState.state == TransactionState.STATE_PREPARED_READONLY) {
return;
}
// GDH
// If the TopCoordinator has already completed due to recovery
// resync thread, return. (Note there is no
// need to deal with state ROLLED_BACK here as nothing should have
// caused us to enter that state and subsequently receive a commit.
// However the opposite cannot be said to be true as presumed abort
// can cause a rollback to occur when
// replay_completion is called on a transaction that
// has gone away already.
if (tranState.state == TransactionState.STATE_COMMITTED) {
return;
}
// If the TopCoordinator is in the wrong state, return immediately.
if (!tranState.setState(TransactionState.STATE_COMMITTING)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state","commit");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "commit"});
throw new org.omg.CORBA.INTERNAL(msg);
//NotPrepared exc = new NotPrepared();
//Commented out as code is never executed
//throw exc;
}
// Release the lock before proceeding with commit.
}
// Commit all participants. If a fatal error occurs during
// this method, then the process must be ended with a fatal error.
Throwable heuristicExc = null;
if (participants != null) {
try {
participants.distributeCommit();
} catch (Throwable exc) {
if (exc instanceof HeuristicMixed ||
exc instanceof HeuristicHazard) {
heuristicExc = exc;
}
// ADDED(Ram J) percolate any system exception
// back to the caller.
if (exc instanceof INTERNAL) {
throw (INTERNAL) exc;
}
}
}
// The remainder of the method needs to be synchronized.
synchronized(this) {
// Record that objects have been told to commit.
// Set the state
if (!tranState.setState(TransactionState.STATE_COMMITTED)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state","commit");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "commit"});
throw new org.omg.CORBA.INTERNAL(msg);
}
// Clean up the TopCoordinator after a commit. In the case where
// the TopCoordinator is a root, the CoordinatorTerm object must be
// informed that the transaction has completed so that if another
// caller has committed the transaction the object normally
// responsible for terminating the transaction can take the
// appropriate action. NOTE: This may DESTROY the TopCoordinator
// object so NO INSTANCE VARIABLES should be referenced after the
// call. In the case where the TopCoordinator is a subordinate, the
// CoordinatorResource object must be informed that the transaction
// has been completed so that it can handle any subsequent requests
// for the transaction.
if (terminator != null) {
terminator.setCompleted(false, heuristicExc != null);
}
/* commented out (Ram J) for memory leak fix.
// If there are no registered Synchronization objects,
// there is nothing left to do, so get the RecoveryManager
// to forget about us, then self-destruct.
if (!root && (synchronizations == null ||
!synchronizations.involved())
) {
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
false);
destroy();
}
*/
// added (Ram J) for memory leak fix
// if subordinate, send out afterCompletion. This will
// destroy the CoordinatorSynchronization and coordinator.
if (!root) {
afterCompletion(Status.StatusCommitted);
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* NO INSTANCE VARIABLES MAY BE ACCESSED FROM THIS POINT ON. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
// If there was heuristic damage, report it.
if (heuristicExc != null) {
if (heuristicExc instanceof HeuristicMixed) {
throw (HeuristicMixed) heuristicExc;
} else {
throw (HeuristicHazard) heuristicExc;
}
}
}
| boolean | commitOnePhase()commitOnePhase
synchronized (this) {
// First check for active children, before getting too far
// into the prepare. This is only done for
// the root Coordinator as for any others it is too late.
if (root && nestingInfo != null &&
nestingInfo.numChildren() != 0) {
INVALID_TRANSACTION exc =
new INVALID_TRANSACTION(
MinorCode.UnfinishedSubtransactions,
CompletionStatus.COMPLETED_NO);
throw exc;
}
// If the Coordinator has > 1 resource return smoothly
if (participants != null && participants.numRegistered() > 1) {
// GDH COPDEF1
return false;
}
// If the TopCoordinator is in the wrong state, return immediately.
if (!tranState.
setState(TransactionState.STATE_COMMITTING_ONE_PHASE)) {
return false;
}
// Check for marked rollback-only, if we are then we can drive
// the rollback directly from the 2PC process so return false
// this will cause use to enter prepare (which will do nothing as
// it checks the same flag
// thence directly into rollback
if (rollbackOnly) {
return false;
}
int timeoutType = TimeoutManager.NO_TIMEOUT;
// In the root, there is no need for an in-doubt timeout,
// so cancel the timeout so that the transaction is
// not rolled back. Otherwise set anin-doubt timeout of 60 seconds.
if (root) {
TimeoutManager.setTimeout(superInfo.localTID,
TimeoutManager.NO_TIMEOUT,
60);
}
else {
TimeoutManager.setTimeout(superInfo.localTID,
TimeoutManager.IN_DOUBT_TIMEOUT,
60);
}
//
// Contact the resource (note: participants can exist with
// no resourcesafter recovery)
//
} // first synchronised bit ends now to allow possible callbacks.
if ((participants != null) && (participants.numRegistered() == 1)) {
Throwable heuristicExc = null;
boolean rolled_back = false;
try {
participants.commitOnePhase();
} catch (Throwable exc) {
if (exc instanceof HeuristicMixed) {
// IASRI START 4722886
// heuristicExc = exc;
// IASRI END 4722886
if (!tranState.setState(
TransactionState.
STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (1)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[]
{ "COMMIT_ONE_PHASE (1)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
// IASRI START 4722886
throw (HeuristicMixed)exc;
// IASRI END 4722886
} else if (exc instanceof HeuristicHazard) {
// IASRI START 4722886
// heuristicExc = exc;
// IASRI END 4722886
if (!tranState.setState(
TransactionState.
STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (2)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[]
{ "COMMIT_ONE_PHASE (2)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
// IASRI START 4722886
throw (HeuristicHazard)exc;
// IASRI END 4722886
} else if (exc instanceof TRANSACTION_ROLLEDBACK) {
rolled_back = true;
// GDH COPDEF2 Removed code below that changesd state to
// COMMIT_ONE_PHASE_ROLLED_BACK this was unnecessary
// as setting the rolled_back flag is picked up just below
// where the state is changed. Prior to this change we
// tried to set the state to COP_RB twice
// in a row which is an error.
} else if (exc instanceof INTERNAL) {
// ADDED (Ram J) percolate up any system exception.
throw (INTERNAL) exc;
} // end else if cascade on the exception types
// (Other exceptions are not passed back
// by RegisteredResources)
} // end of catch block for exceptions
// (GDH COPDEF1 was after if-else block below)
// Set the final state now
if (rolled_back) {
// GDH COPDEF1 Changed state movement to be via COP_RB
// even though possible to go direct to RB
// Change state in two steps - this is traced and only the
// first change would need a forced log write traditionaly
if (!tranState.setState(
TransactionState.
STATE_COMMIT_ONE_PHASE_ROLLED_BACK)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (4)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[]
{ "COMMIT_ONE_PHASE (4)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
/**
if (!tranState.setState(TransactionState.STATE_ROLLED_BACK)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (5)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "COMMIT_ONE_PHASE (5)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
**/
} else { // we commited
// GDH COPDEF1 Changed state movement to be via COP_OK
// this is needed by the state tables as the first
// state change should ideall be a forced log write.
// We do the state change in two jumps for better trace
// and to log the fact that a COP_OK represents a successful
// prepare - only the fist state changed needs a flushed write.
if (!tranState.setState(TransactionState.
STATE_COMMITTED_ONE_PHASE_OK)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (6)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "COMMIT_ONE_PHASE (6)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
/**
// Now set this coord to commited finally.
if (!tranState.setState(TransactionState.STATE_COMMITTED)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (7)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "COMMIT_ONE_PHASE (7)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
**/
} // else we did not rollback
// The remainder of the method needs to be synchronized too!
synchronized (this) {
// Clean up the TopCoordinator after a commit. In the case
// where the TopCoordinator is a root,
// the CoordinatorTerm object must be informed that
// the transaction has completed so that if another
// caller has committed the transaction the object
// normally responsible for terminating the
// transaction can take the appropriate action. NOTE: This may
// DESTROY the TopCoordinator object so NO INSTANCE VARIABLES
// should be referenced after the call.
// In the case where the TopCoordinator is a subordinate, the
// CoordinatorResource object must be informed that the
// transaction has been completed so that it can
// handle any subsequent requests for the
// transaction.
if (terminator != null) {
terminator.setCompleted(false, heuristicExc != null);
}
/* commented out (Ram J) for memory leak fix.
// If there are no registered Synchronization objects,
// there is nothing left to do, so get the RecoveryManager
// to forget about us, then self-destruct.
if (!root && (synchronizations == null ||
!synchronizations.involved())
) {
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
false);
destroy();
}
*/
// added (Ram J) for memory leak fix
// if subordinate, send out afterCompletion. This will
// destroy the CoordinatorSynchronization and coordinator.
if (!root) {
afterCompletion(Status.StatusCommitted);
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* NO INSTANCE VARIABLES MAY BE ACCESSED FROM THIS POINT ON. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
// If there was heuristic damage, report it.
if (heuristicExc != null) {
if (heuristicExc instanceof HeuristicMixed) {
throw (HeuristicMixed)heuristicExc;
} else {
throw (HeuristicHazard)heuristicExc;
}
}
// If the resource rolled back throw TRANSACTION_ROLLEDBACK
if (rolled_back) {
TRANSACTION_ROLLEDBACK exc =
new TRANSACTION_ROLLEDBACK(
0, CompletionStatus.COMPLETED_YES);
throw exc;
}
} //end of any synchronised work
// Otherwise return normally.
return true;
} else {
// GDH COPDEF1
// No resources at all - just complete state as for commited
// We do the state change in two jumps for better trace and to
// log the fact that a COP_OK represents a successful prepare,
// only the fist state changed needs a flushed write.
// (Can't turn of NTFS file caching in Java anyway but the
// intention is in that direction)
if (!tranState.
setState(TransactionState.STATE_COMMITTED_ONE_PHASE_OK)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (8)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "COMMIT_ONE_PHASE (8)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
// Now set this coord to commited finally.
/*
if (!tranState.setState(TransactionState.STATE_COMMITTED)) {
_logger.log(Level.SEVERE,"jts.transaction_wrong_state",
"COMMIT_ONE_PHASE (9)");
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.transaction_wrong_state",
new java.lang.Object[] { "COMMIT_ONE_PHASE (9)"});
throw new org.omg.CORBA.INTERNAL(msg);
}
*/
} // end of else clause if no resources
return true;
| public synchronized Control | create_subtransaction()Creates a subtransaction and returns a Control object that represents
the child transaction.
Control result = null;
// First check the state of the transaction. If it is not active,
// do not allow the subtransaction to be created.
if (tranState == null ||
tranState.state != TransactionState.STATE_ACTIVE) {
Inactive exc = new Inactive();
throw exc;
}
// Set up the sequence of ancestors to hold the single reference
// and global identifier of the top-level
// TopCoordinator as there are no ancestors.
// We do not need to make a copy of the global TID as this is done
// by the factory when it creates the child.
CoordinatorImpl[] ancestors = new CoordinatorImpl[1];
ancestors[0] = this;
// Create a new SubCoordinator, and initialise it with the given
// identifiers and ancestry. If the operation fails,
// return a NULL Control object, and
// the SubtransactionsUnavailable exception. Note that the
// ancestor sequence is not copied by the creation operation.
SubCoordinator child = null;
TerminatorImpl terminator = null;
try {
child = new SubCoordinator(superInfo.globalTID,
superInfo.localTID,
ancestors);
// Create a Terminator object, and initialise it with the
// SubCoordinator reference and a flag to indicate that it
// represents a subtransaction.
terminator = new TerminatorImpl(child,true);
// Create a Control object, and initialise it with the Terminator,
// SubCoordinator and global OMGtid.
result = new ControlImpl(terminator, child,
new GlobalTID(child.getGlobalTID()),
new Long(child.getLocalTID())
).object();
} catch (Throwable exc) {
Inactive ex2 = new Inactive();
throw ex2;
}
// If the operation succeeded, add the new child to the set
// of children. Ensure that the NestingInfo object is set up.
if (nestingInfo == null) {
nestingInfo = new NestingInfo();
}
nestingInfo.addChild(child);
return result;
| synchronized void | delegated_reconstruct(CoordinatorLog log, java.lang.String logPath)Directs the TopCoordinator to recover its state after a failure,
based on the given CoordinatorLog object for the given logpath.
If the TopCoordinator has already been defined or recovered,
the operation returns immediately. Otherwise the
TopCoordinator restores the state of its internal objects using their
recovery operations, which in turn recover their state from the
CoordinatorLog object.
// Set up instance variables.
rollbackOnly = false;
registered = false;
registeredSync = false;
root = false;
dying = false;
temporary = false;
terminator = null;
logRecord = log;
name = null;
// Zero out NestingInfo and Synchronizations references. These won't be
// needed for a recovered transaction.
nestingInfo = null;
synchronizations = null;
delegated = true;
this.logPath = logPath;
// Use the result of the TransactionState reconstruction to
// decide whether to continue with recovery of this transaction.
tranState = new TransactionState();
// int state = tranState.delegated_reconstruct(log);
int state = tranState.reconstruct(log);
if (state == TransactionState.STATE_NONE ||
state == TransactionState.STATE_COMMITTED ||
// state == TransactionState.STATE_COMMITTED_ONE_PHASE_OK ||
// state == TransactionState.STATE_COMMIT_ONE_PHASE_ROLLED_BACK ||
state == TransactionState.STATE_ROLLED_BACK) {
// If the transaction is discarded, then ensure that
// the log record is discarded.
CoordinatorLog.removeLog(log.localTID, logPath);
destroy();
} else {
// Otherwise continue with reconstruction.
participants = new RegisteredResources(this);
participants.reconstruct(log);
// Reconstruct the SuperiorInfo object. This will result in a
// call to RecoveryManager.addCoordinator (which is done
// because reconstruction of the object references in the
// SuperiorInfo requires the Coordinator to
// already be known to the RecoveryManager).
superInfo = new SuperiorInfo();
superInfo.delegated_reconstruct(log, this, logPath);
// Cache the name - create a buffer and print the
// global XID into it.
name = superInfo.globalTID.toString();
// Cache the hash value of the Coordinator.
hash = superInfo.globalTID.hashCode();
}
| void | directRegisterResource(Resource res)Registers the given Resource object with the Coordinator with no regard
for the state of the transaction or registration with the superior.
This is intended to be used during recovery to enable XA Resource
Managers to participate in resync without needing the XA Resource
objects to have persistent references.
The Resource object parameter should only refer to a local object.
// If the set has not already been created, create it now.
// Note that we do notpass the CoordinatorLog object to the
// RegisteredResources as we do not want to do anything
// with it here. Generally participants will not be null
// as this method will be called diring recovery.
if (participants == null) {
participants = new RegisteredResources(null, this);
}
// Add the reference to the set. The reference is not duplicated,
// as this operation should only be called for local Resource objects.
participants.addRes(res);
if(_logger.isLoggable(Level.FINE))
{
_logger.logp(Level.FINE,"TopCoordinator","directRegisterResource()",
"Registered resource :" + res );
}
| public synchronized void | doFinalize()Cleans up the objects state.
// Set the flag to indicate that the coordinator is being destroyed.
dying = true;
// What we do when destroyed depends on the transaction's state.
// We assume that temporary Coordinators have rolled bak at this point.
int state = TransactionState.STATE_ROLLED_BACK;
if (tranState != null && !temporary) {
state = tranState.state;
}
switch (state) {
// If the transaction is active it should be rolled back. This
// will result in the TopCoordinator self-destructing at the
// end of two-phase commit.
case TransactionState.STATE_ACTIVE :
try {
rollback(true);
} catch (Throwable exc) {}
break;
// For committed or rolled-back, we really need to destroy the
// object. Also for prepared_readonly.
case TransactionState.STATE_PREPARED_READONLY :
case TransactionState.STATE_COMMITTED :
case TransactionState.STATE_ROLLED_BACK :
case TransactionState.STATE_COMMITTED_ONE_PHASE_OK :
case TransactionState.STATE_COMMIT_ONE_PHASE_ROLLED_BACK :
/*
if (tranState != null) {
tranState.finalize();
}
*/
if (superInfo != null) {
superInfo.doFinalize();
}
/*
if (nestingInfo != null) {
nestingInfo.doFinalize();
}
if (participants != null) {
participants.finalize();
}
if (synchronizations != null) {
synchronizations.finalize();
}
*/
tranState = null;
superInfo = null;
nestingInfo = null;
participants = null;
synchronizations = null;
logRecord = null;
terminator = null;
name = null;
break;
// For any other state, the transaction is completing, so the
// TopCoordinator will eventually self-destruct. We do nothing here.
default :
break;
}
| public boolean | equals(java.lang.Object other)Determines equality of the object with the parameter.
// Do a quick check on the object references.
if( this == other ) return true;
// Obtain the global identifier for the other Coordinator.
otid_t otherTID = null;
// For local Coordinator objects which are really instances of the
// CoordinatorImpl class, get the global TID via a private method call.
if (other instanceof CoordinatorImpl) {
if (other instanceof TopCoordinator) {
otherTID = ((TopCoordinator) other).
superInfo.globalTID.realTID;
}
} else if (other instanceof org.omg.CORBA.Object) {
// For remote Coordinator objects which are instances of
// the JCoordinator class, use the getGlobalTID method remotely.
try {
JCoordinator jcoord = JCoordinatorHelper.
narrow((org.omg.CORBA.Object) other);
otherTID = jcoord.getGlobalTID();
} catch (BAD_PARAM exc) {
// For remote Coordinator objects which are not
// instances of the JCoordinator class, use the propagation
// context to compare the Coordinators. This relies on the
// availability of the propagation
// context from the target Coordinator.
try {
Coordinator coord =
CoordinatorHelper.narrow((org.omg.CORBA.Object) other);
PropagationContext pc = coord.get_txcontext();
otherTID = pc.current.otid;
} catch (BAD_PARAM ex2) {
// If the other object is not actually a Coordinator,
// then the objects are not the same.
} catch (Unavailable ex2) {
// If the other Coordinator is inactive, then there is
// nothing we can do to get the global identifier for the
// transaction, so we cannot compare the
// Coordinator objects.
INVALID_TRANSACTION ex3 = new INVALID_TRANSACTION(
MinorCode.CompareFailed,
CompletionStatus.COMPLETED_NO);
throw ex3;
}
}
}
// Compare the global identifiers.
if (otherTID != null) {
return superInfo.globalTID.equals(otherTID);
}
return false;
| public synchronized TransIdentity[] | getAncestors()Returns the sequence of ancestors of the transaction.
return null;
| public otid_t | getGlobalTID()Returns a global identifier that represents the TopCoordinator's
transaction.
This operation references only the global identifier, and so can be
implemented locally in a proxy class.
This method is currently not synchronized because that causes a deadlock
in resync. I don't think this is a problem as the global identifier is
allocated in the constructor and then never changes.
otid_t result = superInfo.globalTID.realTID;
return result;
| public GlobalTID | getGlobalTid()
return superInfo.globalTID;
| public long | getLocalTID()Returns the internal identifier for the transaction.
This method is currently not synchronized because that causes a deadlock
in resync.
long result = superInfo.localTID.longValue();
return result;
| Coordinator | getParent()Gets the parent coordinator of the transaction. As this is a top level
coordinator, a parent does not exist so NULL is returned.
Coordinator result = null;
return result;
| public int | getParticipantCount()
if (participants == null) {
return 0;
}
return participants.numRegistered();
| Coordinator | getSuperior()Gets the superior Coordinator for this transaction.
Coordinator result = superInfo.superior;
return result;
| CompletionHandler | getTerminator()Gets the object normally responsible for terminating this Coordinator.
CompletionHandler result = terminator;
return result;
| public Status | get_parent_status()Gets the local state of the transaction.
For a top-level transaction this operation is equivalent
to the get_status method.
This operation references no instance variables and so can be
implemented locally in the proxy class.
Status result = get_status();
return result;
| public Status | get_status()Returns the local status of the target transaction.
Status result = Status.StatusUnknown;
if (tranState != null) {
switch (tranState.state) {
// If active, return active or marked rollback-only
// if the flag is set.
case TransactionState.STATE_ACTIVE :
if( rollbackOnly )
result = Status.StatusMarkedRollback;
else
result = Status.StatusActive;
break;
// If prepared, (successfully or otherwise), return prepared.
// If committing return prepared (may want to block in this case).
case TransactionState.STATE_PREPARED_SUCCESS :
case TransactionState.STATE_PREPARED_FAIL :
case TransactionState.STATE_PREPARED_READONLY :
result = Status.StatusPrepared;
break;
// If we have no internal state, return that fact.
// All of these states map directly to the OMG values.
case TransactionState.STATE_NONE :
result = Status.StatusNoTransaction;
break;
case TransactionState.STATE_PREPARING :
case TransactionState.STATE_COMMITTING_ONE_PHASE :
result = Status.StatusPreparing;
break;
case TransactionState.STATE_COMMITTING :
result = Status.StatusCommitting;
break;
case TransactionState.STATE_COMMITTED :
case TransactionState.STATE_COMMITTED_ONE_PHASE_OK :
case TransactionState.STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD :
case TransactionState.STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED :
result = Status.StatusCommitted;
break;
case TransactionState.STATE_ROLLING_BACK :
case TransactionState.STATE_COMMIT_ONE_PHASE_ROLLED_BACK :
result = Status.StatusRollingBack;
break;
case TransactionState.STATE_ROLLED_BACK :
result = Status.StatusRolledBack;
break;
// Any other state, return unknown.
// GDH Including c-o-p heuristic states
default :
result = Status.StatusUnknown;
break;
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public Status | get_top_level_status()Gets the local state of the transaction.
For a top-level transaction this operation is equivalent
to the get_status method.
This operation references no instance variables and so can be
implemented locally in a proxy class.
Status result = get_status();
return result;
| public java.lang.String | get_transaction_name()removed synchronization at method level since only tranState requires
locking
String result = null;
if (tranState != null) {
if (name == null)
name = superInfo.globalTID.toString();
result = new String(name);
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized PropagationContext | get_txcontext()Creates a PropagationContext which contains the information which would
normally be passed implicitly via the CosTSPropagation interfaces.
// First check the state of the transaction. If it is not active,
// do not allow the registration.
if (tranState == null ||
tranState.state != TransactionState.STATE_ACTIVE ||
rollbackOnly ) {
Unavailable exc = new Unavailable();
throw exc;
}
// Work out the timeout value to pass, if any.
// Note that only top-level transactions have timeouts.
// We do not check for timeouts if the Coordinator is remote.
// If the Coordinator does not have a timeout defined, the
// TimeoutManager will return a negative value.
// If the transaction has timed out, the value will be zero.
long timeLeft = TimeoutManager.timeLeft(superInfo.localTID);
int timeout = 0;
if (timeLeft > 0) {
timeout = (int)timeLeft/1000;
} else if (timeLeft == 0) {
// If the timeout has expired, then do not return a context,
// but roll the transaction back and
// throw the TRANSACTION_ROLLEDBACK exception.
TimeoutManager.timeoutCoordinator(superInfo.localTID,
TimeoutManager.ACTIVE_TIMEOUT);
TRANSACTION_ROLLEDBACK exc = new TRANSACTION_ROLLEDBACK(
0, CompletionStatus.COMPLETED_NO);
throw exc;
}
// Fill in the context with the current transaction information,
// and the ancestor information.
TransIdentity current = new TransIdentity(this.object(),
null,
superInfo.globalTID.realTID);
// Ensure that the implementation specific data is filled with a value.
if (emptyData == null){
emptyData = Configuration.getORB().create_any();
emptyData.insert_boolean(false);
}
PropagationContext result = new PropagationContext(
timeout, current,
new TransIdentity[0], emptyData);
if(_logger.isLoggable(Level.FINEST))
{
_logger.logp(Level.FINEST,"TopCoordinator","get_txcontext()",
"Obtained PropagationContext"+"GTID is: "+
superInfo.globalTID.toString());
}
return result;
| synchronized boolean | hasRegistered()Checks whether the TopCoordinator has registered with its superior.
boolean result = registered || registeredSync;
return result;
| public int | hashCode()Returns a hash code for the object.
This very basic method is used by the trace facility and
should not call any method which is traced.
if (hash == 0 && superInfo != null && superInfo.globalTID != null) {
hash = superInfo.globalTID.hashCode();
}
return hash;
| public synchronized int | hash_top_level_tran()Returns a hash value based on the top-level ancestor of the transaction
associated with the target object.
This operation references only the global TID, and so can be
implemented locally in a proxy class.
int result = hash;
if (tranState == null) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public int | hash_transaction()removed synchronization at method level since only tranState requires
locking
int result = hash;
if (tranState == null) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| synchronized boolean | isActive()Checks whether the TopCoordinator is active.
boolean result = (tranState.state == TransactionState.STATE_ACTIVE);
return result;
| public synchronized boolean | isRollbackOnly()Checks whether the TopCoordinator is marked rollback-only.
boolean result = rollbackOnly;
return result;
| public boolean | is_ancestor_transaction(Coordinator other)Determines whether the target TopCoordinator is an ancestor
of the given Coordinator.
For a top-level transaction returns TRUE if and only if
the transaction associated with the target object is an ancestor
of the transaction associated with the parameter object.
This operation references no instance variables and so can be
implemented locally in a proxy class.
boolean result = false;
if (tranState != null) {
result = other.is_descendant_transaction(this.object());
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public boolean | is_descendant_transaction(Coordinator other)Determines whether the target TopCoordinator is a descendant
of the given Coordinator.
For a top-level transaction returns TRUE if and only if
the transaction associated with the target object is the same as
the transaction associated with the parameter object.
This operation references no instance variables and so can be
implemented locally in a proxy class.
boolean result = false;
if (tranState != null) {
result = is_same_transaction(other);
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public boolean | is_related_transaction(Coordinator other)Determines whether the target TopCoordinator is related to
the given Coordinator (i.e. is a member of the same transaction family).
For a top-level transaction returns TRUE if and only if
the transaction associated with the parameter object is a
descendant of the transaction associated with the target object.
This operation references no instance variables and so can be
implemented locally in a proxy class.
boolean result = false;
if (tranState != null) {
result = other.is_descendant_transaction(this.object());
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public boolean | is_root_transaction()Determines whether this TopCoordinator is the root TopCoordinator.
the given Coordinator (i.e. is a member of the same transaction family).
For a root transaction, this method returns TRUE. Otherwise it
returns FALSE.
boolean result = root;
return result;
| public boolean | is_same_transaction(Coordinator other)removed synchronization at method level since only tranState requires
locking
boolean result = false;
// Get the names of the two transactions and compare them.
if (tranState != null) {
if (name == null)
name = superInfo.globalTID.toString();
result = name.equals(other.get_transaction_name());
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public boolean | is_top_level_transaction()Determines whether the target TopCoordinator represents a top-level
(non-nested) transaction.
For a top-level transaction returns TRUE.
This operation references no instance variables and so can be
implemented locally in a proxy class.
boolean result = true;
return result;
| Vote | prepare()Directs the TopCoordinator to prepare to commit.
The TopCoordinator directs all registered Resources to prepare, and
returns the result to the caller. The TopCoordinator must
guarantee that each Resource object registered with it receives
at most one prepare request (This includes the case where the
Recoverable Server registers the same Resource twice).
Vote result = Vote.VoteRollback;
// Until we actually distribute prepare flows, synchronize the method.
synchronized(this) {
// First check for active children, before getting too far into
// the prepare. This is only done for the root Coordinator as for
// any others it is too late.
if (root && nestingInfo != null &&
nestingInfo.numChildren() != 0) {
INVALID_TRANSACTION exc =
new INVALID_TRANSACTION(
MinorCode.UnfinishedSubtransactions,
CompletionStatus.COMPLETED_NO);
throw exc;
}
// If the TopCoordinator is in the wrong state, return immediately.
if (!tranState.setState(TransactionState.STATE_PREPARING)) {
return Vote.VoteRollback;
}
// Check for marked rollback-only.
if (rollbackOnly) {
// Try to set the state to prepared fail.
if (!tranState.
setState(TransactionState.STATE_PREPARED_FAIL)) {
// empty
}
return Vote.VoteRollback;
}
// Release the lock prior to distributing the prepare operations.
// This is to allow the transaction to be marked rollback-only
// (by resources)
} // synchronised bit
// Get the RegisteredResources to distribute prepare operations.
// If a heuristic exception is thrown, then set the state
// to rolled back.
Vote overallResult = Vote.VoteReadOnly;
Throwable heuristicExc = null;
if (participants != null) {
try {
overallResult = participants.distributePrepare();
if (overallResult == Vote.VoteCommit ||
overallResult == Vote.VoteReadOnly) {
if (participants.getLAOResource() != null) {
if (logRecord == null && Configuration.isDBLoggingEnabled()) {
if (!(LogDBHelper.getInstance().addRecord(
tranState.localTID.longValue(),
tranState.globalTID.toTidBytes()))) {
overallResult = Vote.VoteRollback;
}
}
if (overallResult != Vote.VoteRollback) {
participants.getLAOResource().commit();
}
}
}
} catch (Throwable exc) {
// If a heuristic exception was thrown, change the state of
// the Coordinator to rolled back and clean up before throwing
// the exception to the caller.
if (exc instanceof HeuristicMixed ||
exc instanceof HeuristicHazard) {
if (!tranState.
setState(TransactionState.STATE_ROLLED_BACK)) {
// empty
}
/* comented out (Ram J) for memory leak fix.
// Discard the Coordinator if there is no after completion.
// Root Coordinators and those with registered
// Synchronization objects always have after completion
// flows. Otherwise remove the RecoveryManager associations
// and destroy the Coordinator.
if (!root &&
(synchronizations == null ||
!synchronizations.involved())
) {
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
false);
destroy();
}
*/
// added (Ram J) for memory leak fix
// if subordinate, send out afterCompletion. This will
// destroy the CoordinatorSynchronization and coordinator.
if (!root) {
afterCompletion(Status.StatusRolledBack);
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* NO INSTANCE VARIABLES MAY BE */
/* ACCESSED FROM THIS POINT ON.*/
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
if (heuristicExc instanceof HeuristicMixed) {
throw (HeuristicMixed)heuristicExc;
} else {
throw (HeuristicHazard)heuristicExc;
}
}
// For any other exception, change the vote to rollback
overallResult = Vote.VoteRollback;
} // catch for except
} // if block
// The remainder of the method needs to be synchronized.
synchronized(this) {
// If the transaction has been marked rollback-only during
// this process, change the vote.
if (rollbackOnly) {
overallResult = Vote.VoteRollback;
}
// Set the state depending on the result of the prepare operation.
// For read-only, we can throw away the Coordinator if there are no
// synchronization objects, otherwise the Coordinator will
// be destroyed after synchronization.
// Set the state to prepared, read-only.
if (overallResult == Vote.VoteReadOnly) {
if (!tranState.
setState(TransactionState.STATE_PREPARED_READONLY)) {
overallResult = Vote.VoteRollback;
}
/* commented out (Ram J) for memory leak fix.
// When voting readonly, discard the Coordinator if there is
// no after completion. Root Coordinators and those with
// registered Synchronization objects always have after
// completion flows. Otherwise remove the
// RecoveryManager associations and destroy the Coordinator.
if (!root &&
(synchronizations == null ||
!synchronizations.involved())
) {
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
false);
destroy();
}
*/
// added (Ram J) for memory leak fix
// if subordinate, send out afterCompletion. This will
// destroy the CoordinatorSynchronization and coordinator.
// Note: the after_completion status is "unknown", since
// at this point we do not know if the tx completed sucessfully,
// as the sync objects are being called before the root
// actually completes.
if (!root) {
afterCompletion(Status.StatusUnknown);
}
} else if (overallResult == Vote.VoteCommit) {
// For commit, change any active timeout and change the state.
int timeoutType = TimeoutManager.NO_TIMEOUT;
// In the root, there is no need for an in-doubt timeout,
// so cancel the timeout so that the transaction is
// not rolled back. Otherwise set an
// in-doubt timeout of 60 seconds.
if (!root) {
timeoutType = TimeoutManager.IN_DOUBT_TIMEOUT;
}
TimeoutManager.setTimeout(superInfo.localTID, timeoutType, 60);
// Set the state to prepared_success.
if (!tranState.
setState(TransactionState.STATE_PREPARED_SUCCESS)) {
overallResult = Vote.VoteRollback;
}
} else {
// By default, assume rollback. We do not need to cancel
// the timeout as it does not matter
// if the transaction is subsequently rolled back.
if (!tranState.
setState(TransactionState.STATE_PREPARED_FAIL)) {
overallResult = Vote.VoteRollback;
}
}
}
return overallResult;
| synchronized void | reconstruct(CoordinatorLog log)Directs the TopCoordinator to recover its state after a failure,
based on the given CoordinatorLog object.
If the TopCoordinator has already been defined or recovered,
the operation returns immediately. Otherwise the
TopCoordinator restores the state of its internal objects using their
recovery operations, which in turn recover their state from the
CoordinatorLog object.
// Set up instance variables.
rollbackOnly = false;
registered = false;
registeredSync = false;
root = false;
dying = false;
temporary = false;
terminator = null;
logRecord = log;
name = null;
// Zero out NestingInfo and Synchronizations references. These won't be
// needed for a recovered transaction.
nestingInfo = null;
synchronizations = null;
// Use the result of the TransactionState reconstruction to
// decide whether to continue with recovery of this transaction.
tranState = new TransactionState();
int state = tranState.reconstruct(log);
if (state == TransactionState.STATE_NONE ||
state == TransactionState.STATE_COMMITTED ||
// state == TransactionState.STATE_COMMITTED_ONE_PHASE_OK ||
// state == TransactionState.STATE_COMMIT_ONE_PHASE_ROLLED_BACK ||
state == TransactionState.STATE_ROLLED_BACK) {
// If the transaction is discarded, then ensure that
// the log record is discarded.
CoordinatorLog.removeLog(log.localTID);
destroy();
} else {
// Otherwise continue with reconstruction.
participants = new RegisteredResources(this);
participants.reconstruct(log);
// Reconstruct the SuperiorInfo object. This will result in a
// call to RecoveryManager.addCoordinator (which is done
// because reconstruction of the object references in the
// SuperiorInfo requires the Coordinator to
// already be known to the RecoveryManager).
superInfo = new SuperiorInfo();
superInfo.reconstruct(log, this);
// Cache the name - create a buffer and print the
// global XID into it.
name = superInfo.globalTID.toString();
// Cache the hash value of the Coordinator.
hash = superInfo.globalTID.hashCode();
}
| synchronized Status | recover(boolean[] isRoot)Directs the TopCoordinator to perform recovery actions based on its
reconstructed state after a failure, or after an in-doubt timeout has
occurred.
This method is called by the RecoveryManager during recovery, in
which case there is no terminator object, or during normal operation
if the transaction commit retry interval has been
exceeded for the transaction.
If this method is called more times than the retry limit specified in
COMMITRETRY, then the global outcome of the transaction is taken from
the value of HEURISTICDIRECTION.
Status result;
// Determine the global outcome using the transactions state for a root
// Coordinator, or the RecoveryCoordinator for a subordinate.
if (superInfo.recovery != null) {
// For a subordinate, first check whether the global
// outcome is known locally.
// GDH COP For the commit_one_phase operations we need to do the
// following ultimately. However for all c-o-p operations
// We know that the CLIENT/Superior chose to COMMIT.
// Also for all c-o-p operations that are 'past tense'
// the direction (commit or rolled back) is not really
// important as we are using c-o-p for single resources
// not last agent in CORBA CosTransactions.
//
// For clarity, all c-o-p states return a commited direction,
// This is counter intuative but logicaly correct (unimportant)
// even for COMMIT_ONE_PHASE_ROLLED_BACK.
// A well behaved resource will not contact us in any of the
// 'past tense' c-o-p states anyway as they have already returned
// from a c-o-p op and can expect no further flows
// (apart from forget perhaps).
// When it comes to real resource flows we must be careful to
// cause the following actions based on state:
//
// STATE_COMMITTING_ONE_PHASE
// (We only ever enter this state if we have one resource
// even if the c-o-p method was called on our CoordinatorResource)
// The transaction was partway through a commit_one_phase
// operation when the server failed.
// So the commit_one_phase needs to be called again.
// STATE COMMITTED_ONE_PHASE
// STATE COMMITTED_ONE_PHASE_ROLLEDBACK
// The transaction had just completed a commit_one_phase operation.
// Therefore all of the work for the downstream part of the
// transaction is over. The only work to do is to possibly report
// outcome to superior.
// STATE COMMIT_ONE_PHASE_HEURISTIC_MIXED
// STATE COMMIT_ONE_PHASE_HEURISTIC_HAZARD
// Part of the tree has made a heuristic decision. The forget
// message must flow to all subordinate coordinators to allow them
// to end.
switch (tranState.state) {
// GDH Due to the possibility of recovery being attempted
// on more than one thread we must cover the case where
// the transaction has actually COMMITTED already.
case TransactionState.STATE_COMMITTED :
// GDH (added)
case TransactionState.STATE_COMMITTED_ONE_PHASE_OK :
// GDH (added)
case TransactionState.STATE_COMMITTING_ONE_PHASE :
// GDH (added)
case TransactionState.STATE_COMMIT_ONE_PHASE_ROLLED_BACK :
// GDH (added)
case TransactionState.STATE_COMMITTING :
result = Status.StatusCommitted;
break;
// GDH Due to the possibility of recovery being attempted
// on more than one thread we must cover the case where
// the transaction has actually ROLLED_BACK already.
case TransactionState.STATE_ROLLED_BACK : // GDH (added)
case TransactionState.STATE_ROLLING_BACK :
// GDH Note we do not need C-O-P_ROLLED_BACK Here as the actual
// resource rolling back will be done already so it's academic.
result = Status.StatusRolledBack;
break;
// For a subordinate, the replay_completion method is invoked on
// the superior's RecoveryCoordinator. We may need to create a
// CoordinatorResource object to give to the superior in the case
// where we are in recovery. If the number of times
// the replay_completion has bee retried is greater than the value
// specified by COMMITRETRY, then HEURISTICDIRECTION is used
// to determine the transaction outcome.
default :
boolean attemptRetry = true;
// String commitRetryVar;
// int commitRetries = 0;
// If COMMITRETRY is not set, then retry is infinite.
// Otherwise check that
// the current number of retries is less than the limit.
/**
commitRetryVar = Configuration.
getPropertyValue(Configuration.COMMIT_RETRY);
if (commitRetryVar != null) {
try {
commitRetries = Integer.parseInt(commitRetryVar);
} catch( NumberFormatException exc ) {}
if (superInfo.resyncRetries() >= commitRetries) {
attemptRetry = false;
}
}
**/
int commitRetries = Configuration.getRetries();
if (commitRetries >= 0 && (superInfo.resyncRetries() >= commitRetries))
attemptRetry = false;
if (!attemptRetry) {
// If we are not to attempt a retry of the
// replay_completion method, then the HEURISTICDIRECTION
// environment variable is used to get the global outcome.
String heuristicVar;
boolean commitTransaction = false;
result = Status.StatusRolledBack;
heuristicVar =
Configuration.getPropertyValue(
Configuration.HEURISTIC_DIRECTION);
if (heuristicVar != null) {
commitTransaction = (heuristicVar.charAt(0) == '1");
}
if (commitTransaction) {
result = Status.StatusCommitted;
}
} else {
// Otherwise, use the RecoveryCoordinator to get
// the global outcome. Get the global outcome
// from the superior's RecoveryCoordinator.
try {
if(_logger.isLoggable(Level.FINE))
{
_logger.logp(Level.FINE,"TopCoordinator","recover",
"Before invoking replay_completion on Superior Coordinator");
}
if (!delegated) {
result = superInfo.recovery.
replay_completion(superInfo.resource);
} else {
result = ((RecoveryCoordinatorImpl)(superInfo.recovery)).
replay_completion(superInfo.resource, logPath);
}
// GDH
// If the global result is returned as COMMITTING we
// know the outcome of the global transaction
// is COMMITTED.
if (result == Status.StatusCommitting) {
result = Status.StatusCommitted;
}
} catch (Throwable exc) {
// If the exception is neither TRANSIENT or
// COMM_FAILURE, it isunexpected, so display a message
// and assume that the transaction has rolled back.
if (!(exc instanceof COMM_FAILURE) &&
!(exc instanceof TRANSIENT)) {
result = Status.StatusRolledBack;
} else {
// For TRANSIENT or COMM_FAILURE, the outcome
// is unknown.
result = Status.StatusUnknown;
}
}
}
break;
}
// Clear the root Coordinator flag to indicate that
// this is not the root.
root = false;
} else {
// For a top-level Coordinator, we will generally only
// recover in the case where we have successfully prepared.
// If the state is not prepared_success,
// then assume it is rollback.
if (tranState.state == TransactionState.STATE_PREPARED_SUCCESS) {
result = Status.StatusCommitted;
} else {
result = Status.StatusRolledBack;
}
// Set the root Coordinator flag to indicate that this is the root.
root = true;
}
isRoot[0] = root;
return result;
| public synchronized RecoveryCoordinator | register_resource(Resource res)Enables a Resource to be registered as a participant in the completion
of the top-level transaction represented by the TopCoordinator.
If the TopCoordinator is a subordinate, and has not registered with its
superior, it creates a CoordinatorResource and registers it. The
RecoveryCoordinator that is returned is stored in the SuperiorInfo.
RecoveryCoordinator result = null;
// First check the state of the transaction. If it is not active,
// do not allow the registration.
if (tranState == null || tranState.state !=
TransactionState.STATE_ACTIVE) {
Inactive exc = new Inactive();
throw exc;
}
// Check whether the transaction has been marked rollback-only.
if (rollbackOnly) {
TRANSACTION_ROLLEDBACK exc =
new TRANSACTION_ROLLEDBACK(0, CompletionStatus.COMPLETED_NO);
throw exc;
}
// If not previously registered, a CoordinatorResource object must be
// registered with our superior. Note that root TopCoordinators are
// created with the registration flag set, so we do not need to
// check whether we are the root TopCoordinator here.
if (!registered) {
// Initialise the CoordinatorResource with the local id,
// our reference, and a flag to indicate that it does not
// represent a subtransaction.
CoordinatorResourceImpl cImpl =
new CoordinatorResourceImpl(superInfo.globalTID, this, false);
try {
// Register the CoordinatorResource with the superior
// Coordinator, and store the resulting RecoveryCoordinator
// reference.
CoordinatorResource cRes = cImpl.object();
RecoveryCoordinator superRecovery =
superInfo.superior.register_resource(cRes);
if (!(superRecovery instanceof TxInflowRecoveryCoordinator))
superInfo.setRecovery(superRecovery);
superInfo.setResource(cRes);
registered = true;
if(_logger.isLoggable(Level.FINEST))
{
_logger.logp(Level.FINEST,"TopCoordinator","register_resource()",
"CoordinatorResource " + cImpl +
" has been registered with (Root)TopCoordinator"+
superInfo.globalTID.toString());
}
} catch (Exception exc) {
// If an exception was raised, do not store the
// RecoveryCoordinator or set the registration flag.
// Throw an internal exception.
cImpl.destroy();
if (exc instanceof OBJECT_NOT_EXIST) {
// If the exception is a system exception, then allow it
// to percolate to the caller.
TRANSACTION_ROLLEDBACK ex2 =
new TRANSACTION_ROLLEDBACK(
0, CompletionStatus.COMPLETED_NO);
ex2.initCause(exc);
throw ex2;
}
if (exc instanceof Inactive) {
throw (Inactive)exc;
}
if (exc instanceof SystemException) {
throw (SystemException)exc;
}
// Otherwise throw an internal exception.
INTERNAL ex2 = new INTERNAL(MinorCode.NotRegistered,
CompletionStatus.COMPLETED_NO);
ex2.initCause(exc);
throw ex2;
}
}
// If the set has not already been created, create it now.
if (participants == null) {
participants = new RegisteredResources(logRecord, this);
}
// Add a duplicate of the reference to the set. This is done
// because if the registration is for a remote object, the proxy
// will be freed when the registration request returns.
// COMMENT(Ram J) if the res object is a local servant, there is
// no proxy involved. Also, the instanceof operator could be replaced
// by a is_local() method if this class implements the CORBA local
// object contract.
int numRes = 0;
if (res instanceof OTSResourceImpl) {
numRes = participants.addRes(res);
if(_logger.isLoggable(Level.FINEST))
{
_logger.logp(Level.FINEST,"TopCoordinator","register_resource()",
"OTSResource " + res +" has been registered"+"GTID is:"+
superInfo.globalTID.toString());
}
} else {
numRes = participants.addRes((Resource)res._duplicate());
}
temporary = false;
// Create, initialise and return a RecoveryCoordinator
// object to the caller.
// COMMENT(Ram J) a RecoveryCoordinator object need not be
// created for local resources.
if (!(res instanceof OTSResourceImpl)) {
RecoveryCoordinatorImpl rcImpl = null;
try {
rcImpl = new RecoveryCoordinatorImpl(
superInfo.globalTID, numRes);
result = rcImpl.object();
} catch (Exception exc) {
// If the RecoveryCoordinator could not be created,
// report the exception.
INTERNAL ex2 = new INTERNAL(MinorCode.RecCoordCreateFailed,
CompletionStatus.COMPLETED_NO);
throw ex2;
}
// ADD(Ram J) memory leak fix. All Recovery Coordinators need
// to be cleanedup when the transaction completes.
if (recoveryCoordinatorList == null) {
recoveryCoordinatorList = new Vector();
}
recoveryCoordinatorList.add(rcImpl);
}
return result;
| public synchronized void | register_subtran_aware(SubtransactionAwareResource sares)Enables a SubtransactionAwareResource to be registered as a
participant in the completion of a subtransaction.
For a top-level transaction this raises the NotSubtransaction exception.
NotSubtransaction exc = new NotSubtransaction();
throw exc;
| public synchronized void | register_synchronization(Synchronization sync)Informs the TopCoordinator that the given object requires
synchronization before and after completion of the transaction.
If possible, a CoordinatorSync object is registered
with the superior Coordinator. Otherwise this
Coordinator becomes the root of a sub-tree for
synchronization.
// First check the state of the transaction. If it is not active,
// do not allow the registration.
if (tranState == null ||
tranState.state != TransactionState.STATE_ACTIVE) {
Inactive exc = new Inactive();
throw exc;
}
// If not previously registered, a CoordinatorSync object must be
// registered with our superior. Note that root TopCoordinators
// are created with the registration flag set, so we do not need to
// check whether we are the root TopCoordinator here.
if (!registeredSync) {
// Initialise the CoordinatorSync with the local id, our reference,
// and a flag to indicate that does not represent a subtransaction.
CoordinatorSynchronizationImpl sImpl =
new CoordinatorSynchronizationImpl(this);
// Register the CoordinatorSync with the superior CoordinatorImpl.
try {
Synchronization subSync = sImpl.object();
superInfo.superior.register_synchronization(subSync);
registeredSync = true;
// added (Ram J) for memory leak fix.
this.coordSyncImpl = sImpl;
if(_logger.isLoggable(Level.FINER))
{
_logger.logp(Level.FINER,"TopCoordinator",
"register_synchronization()",
"CoordinatorSynchronizationImpl :" + sImpl +
" has been registered with (Root)TopCoordinator"+
"GTID is: "+ superInfo.globalTID.toString());
}
} catch (Exception exc) {
// If an exception was raised, dont set the registration flag.
sImpl.destroy();
// If the exception is a system exception, then allow it
// to percolate to the caller.
if (exc instanceof OBJECT_NOT_EXIST) {
TRANSACTION_ROLLEDBACK ex2 =
new TRANSACTION_ROLLEDBACK(
0, CompletionStatus.COMPLETED_NO);
throw ex2;
}
if (exc instanceof Inactive) {
throw (Inactive)exc;
}
if (exc instanceof SystemException) {
throw (SystemException) exc;
}
// Otherwise throw an internal exception.
INTERNAL ex2 = new INTERNAL(MinorCode.NotRegistered,
CompletionStatus.COMPLETED_NO);
throw ex2;
}
}
// Make sure the RegisteredSyncs instance variable has been set up.
if (synchronizations == null) {
synchronizations = new RegisteredSyncs();
}
// Add a duplicate of the reference to the set. This is done
// because if the registration is for a remote object,
// the proxy will be freed
// when the registration request returns.
// COMMENT(Ram J) if the sync object is a local servant, there is
// no proxy involved. Also the instanceof operator could be replaced
// by a is_local() method if this class implements the CORBA local
// object contract.
if (sync instanceof com.sun.jts.jta.SynchronizationImpl) {
synchronizations.addSync(sync);
if(_logger.isLoggable(Level.FINER))
{
_logger.logp(Level.FINER,"TopCoordinator",
"register_synchronization()",
"SynchronizationImpl :" + sync +
" has been registeredwith TopCoordinator :"+
"GTID is : "+ superInfo.globalTID.toString().toString());
}
} else {
synchronizations.addSync((Synchronization) sync._duplicate());
}
temporary = false;
| synchronized boolean | removeChild(CoordinatorImpl child)Removes the given Coordinator from the set of children of the
target TopCoordinator.
If the TopCoordinator is a temporary ancestor, and has no
recoverable state after the child is removed, it destroys itself.
boolean result = false;
// Remove the child from the set of children. If the NestingInfo
// instance variable has not been set up, then the child cannot
// be removed.
if (nestingInfo != null) {
result = nestingInfo.removeChild(child);
}
// If the removal results in an empty, temporary Coordinator, then this
// Coordinator must be cleaned up. The RecoveryManager is called to
// clean up the transaction.
if (temporary && !registered &&
!(participants != null && participants.involved()) &&
!(synchronizations != null && synchronizations.involved()) &&
!(nestingInfo != null && nestingInfo.numChildren() > 0)) {
cleanUpEmpty(null);
}
return result;
| synchronized CoordinatorImpl | replyAction(int[] action)Indicates that a method reply is being sent and requests
the TopCoordinator's action.
If the Coordinator has active children, which are not
registered with their superior (includes root Coordinators)
then this method returns activeChildren.
If it has already been registered, the method returns doNothing.
Otherwise the TopCoordinator returns forgetMe.
CoordinatorImpl result = null;
action[0] = CoordinatorImpl.doNothing;
// If this Coordinator is not a root, and there are active children,
// report that fact to the caller. If the NestingInfo instance variable
// has not been set up, there are no children.
if (!root && nestingInfo != null && nestingInfo.replyCheck()) {
action[0] = CoordinatorImpl.activeChildren;
// If there are no active children, then check whether this
// transaction needs to be destroyed, or registered on reply.
} else {
// If there are participants, and we have not registered,
// raise an exception.
if (!registered) {
if (participants != null && participants.involved()) {
INTERNAL ex2 = new INTERNAL(MinorCode.NotRegistered,
CompletionStatus.COMPLETED_NO);
throw ex2;
} else if (!registeredSync) {
action[0] = forgetMe;
}
}
// If there are synchronization objects, and we have not
// registered, raise an exception.
if (!registeredSync) {
if (synchronizations != null && synchronizations.involved()) {
INTERNAL ex2 = new INTERNAL(MinorCode.NotRegistered,
CompletionStatus.COMPLETED_NO);
throw ex2;
} else if (action[0] == doNothing && !registered) {
// If we are not registered, and have no participants,
// we have no reason to exist, so tell the caller to
// forget about us. The TransactionManager will take
// care of cleaning everything else up when
// it receives the forgetMe response.
action[0] = forgetMe;
}
}
}
// Default action is do nothing when we are registered.
result = null;
return result;
| void | rollback(boolean force)Directs the TopCoordinator to roll back the transaction.
The TopCoordinator directs all registered Resources to rollback.
If any Resources raise Heuristic exceptions,
the information is recorded, and the Resources are directed
to forget the transaction before the
Coordinator returns a heuristic exception to its caller.
// Until we actually distribute prepare flows, synchronize the method.
synchronized(this){
if(_logger.isLoggable(Level.FINE))
{
_logger.logp(Level.FINE,"TopCoordinator","rollback()",
"Within TopCoordinator.rollback() :"+"GTID is : "+
superInfo.globalTID.toString());
}
// If the transaction has already been rolled back, just return.
if (tranState == null) {
return;
}
// GDH
// If the TopCoordinator has already completed (eg due to
// recovery resync thread and this is now running on
// the 'main' one) we can safely ignore the error
if (tranState.state == TransactionState.STATE_ROLLED_BACK) {
return;
}
// GDH
// The state could even be commited, which can be OK if it was
// committed, and thus completed, when the recovery thread asked
// the superior about the txn. The superior would
// no longer had any knowledge of it. In this case, due to presumed
// abort, the recovery manager would then
// now default to aborting it.
// In this case if the TopCoordinator has committed already
// we should also just return ignoring the error.
if (tranState.state == TransactionState.STATE_COMMITTED) {
return;
}
// If this is not a forced rollback and the coordinator
// has prepared or is in an inappropriate state, do not continue
// and return FALSE.
if (!force && ((tranState.state ==
TransactionState.STATE_PREPARED_SUCCESS) ||
(!tranState.setState(
TransactionState.STATE_ROLLING_BACK))
)) {
return;
}
// We do not care about invalid state changes as we are
// rolling back anyway. If the TopCoordinator is
// temporary, we do not change state as this would
// cause a log force in a subordinate, which is not required.
if( !temporary &&
!tranState.setState(TransactionState.STATE_ROLLING_BACK)) {
// empty
}
// Rollback outstanding children. If the NestingInfo instance
// variable has not been created, there are no
// children to rollback.
if (nestingInfo != null) {
nestingInfo.rollbackFamily();
}
// Release the lock before proceeding with rollback.
}
// Roll back all participants. If a fatal error occurs during
// this method, then the process must be ended with a fatal error.
Throwable heuristicExc = null;
if (participants != null) {
try {
participants.distributeRollback(false);
} catch(Throwable exc) {
if (exc instanceof HeuristicMixed ||
exc instanceof HeuristicHazard) {
heuristicExc = exc;
}
// ADDED (Ram J) percolate up any system exception.
if (exc instanceof INTERNAL) {
throw (INTERNAL) exc;
}
}
}
// The remainder of the method needs to be synchronized.
synchronized(this) {
// Set the state. Only bother doing this if the coordinator
// is not temporary.
if (!temporary &&
!tranState.setState(TransactionState.STATE_ROLLED_BACK)) {
// empty
}
// Clean up the TopCoordinator after a rollback.
// In the case where the TopCoordinator is a root,
// the CoordinatorTerm object must be informed that the transaction
// has completed so that if another caller has rolled back
// the transaction (time-out for example) the object normally
// responsible for terminating the transaction can take the
// appropriate action. NOTE: This may DESTROY
// the TopCoordinator object so NO INSTANCE VARIABLES
// should be referenced after the call. In the case where
// the TopCoordinator is a subordinate, the CoordinatorResource
// object must be informed that the transaction has been
// completed so that it can handle any subsequent requests for the
// transaction.
if (terminator != null) {
terminator.setCompleted(true, heuristicExc != null);
}
/* commented out (Ram J) for memory leak fix.
// If there are no registered Synchronization objects, there is
// nothing left to do, so get the RecoveryManager to forget
// about us, then self-destruct.
if (!root && (synchronizations == null ||
!synchronizations.involved())
) {
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID,
true);
if (!dying) {
destroy();
}
}
*/
// added (Ram J) for memory leak fix
// if subordinate, send out afterCompletion. This will
// destroy the CoordinatorSynchronization and coordinator.
if (!root) {
afterCompletion(Status.StatusRolledBack);
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* NO INSTANCE VARIABLES MAY BE ACCESSED FROM THIS POINT ON. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
// If there was heuristic damage, report it.
if (heuristicExc != null) {
if (heuristicExc instanceof HeuristicMixed) {
throw (HeuristicMixed) heuristicExc;
} else {
throw (HeuristicHazard) heuristicExc;
}
}
}
// Otherwise return normally.
| public void | rollback_only()Ensures that the transaction represented by the target TopCoordinator
cannot be committed.
if (tranState == null ||
tranState.state != TransactionState.STATE_ACTIVE) {
Inactive exc = new Inactive();
throw exc;
} else {
// Set the rollback-only flag.
rollbackOnly = true;
}
| synchronized java.lang.Long | setPermanent()Marks the TopCoordinator as permanent.
Long result = superInfo.localTID;
temporary = false;
return result;
| synchronized void | setTerminator(CompletionHandler term)Informs the TopCoordinator of the identity of the
object that is normally responsible for directing
it through termination. The CoordinatorTerm /
CoordinatorResource object is informed by the Coordinator when the
transaction aborts so that they can cope with asynchronous aborts.
terminator = term;
|
|