Package net.sf.ehcache.transaction.local

Source Code of net.sf.ehcache.transaction.local.LocalTransactionContext

/**
*  Copyright 2003-2010 Terracotta, Inc.
*
*  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 net.sf.ehcache.transaction.local;

import net.sf.ehcache.transaction.SoftLock;
import net.sf.ehcache.transaction.TransactionException;
import net.sf.ehcache.transaction.TransactionID;
import net.sf.ehcache.transaction.TransactionTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A local transaction's thread context
*
* @author Ludovic Orban
*/
public class LocalTransactionContext {

    private static final Logger LOG = LoggerFactory.getLogger(LocalTransactionContext.class.getName());
    private static final long MILLISECONDS_PER_SECOND = 1000L;

    private boolean rollbackOnly;
    private final long expirationTimestamp;
    private final TransactionID transactionId;
    private final Map<String, List<SoftLock>> softLockMap = new HashMap<String, List<SoftLock>>();
    private final Map<String, LocalTransactionStore> storeMap = new HashMap<String, LocalTransactionStore>();
    private final List<TransactionListener> listeners = new ArrayList<TransactionListener>();

    /**
     * Create a new LocalTransactionContext
     * @param transactionTimeout the timeout before the context expires
     * @param transactionId the unique transaction ID of the context
     */
    public LocalTransactionContext(int transactionTimeout, TransactionID transactionId) {
        this.expirationTimestamp = System.currentTimeMillis() + transactionTimeout * MILLISECONDS_PER_SECOND;
        this.transactionId = transactionId;
    }

    /**
     * Get the timestamp at which this context will expire
     * @return a timestamp in milliseconds at which this context will expire
     */
    public long getExpirationTimestamp() {
        return expirationTimestamp;
    }

    /**
     * Check if the context timed out
     * @return true if the context timed out, false otherwise
     */
    public boolean timedOut() {
        return expirationTimestamp <= System.currentTimeMillis();
    }

    /**
     * Mark the context for rollback
     */
    public void setRollbackOnly() {
        this.rollbackOnly = true;
    }

    /**
     * Register a soft lock in the context
     * @param cacheName the name of the cache this soft lock is in
     * @param store the LocalTransactionStore this soft lock is in
     * @param softLock the soft lock
     */
    public void registerSoftLock(String cacheName, LocalTransactionStore store, SoftLock softLock) {
        List<SoftLock> softLocks = softLockMap.get(cacheName);
        if (softLocks == null) {
            softLocks = new ArrayList<SoftLock>();
            softLockMap.put(cacheName, softLocks);
            storeMap.put(cacheName, store);
        }
        softLocks.add(softLock);
    }

    //todo this method isn't needed if there is no copy on read/write in the underlying store
    /**
     * Update a soft lock already registered in the context
     * @param cacheName the name of the cache this soft lock is in
     * @param softLock the soft lock
     */
    public void updateSoftLock(String cacheName, SoftLock softLock) {
        List<SoftLock> softLocks = softLockMap.get(cacheName);
        softLocks.remove(softLock);
        softLocks.add(softLock);
    }

    /**
     * Get all soft locks registered in this context for a specific cache
     * @param cacheName the name of the cache
     * @return a List of registered soft locks for this cache
     */
    public List<SoftLock> getSoftLocksForCache(String cacheName) {
        List<SoftLock> softLocks = softLockMap.get(cacheName);
        if (softLocks == null) {
            return Collections.emptyList();
        }

        return Collections.unmodifiableList(softLocks);
    }

    /**
     * Commit all work done in the context and release all registered soft locks
     * @param ignoreTimeout true if commit should proceed no matter the timeout
     */
    public void commit(boolean ignoreTimeout) {
        if (!ignoreTimeout && timedOut()) {
            rollback();
            throw new TransactionTimeoutException("transaction timed out, rolled back on commit");
        }
        if (rollbackOnly) {
            rollback();
            throw new TransactionException("transaction was marked as rollback only, rolled back on commit");
        }

        try {
            fireBeforeCommitEvent();
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} participating cache(s), committing transaction {}", softLockMap.keySet().size(), transactionId);
            }
            freeze();
            transactionId.markForCommit();

