org.compass.spring.transaction.SpringSyncTransaction.java Source code

Java tutorial

Introduction

Here is the source code for org.compass.spring.transaction.SpringSyncTransaction.java

Source

/*
 * Copyright 2004-2009 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.compass.spring.transaction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.core.CompassException;
import org.compass.core.CompassSession;
import org.compass.core.config.CompassEnvironment;
import org.compass.core.spi.InternalCompassSession;
import org.compass.core.transaction.AbstractTransaction;
import org.compass.core.transaction.TransactionException;
import org.compass.core.transaction.TransactionFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class SpringSyncTransaction extends AbstractTransaction {

    private static final Log log = LogFactory.getLog(SpringSyncTransaction.class);

    private TransactionStatus status;

    /**
     * This flag means if this transaction controls the COMPASS transaction (i.e. it is the top level compass
     * transaction)
     */
    private boolean controllingNewTransaction = false;

    private boolean commitFailed;

    private PlatformTransactionManager transactionManager;

    private InternalCompassSession session;

    public SpringSyncTransaction(TransactionFactory transactionFactory) {
        super(transactionFactory);
    }

    public void begin(PlatformTransactionManager transactionManager, InternalCompassSession session,
            boolean commitBeforeCompletion) {

        this.session = session;
        this.transactionManager = transactionManager;

        // the factory called begin, so we are in charge, if we were not, than
        // it would not call begin (we are in charge of the COMPASS transaction,
        // the spring one is handled later)
        controllingNewTransaction = true;

        if (transactionManager != null) {
            DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
            transactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
            int timeout = session.getSettings().getSettingAsInt(CompassEnvironment.Transaction.TRANSACTION_TIMEOUT,
                    -1);
            if (timeout != -1) {
                transactionDefinition.setTimeout(timeout);
            }
            transactionDefinition.setReadOnly(session.isReadOnly());
            status = transactionManager.getTransaction(transactionDefinition);
        }

        session.getSearchEngine().begin();

        SpringTransactionSynchronization sync;
        if (transactionManager != null) {
            if (log.isDebugEnabled()) {
                if (status.isNewTransaction()) {
                    log.debug("Beginning new Spring transaction, and a new compass transaction on thread ["
                            + Thread.currentThread().getName() + "]");
                } else {
                    log.debug("Joining Spring transaction, and starting a new compass transaction on thread ["
                            + Thread.currentThread().getName() + "]");
                }
            }
            sync = new SpringTransactionSynchronization(session, status.isNewTransaction(), commitBeforeCompletion,
                    transactionFactory);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Joining Spring transaction, and starting a new compass transaction on thread ["
                        + Thread.currentThread().getName() + "]");
            }
            sync = new SpringTransactionSynchronization(session, false, commitBeforeCompletion, transactionFactory);
        }
        TransactionSynchronizationManager.registerSynchronization(sync);

        setBegun(true);
    }

    /**
     * Called by factory when already in a running compass transaction
     */
    public void join(InternalCompassSession session) throws CompassException {
        this.session = session;
        controllingNewTransaction = false;
        if (log.isDebugEnabled()) {
            log.debug(
                    "Joining an existing compass transcation on thread [" + Thread.currentThread().getName() + "]");
        }
    }

    protected void doCommit() throws CompassException {
        if (!controllingNewTransaction) {
            if (log.isDebugEnabled()) {
                log.debug("Not committing transaction since compass does not control it on thread ["
                        + Thread.currentThread().getName() + "]");
            }
            return;
        }

        if (transactionManager == null) {
            // do nothing, it could only get here if the spring transaction was
            // started and we synch on it
            return;
        }

        if (status.isNewTransaction()) {
            if (log.isDebugEnabled()) {
                log.debug("Committing Spring transaction controlled by compass on thread ["
                        + Thread.currentThread().getName() + "]");
            }
            try {
                transactionManager.commit(status);
            } catch (Exception e) {
                commitFailed = true;
                // so the transaction is already rolled back, by JTA spec
                throw new TransactionException("Commit failed", e);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Commit called, let Spring synchronization commit the transaciton on thread ["
                        + Thread.currentThread().getName() + "]");
            }
        }
    }

    protected void doRollback() throws CompassException {

        if (transactionManager == null) {
            // do nothing, it could only get here if the spring transaction was
            // started and we synch on it
            return;
        }

        try {
            if (status.isNewTransaction()) {
                if (log.isDebugEnabled()) {
                    log.debug("Rolling back Spring transaction controlled by compass on thread ["
                            + Thread.currentThread().getName() + "]");
                }
                if (!commitFailed)
                    transactionManager.rollback(status);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Marking Spring transaction as rolled back since compass controlls it on thread ["
                            + Thread.currentThread().getName() + "]");
                }
                status.setRollbackOnly();
            }
        } catch (Exception e) {
            throw new TransactionException("Rollback failed with exception", e);
        }
    }

    public boolean wasRolledBack() throws CompassException {
        throw new TransactionException("Not supported");
    }

    public boolean wasCommitted() throws CompassException {
        throw new TransactionException("Not supported");
    }

    public CompassSession getSession() {
        return this.session;
    }

    public static class SpringTransactionSynchronization implements TransactionSynchronization {

        private InternalCompassSession session;

        private boolean compassControledTransaction;

        private boolean commitBeforeCompletion;

        private TransactionFactory transactionFactory;

        public SpringTransactionSynchronization(InternalCompassSession session, boolean compassControledTransaction,
                boolean commitBeforeCompletion, TransactionFactory transactionFactory) {
            this.session = session;
            this.compassControledTransaction = compassControledTransaction;
            this.commitBeforeCompletion = commitBeforeCompletion;
            this.transactionFactory = transactionFactory;
        }

        public InternalCompassSession getSession() {
            return this.session;
        }

        public void suspend() {
        }

        public void resume() {
        }

        public void beforeCommit(boolean readOnly) {
        }

        public void beforeCompletion() {
            if (!commitBeforeCompletion) {
                return;
            }
            if (log.isDebugEnabled()) {
                log.debug("Committing compass transaction using Spring synchronization beforeCompletion on thread ["
                        + Thread.currentThread().getName() + "]");
            }
            session.getSearchEngine().commit(true);
        }

        public void afterCommit() {

        }

        public void afterCompletion(int status) {
            try {
                if (status == STATUS_COMMITTED) {
                    if (!commitBeforeCompletion) {
                        if (log.isDebugEnabled()) {
                            log.debug(
                                    "Committing compass transaction using Spring synchronization afterCompletion on thread ["
                                            + Thread.currentThread().getName() + "]");
                        }
                        session.getSearchEngine().commit(true);
                    }
                } else {
                    // in case of STATUS_ROLLBACK or STATUS_UNKNOWN
                    if (log.isDebugEnabled()) {
                        log.debug(
                                "Rolling back compass transaction using Spring synchronization afterCompletion on thread ["
                                        + Thread.currentThread().getName() + "]");
                    }
                    session.getSearchEngine().rollback();
                }
            } catch (Exception e) {
                log.error("Exception occured when sync with transaction", e);
                // TODO swallow??????
            } finally {
                ((SpringSyncTransactionFactory) transactionFactory).unbindSessionFromTransaction(this, session);
                session.evictAll();
                // close the session AFTER we cleared it from the transaction,
                // so it will be actually closed. Also close it only if we do
                // not contoll the transaction
                // (otherwise it will be closed by the calling template)
                if (!compassControledTransaction) {
                    session.close();
                }
            }
        }

    }

}