org.alfresco.repo.transaction.AlfrescoTransactionSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.transaction.AlfrescoTransactionSupport.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.repo.transaction;

import java.util.Set;

import org.alfresco.repo.cache.TransactionalCache;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcher;
import org.alfresco.util.transaction.TransactionListener;
import org.alfresco.util.transaction.TransactionSupportUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * Repo Specific Helper class to manage transaction synchronization.  This provides helpers to
 * ensure that the necessary <code>TransactionSynchronization</code> instances
 * are registered on behalf of the application code.
 * <p>
 * This class remains for backward API compatibility, the majority of transaction support has been moved to
 * TransactionSupportUtil in the Core project.
 * 
 * @author Derek Hulley
 * @author mrogers
 */
public abstract class AlfrescoTransactionSupport extends TransactionSupportUtil {
    /*
     * The registrations of services is very explicit on the interface.  This
     * is to convey the idea that the execution of these services when the
     * transaction completes is very explicit.  As we only have a finite
     * list of types of services that need registration, this is still
     * OK.
     */

    private static int COMMIT_ORDER_NORMAL = 0;
    private static int COMMIT_ORDER_INTEGRITY = 1;
    private static int COMMIT_ORDER_LUCENE = 2;
    private static int COMMIT_ORDER_DAO = 3;
    private static int COMMIT_ORDER_CACHE = 4;

    /**
     * The order of synchronization set to be 100 less than the Hibernate synchronization order
     */
    public static final int SESSION_SYNCHRONIZATION_ORDER = SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER - 100;

    private static Log logger = LogFactory.getLog(AlfrescoTransactionSupport.class);

    /**
     * 
     * @author Derek Hulley
     * @since 2.1.4
     */
    public static enum TxnReadState {
        /** No transaction is active */
        TXN_NONE,
        /** The current transaction is read-only */
        TXN_READ_ONLY,
        /** The current transaction supports writes */
        TXN_READ_WRITE
    }

