com.newmainsoftech.spray.slingong.datastore.Slim3PlatformTransactionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.newmainsoftech.spray.slingong.datastore.Slim3PlatformTransactionManager.java

Source

/*
 * Copyright (C) 2011-2013 NewMain Softech
 *
 * 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 com.newmainsoftech.spray.slingong.datastore;

import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slim3.datastore.Datastore;
import org.slim3.datastore.GlobalTransaction;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.InvalidTimeoutException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.SmartTransactionObject;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.google.apphosting.api.DeadlineExceededException;

/* 
 * Need to create Aspect to check whether transactional annotation values are supported values. 
 *    Not supporting values
 *       
 *       TransactionDefinition.PROPAGATION_NESTED
 *       SYNCHRONIZATION_ALWAYS
 * 
 * Also create Aspect to alert the attempt of persisting data in read-only transaction. 
 * 
 * About deadline setting
 *    - How deadline handling is implemented in AbstractPlatformTransactionManager needs to be researched. 
 *    - Log the warning, when there is active global transaction, saying that the new setting value for 
 *    the deadline time-out won't be applied to current active global transaction but new global transaction 
 *    being created.  
 * 
 * AbstractPlatformTransactionManager:
 * http://static.springsource.org/spring/docs/current/api/org/springframework/transaction/support/AbstractPlatformTransactionManager.html
   AbstractPlatformTransactionManager class provides the following work-flow handling:
   * determines if there is an existing transaction;
   *    This is not ture; AbstractPlatformTransactionManager's default isExistingTransaction returns false for all cases.
   * applies the appropriate propagation behavior;
   * suspends and resumes transactions if necessary;
   * checks the rollback-only flag on commit;
   * applies the appropriate modification on rollback (actual rollback or setting rollback-only);
   * triggers registered synchronization callbacks (if transaction synchronization is active). 
 * 
 * http://static.springsource.org/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-programmatic
 * Simply pass the implementation of the PlatformTransactionManager you are using to your bean through 
 * a bean reference. Then, using the TransactionDefinition and TransactionStatus objects you can initiate 
 * transactions, roll back, and commit.
   DefaultTransactionDefinition def = new DefaultTransactionDefinition();
   // explicitly setting the transaction name is something that can only be done programmatically
   def.setName( "SomeTxName");
   def.setPropagationBehavior( TransactionDefinition.PROPAGATION_REQUIRED);
       
   TransactionStatus status = txManager.getTransaction( def);
   try {
 // execute your business logic here
   }
   catch( MyException ex) {
 txManager.rollback( status);
 throw ex;
   }
   txManager.commit( status);
 *    
 * 
 * http://static.springsource.org/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-strategies
   public interface PlatformTransactionManager {
 TransactionStatus getTransaction( TransactionDefinition definition) throws TransactionException;
 void commit( TransactionStatus status) throws TransactionException;
 void rollback( TransactionStatus status) throws TransactionException;
   }   
 * 
   The TransactionDefinition interface specifies (Understanding these concepts is essential to using 
   the Spring Framework or any transaction management solution):
  - Isolation: 
     The degree to which this transaction is isolated from the work of other transactions. 
     For example, can this transaction see uncommitted writes from other transactions?
  - Propagation: 
     Typically, all code executed within a transaction scope will run in that transaction. 
     However, you have the option of specifying the behavior in the event that a transactional 
     method is executed when a transaction context already exists. 
     For example, code can continue running in the existing transaction (the common case); or 
     the existing transaction can be suspended and a new transaction created. 
     Spring offers all of the transaction propagation options familiar from EJB CMT. 
     To read about the semantics of transaction propagation in Spring, see 
     Section 10.5.7, "Transaction propagation".
  - Timeout: 
     How long this transaction runs before timing out and being rolled back automatically by 
     the underlying transaction infrastructure.
  - Read-only status: 
     A read-only transaction can be used when your code reads but does not modify data. 
     Read-only transactions can be a useful optimization in some cases, such as when you are 
     using Hibernate. 
 * 
   public interface TransactionStatus extends SavepointManager {
  boolean isNewTransaction();
  boolean hasSavepoint();
  void setRollbackOnly();
  boolean isRollbackOnly();
  void flush();
  boolean isCompleted();
   }
 * 
 * http://www.javaworld.com/javaworld/jw-04-2007/jw-04-xa.html
 * TransactionAttribute interface allows the application to specify which exceptions will cause a rollback and which ones will be committed. 
 * 
 * Exceptions handling can be referred at the following page: 
 *    http://publib.boulder.ibm.com/infocenter/wsdoc400/v6r0/topic/com.ibm.websphere.iseries.doc/info/ae/ae/rdat_dawp02.html
 * 
 * Made debug level log to info level since GAE/J cannot set logging level to debug or below.  
 * For detail refer to http://code.google.com/p/googleappengine/issues/detail?id=4591
 */
public class Slim3PlatformTransactionManager extends AbstractPlatformTransactionManager {
    public static final int GAEJ_REQUEST_DEADLINE = 30; // seconds

    public Slim3PlatformTransactionManager() {
        super();
        //      setTransactionSynchronization( SYNCHRONIZATION_NEVER);
        if (isNestedTransactionAllowed())
            setNestedTransactionAllowed(false);
        /* Assuring default value of nestedTransactionAllowed member field set to false, although 
         * current design supports nested transaction by using nested begin-and-commit/rollback.
         */
        if (isValidateExistingTransaction())
            setValidateExistingTransaction(false);
        /* Assuring default value of validateExistingTransaction member field set to false, although 
         * current design ignores the read-only setting (since Slim3 does not support restricting 
         * operation to read-only) and just logs warning for potential violation.
         */
    }

    static class Slim3GlobalTransactionObject implements SmartTransactionObject {
        // Actual Slim3's GlobalTransaction object will be obtained via Slim3 Datastore class
        private String globalTransactionIdStr = null;

        public final String getGlobalTransactionIdStr() {
            return globalTransactionIdStr;
        }

        public final void setGlobalTransactionIdStr(String globalTransactionIdStr) {
            this.globalTransactionIdStr = globalTransactionIdStr;
        }

        public Slim3GlobalTransactionObject() {
        }

