/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.transaction; import org.jboss.cache.Fqn; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.config.Option; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.util.Immutables; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; /** * An abstract transaction context */ public abstract class AbstractTransactionContext implements TransactionContext { /** * Local transaction */ private Transaction ltx = null; private Option option; private OrderedSynchronizationHandler orderedSynchronizationHandler; private boolean forceAsyncReplication = false; private boolean forceSyncReplication = false; /** * List<ReversibleCommand> of modifications ({@link org.jboss.cache.commands.WriteCommand}). They will be replicated on TX commit */ private List modificationList; /** * A list of modifications that have been encountered with a LOCAL mode option. These will be removed from the modification list during replication. */ private List localModifications; /** * LinkedHashSet of locks acquired by the transaction. We use a LinkedHashSet because we need efficient Set semantics * but also need guaranteed ordering for use by lock release code (see JBCCACHE-874). *

* This needs to be unchecked since we support both MVCC (Fqns held here) or legacy Opt/Pess locking (NodeLocks held here). * once we drop support for opt/pess locks we can genericise this to contain Fqns. - Manik Surtani, June 2008 */ private LinkedHashSet transactionLocks; /** * A list of dummy uninitialised nodes created by the cache loader interceptor to load data for a * given node in this tx. */ private List dummyNodesCreatedByCacheLoader; /** * List of nodes that have been removed by the transaction */ private List removedNodes = null; public AbstractTransactionContext(Transaction tx) throws SystemException, RollbackException { ltx = tx; orderedSynchronizationHandler = new OrderedSynchronizationHandler(tx); } public void addModification(WriteCommand command) { if (command == null) return; if (modificationList == null) modificationList = new LinkedList(); modificationList.add(command); } public List getModifications() { if (modificationList == null) return Collections.emptyList(); return modificationList; } public void addLocalModification(WriteCommand command) { if (command == null) throw new NullPointerException("Command is null!"); if (localModifications == null) localModifications = new LinkedList(); localModifications.add(command); } public List getLocalModifications() { if (localModifications == null) return Collections.emptyList(); return localModifications; } public void addRemovedNode(Fqn fqn) { if (fqn == null) throw new NullPointerException("Fqn is null!"); if (removedNodes == null) removedNodes = new LinkedList(); removedNodes.add(fqn); } public List getRemovedNodes() { if (removedNodes == null) return Collections.emptyList(); return new ArrayList(removedNodes); } public void setTransaction(Transaction tx) { ltx = tx; } public Transaction getTransaction() { return ltx; } @SuppressWarnings("unchecked") public void addLock(Object lock) { // no need to worry about concurrency here - a context is only valid for a single thread. if (transactionLocks == null) transactionLocks = new LinkedHashSet(5); transactionLocks.add(lock); } @SuppressWarnings("unchecked") public void removeLock(Object lock) { // no need to worry about concurrency here - a context is only valid for a single thread. if (transactionLocks != null) transactionLocks.remove(lock); } public void clearLocks() { if (transactionLocks != null) transactionLocks.clear(); } public boolean hasLock(Object lock) { return transactionLocks != null && transactionLocks.contains(lock); } @SuppressWarnings("unchecked") public void addAllLocks(List newLocks) { // no need to worry about concurrency here - a context is only valid for a single thread. if (transactionLocks == null) transactionLocks = new LinkedHashSet(5); transactionLocks.addAll(newLocks); } @SuppressWarnings("unchecked") public List getLocks() { return transactionLocks == null || transactionLocks.isEmpty() ? Collections.emptyList() : Immutables.immutableListConvert(transactionLocks); } public boolean isForceAsyncReplication() { return forceAsyncReplication; } public void setForceAsyncReplication(boolean forceAsyncReplication) { this.forceAsyncReplication = forceAsyncReplication; if (forceAsyncReplication) { forceSyncReplication = false; } } public boolean isForceSyncReplication() { return forceSyncReplication; } public void setForceSyncReplication(boolean forceSyncReplication) { this.forceSyncReplication = forceSyncReplication; if (forceSyncReplication) { forceAsyncReplication = false; } } /** * Returns debug information about this transaction. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("TransactionEntry\nmodificationList: ").append(modificationList); return sb.toString(); } public void addDummyNodeCreatedByCacheLoader(Fqn fqn) { if (dummyNodesCreatedByCacheLoader == null) dummyNodesCreatedByCacheLoader = new LinkedList(); dummyNodesCreatedByCacheLoader.add(fqn); } public List getDummyNodesCreatedByCacheLoader() { if (dummyNodesCreatedByCacheLoader == null) return Collections.emptyList(); return dummyNodesCreatedByCacheLoader; } public void setOption(Option o) { this.option = o; } public Option getOption() { return this.option; } public OrderedSynchronizationHandler getOrderedSynchronizationHandler() { return orderedSynchronizationHandler; } public void setOrderedSynchronizationHandler(OrderedSynchronizationHandler orderedSynchronizationHandler) { this.orderedSynchronizationHandler = orderedSynchronizationHandler; } public boolean hasModifications() { return modificationList != null && !modificationList.isEmpty(); } public boolean hasLocalModifications() { return localModifications != null && !localModifications.isEmpty(); } public boolean hasAnyModifications() { return hasModifications() || hasLocalModifications(); } public void reset() { orderedSynchronizationHandler = null; modificationList = null; localModifications = null; option = null; if (transactionLocks != null) transactionLocks.clear(); if (dummyNodesCreatedByCacheLoader != null) dummyNodesCreatedByCacheLoader.clear(); if (removedNodes != null) removedNodes.clear(); } }