/*
   Copyright The Narayana Authors
   SPDX short identifier: Apache-2.0
 */

package com.arjuna.ats.arjuna.common;

import com.arjuna.ats.arjuna.coordinator.CheckedActionFactory;
import com.arjuna.ats.arjuna.coordinator.TransactionReaper;
import com.arjuna.ats.arjuna.utils.Utility;
import com.arjuna.ats.internal.arjuna.coordinator.CheckedActionFactoryImple;
import com.arjuna.ats.internal.arjuna.objectstore.HashedActionStore;
import com.arjuna.common.internal.util.ClassloadingUtility;
import com.arjuna.common.internal.util.propertyservice.FullPropertyName;
import com.arjuna.common.internal.util.propertyservice.PropertyPrefix;

/**
 * A JavaBean containing configuration properties for the core transaction coordinator.
 *
 * @author Jonathan Halliday (jonathan.halliday@redhat.com)
 */
@PropertyPrefix(prefix = "com.arjuna.ats.arjuna.coordinator.")
public class CoordinatorEnvironmentBean implements CoordinatorEnvironmentBeanMBean
{
    private volatile boolean asyncCommit = false;
    private volatile boolean asyncPrepare = false;
    private volatile boolean asyncRollback = false;

    private volatile boolean asyncBeforeSynchronization;
    private volatile boolean asyncAfterSynchronization;

    private volatile boolean commitOnePhase = true;
    private volatile boolean maintainHeuristics = true;
    @Deprecated
    private volatile boolean transactionLog = false; // rename to useTransactionLog ?

    private volatile int maxTwoPhaseCommitThreads = 100;

    // public static final String TRANSACTION_LOG_REMOVAL_MARKER = "com.arjuna.ats.arjuna.coordinator.transactionLog.removalMarker";
    //private String removalMarker;

    @FullPropertyName(name = "com.arjuna.ats.arjuna.coordinator.transactionLog.writeOptimisation")
    private volatile boolean writeOptimisation = false;

    private volatile boolean dynamic1PC = true;
    private volatile boolean readonlyOptimisation = true;
    private volatile boolean classicPrepare = false;
    private volatile boolean enableStatistics = false;
    @Deprecated
    private volatile boolean sharedTransactionLog = false;
    private volatile boolean startDisabled = false; // rename/repurpose to 'enable'?
    private volatile String txReaperMode = "DYNAMIC"; // rename bool txReaperModeDynamic?

    private volatile long txReaperTimeout = TransactionReaper.defaultCheckPeriod;
    private volatile long txReaperCancelWaitPeriod = TransactionReaper.defaultCancelWaitPeriod;
    private volatile long txReaperCancelFailWaitPeriod = TransactionReaper.defaultCancelFailWaitPeriod;
    private volatile int txReaperZombieMax = TransactionReaper.defaultZombieMax;
    private volatile long txReaperTraceGracePeriod = TransactionReaper.defaultUntracedPeriod;
    private volatile long txReaperTraceInterval = TransactionReaper.defaultTracePeriod;

    private volatile int defaultTimeout = 60; // seconds
    private volatile boolean transactionStatusManagerEnable = true;

    @FullPropertyName(name = "com.arjuna.ats.coordinator.beforeCompletionWhenRollbackOnly")
    private volatile boolean beforeCompletionWhenRollbackOnly = false;

    @FullPropertyName(name = "com.arjuna.ats.coordinator.checkedActionFactory")
    private volatile String checkedActionFactoryClassName = "com.arjuna.ats.internal.arjuna.coordinator.CheckedActionFactoryImple";
    private volatile CheckedActionFactory checkedActionFactory = null;
    
    private volatile boolean allowCheckedActionFactoryOverride; 

    private volatile boolean alternativeRecordOrdering = false;

    @Deprecated
    private volatile String communicationStore = HashedActionStore.class.getName();

    private volatile boolean finalizeBasicActions = false;

    /**
     * Returns true if anynchronous commit behaviour is enabled.
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.asyncCommit
     *
     * @return true if asynchronous commit is enabled, value otherwise.
     */
    public boolean isAsyncCommit()
    {
        return asyncCommit;
    }

    /**
     * Sets if asynchronous commit behaviour should be enabled or not.
     * Note: heuristics cannot be reported programatically if asynchronous commit is used.
     *
     * If true then a separate thread will be created to complete the second phase of the action
     * (provided that knowledge about heuristic outcomes is not required).
     *
     * @param asyncCommit true to enable asynchronous commit, false to disable.
     */
    public void setAsyncCommit(boolean asyncCommit)
    {
        this.asyncCommit = asyncCommit;
    }