        public Slim3GlobalTransactionObject(String globalTransactionIdStr) {
            this.globalTransactionIdStr = globalTransactionIdStr;
        }

        // Implementation of SmartTransactionObject -----------------------------------------------
        @Override
        public void flush() { // Do nothing
        }

        private boolean rollbackOnly = false;

        @Override
        public boolean isRollbackOnly() {
            return rollbackOnly;
        }

        public void setRollbackOnly(boolean rollbackOnly) {
            this.rollbackOnly = rollbackOnly;
        }
        // ----------------------------------------------------------------------------------------
    } // static class Slim3GlobalTransactionObject implements SmartTransactionObject

    /** 
     * If Slim3GlobalTransactionObject object corresponding to the particular GlobalTransaction ID string 
     * does not exist in slim3GtxObjMapThreadLocal, then that means one of the following cases:
     *    - Slim3GlobalTransactionObject object has not been created for that GlobalTransaction object yet.
     *    - That GlobalTransaction object has already been successfully committed.
     *    - That GlobalTransaction object has failed on commit and either one of next cases happened:
     *       - Rolled back automatically by beneath Slim3 framework for 
     *         ConcurrentModificationException or DeadlineExceededException.
     *       - GlobalTransaction object became inactive.
     */
    protected static final ThreadLocal<Map<String, Slim3GlobalTransactionObject>> slim3GtxObjMapThreadLocal = new ThreadLocal<Map<String, Slim3GlobalTransactionObject>>() {
        @Override
        protected Map<String, Slim3GlobalTransactionObject> initialValue() {
            return Collections.synchronizedMap(new HashMap<String, Slim3GlobalTransactionObject>());
        }
    };

    //TODO Need to review the timing of each statement calling sweepSlim3GtxObjMapThreadLocal method
    /**
     * Clean up elements in slim3GtxObjMapThreadLocal member field to only ones corresponding to 
     * active GlobalTransaction instances in order to prevent memory leak. 
     * Thereby, caution needs to be required to call this. There will be timing that GlobalTransaction 
     * instances is inactive but the transaction on that GlobalTransaction instances is not completed yet. 
     * For an example, GlobalTransaction object may become inactive after encountering 
     * ConcurrentModificationException exception at its commit before corresponding transaction becomes  
     * complete state after roll back attempt for that exception. 
     */
    protected void sweepSlim3GtxObjMapThreadLocal() {
        Collection<GlobalTransaction> globalTransactions = Collections
                .synchronizedCollection(Datastore.getActiveGlobalTransactions());
        Set<String> gtxIdSet = new HashSet<String>();
        for (GlobalTransaction gtxObj : globalTransactions) {
            gtxIdSet.add(gtxObj.getId());
        } // for
        synchronized (slim3GtxObjMapThreadLocal) {
            if (gtxIdSet.size() > 0) {
                //TODO I need to test the case that slim3GtxObjMapThreadLocal contains less elements than gtxIdSet
                slim3GtxObjMapThreadLocal.get().keySet().retainAll(gtxIdSet);
            } else {
                slim3GtxObjMapThreadLocal.get().clear();
            }
        } // synchronized
    } // protected void sweepSlim3GtxObjMapThreadLocal()

    protected enum GlobalTransactionState {
        GlobalTransactionInstance, ActiveGlobalTransaction, CurrentGlobalTransaction
    }

    protected Set<GlobalTransactionState> getGlobalTransactionStates(final GlobalTransaction globalTransaction) {
        Set<GlobalTransactionState> gtxStateSet = new HashSet<GlobalTransactionState>();

        if (globalTransaction instanceof GlobalTransaction) {
            gtxStateSet.add(GlobalTransactionState.GlobalTransactionInstance);

            GlobalTransaction gtx = Datastore.getCurrentGlobalTransaction();
            if (gtx instanceof GlobalTransaction) {
                if (globalTransaction.getId().equals(gtx.getId())) {
                    gtxStateSet.add(GlobalTransactionState.CurrentGlobalTransaction);
                }
            }

            if (globalTransaction.isActive()) {
                gtxStateSet.add(GlobalTransactionState.ActiveGlobalTransaction);
            }
        }

        return gtxStateSet;
    } // protected Set<GlobalTransactionState> getGlobalTransactionStates( final GlobalTransaction globalTransaction)

    protected void validateIsolationLevel(final int isolationLevel) throws TransactionException {
        if ((isolationLevel != TransactionDefinition.ISOLATION_DEFAULT)
                && (isolationLevel != TransactionDefinition.ISOLATION_SERIALIZABLE)) {
            throw new InvalidIsolationLevelException(String.format(
                    "%1$d is not among the supported isolation levels; "
                            + "only ISOLATION_DEFAULT (%2$d) and ISOLATION_SERIALIZABLE (%3$d) are supported.",
                    isolationLevel, TransactionDefinition.ISOLATION_DEFAULT,
                    TransactionDefinition.ISOLATION_SERIALIZABLE));
        }
    } // protected void validateIsolationLevel( final int isolationLevel) throws TransactionException

    protected void setSlim3AsnyncTimeout(final int timeoutToUse) throws TransactionException {
        if (timeoutToUse == TransactionDefinition.TIMEOUT_DEFAULT) {
            Datastore.deadline(null);
        } else if ((timeoutToUse > 0) || (timeoutToUse < GAEJ_REQUEST_DEADLINE)) {
            Datastore.deadline((new Integer(timeoutToUse)).doubleValue());
        } else {
            throw new InvalidTimeoutException(
                    String.format(
                            "The specified time-out value for the transaction is not valid; "
                                    + "it should be bigger than 0[sec] and less than %1$d[sec].",
                            GAEJ_REQUEST_DEADLINE),
                    timeoutToUse);
        }
    } // protected void setSlim3AsnyncTimeout( final int timeoutToUse) throws TransactionException

