com.sun.jts.CosTransactions.SubCoordinator Maven / Gradle / Ivy
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995-1997 IBM Corp. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
//----------------------------------------------------------------------------
//
// Module: SubCoordinator.java
//
// Description: Nested transaction Coordinator object implementation.
//
// Product: com.sun.jts.CosTransactions
//
// Author: Simon Holdsworth
//
// Date: March, 1997
//----------------------------------------------------------------------------
package com.sun.jts.CosTransactions;
import com.sun.jts.codegen.otsidl.CoordinatorResource;
import com.sun.jts.codegen.otsidl.JCoordinator;
import com.sun.jts.codegen.otsidl.JCoordinatorHelper;
import com.sun.jts.utils.LogFormatter;
import com.sun.logging.LogDomains;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.INVALID_TRANSACTION;
import org.omg.CORBA.OBJECT_NOT_EXIST;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TRANSACTION_ROLLEDBACK;
import org.omg.CosTransactions.*;
/**
* 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.
*
* @version 0.01
*
* @author Simon Holdsworth, IBM Corporation
*
* @see
*/
//----------------------------------------------------------------------------
// CHANGE HISTORY
//
// Version By Change Description
// 0.01 SAJH Initial implementation.
//----------------------------------------------------------------------------
class SubCoordinator extends CoordinatorImpl {
String name = null;
RegisteredResources participants = null;
SuperiorInfo superInfo = null;
NestingInfo nestingInfo = null;
TransactionState tranState = null;
CompletionHandler terminator = null;
boolean registered = false;
boolean root = true;
boolean rollbackOnly = false;
boolean dying = false;
boolean temporary = false;
int hash = 0;
/*
Logger to log transaction messages
*/
static Logger _logger = LogDomains.getLogger(SubCoordinator.class,LogDomains.TRANSACTION_LOGGER);
/**
* Creates and initialises a SubCoordinator, given the parent's local
* and global identifiers and the sequence of ancestors.
*
* @param parentGlobalTID The parent's global transaction identifier.
* @param parentLocalTID The parent's local transaction identifier.
* @param ancestors This transactions's ancestors (includes parent).
*
* @return
*
* @exception LogicErrorException An internal logic error occurred.
*
* @see
*/
SubCoordinator(GlobalTID parentGlobalTID, Long parentLocalTID,
CoordinatorImpl[] ancestors) throws LogicErrorException {
// 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);
}
}
/**
* 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.
*
* @param globalTID The global identifier for the transaction.
* @param superior The superior Coordinator.
* @param temporary The temporary flag.
* @param ancestors The ancestors of the transaction.
* @return
* @exception LogicErrorException An internal logic error occurred.
* @see
*/
SubCoordinator(GlobalTID globalTID, Coordinator superior,
boolean temporary, CoordinatorImpl[] ancestors)
throws LogicErrorException {
// 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;
}
}
/**
* Cleans up the state of the object.
*
* @param
*
* @return
*
* @see
*/
public void doFinalize() {
// 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( superInfo != null ) {
superInfo.doFinalize();
}
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;
}
}
/**
* Returns the local status of the target transaction.
*
* @param
*
* @return The status of the transaction.
*
* @see
*/
synchronized public Status get_status() {
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;
}
/**
* Gets the local state of the parent transaction.
* This operation references no instance variables and so can be
* implemented locally in the proxy class.
*
* @param
*
* @return The parent transaction's status.
*
* @exception SystemException The parent could not be reached.
*
* @see
*/
synchronized public Status get_parent_status() throws SystemException {
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;
}
/**
* 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.
*
* @param
*
* @return The top-level transaction status.
*
* @exception SystemException The top-level ancestor could not be reached.
*
* @see
*/
synchronized public Status get_top_level_status() throws SystemException {
// 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;
}
/**
* 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.
*
* @param other The other Coordinator to be compared.
*
* @return Indicates equality of the transactions the objects
* represent.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
synchronized public boolean is_same_transaction(Coordinator other)
throws SystemException {
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;
}
/**
* 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.
*
* @param other The other Coordinator.
*
* @return Indicates the relationship.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
synchronized public boolean is_related_transaction(Coordinator other)
throws SystemException {
// 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;
}
/**
* 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.
*
* @param other The other Coordinator.
*
* @return Indicates the relationship.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
public boolean is_ancestor_transaction(Coordinator other)
throws SystemException {
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;
}
/**
* 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.
*
* @param other The other Coordinator.
*
* @return Indicates the relationship.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
synchronized public boolean is_descendant_transaction(Coordinator other)
throws SystemException {
// 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;
}
/**
* 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.
*
* @param
*
* @return Indicates the transaction is top-level.
*
* @see
*/
public boolean is_top_level_transaction() {
boolean result = false;
if (tranState == null) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
}
/**
* Returns a hash value based on the transaction associated with the target
* object.
*
* @param
*
* @return The hash value for the transaction.
*
* @see
*/
synchronized public int hash_transaction() {
int result = hash;
if (tranState == null) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
}
/**
* Returns a hash value based on the top-level ancestor of the transaction
* associated with the target object.
*
* @param
*
* @return The hash value for the top-level ancestor.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
synchronized public int hash_top_level_tran() throws SystemException {
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;
}
/**
* 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.
*
* @param res The Resource to be registered.
*
* @return The RecoveryCoordinator object from the
* registration with the top-level ancestor.
*
* @exception Inactive The Coordinator is completing the transaction and
* cannot accept this registration.
* @exception TRANSACTION_ROLLEDBACK The transaction which the Coordinator
* represents has already been rolled back, or been marked
* rollback-only.
* @exception SystemException The operation failed.
*
* @see
*/
synchronized public RecoveryCoordinator register_resource(Resource res)
throws SystemException, Inactive, TRANSACTION_ROLLEDBACK {
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;
}
/**
* 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.
*
* @param sares The SubtransactionAwareResource to be registered.
*
* @return
*
* @exception Inactive The Coordinator is completing the transaction and
* cannot accept this registration.
* @exception TRANSACTION_ROLLEDBACK The transaction which the Coordinator
* represents has already been rolled back, or has been marked
* rollback-only.
* @exception SystemException The operation failed.
*
* @see
*/
synchronized public void register_subtran_aware(SubtransactionAwareResource sares)
throws SystemException, Inactive, TRANSACTION_ROLLEDBACK {
// 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;
}
/**
* Ensures that the transaction represented by the target SubCoordinator
* cannot be committed.
*
* @param
*
* @return
*
* @exception Inactive The Coordinator is already completing the
* transaction.
* @see
*/
synchronized public void rollback_only() throws Inactive {
if (tranState.state != TransactionState.STATE_ACTIVE) {
Inactive exc = new Inactive();
throw exc;
} else {
// Set the rollback-only flag.
rollbackOnly = true;
}
}
/**
* 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.
*
* @param
*
* @return The transaction name.
*
* @see
*/
synchronized public String get_transaction_name() {
String result = null;
if (tranState != null) {
result = name;
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(MinorCode.Completed, CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
}
/**
* Creates a subtransaction and returns a Control object that
* represents the child transaction.
*
* @param
*
* @return The Control object for the new child transaction.
*
* @exception Inactive The Coordinator is completing the
* subtransaction and cannot create a new child.
*
* @see
*/
synchronized public Control create_subtransaction() throws Inactive {
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()),
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;
}
/**
* 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.
*
* @param
*
* @return The global identifier for the transaction.
*
* @see
*/
public otid_t getGlobalTID() {
otid_t result = null;
result = superInfo.globalTID.realTID;
return result;
}
/**
* Returns the internal identifier for the transaction.
* This method is currently not synchronized because that causes a deadlock
* in resync.
*
* @param
*
* @return The local transaction identifier.
*
* @see
*/
public long getLocalTID() {
long result = superInfo.localTID.longValue();
return result;
}
/**
* 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.
*
* @param action A 1-element array to hold the reply action.
*
* @return The parent coordinator if any.
*
* @exception SystemException An error occurred. The minor code indicates
* the reason for the exception.
*
* @see
*/
synchronized CoordinatorImpl replyAction(int[/* 1 */] action) throws SystemException {
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;
}
/**
* Marks the SubCoordinator as permanent.
*
* @param
*
* @return The local transaction identifier.
*
* @see
*/
synchronized Long setPermanent() {
Long result = superInfo.localTID;
temporary = false;
return result;
}
/**
* Checks whether the SubCoordinator is marked rollback-only.
*
* @param
*
* @return Indicates whether the transaction is rollback-only.
*
* @see
*/
synchronized public boolean isRollbackOnly() {
boolean result = rollbackOnly;
return result;
}
/**
* Checks whether the SubCoordinator is active.
*
* @param
*
* @return Indicates the transaction is active.
*
* @see
*/
synchronized boolean isActive() {
boolean result = (tranState.state == TransactionState.STATE_ACTIVE);
return result;
}
/**
* Checks whether the SubCoordinator has registered with its superior.
*
* @param
*
* @return Indicates the registration status.
*
* @see
*/
synchronized boolean hasRegistered() {
boolean result = registered;
return result;
}
/**
* Returns the sequence of ancestors of the transaction.
*
* @param
*
* @return The sequence of ancestors.
*
* @see
*/
synchronized public TransIdentity[] getAncestors() {
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;
}
/**
* Adds the given Coordinator reference to the set of children
* of the target SubCoordinator.
*
* @param child The child Coordinator.
*
* @return Indicates success of the operation.
*
* @see
*/
synchronized boolean addChild(CoordinatorImpl child) {
boolean result = nestingInfo.addChild(child);
return result;
}
/**
* 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.
*
* @param child The child Coordinator.
*
* @return Indicates success of the operation.
*
* @see
*/
synchronized boolean removeChild(CoordinatorImpl child) {
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()) &&
!(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;
}
/**
* Directs the SubCoordinator to prepare to commit.
* The SubCoordinator checks that the subtransaction can be committed.
* It does not distribute prepare operations to the participants.
*
* @param
*
* @return The consolidated vote.
*
* @exception INVALID_TRANSACTION The transaction is not in a
* state to commit, due to outstanding work.
*
* @see
*/
static String[] resultName = { "Commit"/*#Frozen*/, "Rollback"/*#Frozen*/, "Read-only"/*#Frozen*/ };
synchronized Vote prepare() throws INVALID_TRANSACTION {
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;
}
/**
* Directs the SubCoordinator to commit the transaction.
* The SubCoordinator directs all registered Resources to commit.
*
* @param
*
* @return
*
* @see
*/
void 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. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
}
}
/**
* Directs the SubCoordinator to roll back the transaction.
* The SubCoordinator directs all registered Resources to rollback.
*
* @param force
*
* @return
*
* @see
*/
void rollback(boolean force) {
// 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)) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"SubCoordinator - setState(TransactionState.STATE_ROLLED_BACK) returned false");
}
}
// 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)) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"SubCoordinator - setState(TransactionState.STATE_ROLLED_BACK) returned false");
}
}
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. */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
}
}
/**
* 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.
*
* @param sync The Synchronization object to be registered.
*
* @return
*
* @exception Inactive The Coordinator is in the process of completing the
* transaction and cannot accept this registration.
* @exception SynchronizationUnavailable The transaction service
* cannot support synchronization.
*
* @see
*/
synchronized public void register_synchronization(Synchronization sync)
throws Inactive, SynchronizationUnavailable {
// 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);
}
/**
* 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.
*
* @param term The object normally responsible for terminating the
* Coordinator.
*
* @return
*
* @see
*/
synchronized void setTerminator(CompletionHandler term) {
terminator = term;
}
/**
* Gets the parent coordinator of the transaction.
*
* @param
*
* @return The parent Coordinator
*
* @see
*/
Coordinator getParent() {
Coordinator result = nestingInfo.getParent(false).object();
return result;
}
/**
* Gets the superior Coordinator for this transaction.
*
* @param
*
* @return The superior Coordinator
*
* @see
*/
Coordinator getSuperior() {
Coordinator result = superInfo.superior;
return result;
}
/**
* Returns the Resource objects and their states.
*
* @param resources The object which will contain the Resources
* @param states The object which will contain the states.
*
* @return
*
* @see
*/
/* COMMENT(Ram J) only Admin package needs this.
public void getResources(ResourceSequenceHolder resources,
ResourceStatusSequenceHolder states) {
if (participants != null) {
participants.getResources(resources, states);
// Validate each of the Resource objects in
// the list before returning it.
for (int i = 0; i < resources.value.length; i++) {
if (resources.value[i]._non_existent()) {
resources.value[i] = null;
}
}
} else {
resources.value = null;
states.value = null;
}
}
*/
/**
* Gets the object normally responsible for terminating this Coordinator.
*
* @param
*
* @return The object normally responsible for terminating
* the Coordinator.
*
* @see
*/
CompletionHandler getTerminator() {
CompletionHandler result = terminator;
return result;
}
private static Any emptyData = null;
/**
* Creates a PropagationContext which contains the information which would
* normally be passed implicitly via the CosTSPropagation interfaces.
*
* @param
*
* @return The transaction context.
*
* @exception Inactive The Coordinator is in the process of completing the
* transaction and cannot return the information.
*
* @see
*/
synchronized public PropagationContext get_txcontext() throws Unavailable {
// 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;
}
/**
* Cleans up an empty Coordinator.
*
* @param parent The parent Coordinator, if any.
*
* @return
*
* @see
*/
void cleanUpEmpty(CoordinatorImpl parent) {
// 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);
}
}
/**
* Directs the SubCoordinator to commit the transaction in one phase
* The SubCoordinator directs all registered Resources to commit.
*
* @param
*
* @return
*
* @see
*/
boolean commitOnePhase() {
// 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;
}
/**
* 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.
*
* @param
*
* @return The hash code for the object.
*
* @see
*/
public int hashCode() {
return hash;
}
/**
* 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.
*
* @param other The other object.
*
* @return Indicates equality.
*
* @see
*/
public boolean equals(java.lang.Object other) throws INVALID_TRANSACTION {
// 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.isSameTID(otherTID);
}
return false;
}
}