    /**
     * Returns true if asynchronous prepare behaviour is enabled.
     *
     * If true then during the prepare phase of an action a separate thread will be created for
     * preparing each participant registered with the action
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.asyncPrepare
     *
     * @return true if asynchronous prepare is enabled, false otherwise.
     */
    public boolean isAsyncPrepare()
    {
        return asyncPrepare;
    }

    /**
     * Sets if asynchronous prepare behaviour should be enabled or not.
     *
     * @param asyncPrepare true to enable asynchronous prepare, false to disable.
     */
    public void setAsyncPrepare(boolean asyncPrepare)
    {
        this.asyncPrepare = asyncPrepare;
    }

    /**
     * Returns true if asynchronous rollback behaviour is enabled.
     *
     * If true then a separate thread will be created to complete the second phase of the action
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.asyncRollback
     *
     * @return true if asynchronous rollback is enabled, false otherwise.
     */
    public boolean isAsyncRollback()
    {
        return asyncRollback;
    }

    /**
     * Sets if asynchronous rollback behaviour should be enabled or not.
     *
     * @param asyncRollback true to enable asynchronous rollback, false to disable.
     */
    public void setAsyncRollback(boolean asyncRollback)
    {
        this.asyncRollback = asyncRollback;
    }

    /**
     * Returns maximum thread pool size allowed for two phase commits.
     *
     * Default: 100
     *
     * @return maximum number of threads in a thread pool
     */
    public int getMaxTwoPhaseCommitThreads() {
        return maxTwoPhaseCommitThreads;
    }

    /**
     * Sets maximum thread pool size for two phase commits.
     *
     * @param maxTwoPhaseCommitThreads maximum number of threads in a thread pool
     */
    public void setMaxTwoPhaseCommitThreads(int maxTwoPhaseCommitThreads) {
        this.maxTwoPhaseCommitThreads = maxTwoPhaseCommitThreads;
    }

    /**
     * Returns true if one phase commit optimization is to be used.
     *
     * Default: true
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.commitOnePhase
     *
     * @return true if one phase commit is enabled, false otherwise.
     */
    public boolean isCommitOnePhase()
    {
        return commitOnePhase;
    }

    /**
     * Sets if one phase commit behaviour is enabled or not.
     *
     * @param commitOnePhase true to enable, false to disable.
     */
    public void setCommitOnePhase(boolean commitOnePhase)
    {
        this.commitOnePhase = commitOnePhase;
    }
    
    /**
     * Returns true if dynamic one phase commit optimization is to be used. This means that
     * if the first N-1 participants in the intentions list return read-only then commit_one_phase
     * will be called on the last participant.
     *
     * Default: true
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.dynamic1PC
     *
     * @return true if one phase commit is enabled, false otherwise.
     */
    public boolean getDynamic1PC()
    {
        return dynamic1PC;
    }

    /**
     * Sets if dynamic one phase commit behaviour is enabled or not.
     *
     * @param dynamic1PC true to enable, false to disable.
     */
    public void setDynamic1PC(boolean dynamic1PC)
    {
        this.dynamic1PC = dynamic1PC;
    }

    /**
     * Returns true if heuristic outcomes should be recorded.
     *
     * Default: true
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.maintainHeuristics
     *
     * @return true if heuristics should be recorded, false otherwise.
     */
    public boolean isMaintainHeuristics()
    {
        return maintainHeuristics;
    }

    /**
     * Sets if heuristics should be recorded or not.
     *
     * @param maintainHeuristics true to enable recording of heuristics, false to disable.
     */
    public void setMaintainHeuristics(boolean maintainHeuristics)
    {
        this.maintainHeuristics = maintainHeuristics;
    }

    /**
     * Returns true if write optimisation protocol should be used for PersistenceRecord.
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.transactionLog.writeOptimisation
     *
     * @return true if write optimisation is enabled, false otherwise.
     */
    public boolean isWriteOptimisation()
    {
        return writeOptimisation;
    }

    /**
     * Sets if write optimization protocol should be used for PersistenceRecord.
     *
     * @param writeOptimisation true to enable write optimization, false to disable.
     */
    public void setWriteOptimisation(boolean writeOptimisation)
    {
        this.writeOptimisation = writeOptimisation;
    }

