Package org.apache.slide.transaction

Source Code of org.apache.slide.transaction.SlideTransaction

/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/transaction/SlideTransaction.java,v 1.20.2.2 2004/02/19 17:01:33 ozeigermann Exp $
* $Revision: 1.20.2.2 $
* $Date: 2004/02/19 17:01:33 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.slide.transaction;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.apache.slide.util.Messages;
import org.apache.slide.util.logger.Logger;

/**
* JTA Transaction implementation.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @version $Revision: 1.20.2.2 $
*/
public final class SlideTransaction implements Transaction {
   
   
    // -------------------------------------------------------------- Constants
   
   
    protected static final String LOG_CHANNEL =
        SlideTransaction.class.getName();
   
   
    // ------------------------------------------------------------ Constructor
   
   
    /**
     * Constructor.
     */
    public SlideTransaction(SlideTransactionManager transactionManager) {
        // Generate the transaction id
        globalCreatedTransactions++;
        currentTransactionNumber  = globalCreatedTransactions;
        currentThreadName         = Thread.currentThread().getName();
        xid = new SlideXid
            ((currentThreadName + "-" + System.currentTimeMillis() + "-"
                  + currentTransactionNumber).getBytes(),
             0, new byte[0]);
        this.transactionManager = transactionManager;
    }
   
   
    // ----------------------------------------------------- Instance Variables
   
   
    /**
     * Global transaction id.
     */
    private SlideXid xid;
   
   
    /**
     * Branches.
     * Keyed : branch xid -> resource manager.
     */
    private Hashtable branches = new Hashtable();
   
   
    /**
     * Active branches.
     * Keyed : resource manager -> branches xid.
     */
    private Hashtable activeBranches = new Hashtable();
   
   
    /**
     * Enlisted resources.
     */
    private Vector enlistedResources = new Vector();
   
   
    /**
     * Suspended resources.
     * Keyed : resource manager -> branches xid.
     */
    private Hashtable suspendedResources = new Hashtable();
   
   
    /**
     * Transaction status.
     */
    private int status = Status.STATUS_ACTIVE;
   
   
    /**
     * Synchronization objects.
     */
    private Vector synchronizationObjects = new Vector();
   
   
    /**
     * Branch counter.
     */
    private int branchCounter = 1;
   
   
    /**
     * Number of transactions created.
     */
    private static int globalCreatedTransactions = 0;
   
   
    /**
     * Transaction number.
     */
    private int currentTransactionNumber = 0;
   
   
    /**
     * Name of the thread bound to the transaction.
     */
    private String currentThreadName = null;
   
   
    /**
     * Associated transaction manager.
     */
    private SlideTransactionManager transactionManager;
   
   
    // ------------------------------------------------------------- Properties
   
   
    // ---------------------------------------------------- Transaction Methods
   
   
    /**
     * Complete the transaction represented by this Transaction object.
     *
     * @exception RollbackException Thrown to indicate that the transaction
     * has been rolled back rather than committed.
     * @exception HeuristicMixedException Thrown to indicate that a heuristic
     * decision was made and that some relevant updates have been committed
     * while others have been rolled back.
     * @exception HeuristicRollbackException Thrown to indicate that a
     * heuristic decision was made and that some relevant updates have been
     * rolled back.
     * @exception SecurityException Thrown to indicate that the thread is not
     * allowed to commit the transaction.
     * @exception IllegalStateException Thrown if the current thread is not
     * associated with a transaction.
     * @exception SystemException Thrown if the transaction manager encounters
     * an unexpected error condition.
     */
    public void commit()
        throws RollbackException, HeuristicMixedException,
        HeuristicRollbackException, SecurityException, IllegalStateException,
        SystemException {
       
        //System.out.println(this + "  COMMIT ");
       
        if (status == Status.STATUS_MARKED_ROLLBACK) {
            rollback();
            return;
        }
       
        // Check status ACTIVE
        if (status != Status.STATUS_ACTIVE)
            throw new IllegalStateException();
       
        // Call synchronized objects beforeCompletion
        Enumeration syncList = synchronizationObjects.elements();
        while (syncList.hasMoreElements()) {
            Synchronization sync = (Synchronization) syncList.nextElement();
            sync.beforeCompletion();
        }
       
        Vector exceptions = new Vector();
        boolean fail = false;
       
        Enumeration enum = branches.keys();
       
        if (enlistedResources.size() == 1) {
           
            // One phase commit
            status = Status.STATUS_COMMITTING;
            while (enum.hasMoreElements()) {
                Object key = enum.nextElement();
                XAResource resourceManager =
                    (XAResource) branches.get(key);
                try {
                    if (!fail)
                        resourceManager.commit(xid, true);
                    else
                        resourceManager.rollback(xid);
                } catch (Throwable e) {
                    // Adding the exception to the error code list
                    String logMessage = Messages.format
                        (SlideTransaction.class.getName() + ".commitFail",
                         resourceManager, getXAErrorCode(e), toString());
                    transactionManager.getLogger().log
                        (logMessage, e, LOG_CHANNEL, Logger.WARNING);
                    exceptions.addElement(e);
                    fail = true;
                    status = Status.STATUS_MARKED_ROLLBACK;
                }
            }
            if (!fail) {
                status = Status.STATUS_COMMITTED;
            } else {
                status = Status.STATUS_ROLLEDBACK;
            }
           
        } else if (enlistedResources.size() != 0) {
           
            // Prepare each enlisted resource
            status = Status.STATUS_PREPARING;
            while ((!fail) && (enum.hasMoreElements())) {
                Object key = enum.nextElement();
                XAResource resourceManager =
                    (XAResource) branches.get(key);
                try {
                    // Preparing the resource manager using its branch xid
                    resourceManager.prepare((Xid) key);
                } catch (Throwable e) {
                    String logMessage = Messages.format
                        (SlideTransaction.class.getName() + ".prepareFail",
                         resourceManager, getXAErrorCode(e), toString());
                    transactionManager.getLogger().log
                        (logMessage, e, LOG_CHANNEL, Logger.WARNING);
                    // Adding the exception to the error code list
                    exceptions.addElement(e);
                    fail = true;
                    status = Status.STATUS_MARKED_ROLLBACK;
                }
            }
           
            if (!fail)
                status = Status.STATUS_PREPARED;
           
            // If fail, rollback
            if (fail) {
                status = Status.STATUS_ROLLING_BACK;
                fail = false;
                // Rolling back all the prepared (and unprepared) branches
                enum = branches.keys();
                while (enum.hasMoreElements()) {
                    Object key = enum.nextElement();
                    XAResource resourceManager =
                        (XAResource) branches.get(key);
                    try {
                        resourceManager.rollback((Xid) key);
                    } catch(Throwable e) {
                        String logMessage = Messages.format
                            (SlideTransaction.class.getName() + ".rollbackFail",
                             resourceManager, getXAErrorCode(e), toString());
                        transactionManager.getLogger().log
                            (logMessage, e, LOG_CHANNEL, Logger.WARNING);
                        exceptions.addElement(e);
                        fail = true;
                    }
                }
                status = Status.STATUS_ROLLEDBACK;
            } else {
                status = Status.STATUS_COMMITTING;
                // Commit each enlisted resource
                enum = branches.keys();
                while (enum.hasMoreElements()) {
                    Object key = enum.nextElement();
                    XAResource resourceManager =
                        (XAResource) branches.get(key);
                    try {
                        resourceManager.commit((Xid) key, false);
                    } catch(Throwable e) {
                        String logMessage = Messages.format
                            (SlideTransaction.class.getName() + ".commitFail",
                             resourceManager, getXAErrorCode(e), toString());
                        transactionManager.getLogger().log
                            (logMessage, e, LOG_CHANNEL, Logger.WARNING);
                        exceptions.addElement(e);
                        fail = true;
                    }
                }
                status = Status.STATUS_COMMITTED;
            }
           
        }
       
        // Call synchronized objects afterCompletion
        syncList = synchronizationObjects.elements();
        while (syncList.hasMoreElements()) {
            Synchronization sync =
                (Synchronization) syncList.nextElement();
            sync.afterCompletion(status);
        }
       
        // Parsing exception and throwing an appropriate exception
        enum = exceptions.elements();
        if (enum.hasMoreElements()) {
            if ((status == Status.STATUS_ROLLEDBACK) && (!fail))
                throw new RollbackException();
            if (status == Status.STATUS_ROLLEDBACK)
                throw new HeuristicRollbackException();
            if ((status == Status.STATUS_COMMITTED) && (fail))
                throw new HeuristicMixedException();
        }
       
    }
   
   
    /**
     * Delist the resource specified from the current transaction associated
     * with the calling thread.
     *
     * @param xaRes The XAResource object representing the resource to delist
     * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL
     * @exception IllegalStateException Thrown if the transaction in the
     * target object is inactive.
     * @exception SystemException Thrown if the transaction manager encounters
     * an unexpected error condition.
     */
    public boolean delistResource(XAResource xaRes, int flag)
        throws IllegalStateException, SystemException {
       
        //System.out.println(this + "  DELIST " + xaRes);
       
        // Check status ACTIVE
        if (status != Status.STATUS_ACTIVE)
            throw new IllegalStateException();
       
        Xid xid = (Xid) activeBranches.get(xaRes);
       
        if (xid == null)
            throw new IllegalStateException();
       
        activeBranches.remove(xaRes);
       
        if (transactionManager.getLogger().isEnabled
                (LOG_CHANNEL, Logger.DEBUG)) {
            String logMessage = Messages.format
                (SlideTransaction.class.getName() + ".delist", xaRes,
                 getXAFlag(flag), toString());
            transactionManager.getLogger().log
                (logMessage, LOG_CHANNEL, Logger.DEBUG);
        }
       
        XAException exception = null;
       
        try {
            xaRes.end(xid, flag);
        } catch (XAException e) {
            exception = e;
        }
       
        if (exception != null) {
            String logMessage = Messages.format
                (SlideTransaction.class.getName() + ".delistFail",
                 xaRes, getXAErrorCode(exception), toString());
            transactionManager.getLogger().log
                (logMessage, LOG_CHANNEL, Logger.WARNING);
            return false;
        }
       
        if (flag == XAResource.TMSUSPEND)
            suspendedResources.put(xaRes, xid);
       
        //System.out.println("Delisted ok(" + this + ") = " + xaRes + " xid: " + xid);
       
        return true;
       
    }
   
   
    /**
     * Enlist the resource specified with the current transaction context of
     * the calling thread.
     *
     * @param xaRes The XAResource object representing the resource to delist
     * @return true if the resource was enlisted successfully; otherwise false.
     * @exception RollbackException Thrown to indicate that the transaction
     * has been marked for rollback only.
     * @exception IllegalStateException Thrown if the transaction in the
     * target object is in prepared state or the transaction is inactive.
     * @exception SystemException Thrown if the transaction manager
     * encounters an unexpected error condition.
     */
    public boolean enlistResource(XAResource xaRes)
        throws RollbackException, IllegalStateException, SystemException {
       
        //System.out.println(this + "  ENLIST " + xaRes);
       
        if (status == Status.STATUS_MARKED_ROLLBACK)
            throw new RollbackException();
       
        // Check status ACTIVE
        if (status != Status.STATUS_ACTIVE)
            throw new IllegalStateException();
       
        // Preventing two branches from being active at the same time on the
        // same resource manager
        Xid activeXid = (Xid) activeBranches.get(xaRes);
        if (activeXid != null)
            return false;
       
        boolean alreadyEnlisted = false;
        int flag = XAResource.TMNOFLAGS;
       
        Xid branchXid = (Xid) suspendedResources.get(xaRes);
       
        if (branchXid == null) {
            Enumeration enum = enlistedResources.elements();
            while ((!alreadyEnlisted) && (enum.hasMoreElements())) {
                XAResource resourceManager = (XAResource) enum.nextElement();
                try {
                    if (resourceManager.isSameRM(xaRes)) {
                        flag = XAResource.TMJOIN;
                        alreadyEnlisted = true;
                    }
                } catch (XAException e) {
                }
            }
            branchXid = this.xid.newBranch(branchCounter++);
           
            //System.out.println(this + "  Creating new branch for " + xaRes);
           
        } else {
            alreadyEnlisted = true;
            flag = XAResource.TMRESUME;
            suspendedResources.remove(xaRes);
        }
       
        if (transactionManager.getLogger().isEnabled
                (LOG_CHANNEL, Logger.DEBUG)) {
            String logMessage = Messages.format
                (SlideTransaction.class.getName() + ".enlist", xaRes,
                 getXAFlag(flag), toString());
            transactionManager.getLogger().log
                (logMessage, LOG_CHANNEL, Logger.DEBUG);
        }
       
        try {
            //System.out.println("Starting(" + this + ") = " + xaRes + " Branch: " + branchXid + " Flag: " + flag);
           
            xaRes.start(branchXid, flag);
        } catch (XAException e) {
            String logMessage = Messages.format
                (SlideTransaction.class.getName() + ".enlistFail",
                 xaRes, getXAErrorCode(e), toString());
            transactionManager.getLogger().log
                (logMessage, LOG_CHANNEL, Logger.WARNING);
            return false;
        }
       
        if (!alreadyEnlisted) {
            enlistedResources.addElement(xaRes);
        }
       
        branches.put(branchXid, xaRes);
        activeBranches.put(xaRes, branchXid);
       
        return true;
       
    }
   
   
    /**
     * Roll back the transaction associated with the current thread. When
     * this method completes, the thread becomes associated with no
     * transaction.
     *
     * @exception SecurityException Thrown to indicate that the thread is not
     * allowed to commit the transaction.
     * @exception IllegalStateException Thrown if the current thread is not
     * associated with a transaction.
     * @exception SystemException Thrown if the transaction manager encounters
     * an unexpected error condition.
     */
    public void rollback()
        throws SecurityException, IllegalStateException, SystemException {
       
        //System.out.println(this + "  ROLLBACK ");
       
        // Check status ACTIVE
        if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK)
            throw new IllegalStateException();
       
