com.sun.jts.CosTransactions.TopCoordinator Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016] [Payara Foundation and/or its affiliates]
//----------------------------------------------------------------------------
//
// Module: TopCoordinator.java
//
// Description: Top-level transaction Coordinator object implementation.
//
// Product: com.sun.jts.CosTransactions
//
// Author: Simon Holdsworth
//
// Date: March, 1997
//
// Copyright (c): 1995-1997 IBM Corp.
//
// The source code for this program is not published or otherwise divested
// of its trade secrets, irrespective of what has been deposited with the
// U.S. Copyright Office.
//
// This software contains confidential and proprietary information of
// IBM Corp.
//----------------------------------------------------------------------------
package com.sun.jts.CosTransactions;
import java.util.*;
import org.omg.CORBA.*;
import org.omg.CosTransactions.*;
import com.sun.jts.codegen.otsidl.*;
import com.sun.jts.jtsxa.OTSResourceImpl;
//import com.sun.jts.codegen.otsidl.JCoordinatorHelper;
//import com.sun.jts.codegen.otsidl.JCoordinatorOperations;
//import java.io.PrintStream;
//import java.util.Vector;
//import com.sun.enterprise.transaction.OTSResourceImpl;
//import com.sun.enterprise.transaction.SynchronizationImpl;
import java.util.logging.Logger;
import java.util.logging.Level;
import com.sun.logging.LogDomains;
import com.sun.jts.utils.LogFormatter;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 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.
*
* @version 0.02
*
* @author Simon Holdsworth, IBM Corporation
*
* @see
*/
//----------------------------------------------------------------------------
// CHANGE HISTORY
//
// Version By Change Description
// 0.01 SAJH Initial implementation.
// 0.02 GDH Gordon Hutchison April 1998
// Some improvements to the way the additional tran states in
// OTS 1.1 (vs 1.0) are handled and also improvements to logic
// used in recovery.
// 0.03 GDH Gordon 27th Jan 1999
// Changes marked by COPDEF1 to fix two defects in COP work.
// 0.04 GDH Gordon 28th Jan 1999
// Change marked by COPDEF2, in COP if resource voted rb
// our code set state to COB_RB twice which is invalid:
// first state change removed as unnecessary.
//-----------------------------------------------------------------------------
public class TopCoordinator extends CoordinatorImpl {
String name = null;
RegisteredResources participants = null;
RegisteredSyncs synchronizations = null;
SuperiorInfo superInfo = null;
NestingInfo nestingInfo = null;
TransactionState tranState = null;
CoordinatorLog logRecord = null;
CompletionHandler terminator = null;
boolean registered = false;
boolean registeredSync = false;
boolean root = true;
boolean rollbackOnly = false;
boolean dying = false;
boolean temporary = false;
int hash = 0;
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
/*
Logger to log transaction messages
*/
static Logger _logger = LogDomains.getLogger(TopCoordinator.class,LogDomains.TRANSACTION_LOGGER);
// added (Ram J) for memory Leak fix.
Vector recoveryCoordinatorList = null;
CoordinatorSynchronizationImpl coordSyncImpl = null;
// added (sankar) for delegated recovery support
boolean delegated = false;
String logPath = null;
/**
* Default TopCoordinator constructor.
*
* @param
*
* @return
*
* @see
*/
TopCoordinator() {
// No persistent reference is created in this case.
}
/**
* 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.
*
* @param timeOut The time-out value for the transaction.
*
* @return
*
* @exception LogicErrorException An internal logic error occurred.
*
* @see
*/
TopCoordinator(int timeOut) throws LogicErrorException {
// 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;
}
}
}
/**
* 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.
*
* @param timeOut The timeout value for the transaction.
* @param globalTID The global identifier for the transaction.
* @param superior The superior Coordinator.
* @param temporary The temporary indicator.
*
* @return
*
* @exception LogicErrorException An internal logic error occurred.
*
* @see
*/
TopCoordinator(int timeOut, GlobalTID globalTID, Coordinator superior,
boolean temporary) throws LogicErrorException {
// 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;
}
}
}
/**
* Cleans up the objects state.
*
* @param
*
* @return
*
* @see
*/
public void doFinalize() {
writeLock.lock();
try {
// 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) {
_logger.log(Level.FINE, "", 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 (superInfo != null) {
superInfo.doFinalize();
}
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;
}
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param log The CoordinatorLog object which contains the Coordinators
* state.
* @return
*
* @see
*/
void reconstruct(CoordinatorLog log) {
writeLock.lock();
try {
// 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();
}
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param log The CoordinatorLog object which contains the Coordinators
* state.
*
* @param logPath Location of the log file
* @return
*
* @see
*/
void delegated_reconstruct(CoordinatorLog log, String logPath ) {
writeLock.lock();
try {
// 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();
}
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param isRoot A 1-element array which will be filled in with the
* root flag.
*
* @return The state of the recovered transaction.
*
* @see
*/
Status recover(boolean[/*1*/] isRoot) {
writeLock.lock();
try {
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) {
_logger.log(Level.FINE, "", 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;
}
finally {
writeLock.unlock();
}
}
/**
* Returns the local status of the target transaction.
*
* @return The status of the transaction.
*
* @see
*/
@Override
public Status get_status() {
readLock.lock();
try {
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:
case TransactionState.STATE_COMMITTING_ONE_PHASE:
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;
}
finally {
readLock.unlock();
}
}
/**
* 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.
*
* @return The status of the transaction.
*
* @see
*/
@Override
public Status get_parent_status() {
Status result = get_status();
return result;
}
/**
* 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.
*
* @return The status of the transaction.
*
* @see
*/
@Override
public Status get_top_level_status() {
Status result = get_status();
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
*/
/*
removed synchronization at method level since only tranState requires
locking
*/
@Override
public boolean is_same_transaction(Coordinator other)
throws SystemException {
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;
}
/**
* 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.
*
* @param other The other Coordinator.
*
* @return Indicates the relationship.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
@Override
public boolean is_related_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 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.
*
* @return Indicates if this is the root TopCoordinator.
*
* @see
*/
public boolean is_root_transaction() {
boolean result = root;
return result;
}
/**
* 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.
*
* @param other The other Coordinator.
*
* @return Indicates the relationship.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
@Override
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 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.
*
* @param other The other Coordinator.
*
* @return Indicates the relationship.
*
* @exception SystemException The other Coordinator could not be reached.
*
* @see
*/
@Override
public boolean is_descendant_transaction(Coordinator other)
throws SystemException {
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;
}
/**
* 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.
*
*
* @return Indicates this is a top-level transaction.
*
* @see
*/
@Override
public boolean is_top_level_transaction() {
boolean result = true;
return result;
}
/**
* Returns a hash value based on the transaction associated with the target
* object.
* This operation references only the global TID, and so can be
* implemented locally in a proxy class.
*
* @return The hash value for the transaction.
*
* @see
*/
/*
removed synchronization at method level since only tranState requires
locking
*/
@Override
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.
* This operation references only the global TID, and so can be
* implemented locally in a proxy class.
*
*
* @return The hash value for the transaction.
*
* @see
*/
@Override
public int hash_top_level_tran() {
readLock.lock();
try {
int result = hash;
if (tranState == null) {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
}
finally {
readLock.unlock();
}
}
/**
* 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.
*
* @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 has been marked
* rollback-only.
*
* @see
*/
@Override
public RecoveryCoordinator register_resource(Resource res)
throws Inactive, TRANSACTION_ROLLEDBACK {
writeLock.lock();
try {
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 && DefaultTransactionService.isORBAvailable()) {
// 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);
ex2.initCause(exc);
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;
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param sares The SubtransactionAwareResource to be registered.
*
* @exception NotSubtransaction The Coordinator represents a top-level
* transaction and cannot accept the registration.
*
* @see
*/
@Override
public void register_subtran_aware(
SubtransactionAwareResource sares) throws NotSubtransaction {
NotSubtransaction exc = new NotSubtransaction();
throw exc;
}
/**
* Ensures that the transaction represented by the target TopCoordinator
* cannot be committed.
*
* @exception Inactive The Coordinator is already completing the
* transaction.
* @see
*/
@Override
public void rollback_only() throws Inactive {
writeLock.lock();
try {
if (tranState == null
|| tranState.state != TransactionState.STATE_ACTIVE) {
Inactive exc = new Inactive();
throw exc;
} else {
// Set the rollback-only flag.
rollbackOnly = true;
}
}
finally {
writeLock.unlock();
}
}
/**
* Returns a printable string that represents the TopCoordinator.
* This operation references only the global TID, and so can be
* implemented locally in a proxy class.
*
* @return The transaction name.
*
* @see
*/
/*
removed synchronization at method level since only tranState requires
locking
*/
@Override
public String get_transaction_name() {
readLock.lock();
try {
String result = null;
if (tranState != null) {
if (name == null) {
name = superInfo.globalTID.toString();
}
result = name;
} else {
INVALID_TRANSACTION exc = new INVALID_TRANSACTION(
MinorCode.Completed,
CompletionStatus.COMPLETED_NO);
throw exc;
}
return result;
}
finally {
readLock.unlock();
}
}
/**
* Creates a subtransaction and returns a Control object that represents
* the child transaction.
*
* @return The Control object for the new child transaction.
*
* @exception Inactive The Coordinator is completing the subtransaction
* and cannot create a new child.
*
* @see
*/
@Override
public Control create_subtransaction() throws Inactive {
writeLock.lock();
try {
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()),
child.getLocalTID()
).object();
} catch (Throwable exc) {
Inactive ex2 = new Inactive();
ex2.initCause(exc);
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;
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @return The global transaction identifier.
*
* @see
*/
@Override
public otid_t getGlobalTID() {
otid_t result = superInfo.globalTID.realTID;
return result;
}
public GlobalTID getGlobalTid() {
return superInfo.globalTID;
}
public int getParticipantCount() {
if (participants == null) {
return 0;
}
return participants.numRegistered();
}
/**
* Returns the internal identifier for the transaction.
* This method is currently not synchronized because that causes a deadlock
* in resync.
*
* @return The local identifier.
*
* @see
*/
@Override
public long getLocalTID() {
long result = superInfo.localTID;
return result;
}
/**
* 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.
*
* @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
*/
@Override
CoordinatorImpl replyAction(int[/*1*/] action)
throws SystemException {
readLock.lock();
try {
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;
}
finally {
readLock.unlock();
}
}
/**
* Marks the TopCoordinator as permanent.
*
* @return The local transaction identifier.
*
* @see
*/
@Override
Long setPermanent() {
writeLock.lock();
try {
Long result = superInfo.localTID;
temporary = false;
return result;
}
finally {
writeLock.unlock();
}
}
/**
* Checks whether the TopCoordinator is marked rollback-only.
*
* @return Indicates whether the transaction is rollback-only.
*
* @see
*/
@Override
public boolean isRollbackOnly() {
readLock.lock();
try {
boolean result = rollbackOnly;
return result;
}
finally {
readLock.unlock();
}
}
/**
* Checks whether the TopCoordinator is active.
*
* @return Indicates the transaction is active.
*
* @see
*/
@Override
boolean isActive() {
readLock.lock();
try {
boolean result = (tranState.state == TransactionState.STATE_ACTIVE);
return result;
}
finally {
readLock.unlock();
}
}
/**
* Checks whether the TopCoordinator has registered with its superior.
*
* @param
*
* @return Indicates the registration status.
*
* @see
*/
@Override
boolean hasRegistered() {
readLock.lock();
try {
boolean result = registered || registeredSync;
return result;
}
finally {
readLock.unlock();
}
}
/**
* Returns the sequence of ancestors of the transaction.
*
* @return The sequence of ancestors.
*
* @see
*/
@Override
public TransIdentity[] getAncestors() {
return null;
}
/**
* Adds the given Coordinator reference to the set of children of the
* target TopCoordinator.
*
* @param child The child Coordinator.
*
* @return Indicates success of the operation.
*
* @see
*/
@Override
boolean addChild(CoordinatorImpl child) {
writeLock.lock();
try {
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;
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param child The child Coordinator.
*
* @return Indicates success of the operation.
*
* @see
*/
@Override
boolean removeChild(CoordinatorImpl child) {
writeLock.lock();
try {
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;
}
finally {
writeLock.unlock();
}
}
static String[] resultName = { "Commit"/*#Frozen*/, "Rollback"/*#Frozen*/, "Read-only"/*#Frozen*/ };
/**
* 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).
*
* @param
*
* @return The consolidated vote.
*
* @exception INVALID_TRANSACTION The transaction is not in a state to
* commit, due to outstanding work.
* @exception HeuristicMixed Indicates that a participant voted
* to roll the transaction back, but one or more others
* have already heuristically committed.
* @exception HeuristicHazard Indicates that a participant voted to roll
* the transaction back, but one or more others may have already
* heuristically committed.
*
* @see
*/
@Override
Vote prepare()
throws INVALID_TRANSACTION, HeuristicMixed, HeuristicHazard {
// Until we actually distribute prepare flows, synchronize the method.
writeLock.lock();
try {
// 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)) {
if(_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"TopCoordinator - setState(TransactionState.STATE_PREPARED_FAIL) returned false");
}
}
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
finally {
writeLock.unlock();
}
// 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 (participants.getLAOResource() != null) {
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 ||
exc instanceof INTERNAL) {
heuristicExc = exc;
if (!tranState.
setState(TransactionState.STATE_ROLLED_BACK)) {
if(_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"TopCoordinator - setState(TransactionState.STATE_ROLLED_BACK) returned false");
}
}
/* 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 if (heuristicExc instanceof INTERNAL) {
throw (INTERNAL)heuristicExc;
} else {
throw (HeuristicHazard)heuristicExc;
}
} else {
_logger.log(Level.FINE, "", exc);
}
// 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.
writeLock.lock();
try {
// 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.
if (!root) {
afterCompletion(Status.StatusCommitted);
}
} 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;
}
}
}
finally {
writeLock.unlock();
}
return overallResult;
}
/**
* 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.
*
* @param
*
* @return
*
* @exception HeuristicMixed A Resource has taken an heuristic decision
* which has resulted in part of the transaction being rolled back.
* @exception HeuristicHazard Indicates that heuristic decisions may have
* been taken which have resulted in part of the transaction
* being rolled back.
* @exception NotPrepared The transaction has not been prepared.
*
* @see
*/
@Override
void commit() throws HeuristicMixed, HeuristicHazard, NotPrepared {
// Until we actually distribute prepare flows, synchronize the method.
writeLock.lock();
try {
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.
}
finally {
writeLock.unlock();
}
// Commit all participants. If a fatal error occurs during
// this method, then the process must be ended with a fatal error.
Throwable heuristicExc = null;
Throwable internalExc = null;
if (participants != null) {
try {
participants.distributeCommit();
} catch (Throwable exc) {
if (exc instanceof HeuristicMixed ||
exc instanceof HeuristicHazard) {
heuristicExc = exc;
} else if (exc instanceof INTERNAL) {
// ADDED(Ram J) percolate any system exception
// back to the caller.
internalExc = exc; // throw (INTERNAL) exc;
} else {
_logger.log(Level.WARNING, "", exc);
}
}
}
// The remainder of the method needs to be synchronized.
writeLock.lock();
try {
// 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 || internalExc != 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;
}
} else if (internalExc != null) {
throw (INTERNAL) internalExc;
}
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param force Indicates that the transaction must rollback regardless.
*
* @return
*
* @exception HeuristicMixed A Resource has taken an heuristic decision
* which has resulted in part of the transaction being committed.
* @exception HeuristicHazard Indicates that heuristic decisions may
* have been taken which have resulted in part of the transaction
* being rolled back.
* @see
*/
@Override
void rollback(boolean force) throws HeuristicMixed, HeuristicHazard {
// Until we actually distribute prepare flows, synchronize the method.
writeLock.lock();
try {
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)) {
if(_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"TopCoordinator - setState(TransactionState.STATE_ROLLED_BACK) returned false");
}
}
// 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.
}
finally {
writeLock.unlock();
}
// 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;
} else if (exc instanceof INTERNAL) {
// ADDED (Ram J) percolate up any system exception.
throw (INTERNAL) exc;
} else {
_logger.log(Level.WARNING, "", exc);
}
}
}
// The remainder of the method needs to be synchronized.
writeLock.lock();
try {
// Set the state. Only bother doing this if the coordinator
// is not temporary.
if (!temporary &&
!tranState.setState(TransactionState.STATE_ROLLED_BACK)) {
if(_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"TopCoordinator - setState(TransactionState.STATE_ROLLED_BACK) returned false");
}
}
// 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;
}
}
}
finally {
writeLock.unlock();
}
// Otherwise return normally.
}
/**
* 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.
*
* @param sync The Synchronization object to be registered.
*
* @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.
* @exception SystemException The operation failed.
*
* @see
*/
@Override
public void register_synchronization(Synchronization sync)
throws SystemException, Inactive, SynchronizationUnavailable {
writeLock.lock();
try {
// 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 && DefaultTransactionService.isORBAvailable()) {
// 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);
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;
}
}
// 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());
}
} else {
synchronizations.addSync((Synchronization) sync._duplicate());
}
temporary = false;
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param
*
* @return
*
* @exception INVALID_TRANSACTION The transaction is not in a state to
* commit, due to outstanding work.
*
* @see
*/
void beforeCompletion() throws INVALID_TRANSACTION {
writeLock.lock();
RegisteredSyncs localSyncs = null;
try {
// 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) {
localSyncs = synchronizations.clone();
}
}
catch (CloneNotSupportedException e) {
rollbackOnly = true;
throw new RuntimeException("Internal: Syncs must support clone()", e);
}
finally {
writeLock.unlock();
}
if (localSyncs != null) {
// Tell the RegisteredSyncs to distribute the before completion
// messages. If an exception is raised, then mark the transaction
// rollback-only.
try {
if (!localSyncs.distributeBefore()) {
rollbackOnly = true;
}
} catch (RuntimeException ex) {
writeLock.lock();
try {
rollbackOnly = true;
}
finally {
writeLock.unlock();
}
throw ex;
}
}
}
/**
* 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.
*
* @param status Indicates whether the transaction committed or aborted.
*
* @return
*
* @see
*/
void afterCompletion(Status status) {
writeLock.lock();
RegisteredSyncs localSyncs = null;
try {
// 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) {
localSyncs = synchronizations.clone();
}
}
catch (CloneNotSupportedException e) {
throw new RuntimeException("Internal: Syncs must support clone()", e);
}
finally {
writeLock.unlock();
}
if(localSyncs != null) {
// Tell the RegisteredSyncs to distribute the after completion
// messages. If an exception occurs, just report it.
// synchronizations.distributeAfter(get_status());
localSyncs.distributeAfter(status);
}
writeLock.lock();
try {
// 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();
}
finally {
writeLock.unlock();
}
}
/**
* 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.
*
* @param term The object normally responsible for terminating the
* Coordinator.
* @return
*
* @see
*/
@Override
void setTerminator(CompletionHandler term) {
writeLock.lock();
try {
terminator = term;
}
finally {
writeLock.unlock();
}
}
/**
* Gets the parent coordinator of the transaction. As this is a top level
* coordinator, a parent does not exist so NULL is returned.
*
* @return The parent Coordinator, null.
*
* @see
*/
@Override
Coordinator getParent() {
Coordinator result = null;
return result;
}
/**
* Gets the superior Coordinator for this transaction.
*
* @return The superior Coordinator
*
* @see
*/
@Override
Coordinator getSuperior() {
readLock.lock();
try {
Coordinator result = superInfo.superior;
return result;
}
finally {
readLock.unlock();
}
}
/**
* 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 = new Resource[0];
states.value = new ResourceStatus[0];
}
}
*/
/**
* Gets the object normally responsible for terminating this Coordinator.
*
* @return The object normally responsible for terminating
* the Coordinator.
*
* @see
*/
@Override
CompletionHandler getTerminator() {
CompletionHandler result = terminator;
return result;
}
/**
* 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.
*
* @param res The Resource to be directly registered.
*
* @return
*
* @see
*/
void directRegisterResource(Resource res) {
// 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 );
}
}
private static Any emptyData = null;
/**
* Creates a PropagationContext which contains the information which would
* normally be passed implicitly via the CosTSPropagation interfaces.
*
* @return The transaction context.
*
* @exception Unavailable The Coordinator is in the process of completing the
* transaction and cannot return the information.
*
* @see
*/
@Override
public PropagationContext get_txcontext() throws Unavailable {
readLock.lock();
try {
// 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;
}
finally {
readLock.unlock();
}
}
/**
* Cleans up an empty Coordinator.
*
* @param parent The parent Coordinator
* (always null for a TopCoordinator).
* @return
*
* @see
*/
@Override
void cleanUpEmpty(CoordinatorImpl parent) {
// Roll the transaction back, ignoring any exceptions.
try {
rollback(true);
} catch( Throwable exc ) {
_logger.log(Level.FINE, "", exc);
}
}
//-------------------------------------------------------------------------
// Method: TopCoordinator.commmitOnePhase
//
// Comments: This method is called by CoordinatorResource when
// this Coordinator is a subordinate coordinator. 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.
//-------------------------------------------------------------------------
/**
* commitOnePhase
*
* @param none
* @return boolean indicating success or whether two phase commit
* should be tried.
* @see
*/
@Override
boolean commitOnePhase() throws HeuristicMixed, HeuristicHazard {
writeLock.lock();
try {
// 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.
finally {
writeLock.unlock();
}
if ((participants != null) && (participants.numRegistered() == 1)) {
Throwable heuristicExc = null;
Throwable internalExc = null;
boolean rolled_back = false;
try {
participants.commitOnePhase();
} catch (Throwable exc) {
if (exc instanceof HeuristicMixed) {
// revert IASRI START 4722886
heuristicExc = exc;
// revert 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);
}
// revert IASRI START 4722886
// throw (HeuristicMixed)exc;
// revert IASRI END 4722886
} else if (exc instanceof HeuristicHazard) {
// revert IASRI START 4722886
heuristicExc = exc;
// revert 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);
}
// revert IASRI START 4722886
// throw (HeuristicHazard)exc;
// revert 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.
internalExc = exc;
} else {
_logger.log(Level.WARNING, "", 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 if (heuristicExc == null) { // we commited without a Heuristic exception
// 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!
writeLock.lock();
try {
// 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 || internalExc != 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;
}
} else if (internalExc != null) {
throw (INTERNAL) internalExc;
}
// 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
finally {
writeLock.unlock();
}
// 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;
}
/**
* 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 The hash code for the object.
*
* @see
*/
@Override
public int hashCode() {
readLock.lock();
try {
if (hash == 0 && superInfo != null && superInfo.globalTID != null) {
hash = superInfo.globalTID.hashCode();
}
return hash;
}
finally {
readLock.unlock();
}
}
/**
* Determines equality of the object with the parameter.
*
* @param other The other object.
*
* @return Indicates equality.
*
* @see
*/
@Override
public boolean equals(java.lang.Object other) {
readLock.lock();
try {
// 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);
ex3.initCause(exc);
throw ex3;
}
}
}
// Compare the global identifiers.
if (otherTID != null) {
return superInfo.globalTID.isSameTID(otherTID);
}
return false;
}
finally {
readLock.unlock();
}
}
}