    /* Although doBegin method will be called right after newTransactionStatus method, transaction Object 
     * argument does not refer to the GlobalTransaction instance newly created by newTransactionStatus 
     * method but refer to the GlobalTransaction instance available before newTransactionStatus method. 
     * 
     * doBegin method became doing nothing but sanity check on status of transaction Object local field.
     * 
     * As AbstractPlatformTransactionManager class of Spring ver 3.0.4, possible propagation behaviors 
     * that will reach the doBegin method are:
     *    - TransactionDefinition.PROPAGATION_REQUIRED
     *    - TransactionDefinition.PROPAGATION_REQUIRES_NEW
     *    - TransactionDefinition.PROPAGATION_NESTED 
     * (non-Javadoc)
     * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doBegin(java.lang.Object, org.springframework.transaction.TransactionDefinition)
     */
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
        // Validation on transaction argument -----------------------------------------------------
        /* transaction Object should be either 
         *       null
         *          either TransactionDefinition.PROPAGATION_REQUIRED, 
         *          TransactionDefinition.PROPAGATION_REQUIRES_NEW or TransactionDefinition.PROPAGATION_NESTED
         *       active GlobalTransactoin object (one before current one)
         *          either TransactionDefinition.PROPAGATION_REQUIRES_NEW or 
         *          TransactionDefinition.PROPAGATION_NESTED
         *             if TransactionDefinition.PROPAGATION_NESTED case, then 
         *             useSavepointForNestedTransaction method returns false
         */
        GlobalTransaction gtx = (GlobalTransaction) transaction;
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(gtx);
        GlobalTransaction currentGtx = Datastore.getCurrentGlobalTransaction();
        Set<GlobalTransactionState> currentGtxStateSet = getGlobalTransactionStates(currentGtx);
        if (!currentGtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)) {
            throw new IllegalTransactionStateException(String.format(
                    "Unexpected system state: getCurrentGlobalTransaction method of Slim3 Datastore "
                            + "returned inactive GlobalTransaction instance (ID:%3$s). Expected it to return "
                            + "active GlobalTransaction instance as new GlobalTransaction instance has begun "
                            + "by newTransactionStatus method.",
                    (currentGtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)
                            ? currentGtx.getId()
                            : "null")));
        }
        String gtxIdStr = (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId()
                : "null");
        if (gtxIdStr.equals(currentGtx.getId())) {
            throw new IllegalTransactionStateException(String.format(
                    "Unexpected system state: the transaction Object argument refers to current "
                            + "active GlobalTransaction instance (ID:%1$s). Expected it not to refer to "
                            + "current active GlobalTransaction instance rather refer to the GlobalTransaction "
                            + "instance available before newTransaction method execution or hold null value.",
                    gtxIdStr));
        }

        if (!gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
            switch (definition.getPropagationBehavior()) {
            case TransactionDefinition.PROPAGATION_REQUIRED:
            case TransactionDefinition.PROPAGATION_REQUIRES_NEW:
            case TransactionDefinition.PROPAGATION_NESTED:
                break;
            default:
                throw new IllegalTransactionStateException(String.format(
                        "Unexpected system state: found that the %1$s propagation behavior (%2$d) was "
                                + "specified when the transaction Object argument holds null value. Expected "
                                + "propagation behavior to be either PROPAGATION_REQUIRED (%3$d), "
                                + "PROPAGATION_REQUIRES_NEW (%4$d) or PROPAGATION_NESTED (%5$d) when the "
                                + "transaction Object argument holds null value.",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior(), TransactionDefinition.PROPAGATION_REQUIRED,
                        TransactionDefinition.PROPAGATION_REQUIRES_NEW, TransactionDefinition.PROPAGATION_NESTED));
            } // switch
        } else if (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)) {
            switch (definition.getPropagationBehavior()) {
            case TransactionDefinition.PROPAGATION_REQUIRES_NEW:
            case TransactionDefinition.PROPAGATION_NESTED:
                break;
            default:
                throw new IllegalTransactionStateException(String.format(
                        "Unexpected system state: found that the %1$s propagation behavior (%2$d) was "
                                + "specified when the transaction Object argument holds active "
                                + "GlobalTransaction instance (ID:%3$s). Expected propagation behavior to be "
                                + "either PROPAGATION_REQUIRES_NEW (%4$d) or PROPAGATION_NESTED (%5$d) when "
                                + "the transaction Object argument holds active GlobalTransaction instance.",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior(), gtx.getId(),
                        TransactionDefinition.PROPAGATION_REQUIRES_NEW, TransactionDefinition.PROPAGATION_NESTED));
            } // switch
        } else {
            throw new IllegalTransactionStateException(
                    String.format("Unexpected system state: the transaction Object argument holds inactive "
                            + "GlobalTransaction instance (ID:%1$s). Expected it to hold either null or "
                            + "active GlobalTransaction instance.", gtx.getId()));
        }
        // ----------------------------------------------------------------------------------------
    } // protected void doBegin( Object transaction, TransactionDefinition transactionDefinition)

    /*
     * (non-Javadoc)
     * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus)
     */
    @Override
    protected void doCommit(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
        GlobalTransaction gtx = (GlobalTransaction) defaultTransactionStatus.getTransaction();
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(gtx);

        // Sanity check on precondition -----------------------------------------------------------
        /*
         * This will be called at outermost transaction boundary (defaultTransactionStatus.isNewTransaction() 
         * will return true). 
         * When defaultTransactionStatus has been set for roll back, roll back doesn't need to be handled 
         * within doCommit method. 
         *    When either defaultTransactionStatus's isGlobalRollbackOnly and shouldCommitOnGlobalRollbackOnly 
         *    returns true or its isLocalRollbackOnly returns true, then logic flow won't reach here and 
         *    roll back should have been performed by doRollback.
         */
        if (defaultTransactionStatus.isRollbackOnly()) {
            throw new TransactionSystemException(String.format(
                    "Unexpected system state: the transaction for the GlobalTransaction "
                            + "instance (ID:%1$s) has been marked as roll-back only "
                            + "(LocalRollbackOnly:%2$b, GlobalRollbackOnly:%3$b).",
                    (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId() : "null"),
                    defaultTransactionStatus.isLocalRollbackOnly(),
                    defaultTransactionStatus.isGlobalRollbackOnly()));
        }
        if (!defaultTransactionStatus.isNewTransaction()) {
            throw new TransactionSystemException(String.format(
                    "Unexpected system state: attempted to commit from participation of an existing "
                            + "transaction (of which GlobalTransacion Id is %1$s).",
                    (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId()
                            : "null")));
        }
        // ----------------------------------------------------------------------------------------

        // Sanity check on gtx GlobalTransaction instance -----------------------------------------
        if (!gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)) {
            String message;

            if (!gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                message = "Unexpected system state: The GlobalTransaction object passed as the transaction "
                        + "Object argument is null.";
            } else {
                message = String
                        .format("Unexpected system state: The GlobalTransaction instance (ID:%1$s) passed as the "
                                + "transaction Object argument is inactive.", gtx.getId());
            }

            throw new IllegalTransactionStateException(message);
        }
        if (!gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction)) {
            /* The one possible case of the control flow reaching here is that GlobalTransaction object 
             * is manually instantiated at the outside of Spring transaction framework, and it is left active.
             * 
             * In nesting global transaction, easy to yield ConcurrentModificationException without care.
             * Nesting global transaction should be less necessary, since Slim3 isolate between (Global) 
             * transactions and cannot change that isolation setting, and this uses Slim3 GlobalTransaction, 
             * as GAE/J datastore API is not provide suspend feature as well.
             */

            GlobalTransaction currentGtx = Datastore.getCurrentGlobalTransaction();
            if (logger.isWarnEnabled()) {
                logger.warn(String.format(
                        "Though active GlobalTransaction instance (ID:%1$s) is passed as transaction "
                                + "Object argument, it's not current GlobalTransaction instance (ID:%2$s).",
                        gtx.getId(), ((currentGtx instanceof GlobalTransaction) ? currentGtx.getId() : "null")));
            }
        }
        // ----------------------------------------------------------------------------------------
        boolean exceptionUp = false;
        try {
            gtx.commit();
            slim3GtxObjMapThreadLocal.get().remove(gtx.getId());
            if (logger.isInfoEnabled()) {
                logger.info(String.format("Slim3 GlobalTransaction (ID:%1$s) committed.", gtx.getId()));
            }
        } catch (Throwable throwable) {
            /* Set rollback only flag so calling processRollback method to roll back will occur at 
             * commit method by AnnotationTransactionAspect, even for other exceptions than ones specified 
             * for rollbackFor or rollbackForClassname attributes of @Transactional annotation.
             */
            defaultTransactionStatus.setRollbackOnly();

            exceptionUp = true;

            String message = String.format("Slim3 GlobalTransaction (ID:%1$s) failed on commit.", gtx.getId());
            if ((throwable instanceof ConcurrentModificationException)
                    || (throwable instanceof DeadlineExceededException)) {
                /* Slim3 should have already rolled back automatically on either
                 * ConcurrentModificationException or DeadlineExceededException.
                 * 
                 * About DeadlineExceededException case: It seems like no harm
                 * will be made even active global transaction at
                 * DeadlineExceededException case left behind. As looking
                 * briefly through the Slim3 source codes, the locks by that
                 * global transaction seems to be released while it rolls back
                 * by DatastoreFilter, and itself seem to be removed from
                 * TheadLocal stack of the global transactions. So, it won't be
                 * returned by such Datastore.getCurrentGlobalTransaction()
                 * method. See at
                 * http://groups.google.com/group/slim3-user/browse_frm
                 * /thread/e3d47d8f28c8e8d3
                 * /9e43553f3b56d1f2?tvc=1#9e43553f3b56d1f2 If it turns out
                 * opposite, then that active global transaction can be cleared
                 * some how at here for DeadlineExceededException case.
                 */

                message = String.format("Slim3 GlobalTransaction (ID:%1$s) failed on commit. %n"
                        + "Slim3 must have already rolled back automatically behind "
                        + "Spring transaction framework.", gtx.getId());
            }

            throw new TransactionSystemException(message, throwable);
        } finally {
            if (gtx.isActive()) {
                if (exceptionUp) {
                    if (logger.isInfoEnabled()) {
                        logger.info(String.format(
                                "GlobalTransaction instance (ID:%1$s) remains active after exception "
                                        + "durring commit attempt. That GlobalTransaction instance has not "
                                        + "been removed yet from slim3GtxObjMapThreadLocal member field.",
                                gtx.getId(), this.getClass().getName()));
                    }
                } else {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String.format(
                                "Unexpected system state: GlobalTransaction instance (ID:%1$s) "
                                        + "remains active even after commit attempt. That GlobalTransaction "
                                        + "instance was removed from slim3GtxObjMapThreadLocal member field.",
                                gtx.getId(), this.getClass().getName()));
                    }
                }
            }
        }
    } // protected void doCommit(DefaultTransactionStatus defaultTransactionStatus)

    /*
     * (non-Javadoc) Returns object containing information about existing
     * transaction what has already started before the current getTransaction
     * call.
     * 
     * @see
     * org.springframework.transaction.support.AbstractPlatformTransactionManager#doGetTransaction() 
     * 
     * doGetTransaction method will be called from getTransaction method what will return 
     * the currently active transaction according to Spring's PlatformTransactionManager document.
     */
    @Override
    protected Object doGetTransaction() throws TransactionException {
        GlobalTransaction currentGtx = Datastore.getCurrentGlobalTransaction();
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(currentGtx);
        if (!gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)) {
            return null;
        }
        Slim3GlobalTransactionObject gtxObj = slim3GtxObjMapThreadLocal.get().get(currentGtx.getId());
        if (gtxObj == null) {
            /* Case that current GlobalTransaction instance has been instantiated manually outside of 
             * Spring transaction framework.
             */
            if (logger.isWarnEnabled()) {
                logger.warn(String.format(
                        "GlobalTransaction instance (ID:%1$s), what was given by Slim3 as current "
                                + "GlobalTransaction instance, is not among ones managed by %2$s.",
                        currentGtx.getId(), this.getClass().getName()));
            }
            return null;
        }

        return currentGtx;
    } // protected Object doGetTransaction() throws TransactionException

    protected String getPropagationBehaviorStr(final int propagationBehavior) {
        String propagationBehaviorStr;
        switch (propagationBehavior) {
        case TransactionAttribute.PROPAGATION_MANDATORY:
            propagationBehaviorStr = "PROPAGATION_MANDATORY";
            break;
        case TransactionAttribute.PROPAGATION_NESTED:
            propagationBehaviorStr = "PROPAGATION_NESTED";
            break;
        case TransactionAttribute.PROPAGATION_NEVER:
            propagationBehaviorStr = "PROPAGATION_NEVER";
            break;
        case TransactionAttribute.PROPAGATION_NOT_SUPPORTED:
            propagationBehaviorStr = "PROPAGATION_NOT_SUPPORTED";
            break;
        case TransactionAttribute.PROPAGATION_REQUIRED:
            propagationBehaviorStr = "PROPAGATION_REQUIRED";
            break;
        case TransactionAttribute.PROPAGATION_REQUIRES_NEW:
            propagationBehaviorStr = "PROPAGATION_REQUIRES_NEW";
            break;
        case TransactionAttribute.PROPAGATION_SUPPORTS:
            propagationBehaviorStr = "PROPAGATION_SUPPORTS";
            break;
        default:
            propagationBehaviorStr = "Unkown";
        } // switch

        return propagationBehaviorStr;
    } // protected String getPropagationBehaviorStr( int propagationBehavior)

    protected GlobalTransaction beginGlogalTransaction(GlobalTransaction gtx,
            Set<GlobalTransactionState> gtxStateSet, TransactionDefinition definition)
            throws CannotCreateTransactionException {
        setSlim3AsnyncTimeout(definition.getTimeout());

        GlobalTransaction newGtx;
        try {
            newGtx = Datastore.beginGlobalTransaction();
        } catch (Throwable throwable) {
            String message;
            if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                message = String.format(
                        "Failure to begin new Slim3 GlobalTransaction instance under %1$s propagation "
                                + "behavior (%2$d) over the transaction what has already begun GlobalTransaction "
                                + "instance (ID:%3$s)",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior(), gtx.getId());
            } else {
                message = String.format(
                        "Failure to begin new Slim3 GlobalTransaction instance under "
                                + "%1$s propergation behavior (%2$d)",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior());
            }
            throw new CannotCreateTransactionException(message, throwable);
        }

        String newGtxIdStr = newGtx.getId();

        synchronized (slim3GtxObjMapThreadLocal) {
            Slim3GlobalTransactionObject gtxObj = new Slim3GlobalTransactionObject(newGtxIdStr);
            slim3GtxObjMapThreadLocal.get().put(newGtxIdStr, gtxObj);
        } // synchronized

        if (logger.isInfoEnabled()) {
            logger.info(String.format(
                    "Slim3 GlobalTransaction (ID:%1$s) began successfully under "
                            + "%2$s propergation behavior (%3$d).",
                    newGtx.getId(), getPropagationBehaviorStr(definition.getPropagationBehavior()),
                    definition.getPropagationBehavior()));
        }

        return newGtx;
    } // protected GlobalTransaction beginGlogalTransaction( ...)

    protected boolean validateCurrentActiveGlobalTransactionToParticipate(final GlobalTransaction gtx,
            final TransactionDefinition definition) throws IllegalTransactionStateException {
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(gtx);
        if ((!gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction))
                || (!gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction))) {
            throw new IllegalTransactionStateException(String.format(
                    "Though %1$s propergation behavior (%2$d) was specified, "
                            + "the GlobalTransaction instance (ID:%3$s) (what has been begun this "
                            + "transaction before) is %4$s %5$s one. Expected it to be current active " + "one.",
                    getPropagationBehaviorStr(definition.getPropagationBehavior()),
                    definition.getPropagationBehavior(), gtx.getId(),
                    (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                            : "not-current"),
                    (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                            : "inactive")));
        }

        return true;
    } // protected boolean validateCurrentActiveGlobalTransactionToParticipate( TransactionDefinition definition)

    @Override
    protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, Object transaction,
            boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) {
        // Validate isolation level and time-out settings 
        validateIsolationLevel(definition.getIsolationLevel());

        boolean actualNewSynchronization = newSynchronization
                && !TransactionSynchronizationManager.isSynchronizationActive();

        GlobalTransaction gtx = (GlobalTransaction) transaction;
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(gtx);

        GlobalTransaction newGtx;

        switch (definition.getPropagationBehavior()) {
        case TransactionAttribute.PROPAGATION_NESTED:
            if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                if (!isNestedTransactionAllowed()) {
                    throw new TransactionSystemException(String.format(
                            "Unexpected system state: the value of nestedTransactionAllowed boolean "
                                    + "member field is false. It should have been checked early as true within "
                                    + "handleExistingTransaction method, in order to provide the nested "
                                    + "propagation behavior over the transaction (what has already begun "
                                    + "GlobalTransaction instance (ID:%1$s)) by nested begin and commit/rollback calls.",
                            (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId()
                                    : "null")));
                }
                if (logger.isInfoEnabled()) {
                    logger.info(String.format(
                            "Going to provide nested propagation behavior by nested begin and "
                                    + "commit/rollback calls on new GlobalTransaction instance over the "
                                    + "transaction what has already begun GlobalTransaction instance (ID:%1$s).",
                            gtx.getId()));
                }
            }
            // Enter to below logic (for TransactionAttribute.PROPAGATION_REQUIRES_NEW case)
        case TransactionAttribute.PROPAGATION_REQUIRES_NEW:
            // Sanity check on system state
            if (!newTransaction) {
                throw new IllegalTransactionStateException(String.format(
                        "Unexpected system state: the value of newTransaction boolean member field "
                                + "is false for %1$s propagation behavior (%2$d). Exptected it to be true.",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior()));
            }

            // Sanity check on current GlobalTransaction instances
            if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                validateCurrentActiveGlobalTransactionToParticipate(gtx, definition);
            }

            // begin new GlobalTransaction
            newGtx = beginGlogalTransaction(gtx, gtxStateSet, definition);

            return new DefaultTransactionStatus(newGtx, newTransaction, actualNewSynchronization,
                    definition.isReadOnly(), debug, suspendedResources);
        case TransactionAttribute.PROPAGATION_REQUIRED:
            if (newTransaction) {
                // Sanity check on current GlobalTransaction instances
                if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                    throw new IllegalTransactionStateException(String.format(
                            "Unexpected system state: the value of newTransaction boolean member "
                                    + "field is true what direct to create new transaction for %1$s "
                                    + "propagation behavior (%2$d) though this transaction has already begun "
                                    + "the GlobalTransaction instance (ID:%3$s). Exptected it to be false and "
                                    + "participate to the existing transaction.",
                            getPropagationBehaviorStr(definition.getPropagationBehavior()),
                            definition.getPropagationBehavior(), gtx.getId()));
                }

                // begin new GlobalTransaction
                newGtx = beginGlogalTransaction(gtx, gtxStateSet, definition);

                return new DefaultTransactionStatus(newGtx, newTransaction, actualNewSynchronization,
                        definition.isReadOnly(), debug, suspendedResources);
            } else {
                // Sanity check on current GlobalTransaction instances
                validateCurrentActiveGlobalTransactionToParticipate(gtx, definition);

                return new DefaultTransactionStatus(gtx, newTransaction, actualNewSynchronization,
                        definition.isReadOnly(), debug, suspendedResources);
            }
        case TransactionAttribute.PROPAGATION_NEVER:
            if (newTransaction && !gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                return new DefaultTransactionStatus(null, newTransaction, actualNewSynchronization,
                        definition.isReadOnly(), debug, suspendedResources);
            } else {
                throw new IllegalTransactionStateException(String.format(
                        "Unexpected system state: for %1$s propagation behavior (%2$d), the value of "
                                + "newTransaction boolean member field is expected to be true (actual: %3$b) "
                                + "and transaction Object argument is expected to hold current active "
                                + "GlobalTransaction instance (actual: %4$s %5$s one (ID:%6$s)).",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior(), newTransaction,
                        (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                                : "not-current"),
                        (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                                : "inactive"),
                        (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId()
                                : "null")));
            }
        case TransactionAttribute.PROPAGATION_NOT_SUPPORTED:
            if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                throw new IllegalTransactionStateException(String.format(
                        "Unexpected system state: for %1$s propagation behavior (%2$d), the "
                                + "transaction Object argument is expected to hold null value (actual: "
                                + "%3$s %4$s GlobalTransaction instance (ID:%5$s)).",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior(),
                        (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                                : "not-current"),
                        (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                                : "inactive"),
                        gtx.getId()));
            }

            // Create DeafultTransactionState with null transaction
            return new DefaultTransactionStatus(null, newTransaction, actualNewSynchronization,
                    definition.isReadOnly(), debug, suspendedResources);
        case TransactionAttribute.PROPAGATION_SUPPORTS:
            if (newTransaction) {
                if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
                    throw new IllegalTransactionStateException(String.format(
                            "Unexpected system state: for %1$s propagation behavior (%2$d), when the "
                                    + "newTransaction is true, the transaction Object argument is expected to "
                                    + "hold null value (actual: %3$s %4$s GlobalTransaction instance (ID:%5$s)).",
                            getPropagationBehaviorStr(definition.getPropagationBehavior()),
                            definition.getPropagationBehavior(),
                            (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                                    : "not-current"),
                            (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                                    : "inactive"),
                            gtx.getId()));
                }
                return new DefaultTransactionStatus(null, newTransaction, actualNewSynchronization,
                        definition.isReadOnly(), debug, suspendedResources);
            } else {
                if (!gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)
                        || !gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction)) {
                    throw new IllegalTransactionStateException(String.format(
                            "Unexpected system state: for %1$s propagation behavior (%2$d), when the "
                                    + "newTransaction is false, the transaction Object argument is expected to "
                                    + "hold current active GlobalTransaction instance (actual: %3$s %4$s "
                                    + "GlobalTransaction instance (ID:%5$s)).",
                            getPropagationBehaviorStr(definition.getPropagationBehavior()),
                            definition.getPropagationBehavior(),
                            (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                                    : "not-current"),
                            (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                                    : "inactive"),
                            (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId()
                                    : "null")));
                }
                return new DefaultTransactionStatus(gtx, newTransaction, actualNewSynchronization,
                        definition.isReadOnly(), debug, suspendedResources);
            }

        case TransactionAttribute.PROPAGATION_MANDATORY:
            if (newTransaction || !gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)
                    || !gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction)) {
                throw new IllegalTransactionStateException(String.format(
                        "Unexpected system state: for %1$s propagation behavior (%2$d), the "
                                + "newTransaction is expected to be false (actual: %3$b) and the transaction "
                                + "Object argument is expected to hold current active GlobalTransaction "
                                + "instance (actual: %3$s %4$s GlobalTransaction instance (ID:%5$s)).",
                        getPropagationBehaviorStr(definition.getPropagationBehavior()),
                        definition.getPropagationBehavior(),
                        (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                                : "not-current"),
                        (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                                : "inactive"),
                        (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance) ? gtx.getId()
                                : "null")));
            }

            return new DefaultTransactionStatus(gtx, newTransaction, actualNewSynchronization,
                    definition.isReadOnly(), debug, suspendedResources);

        default:
            throw new IllegalTransactionStateException(String.format(
                    "Unknown propagation behavior (%1$d) is specified. Supported propagation "
                            + "behaviors is either PROPAGATION_MANDATORY (%2$d), PROPAGATION_NESTED (%3$d), "
                            + "PROPAGATION_NEVER (%4$d), PROPAGATION_NOT_SUPPORTED (%5$d), "
                            + "PROPAGATION_REQUIRED (%6$d), PROPAGATION_REQUIRES_NEW (%7$d), "
                            + "or PROPAGATION_SUPPORTS (%8$d).",
                    definition.getPropagationBehavior(), TransactionAttribute.PROPAGATION_MANDATORY,
                    TransactionAttribute.PROPAGATION_NESTED, TransactionAttribute.PROPAGATION_NEVER,
                    TransactionAttribute.PROPAGATION_NOT_SUPPORTED, TransactionAttribute.PROPAGATION_REQUIRED,
                    TransactionAttribute.PROPAGATION_REQUIRES_NEW, TransactionAttribute.PROPAGATION_SUPPORTS));

        } // switch
    } // protected DefaultTransactionStatus newTransactionStatus( ......)

    /*
     * isExistingTransaction method would be called by getTransaction method
     * what will return the currently active transaction according to Spring's
     * PlatformTransactionManager document.
     * 
     * If this isExistingTransaction method is not called from other methods than getTransaction method, 
     * then it can strip down to return false when transaction Object argument is null, and return true 
     * when transaction Object argument is GlobalTransaction instance.
     * 
     * @see
     * http://static.springsource.org/spring/docs/current/api/org/springframework
     * /transaction/PlatformTransactionManager.html#getTransaction%28org.
     * springframework.transaction.TransactionDefinition%29 I take that
     * "the currently active transaction" means the inner-most active
     * transaction. (non-Javadoc)
     * 
     * @see
     * org.springframework.transaction.support.AbstractPlatformTransactionManager
     * #isExistingTransaction(java.lang.Object)
     */
    @Override
    protected boolean isExistingTransaction(Object transaction) throws TransactionException {
        GlobalTransaction gtx = (GlobalTransaction) transaction;
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(gtx);
        if (gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
            if (slim3GtxObjMapThreadLocal.get().get(gtx.getId()) == null) {
                if (logger.isWarnEnabled()) {
                    logger.warn(String.format(
                            "GlobalTransaction instance (ID:%1$s), what is not among ones managed "
                                    + "by %2$s, was given as transaction Object argument.",
                            gtx.getId(), this.getClass().getName()));
                }

                return false;
            } else { // GlobalTransaction instance held by transaction Object argument is one managed by this
                if (!gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction)
                        || !gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String.format(
                                "GlobalTransaction instance (ID:%1$s) given as transaction Object "
                                        + "argument is %2$s %3$s instance.",
                                gtx.getId(),
                                (gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction) ? "current"
                                        : "not-current"),
                                (gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction) ? "active"
                                        : "inactive")));
                    }
                }

                return true;
            }
        }

        return false;
    } // protected boolean isExistingTransaction( Object transaction) throws TransactionException

    /*
     * Called by processRollback method or doRollbackOnCommitException method.
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.transaction.support.AbstractPlatformTransactionManager
     * #doSetRollbackOnly(org.springframework.transaction.support.
     * DefaultTransactionStatus)
     */
    @Override
    protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
        String message = String.format("Setting roll-back-only flag(s) up:");

        /*
         * Set AbstractTransactionStatus's rollbackOnly member field to true
         * what will make DefaultTransactionStatus's isLocalRollbackOnly method
         * returns true. AbstractPlatformTransactionManager's commit method
         * initiate roll back when its isLocalRollbackOnly method returns true.
         * 
         * However, the analysis of the AbstractPlatformTransactionManager's
         * logic flow makes me doubt calling this method will never occurs:
         * 
         * About the case called by processRollback method (that will be called
         * by commit method): commit method calls processRollback method when
         * DefaultTransactionStatus's isLocalRollbackOnly method returns true.
         * DefaultTransactionStatus's isLocalRollbackOnly method is also called by
         * processRollback method, in order for participating transaction to set
         * rollbackOnly member field what is done by calling this
         * doSetRollbackOnly method. Hence, it doesn't make much sense for this
         * case since, in that logic only, this will never be called.
         * 
         * About the case called by doRollbackOnCommitException method (that
         * will be called by processRollback method): When it's participating
         * transaction and globalRollbackOnParticipationFailure flag value is
         * true, then this method is called by doRollbackOnCommitException
         * method. The default value of globalRollbackOnParticipationFailure
         * flag is true what means that the transaction originator cannot make
         * the transaction commit anymore once the transaction marked globally
         * as rollback-only. However, doRollbackOnCommitException method is
         * called by processCommit method and, according to Spring's
         * AbstractPlatformTransactionManager document (@see
         * http://static.springsource
         * .org/spring/docs/current/api/org/springframework
         * /transaction/support/AbstractPlatformTransactionManager
         * .html#setRollbackOnCommitFailure%28boolean%29) the roll back on
         * commit failure typically not necessary and thus to be avoided, as it
         * can potentially override the commit exception with a subsequent
         * rollback exception. I would take this as it means rather
         * intentionally initiating roll back after recognizing commit failure
         * is the recommended way than letting Spring automatically roll back
         * after commit failure, however it does not make sense since, because
         * commit is automatically handled by Spring, how it can know when and
         * how to roll back. It should be automatically rolled back by Spring
         * when failure occurs on the commit that Spring automatically performs.
         */
        status.setRollbackOnly();
        message = message + String.format("%n%tDefaultTransactionStatus object is marked as roll-back-only.");

        GlobalTransaction gtx = (GlobalTransaction) status.getTransaction();
        if (gtx instanceof GlobalTransaction) {
            String gtxIdStr = gtx.getId();
            Slim3GlobalTransactionObject slim3GtxObj = slim3GtxObjMapThreadLocal.get().get(gtxIdStr);
            if (slim3GtxObj != null) {
                slim3GtxObj.setRollbackOnly(true);
                slim3GtxObjMapThreadLocal.get().put(gtxIdStr, slim3GtxObj);
                message = message + String
                        .format("%n%tSlim3GlobalTransactionObject object for GlobalTransaction instance (ID:%1$s) "
                                + "is marked as roll-back-only.", gtxIdStr);
            } else {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            String.format("Inconsistent system state: GlobalTransaction instance (ID:%1$s) held "
                                    + "by state DefaultTransactionStatus argument is not among ones managed "
                                    + "by %2$s.", gtxIdStr, this.getClass().getName()));
                }
            }
        }

        if (logger.isInfoEnabled())
            logger.info(message);

    } // protected void doSetRollbackOnly(DefaultTransactionStatus status)

    protected GlobalTransaction findGlobalTransaction(final String gtxIdStr) {
        Slim3GlobalTransactionObject gtxObj = slim3GtxObjMapThreadLocal.get().get(gtxIdStr);
        if (gtxObj == null)
            return null;

        for (GlobalTransaction gtx : Datastore.getActiveGlobalTransactions()) {
            if (gtxIdStr.equals(gtx.getId()))
                return gtx;
        } // for

        sweepSlim3GtxObjMapThreadLocal();
        return null;
    } // /protected GlobalTransaction findGlobalTransaction( final String gtxIdStr)

    /*
     * (non-Javadoc)
     * Called by processRollback method or doRollbackOnCommitException method
     * 
     * @see
     * org.springframework.transaction.support.AbstractPlatformTransactionManager
     * #
     * doRollback(org.springframework.transaction.support.DefaultTransactionStatus
     * )
     */
    @Override
    protected void doRollback(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
        // Sanity check on precondition -----------------------------------------------------------
        /* When logic flow got here, it should be not the case of participating existing transaction that  
         * means the target method is the outer most transactional method what began a transaction process 
         * at the first, and that also means newTransaction boolean member field of DefaultTransactionStatus 
         * object should be true.
         */
        if (!defaultTransactionStatus.isNewTransaction()) {
            // Could be caused by unaware code change on org.springframework.transaction.support.AbstractPlatformTransactionManager
            throw new TransactionSystemException(String.format(
                    "Unexpected system state: attempting to roll back from participation of an "
                            + "existing transaction (of which GlobalTransacion Id is %1$s).",
                    ((defaultTransactionStatus.getTransaction() instanceof GlobalTransaction)
                            ? ((GlobalTransaction) defaultTransactionStatus.getTransaction()).getId()
                            : "null")));
        }
        // ----------------------------------------------------------------------------------------

        // Check on GlobalTransaction instance ----------------------------------------------------
        GlobalTransaction gtx = (GlobalTransaction) defaultTransactionStatus.getTransaction();
        Set<GlobalTransactionState> gtxStateSet = getGlobalTransactionStates(gtx);
        if (!gtxStateSet.contains(GlobalTransactionState.GlobalTransactionInstance)) {
            throw new TransactionSystemException(
                    "Inconsistent status: defaultTransactionStatus argument is not holding "
                            + "GlobalTransaction instance.");
        }

        String gtxIdStr = gtx.getId();
        Map<String, Slim3GlobalTransactionObject> slim3GtxObjMap = slim3GtxObjMapThreadLocal.get();
        Slim3GlobalTransactionObject slim3GtxObj = slim3GtxObjMap.get(gtxIdStr);
        if (slim3GtxObj == null) {
            if (logger.isErrorEnabled()) {
                logger.error(String.format("Encountered inconsistent status: GlobalTransaction instance (ID:%1$s) "
                        + "held by defaultTransactionStatus argument is not among ones managed " + "by %2$s.",
                        gtxIdStr, this.getClass().getName()));
            }
        }

        if (!gtxStateSet.contains(GlobalTransactionState.ActiveGlobalTransaction)) {
            /* Reminder: gtx here can be inactive by the ConcurrentModificationException case. 
             * However, it's not 100% certain that, in ConcurrentModificationException case, 
             * gtx here will be always inactive.
             */

            slim3GtxObjMapThreadLocal.get().remove(gtxIdStr);

            if (logger.isWarnEnabled()) {
                logger.warn(String.format("Skipping to perform roll back on GlobalTransaction (ID:%1$s) since "
                        + "it has been already in inactive state.", gtxIdStr));
            }

            return;
        }

        if (!(gtxStateSet.contains(GlobalTransactionState.CurrentGlobalTransaction))) {
            /* Changed from throwing TransactionSystemException to logging incident in order to 
             * avoid leaving GlobalTransaction instance active as much as possible by proceeding further.
             */
            GlobalTransaction currentGtx = Datastore.getCurrentGlobalTransaction();

            if (logger.isErrorEnabled()) {
                logger.error(String.format(
                        "Encountered inconsistent status: GlobalTransaction object (ID:%1$s) held "
                                + "by defaultTransactionStatus argument is not current active GlobalTransaction "
                                + "instance (ID:%2$s).",
                        gtxIdStr, ((currentGtx instanceof GlobalTransaction) ? currentGtx.getId() : "null")));
            }
        }
        // ----------------------------------------------------------------------------------------

        try {
            if (logger.isInfoEnabled()) {
                logger.info(String.format("Rolling back on Slim3 GlobalTransaction (ID:%1$s)", gtxIdStr));
            }

            /* Note: There are the cases that GlobalTransaction instance has already been rolled back by 
             * Slim3 behind scene of Spring transaction framework when commit failed and GlobalTransaction 
             * instance remains active state. Even the control flow reached in those cases, there should be 
             * no harm being made by rolling back here again. 
             */
            gtx.rollback();
        } catch (Throwable throwable) {
            String message = String.format("Rolling back on Slim3 GlobalTransaction (ID:%1$s) failed.", gtxIdStr);
            throw new TransactionSystemException(message, throwable);
        } finally {
            slim3GtxObjMapThreadLocal.get().remove(gtxIdStr);
            /* Not necessary to call defaultTransactionStatus.setCompleted method in this block because 
             * it will be called later in cleanupAfterCompletion method.
                     defaultTransactionStatus.setCompleted();
             */
            if (gtx.isActive()) {
                if (logger.isErrorEnabled()) {
                    logger.error(String.format("Slim3 GlobalTransaction (ID:%1$s) remains active "
                            + "after the attempt of rolling back.", gtxIdStr));
                }
            }
        }
    } // protected void doRollback(DefaultTransactionStatus defaultTransactionStatus)

    /*
     * Cannot implements savepoint. (since jiql (Java JDBC wrapper for Google
     * BigTable) doesn't also support savepoint, I think that GAE/J probably
     * doesn't have the feature implemented.
     * http://code.google.com/p/jiql/source
     * /browse/trunk/jiql/source/org/jiql/jdbc/jiqlConnection.java?r=676 ) No
     * savepoint support means nested transaction needs to be handled by nested
     * begin and commit/rollback calls if going to support nested transaction.
     * JtaTransactionManager may be referenced for that matter.
     * JtaTransactionManager causes a further invocation of doBegin despite an
     * already existing transaction to support nested transaction. JTA
     * implementations might support nested transactions via further
     * UserTransaction.begin() invocations.
     * 
     * Decided not to support nested transaction. Come to think of it, it's not
     * necessary to support nested transaction as long as global transaction is
     * provided. Matter of fact, nested transaction with Slim3 easily encounters
     * ConcurrentModificationException and not worthy to support.
     */
    @Override
    protected boolean useSavepointForNestedTransaction() {
        return false;
    }
}