            for (Map.Entry<String, List<SoftLock>> stringListEntry : softLockMap.entrySet()) {
                String cacheName = stringListEntry.getKey();
                LocalTransactionStore store = storeMap.get(cacheName);
                List<SoftLock> softLocks = stringListEntry.getValue();

                LOG.debug("committing soft locked values of cache {}", cacheName);
                store.commit(softLocks);
            }
            LOG.debug("committed transaction {}", transactionId);
        } finally {
            unfreezeAndUnlock();
            softLockMap.clear();
            storeMap.clear();
            fireAfterCommitEvent();
        }
    }

    /**
     * Rollback all work done in the context and release all registered soft locks
     */
    public void rollback() {
        try {
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} participating cache(s), rolling back transaction {}", softLockMap.keySet().size(), transactionId);
            }
            freeze();

            for (Map.Entry<String, List<SoftLock>> stringListEntry : softLockMap.entrySet()) {
                String cacheName = stringListEntry.getKey();
                LocalTransactionStore store = storeMap.get(cacheName);
                List<SoftLock> softLocks = stringListEntry.getValue();

                LOG.debug("rolling back soft locked values of cache {}", cacheName);
                store.rollback(softLocks);
            }
            LOG.debug("rolled back transaction {}", transactionId);
        } finally {
            unfreezeAndUnlock();
            softLockMap.clear();
            storeMap.clear();
            fireAfterRollbackEvent();
        }
    }

    /**
     * Get the transaction ID of the context
     * @return the transaction ID
     */
    public TransactionID getTransactionId() {
        return transactionId;
    }

    /**
     * Add a TransactionListener to this context
     * @param listener the listener
     */
    public void addListener(TransactionListener listener) {
        this.listeners.add(listener);
    }

    private void fireBeforeCommitEvent() {
        for (TransactionListener listener : listeners) {
            try {
                listener.beforeCommit();
            } catch (Exception e) {
                LOG.error("beforeCommit error", e);
            }
        }
    }

    private void fireAfterCommitEvent() {
        for (TransactionListener listener : listeners) {
            try {
                listener.afterCommit();
            } catch (Exception e) {
                LOG.error("afterCommit error", e);
            }
        }
    }

    private void fireAfterRollbackEvent() {
        for (TransactionListener listener : listeners) {
            try {
                listener.afterRollback();
            } catch (Exception e) {
                LOG.error("afterRollback error", e);
            }
        }
    }

    private void unfreezeAndUnlock() {
        for (Map.Entry<String, List<SoftLock>> stringListEntry : softLockMap.entrySet()) {
            List<SoftLock> softLocks = stringListEntry.getValue();

            for (SoftLock softLock : softLocks) {
                try {
                    softLock.unfreeze();
                } catch (Exception e) {
                    LOG.error("error unfreezing " + softLock, e);
                }
                try {
                    softLock.unlock();
                } catch (Exception e) {
                    LOG.error("error unlocking " + softLock, e);
                }
            }
        }
    }

    private void freeze() {
        for (Map.Entry<String, List<SoftLock>> stringListEntry : softLockMap.entrySet()) {
            List<SoftLock> softLocks = stringListEntry.getValue();

            for (SoftLock softLock : softLocks) {
                softLock.freeze();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return transactionId.hashCode();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof LocalTransactionContext) {
            LocalTransactionContext otherCtx = (LocalTransactionContext) obj;
            return transactionId.equals(otherCtx.transactionId);
        }
        return false;
    }

}
TOP

Related Classes of net.sf.ehcache.transaction.local.LocalTransactionContext

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