        Vector exceptions = new Vector();
       
        Enumeration enum = branches.keys();
       
        status = Status.STATUS_ROLLING_BACK;
        while (enum.hasMoreElements()) {
            Xid xid = (Xid) enum.nextElement();
            XAResource resourceManager = (XAResource) branches.get(xid);
            try {
                resourceManager.rollback(xid);
            } catch (Throwable e) {
                exceptions.addElement(e);
                String logMessage = Messages.format
                    (SlideTransaction.class.getName() + ".rollbackFail",
                     resourceManager, getXAErrorCode(e), toString());
                transactionManager.getLogger().log
                    (logMessage, LOG_CHANNEL, Logger.WARNING);
            }
        }
        status = Status.STATUS_ROLLEDBACK;
       
    }
   
   
    /**
     * Modify the transaction associated with the current thread such that
     * the only possible outcome of the transaction is to roll back the
     * transaction.
     *
     * @exception IllegalStateException Thrown if the current thread is not
     * associated with a transaction.
     * @exception SystemException Thrown if the transaction manager encounters
     * an unexpected error condition.
     */
    public void setRollbackOnly()
        throws IllegalStateException, SystemException {
        status = Status.STATUS_MARKED_ROLLBACK;
    }
   
   
    /**
     * Obtain the status of the transaction associated with the current thread.
     *
     * @exception SystemException Thrown if the transaction manager encounters
     * an unexpected error condition.
     * @return The transaction status. If no transaction is associated with
     * the current thread, this method returns the Status.NoTransaction value.
     */
    public int getStatus()
        throws SystemException {
        return status;
    }
   
   
    /**
     * Register a synchronization object for the transaction currently
     * associated with the calling thread. The transction manager invokes the
     * beforeCompletion method prior to starting the transaction commit
     * process. After the transaction is completed, the transaction manager
     * invokes the afterCompletion method.
     *
     * @param sync The Synchronization object for the transaction associated
     * with the target object.
     * @exception RollbackException Thrown to indicate that the transaction
     * has been marked for rollback only.
     * @exception IllegalStateException Thrown if the transaction in the
     * target object is in prepared state or the transaction is inactive.
     * @exception SystemException Thrown if the transaction manager encounters
     * an unexpected error condition.
     */
    public void registerSynchronization(Synchronization sync)
        throws RollbackException, IllegalStateException, SystemException {
       
        if (status == Status.STATUS_MARKED_ROLLBACK)
            throw new RollbackException();
       
        if (status != Status.STATUS_ACTIVE)
            throw new IllegalStateException();
       
        synchronizationObjects.addElement(sync);
       
    }
   
   
    // --------------------------------------------------------- Public Methods
   
   
    /**
     * Return a String representation of the error code contained in a
     * XAException.
     */
    public static String getXAErrorCode(Throwable throww) {
        String result = null;
        if (throww instanceof XAException)
            result = getXAErrorCode((XAException)throww);
        else {
            StringWriter sw = new StringWriter();
            throww.printStackTrace( new PrintWriter(sw, true) ); //autoFlush=true
            result = sw.toString();
        }
        return result;
    }
    /**
     * Return a String representation of the error code contained in a
     * XAException.
     */
    public static String getXAErrorCode(XAException xae) {
        switch (xae.errorCode) {
            case XAException.XA_HEURCOM:
                return "XA_HEURCOM";
            case XAException.XA_HEURHAZ:
                return "XA_HEURHAZ";
            case XAException.XA_HEURMIX:
                return "XA_HEURMIX";
            case XAException.XA_HEURRB:
                return "XA_HEURRB";
            case XAException.XA_NOMIGRATE:
                return "XA_NOMIGRATE";
            case XAException.XA_RBBASE:
                return "XA_RBBASE";
            case XAException.XA_RBCOMMFAIL:
                return "XA_RBCOMMFAIL";
            case XAException.XA_RBDEADLOCK:
                return "XA_RBBEADLOCK";
            case XAException.XA_RBEND:
                return "XA_RBEND";
            case XAException.XA_RBINTEGRITY:
                return "XA_RBINTEGRITY";
            case XAException.XA_RBOTHER:
                return "XA_RBOTHER";
            case XAException.XA_RBPROTO:
                return "XA_RBPROTO";
            case XAException.XA_RBTIMEOUT:
                return "XA_RBTIMEOUT";
            case XAException.XA_RDONLY:
                return "XA_RDONLY";
            case XAException.XA_RETRY:
                return "XA_RETRY";
            case XAException.XAER_ASYNC:
                return "XAER_ASYNC";
            case XAException.XAER_DUPID:
                return "XAER_DUPID";
            case XAException.XAER_INVAL:
                return "XAER_INVAL";
            case XAException.XAER_NOTA:
                return "XAER_NOTA";
            case XAException.XAER_OUTSIDE:
                return "XAER_OUTSIDE";
            case XAException.XAER_PROTO:
                return "XAER_PROTO";
            case XAException.XAER_RMERR:
                return "XAER_RMERR";
            case XAException.XAER_RMFAIL:
                return "XAER_RMFAIL";
            default:
                return "UNKNOWN";
        }
    }
   
   
    /**
     * Return a String representation of a flag.
     */
    public static String getXAFlag(int flag) {
        switch (flag) {
            case XAResource.TMENDRSCAN:
                return "TMENDRSCAN";
            case XAResource.TMFAIL:
                return "TMFAIL";
            case XAResource.TMJOIN:
                return "TMJOIN";
            case XAResource.TMNOFLAGS:
                return "TMNOFLAGS";
            case XAResource.TMONEPHASE:
                return "TMONEPHASE";
            case XAResource.TMRESUME:
                return "TMRESUME";
            case XAResource.TMSTARTRSCAN:
                return "TMSTARTRSCAN";
            case XAResource.TMSUCCESS:
                return "TMSUCCESS";
            case XAResource.TMSUSPEND:
                return "TMSUSPEND";
            default:
                return "UNKNOWN";
        }
    }
   
   
    /**
     * Print the Transaction object in a debugger friendly manner
     */
    public String toString() {
        return "Transaction " + currentTransactionNumber
            + " xid " + xid + " in thread " + currentThreadName +
            (currentThreadName.equals(Thread.currentThread().getName())?"":
                 " current= " + Thread.currentThread().getName());
    }
   
   
}


TOP

Related Classes of org.apache.slide.transaction.SlideTransaction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.