    /**
     * @return      Returns the read-write state of the current transaction
     * @since 2.1.4
     */
    public static TxnReadState getTransactionReadState() {
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            return TxnReadState.TXN_NONE;
        }
        // Find the read-write state of the txn
        if (getResource(RESOURCE_KEY_TXN_COMPLETING) != null) {
            // Transaction is completing.  For all intents and purposes, we are not in a transaction.
            return TxnReadState.TXN_NONE;
        } else if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
            return TxnReadState.TXN_READ_ONLY;
        } else {
            return TxnReadState.TXN_READ_WRITE;
        }
    }

    /**
     * Checks the state of the current transaction and throws an exception if a transaction
     * is not present or if the transaction is not read-write, if required.
     * 
     * @param requireReadWrite          <tt>true</tt> if the transaction must be read-write
     * 
     * @since 3.2
     */
    public static void checkTransactionReadState(boolean requireReadWrite) {
        TxnReadState readState = AlfrescoTransactionSupport.getTransactionReadState();
        switch (readState) {
        case TXN_NONE:
            throw new IllegalStateException("The current operation requires an active "
                    + (requireReadWrite ? "read-write" : "") + "transaction.");
        case TXN_READ_ONLY:
            if (requireReadWrite) {
                throw new IllegalStateException("The current operation requires an active read-write transaction.");
            }
        case TXN_READ_WRITE:
            // All good
        }
    }

    /**
     * Are there any pending changes which must be synchronized with the store?
     * 
     * @return true => changes are pending
     * 
     * @deprecated  To be replaced by {@code DirtySessionMethodInterceptor}
     */
    public static boolean isDirty() {
        Set<TransactionListener> allListeners = getListeners();
        for (TransactionListener listener : allListeners) {
            if (listener instanceof TransactionalDao) {
                TransactionalDao service = (TransactionalDao) listener;
                if (service.isDirty()) {
                    return true;
                }

            } else if (listener instanceof DAOAdapter) {
                DAOAdapter adapter = (DAOAdapter) listener;
                TransactionalDao service = adapter.getService();
                if (service.isDirty()) {
                    return true;
                }
            }

        }

        return false;
    }

    /**
     * Method that registers a <tt>NodeDaoService</tt> against the transaction.
     * Setting this will ensure that the pre- and post-commit operations perform
     * the necessary cleanups against the <tt>NodeDaoService</tt>.
     * <p>
     * This method can be called repeatedly as long as the service being bound
     * implements <tt>equals</tt> and <tt>hashCode</tt>.
     * 
     * @param daoService TransactionalDao
     */
    public static void bindDaoService(TransactionalDao daoService) {

        DAOAdapter adapter = new DAOAdapter(daoService);

        boolean bound = bindListener(adapter, COMMIT_ORDER_DAO);

        // done
        if (logger.isDebugEnabled()) {
            logBoundService(daoService, bound);
        }
    }

    /**
     * Method that registers an <tt>IntegrityChecker</tt> against the transaction.
     * Setting this will ensure that the pre- and post-commit operations perform
     * the necessary cleanups against the <tt>IntegrityChecker</tt>.
     * <p>
     * This method can be called repeatedly as long as the service being bound
     * implements <tt>equals</tt> and <tt>hashCode</tt>.
     * 
     * @param integrityChecker IntegrityChecker
     */
    public static void bindIntegrityChecker(IntegrityChecker integrityChecker) {

        // bind the service in
        boolean bound = bindListener((TransactionListener) integrityChecker, COMMIT_ORDER_INTEGRITY);

        if (logger.isDebugEnabled()) {
            logBoundService(integrityChecker, bound);
        }
    }

    /**
     * Method that registers a <tt>LuceneIndexerAndSearcherFactory</tt> against
     * the transaction.
     * <p>
     * Setting this will ensure that the pre- and post-commit operations perform
     * the necessary cleanups against the <tt>LuceneIndexerAndSearcherFactory</tt>.
     * <p>
     * Although bound within a <tt>Set</tt>, it would still be better for the caller
     * to only bind once per transaction, if possible.
     * 
     * @param indexerAndSearcher the Lucene indexer to perform transaction completion
     *      tasks on
     */
    public static void bindLucene(LuceneIndexerAndSearcher indexerAndSearcher) {
        LuceneIndexerAndSearcherAdapter adapter = new LuceneIndexerAndSearcherAdapter(indexerAndSearcher);

        boolean bound = bindListener(adapter, COMMIT_ORDER_LUCENE);

        // done
        if (logger.isDebugEnabled()) {
            logBoundService(indexerAndSearcher, bound);
        }
    }

    /**
     * Method maintained for backward compatibility:
     * <a href="https://issues.alfresco.com/jira/browse/ACE-2801">ACE-2801: Package change for TransactionListener</a>.
     * 
     * @see TransactionSupportUtil
     * @see #bindListener(org.alfresco.util.transaction.TransactionListener)
     */
    public static void bindListener(org.alfresco.repo.transaction.TransactionListener listener) {
        AlfrescoTransactionSupport.bindListener((org.alfresco.util.transaction.TransactionListener) listener);
    }

    /**
     * Method that registers a <tt>Listener</tt> against
     * the transaction.
     * <p> will be better for the caller
     * to only bind once per transaction, if possible.
     * 
     * @param listener the transaction listener
     *      tasks on
     *      
     * @since 5.0
     */
    public static void bindListener(TransactionListener listener) {
        boolean bound = false;

        if (listener instanceof IntegrityChecker) {
            bound = bindListener(listener, COMMIT_ORDER_INTEGRITY);
        } else if (listener instanceof TransactionalCache) {
            bound = bindListener(listener, COMMIT_ORDER_CACHE);
        } else {
            bound = bindListener(listener, COMMIT_ORDER_NORMAL);
        }

        if (logger.isDebugEnabled()) {
            logBoundService(listener, bound);
        }
    }

    /**
     * Use as part of a debug statement
     * 
     * @param service the service to report 
     * @param bound true if the service was just bound; false if it was previously bound
     */
    private static void logBoundService(Object service, boolean bound) {
        if (bound) {
            logger.debug("Bound service: \n" + "   transaction: " + getTransactionId() + "\n" + "   service: "
                    + service);
        } else {
            logger.debug("Service already bound: \n" + "   transaction: " + getTransactionId() + "\n"
                    + "   service: " + service);
        }
    }

    /**
     * No-op
     * 
     * @deprecated      No longer does anything
     */
    public static void flush() {
        // No-op
    }

}