    /**
     * Returns true if handling of read only resources should be optimized.
     *
     * Default: true
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.readonlyOptimisation
     *
     * @return true if read only optimization is enabled, false otherwise.
     */
    public boolean isReadonlyOptimisation()
    {
        return readonlyOptimisation;
    }

    /**
     * Sets if handling of read only resources should be optimized.
     *
     * @param readonlyOptimisation true to enable read only optimization, false to disable.
     */
    public void setReadonlyOptimisation(boolean readonlyOptimisation)
    {
        this.readonlyOptimisation = readonlyOptimisation;
    }

    /**
     * Returns true if the old style of prepare handling should be used for PersistenceRecord.
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.classicPrepare
     *
     * @return true is classic prepare handling is enabled, false otherwise.
     */
    public boolean isClassicPrepare()
    {
        return classicPrepare;
    }

    /**
     * Sets if old style prepare handling should be used for PersistenceRecord.
     *
     * @param classicPrepare true to enable classic prepare handling, false to disable.
     */
    public void setClassicPrepare(boolean classicPrepare)
    {
        this.classicPrepare = classicPrepare;
    }

    /**
     * Returns true if transaction statistics should be recorded.
     * Note: Enabling statistics may have a slight performance impact due to locking on the counter variables.
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.enableStatistics
     * Effect: immediate.
     *
     * @return true if recording of transaction statistics is enabled, false otherwise.
     */
    public boolean isEnableStatistics()
    {
        return enableStatistics;
    }

    /**
     * Sets if transaction statistics should be recorded or not.
     *
     * @param enableStatistics true to enable statistics gathering, false to disable.
     */
    public void setEnableStatistics(boolean enableStatistics)
    {
        this.enableStatistics = enableStatistics;
    }

    /**
     * Returns if the transaction log should be run in shared mode or not.
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.sharedTransactionLog
     *
     * @return true if transaction log sharing is enabled, false otherwise.
     */
    @Deprecated
    public boolean isSharedTransactionLog()
    {
        return sharedTransactionLog;
    }

    /**
     * Sets if the transaction log should be run in shared mode or not.
     *
     * @param sharedTransactionLog true to enable transaction log sharing, false to disable.
     */
    @Deprecated
    public void setSharedTransactionLog(boolean sharedTransactionLog)
    {
        this.sharedTransactionLog = sharedTransactionLog;
    }

    /**
     * Returns if the transaction manager should be created in a disabled state or not.
     *
     * Default: false (i.e. transaction manager is enabled on creation)
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.startDisabled
     *
     * @return true if the transaction manager will start in a disabled state, false otherwise.
     */
    public boolean isStartDisabled()
    {
        return startDisabled;
    }

    /**
     * Sets if the transaction manager should be created in a disabled state or not.
     *
     * @param startDisabled true to start in a diabled state, false to start enabled.
     */
    public void setStartDisabled(boolean startDisabled)
    {
        this.startDisabled = startDisabled;
    }

    /**
     * Returns the operating mode of the transaction timeout processing system.
     *
     * Default: "DYNAMIC"
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.txReaperMode
     *
     * @return the operating mode of the transaction reaper.
     */
    public String getTxReaperMode()
    {
        return txReaperMode;
    }

    /**
     * Sets the operating mode of the transaction timeout processing system.
     *
     * @param txReaperMode the name of the required operating mode.
     */
    public void setTxReaperMode(String txReaperMode)
    {
        this.txReaperMode = txReaperMode;
    }

    /**
     * Returns the timeout (wakeup) interval of the reaper's PERIODIC mode, in milliseconds.
     *
     * Default: 120000ms
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.txReaperTimeout
     *
     * @return the sleep interval of the transaction reaper, in milliseconds.
     */
    public long getTxReaperTimeout()
    {
        return txReaperTimeout;
    }

    /**
     * Sets the timeout interval of the transaction reaper.
     *
     * @param txReaperTimeout the reaper sleep interval, in milliseconds.
     */
    public void setTxReaperTimeout(long txReaperTimeout)
    {
        this.txReaperTimeout = txReaperTimeout;
    }

    /**
     * Returns the number of millisecs delay after a cancel is scheduled,
     * before the reaper tries to interrupt the worker thread executing the cancel.
     *
     * Default: 500ms
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.txReaperCancelWaitPeriod
     *
     * @return the reaper cancel wait period, in milliseconds.
     */
    public long getTxReaperCancelWaitPeriod()
    {
        return txReaperCancelWaitPeriod;
    }

