All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jts.CosTransactions.SubCoordinator Maven / Gradle / Ivy

There is a newer version: 8.0.0-JDK17-M7
Show newest version
/*
 * 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy