SubCoordinatorpublic class SubCoordinator extends CoordinatorImpl The SubCoordinator interface is our implementation of the standard
Coordinator interface that is used for subtransactions. It allows
SubtransactionAwareResources to be registered for participation in a
subtransaction. 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 does not need to be
reconstructible in the case of a failure as subtransactions are not
durable. |
Fields Summary |
---|
String | name | RegisteredResources | participants | SuperiorInfo | superInfo | NestingInfo | nestingInfo | TransactionState | tranState | CompletionHandler | terminator | boolean | registered | boolean | root | boolean | rollbackOnly | boolean | dying | boolean | temporary | int | hash | static Logger | _logger | static String[] | resultNameDirects the SubCoordinator to prepare to commit.
The SubCoordinator checks that the subtransaction can be committed.
It does not distribute prepare operations to the participants. | private static Any | emptyData |
Constructors Summary |
---|
SubCoordinator(GlobalTID parentGlobalTID, Long parentLocalTID, CoordinatorImpl[] ancestors)Creates and initialises a SubCoordinator, given the parent's local
and global identifiers and the sequence of ancestors.
// Allocate a new global identifier for the subtransaction.
// If one cannot be allocated, raise an exception as the
// subtransaction cannot be started.
tranState = new TransactionState(parentLocalTID, parentGlobalTID);
// Store information about the superior, ancestors and participants
// of the new subtransaction.
superInfo = new SuperiorInfo(tranState.localTID,
tranState.globalTID, null, null);
// Cache the name - create a buffer and print the global XID into it.
name = superInfo.globalTID.toString();
// Cache the hash code.
hash = superInfo.globalTID.hashCode();
// Create the nesting info object to record the ancestors.
nestingInfo = new NestingInfo(ancestors);
// Zero out the RegisteredResources reference as it will be
// created when needed.
participants = null;
// Set other instance variables.
root = true;
registered = true;
rollbackOnly = false;
dying = false;
temporary = false;
terminator = null;
// Set the state of the subtransaction 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 {
// Inform the RecoveryManager of the existence of this transaction.
RecoveryManager.addCoordinator(tranState.globalTID,
tranState.localTID, this, 0);
}
| SubCoordinator(GlobalTID globalTID, Coordinator superior, boolean temporary, CoordinatorImpl[] ancestors)Creates and initialises a subordinate SubCoordinator, given the global
identifier, a reference to the superior SubCoordinator,
and the ancestors of the transaction.
The temporary subordinate indicator is used for the case where a parent
Coordinator is created when a subtransaction enters a process for the
first time. If the request returns and the subtransaction has no
participants, it is destroyed, along with any temporary ancestors.
// 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,null);
// Store information about the superior, ancestors and participants of
// the new subordinate transaction.
superInfo = new SuperiorInfo(tranState.localTID,
globalTID, superior, null);
// Cache the name - create a buffer and print the global XID into it.
name = superInfo.globalTID.toString();
// Cache the hash code.
hash = superInfo.globalTID.hashCode();
// Create the nesting info object to record the ancestors.
nestingInfo = new NestingInfo(ancestors);
// Zero out the RegisteredResources reference,
// as it will be created when needed.
participants = null;
// Set other instance variables.
root = false;
registered = false;
rollbackOnly = false;
dying = false;
this.temporary = temporary;
terminator = null;
// Set the state of the transaction to active before making it visible
// to the TransactionManager.
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, 0)) {
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 SubCoordinator.
boolean result = nestingInfo.addChild(child);
return result;
| void | cleanUpEmpty(CoordinatorImpl parent)Cleans up an empty Coordinator.
// Roll the transaction back, ignoring any exceptions.
try {
rollback(true);
} catch (Throwable exc) {}
// If the transaction is a subtransaction, remove the
// child from the parent's set of children.
// If the parent is temporary, this will cause the parent
// to call cleanup_empty_coordinator, and so-on until all
// empty ancestors are cleaned up.
if (parent != null) {
parent.removeChild(this);
}
| void | commit()Directs the SubCoordinator to commit the transaction.
The SubCoordinator directs all registered Resources to commit.
Coordinator parent = null;
// Record that the Coordinator is about to commit.
// Until we actually distribute commit flows, synchronize the method.
synchronized (this) {
// If the SubCoordinator 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);
}
// Get the reference of the parent Coordinator.
parent = nestingInfo.getParent(false).object();
// 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.
if (participants != null) {
try {
participants.distributeSubcommit(parent);
} catch (Throwable exc) {
_logger.log(Level.SEVERE,"jts.exception_on_resource_operation",
new java.lang.Object[] { exc.toString(),
"commit"});
String msg = LogFormatter.getLocalizedMessage(_logger,
"jts.exception_on_resource_operation",
new java.lang.Object[]
{exc.toString(),
"commit"});
throw new org.omg.CORBA.INTERNAL(msg);
}
}
// 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);
}
// Remove our reference from the parents set of children
nestingInfo.removeFromParent(this);
// Clean up the SubCoordinator after a commit.
// In the case where the SubCoordinator 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 SubCoordinator object so NO INSTANCE VARIABLES
// should be referenced after the call.
// In the case where the SubCoordinator 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, false);
}
// As subtransactions do not have synchronization,
// there is nothing left to do, so get the
//RecoveryManager to forget about us, then self-destruct.
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID, false);
destroy();
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* NO INSTANCE VARIABLES MAY BE ACCESSED FROM THIS POINT ON. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
}
| boolean | commitOnePhase()Directs the SubCoordinator to commit the transaction in one phase
The SubCoordinator directs all registered Resources to commit.
// The commit of a subtransaction is always a one phase commit.
// The implementation of the prepare/commit methods simply
// split this up into two parts: the prepare checks the state and
// the commit calls the resources. Therefore commit_one_phase can
// simply call these methods directly.
// Also we let any exception pass up through.
Vote v = this.prepare();
if (v == Vote.VoteCommit) {
this.commit();
} else if (v == Vote.VoteReadOnly) {
// Nothing to do
} else {
this.rollback(true);
}
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 SubCoordinator
// 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[] thisAncestors = nestingInfo.getAncestors();
CoordinatorImpl[] ancestors =
new CoordinatorImpl[thisAncestors.length + 1];
System.arraycopy(thisAncestors, 0, ancestors, 1, thisAncestors.length);
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.
nestingInfo.addChild(child);
return result;
| public void | doFinalize()Cleans up the state of the object.
// 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 :
rollback(true);
break;
// For committed or rolled-back, we really need to destroy the object
case TransactionState.STATE_COMMITTED :
case TransactionState.STATE_ROLLED_BACK :
// if( tranState != null ) tranState.finalize();
if( superInfo != null ) superInfo.doFinalize();
// if( nestingInfo != null ) nestingInfo.finalize();
// if( participants != null ) participants.finalize();
tranState = null;
superInfo = null;
nestingInfo = null;
participants = 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.
This relies on the availability of the propagation context from the
target Coordinator.
If the other Coordinator is remote, and not a JCoordinator,
and is in the process of ending the transaction,
then this operation will fail; in this
case we throw the INVALID_TRANSACTION exception with a minor code that
indicates the cause of the failure.
Unfortunately this is the best we can do with the OMG interfaces when
interoperating with a different OTS implementation.
// 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;
if (other instanceof CoordinatorImpl) {
// For local Coordinator objects which are really instances of the
// CoordinatorImpl class, get the global TID via a private
// method call.
if (other instanceof SubCoordinator) {
otherTID = ((SubCoordinator)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.
CoordinatorImpl[] coords = nestingInfo.getAncestors();
TransIdentity[] result = new TransIdentity[coords.length];
for (int i = 0; i < coords.length; i++) {
try {
result[i] = new TransIdentity(coords[i].object(), null,
coords[i].getGlobalTID());
} catch (Throwable exc) {}
}
return result;
| public otid_t | getGlobalTID()Returns a global identifier that represents the SubCoordinator'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 = null;
result = superInfo.globalTID.realTID;
return result;
| 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.
Coordinator result = nestingInfo.getParent(false).object();
return result;
| 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 synchronized Status | get_parent_status()Gets the local state of the parent transaction.
This operation references no instance variables and so can be
implemented locally in the proxy class.
Status result = Status.StatusNoTransaction;
// Return the parents status. If there is none, this is an error;
// return no transaction status (may want to raise a LogicError here).
if (tranState != null) {
CoordinatorImpl parent = nestingInfo.getParent(false);
if (parent != null) {
result = parent.get_status();
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized 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 :
result = Status.StatusPreparing;
break;
case TransactionState.STATE_COMMITTING :
result = Status.StatusCommitting;
break;
case TransactionState.STATE_COMMITTED :
result = Status.StatusCommitted;
break;
case TransactionState.STATE_ROLLING_BACK :
result = Status.StatusRollingBack;
break;
case TransactionState.STATE_ROLLED_BACK :
result = Status.StatusRolledBack;
break;
// Any other state, return unknown.
default :
result = Status.StatusUnknown;
break;
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized Status | get_top_level_status()Gets the local state of the top-level transaction.
This operation references no instance variables and so can be
implemented locally in a proxy class.
// Return the top-level status. If there is none, this is an error;
// return no transaction status (may want to raise a LogicError here).
Status result = Status.StatusNoTransaction;
if (tranState != null) {
CoordinatorImpl topLevel = nestingInfo.getTopLevel();
if (topLevel != null) {
result = topLevel.get_status();
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized java.lang.String | get_transaction_name()Returns a printable string that represents the SubCoordinator.
This operation references only the global TID, and so can be
implemented locally in a proxy class.
String result = null;
if (tranState != null) {
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 operation.
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);
TransIdentity[] parents = getAncestors();
// 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,
parents, emptyData);
return result;
| synchronized boolean | hasRegistered()Checks whether the SubCoordinator has registered with its superior.
boolean result = registered;
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.
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.
int result = 0;
if (tranState != null) {
CoordinatorImpl topLevel = nestingInfo.getTopLevel();
if (topLevel != null) {
result = topLevel.hash_transaction();
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized int | hash_transaction()Returns a hash value based on the transaction associated with the target
object.
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 SubCoordinator is active.
boolean result = (tranState.state == TransactionState.STATE_ACTIVE);
return result;
| public synchronized boolean | isRollbackOnly()Checks whether the SubCoordinator is marked rollback-only.
boolean result = rollbackOnly;
return result;
| public boolean | is_ancestor_transaction(Coordinator other)Determines whether the target SubCoordinator is an ancestor
of the given Coordinator.
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 synchronized boolean | is_descendant_transaction(Coordinator other)Determines whether the target SubCoordinator is a descendant
of the given Coordinator.
This operation references no instance variables and so can be
implemented locally in a proxy class.
// A transaction is considered to be a descendant of itself, so if the
// two transactions are the same, return TRUE.
boolean result = false;
if (tranState != null) {
if (is_same_transaction(other)) {
result = true;
} else {
// Otherwise, go through our ancestors, comparing
// them with the given transaction.
result = nestingInfo.isDescendant(other);
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized boolean | is_related_transaction(Coordinator other)Determines whether the target SubCoordinator is related to
the given Coordinator (i.e. is a member of the same transaction family).
For a subtransaction this is equivalent to saying that the transaction
associated with the parameter object is a descendant of the top-level
ancestor of the transaction associated with the target object.
This operation references no instance variables and so can be
implemented locally in a proxy class.
// Check whether the given transaction is a descendant of our top-level
// transaction.
boolean result = false;
if (tranState != null) {
CoordinatorImpl topLevel = nestingInfo.getTopLevel();
if (topLevel != null) {
result = other.is_descendant_transaction(topLevel.object());
}
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| public synchronized boolean | is_same_transaction(Coordinator other)Compares the given Coordinator object with the target,
and returns TRUE if they represent the same transaction.
This operation needs to be implemented in an efficient manner, without
any cross-process calls. This could be achieved by including the
global identifier in the Coordinator references and comparing them.
This operation references no instance variables and so can be
implemented locally in a proxy class.
boolean result = false;
// Get the names of the two transactions and compare them.
if (name != null) {
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 SubCoordinator represents a top-level
(non-nested) transaction.
For a subtransaction returns FALSE.
This operation references no instance variables and so can be
implemented locally in a proxy class.
boolean result = false;
if (tranState == null) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
| synchronized Vote | prepare()
Vote result = Vote.VoteRollback;
int newState = TransactionState.STATE_PREPARED_FAIL;
// Record that the Coordinator is about to prepare.
// 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.numChildren() != 0) {
INVALID_TRANSACTION exc =
new INVALID_TRANSACTION(MinorCode.UnfinishedSubtransactions,
CompletionStatus.COMPLETED_NO);
throw exc;
}
// If the SubCoordinator is in the wrong state, return immediately.
if (!tranState.setState(TransactionState.STATE_PREPARING)) {
return Vote.VoteRollback;
}
// Check for marked rollback-only.
if (rollbackOnly) {
// Record that the Coordinator is deciding to rollback.
// Try to set the state to prepared fail.
if (!tranState.setState(TransactionState.STATE_PREPARED_FAIL)) {
return Vote.VoteRollback;
}
} else {
newState = TransactionState.STATE_PREPARED_SUCCESS;
result = Vote.VoteCommit;
}
// Record that prepare is complete.
// Set the state.
if (!tranState.setState(newState)) {
result = Vote.VoteRollback;
}
return result;
| public synchronized RecoveryCoordinator | register_resource(Resource res)Enables a Resource to be registered as a participant in the completion
of the subtransaction represented by the SubCoordinator.
If the Resource is a SubtransactionAwareResource, it is registered
with the SubCoordinator; if the SubCoordinator has not registered
with is superior, it creates a CoordinatorResource and registers it
with the superior. The registration is passed on to the top-level
Coordinator in any case.
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;
}
// Register the given Resource with the top-level
// Coordinator first, and remember the RecoveryCoordinator object
// that is returned by the top-level Coordinator.
CoordinatorImpl topLevel = nestingInfo.getTopLevel();
// If the top-level Coordinator raises an exception, then do not
// proceed with the registration, and return the exception.
try {
result = topLevel.register_resource(res);
} catch (SystemException exc) {
throw (SystemException) exc.fillInStackTrace();
} catch (Inactive exc) {
throw (Inactive) exc.fillInStackTrace();
}
// Find out whether the Resource is actually a
// SubtransactionAwareResource.
boolean subAwareRes =
res._is_a(SubtransactionAwareResourceHelper.id());
// If the Resource is actually a SubtransactionAwareResource,
// then it needs to be registered for participation in completion
// of the subtransaction as well as the top level transaction.
if (subAwareRes) {
// If not previously registered, a CoordinatorResource object
// must be registered with our superior. Note that root
// SubCoordinators are created with the registration flag set,
// so we do not need to check
// whether we are the root SubCoordinator 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, true);
// Register the CoordinatorResource with superior Coordinator,
// and store the resulting RecoveryCoordinator reference.
try {
CoordinatorResource cRes = cImpl.object();
superInfo.superior.register_subtran_aware(cRes);
superInfo.setResource(cRes);
registered = true;
} catch (Throwable exc) {
// If an exception was raised, do not set the
// registration flag, and destroy the object.
cImpl.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;
}
}
// Add the SubtransactionAwareResource to the set of participants.
// Make sure the RegisteredResources instance variable has been
// set up.
if (participants == null) {
participants = new RegisteredResources(null, 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.
participants.addRes((Resource)res._duplicate());
temporary = false;
}
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.
If the SubCoordinator has not registered with is superior, it creates a
CoordinatorResource and registers it with the superior.
// 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 SubCoordinators
// are created with the registration flag set, so we do not need
// to check whether we are the root SubCoordinator 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, true);
// Register the CoordinatorResource with the superior Coordinator,
// and store the resulting RecoveryCoordinator reference.
try {
CoordinatorResource cRes = cImpl.object();
superInfo.superior.register_subtran_aware(cRes);
superInfo.setResource(cRes);
registered = true;
} catch(Throwable exc) {
// If an exception was raised, do not set the registration flag.
cImpl.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;
}
}
// Add the SubtransactionAwareResource to the set of participants.
// Make sure the RegisteredResources instance variable has been set up.
if (participants == null) {
participants = new RegisteredResources(null, 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.
participants.addRes((Resource)sares._duplicate());
temporary = false;
| public synchronized void | register_synchronization(Synchronization sync)Informs the SubCoordinator that the given object
requires synchronization before and after completion
of the top-level ancestor transaction.
The registration is passed directly to the top-level ancestor.
// 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;
}
// Register the Synchronization object with the top-level Coordinator.
// Allow any exception to percolate to the caller.
CoordinatorImpl topLevel = nestingInfo.getTopLevel();
topLevel.register_synchronization(sync);
| synchronized boolean | removeChild(CoordinatorImpl child)Removes the given Coordinator from the set of children of the target
SubCoordinator.
If the SubCoordinator 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.
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()) &&
!(nestingInfo != null && nestingInfo.numChildren() > 0)) {
// We pass the parent Coordinator to the RecoveryManager
// so that it can remove the child from the parent's set of
// children after the child is rolled back.
CoordinatorImpl parent = nestingInfo.getParent(true);
cleanUpEmpty(parent);
}
return result;
| synchronized CoordinatorImpl | replyAction(int[] action)Indicates that a method reply is being sent and requests the
SubCoordinator'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 SubCoordinator 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.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 {
action[0] = forgetMe;
}
}
// 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 whenit receives the forgetMe
// response.
if (action[0] == doNothing && !registered)
action[0] = forgetMe;
}
// Default action is do nothing when we are registered.
result = null;
return result;
| void | rollback(boolean force)Directs the SubCoordinator to roll back the transaction.
The SubCoordinator directs all registered Resources to rollback.
// Until we actually distribute rollback flows, synchronize the method.
synchronized (this) {
// If the transaction has already been rolled back, just return.
if (tranState == null) {
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 SubCoordinator 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 roll back.
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.
if (participants != null) {
participants.distributeSubrollback();
}
// The remainder of the method needs to be synchronized.
synchronized(this) {
// Set the state to rolled back.
// Remove our reference from the parents set of children
if (!temporary &&
!tranState.setState(TransactionState.STATE_ROLLED_BACK)) {
// empty
}
nestingInfo.removeFromParent(this);
// Clean up the SubCoordinator after a rollback.
// In the case where the SubCoordinator 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 SubCoordinator object
// so NO INSTANCE VARIABLES should be referenced after the call.
// In the case where the SubCoordinator 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, false);
}
// As subtransactions do not have synchronization,
// there is nothing left to do, so get the RecoveryManager
// to forget about us, then self-destruct.
RecoveryManager.removeCoordinator(superInfo.globalTID,
superInfo.localTID, false);
if (!dying) {
destroy();
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* NO INSTANCE VARIABLES MAY BE ACCESSED FROM THIS POINT ON. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
}
| public synchronized void | rollback_only()Ensures that the transaction represented by the target SubCoordinator
cannot be committed.
if (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 SubCoordinator as permanent.
Long result = superInfo.localTID;
temporary = false;
return result;
| synchronized void | setTerminator(CompletionHandler term)Informs the SubCoordinator 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;
|
|