    /**
     * Sets the delay to allow a cancel to be processed before interrupting it.
     *
     * @param txReaperCancelWaitPeriod in milliseconds.
     */
    public void setTxReaperCancelWaitPeriod(long txReaperCancelWaitPeriod)
    {
        this.txReaperCancelWaitPeriod = txReaperCancelWaitPeriod;
    }

    /**
     * Returns the number of millisecs delay after a worker thread is interrupted,
     * before the reaper writes the it off as a zombie and starts a new thread.
     *
     * Default: 500ms
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.txReaperCancelFailWaitPeriod
     *
     * @return the reaper cancel wait fail period, in milliseconds.
     */
    public long getTxReaperCancelFailWaitPeriod()
    {
        return txReaperCancelFailWaitPeriod;
    }

    /**
     * Sets the delay to allow an interrupted cancel to be cleaned up.
     *
     * @param txReaperCancelFailWaitPeriod in milliseconds.
     */
    public void setTxReaperCancelFailWaitPeriod(long txReaperCancelFailWaitPeriod)
    {
        this.txReaperCancelFailWaitPeriod = txReaperCancelFailWaitPeriod;
    }

    /**
     * Returns the threshold for count of non-exited zombies at which
     * the system starts logging error messages.
     *
     * Default: 8
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.txReaperZombieMax
     *
     * @return the number of zombie threads at which errors will start being reported.
     */
    public int getTxReaperZombieMax()
    {
        return txReaperZombieMax;
    }

    /**
     * Sets the threshold number of zombie threads at which errors will start to be reported.
     *
     * @param txReaperZombieMax the number of threads.
     */
    public void setTxReaperZombieMax(int txReaperZombieMax)
    {
        this.txReaperZombieMax = txReaperZombieMax;
    }

    /**
     * Returns the number of milliseconds delay after a transaction is started,
     * before the reaper will start taking periodic stack traces from it.
     *
     * Default: 180000 (3 minutes)
     *
     * @return the reaper tracing grace period, in milliseconds.
     */
    public long getTxReaperTraceGracePeriod() {
        return txReaperTraceGracePeriod;
    }

    /**
     * Sets the delay after a transaction is started, before it is eligible for periodic tracing.
     *
     * @param txReaperTraceGracePeriod in milliseconds.
     */
    public void setTxReaperTraceGracePeriod(long txReaperTraceGracePeriod) {
        this.txReaperTraceGracePeriod = txReaperTraceGracePeriod;
    }

    /**
     * Returns the number of milliseconds interval between transaction stack trace snapshots.
     *
     * Default: 30000 (30 seconds)
     *
     * @return the reaper tracing interval, in milliseconds.
     */
    public long getTxReaperTraceInterval() {
        return txReaperTraceInterval;
    }

    /**
     * Sets the interval between stack trace snapshots.
     *
     * @param txReaperTraceInterval in milliseconds.
     */
    public void setTxReaperTraceInterval(long txReaperTraceInterval) {
        this.txReaperTraceInterval = txReaperTraceInterval;
    }

    /**
     * Returns the default interval after which a transaction may be considered for timeout, in seconds.
     * Note: depending on the reaper mode and workload, transactions may not be timed out immediately.
     *
     * Default: 60
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.defaultTimeout
     *
     * @return the default transaction lifetime, in seconds.
     */
    public int getDefaultTimeout()
    {
        return defaultTimeout;
    }

    /**
     * Sets the default lifetime after which a transaction may be considered for timeout, in seconds.
     *
     * @param defaultTimeout the default transaction lifetime, in seconds.
     */
    public void setDefaultTimeout(int defaultTimeout)
    {
        this.defaultTimeout = defaultTimeout;
    }

    /**
     * Returns if the transaction status manager (TSM) service, needed for out of process recovery, should be provided or not.
     *
     * Default: true
     * Equivalent deprecated property: com.arjuna.ats.arjuna.coordinator.transactionStatusManagerEnable
     *
     * @return true is the transaction status manager is enabled, false otherwise.
     */
    public boolean isTransactionStatusManagerEnable()
    {
        return transactionStatusManagerEnable;
    }

    /**
     * Sets if the transaction status manager service should be provided or not.
     *
     * @param transactionStatusManagerEnable true to enable the TSM, false to disable.
     */
    public void setTransactionStatusManagerEnable(boolean transactionStatusManagerEnable)
    {
        this.transactionStatusManagerEnable = transactionStatusManagerEnable;
    }

    /**
     * Returns if beforeCompletion should be called on Synchronizations when completing transactions that are marked rollback only.
     *
     * Default: false
     * Equivalent deprecated property: com.arjuna.ats.coordinator.beforeCompletionWhenRollbackOnly
     *
     * @return true if beforeCompletion will be called in rollback only cases, false otherwise.
     */
    public boolean isBeforeCompletionWhenRollbackOnly()
    {
        return beforeCompletionWhenRollbackOnly;
    }

    /**
     * Sets if beforeCompletion should be called on transactions that are set rollback only.
     *
     * @param beforeCompletionWhenRollbackOnly true to call beforeCompletions on rollback only tx, false to skip them.
     */
    public void setBeforeCompletionWhenRollbackOnly(boolean beforeCompletionWhenRollbackOnly)
    {
        this.beforeCompletionWhenRollbackOnly = beforeCompletionWhenRollbackOnly;
    }

    /**
     * Returns the class name of an implementation of CheckedActionFactory
     *
     * Default: "com.arjuna.ats.internal.arjuna.coordinator.CheckedActionFactoryImple"
     * Equivalent deprecated property: com.arjuna.ats.coordinator.checkedActionFactory
     *
     * @return the class name of the CheckedActionFactory implementation to use.
     */
    public String getCheckedActionFactoryClassName()
    {
        return checkedActionFactoryClassName;
    }

    /**
     * Sets the class name of the CheckedActionFactory implementation.
     *
     * @param checkedActionFactoryClassName the name of a class that implements CheckedActionFactory.
     */
    public void setCheckedActionFactoryClassName(String checkedActionFactoryClassName)
    {
        synchronized(this)
        {
        	if (checkedActionFactoryClassName == null || allowCheckedActionFactoryOverride) 
        	{	
	            if(checkedActionFactoryClassName == null)
	            {
	                this.checkedActionFactory = null;
	            }
	            else if(!checkedActionFactoryClassName.equals(this.checkedActionFactoryClassName))
	            {
	            	this.checkedActionFactory = null;
	            }
	            this.checkedActionFactoryClassName = checkedActionFactoryClassName;
        	}
        }
    }

    /**
     * Returns an instance of a class implementing CheckedActionFactory.
     *
     * If there is no pre-instantiated instance set and classloading or instantiation fails,
     * this method will log appropriate warning and return null, not throw an exception.
     *
     * @return a CheckedActionFactory implementation instance, or null.
     */
    public CheckedActionFactory getCheckedActionFactory()
    {
        if(checkedActionFactory == null && checkedActionFactoryClassName != null)
        {
            synchronized (this) {
                if(checkedActionFactory == null && checkedActionFactoryClassName != null) {
                    CheckedActionFactory instance = ClassloadingUtility.loadAndInstantiateClass(CheckedActionFactory.class, checkedActionFactoryClassName, null);
                    checkedActionFactory = instance;
                }
            }
        }

        return checkedActionFactory;
    }

    /**
     * Sets the instance of CheckedActionFactory.
     *
     * @param instance an Object that implements CheckedActionFactory, or null.
     */
    public void setCheckedActionFactory(CheckedActionFactory instance)
    {
        synchronized(this)
        {
        	if (checkedActionFactoryClassName == null || allowCheckedActionFactoryOverride)
        	{
	            CheckedActionFactory oldInstance = this.checkedActionFactory;
	            checkedActionFactory = instance;
	
	            if(instance == null)
	            {
	                this.checkedActionFactoryClassName = null;
	            }
	            else if(instance != oldInstance)
	            {
	                String name = ClassloadingUtility.getNameForClass(instance);
	                this.checkedActionFactoryClassName = name;
	            }
        	}
        }
    }


    /**
     * Whether to use the alternative abstract record ordering.
     * At present this is not fully documented, so stay away!
     *
     * Default: false
     *
     * @return <code>true</code> if order abstract records on type first, or
     * <code>false</code> if order on Uid first.
     */
    public boolean isAlternativeRecordOrdering()
    {
        return alternativeRecordOrdering;
    }

    /**
     * Set whether or not to use the alternative abstract record
     * ordering. Don't try this whilst the system is running!
     *
     * @param alternativeRecordOrdering true for alternative (i.e. type) ordering, false for normal (i.e. Uid) ordering.
     */
    public void setAlternativeRecordOrdering(boolean alternativeRecordOrdering)
    {
        this.alternativeRecordOrdering = alternativeRecordOrdering;
    }

    /**
     * Returns the symbolic name for the communication store type.
     *
     * Default: "HashedActionStore"
     *
     * @return the communication store name.
     */
    @Deprecated
    public String getCommunicationStore()
    {
        return communicationStore;
    }

    /**
     * Sets the symbolic name of the communication store.
     *
     * @param communicationStore the communication store name.
     */
    @Deprecated
    public void setCommunicationStore(String communicationStore)
    {
        this.communicationStore = communicationStore;
    }

    /**
     * Sets whether or not to use finalizers for BasicActions (i.e. transactions).
     * This can provide a useful safety net to ensure cleanup of locks and other
     * resources, but does not perform well.  In most cases it's preferable to
     * set a transaction timeout and rely on the reaper for cleanup.
     *
     * Default: false
     *
     * @return true if a finalize method should be registered for BasicActions instances, false otherwise.
     */
    public boolean isFinalizeBasicActions()
    {
        return finalizeBasicActions;
    }

    /**
     * Sets whether or not to use finalizers for BasicActions (i.e. transactions).
     *
     * @param finalizeBasicActions true to enable finalization, false to disable.
     */
    public void setFinalizeBasicActions(boolean finalizeBasicActions)
    {
        this.finalizeBasicActions = finalizeBasicActions;
    }

    /**
     * Returns true if asynchronous before completion behaviour is enabled.
     *
     * The option is applicable to the volatile phase of the two phase commit protocol
     * which provides a synchronization mechanism that allows an interested party to be notified
     * before and after the transaction completes. If set to true then the beforeCompletion method will be
     * called on all non interposed synchronizations in parallel, after which the beforeCompletion method
     * will be called on all interposed synchronizations in parallel.
     *
     * Default: false
     *
     * @return true if asynchronous before completion is enabled, value otherwise.
     */
    public boolean isAsyncBeforeSynchronization() {
        return asyncBeforeSynchronization;
    }

    /**
     * Sets if asynchronous before completion behaviour should be enabled or not.
     *
     * @param asyncBeforeSynchronization true to enable asynchronous before completion, false to disable.
     */
    public void setAsyncBeforeSynchronization(boolean asyncBeforeSynchronization) {
        this.asyncBeforeSynchronization = asyncBeforeSynchronization;
    }

    /**
     * Returns true if asynchronous after completion behaviour is enabled.
     *
     * The option is applicable to the volatile phase of the two phase commit protocol
     * which provides a synchronization mechanism that allows an interested party to be notified
     * before and after the transaction completes. If set to true then the afterCompletion method will be
     * called on all interposed synchronizations in parallel, after which the afterCompletion method
     * will be called on all non interposed synchronizations in parallel.
     *
     * Caveat: if an action is committed and the caller wishes to be notified of heuristics outcomes then this
     * behaviour is not available and the afterCompletion callbacks will be invoked synchronously. .
     *
     * Default: false
     *
     * @return true if asynchronous after completion is enabled, value otherwise.
     */
    public boolean isAsyncAfterSynchronization() {
        return asyncAfterSynchronization;
    }

    /**
     * Sets if asynchronous after completion behaviour should be enabled or not.
     *
     * @param asyncAfterSynchronization true to enable asynchronous after completion, false to disable.
     */
    public void setAsyncAfterSynchronization(boolean asyncAfterSynchronization) {
        this.asyncAfterSynchronization = asyncAfterSynchronization;
    }

    /**
     * Returns true if configured to allow the checked action factory to be overriden
     *
     * Default: true
     *
     * @return true if checked action factory can be overriden, false otherwise.
     */
    public boolean isAllowCheckedActionFactoryOverride() {
		return allowCheckedActionFactoryOverride;
	}

    /**
     * Can be enabled to allow the checked action factory to be overridden at runtime
     *
     * @param allowCheckedActionFactoryOverride Allow the checked action factory to be overriden
     */
    public void setAllowCheckedActionFactoryOverride(
			boolean allowCheckedActionFactoryOverride) {
		this.allowCheckedActionFactoryOverride = allowCheckedActionFactoryOverride;
	}   
    
}