/* * JBoss, Home of Professional Open Source * Copyright 2011 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt 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.infinispan.transaction; import org.infinispan.CacheException; import org.infinispan.commands.write.WriteCommand; import org.infinispan.container.entries.CacheEntry; import org.infinispan.remoting.transport.Address; import org.infinispan.transaction.xa.GlobalTransaction; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import javax.transaction.Transaction; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * Object that holds transaction's state on the node where it originated; as opposed to {@link RemoteTransaction}. * * @author Mircea.Markus@jboss.com * @author Pedro Ruivo * @author Sebastiano Peluso * @since 5.0 */ public abstract class LocalTransaction extends AbstractCacheTransaction { private static final Log log = LogFactory.getLog(LocalTransaction.class); private static final boolean trace = log.isTraceEnabled(); private final Transaction transaction; private final boolean implicitTransaction; protected Set<Object> readKeys = null; private Set<Address> remoteLockedNodes; /** * mark as volatile as this might be set from the tx thread code on view change */ private volatile boolean isMarkedForRollback; private boolean prepareSent = false; private boolean alreadyReadOnThisNode; private Set<Address> readFrom; public LocalTransaction(Transaction transaction, GlobalTransaction tx, boolean implicitTransaction, int viewId) { super(tx, viewId); this.transaction = transaction; this.implicitTransaction = implicitTransaction; } public void addModification(WriteCommand mod) { if (trace) log.tracef("Adding modification %s. Mod list is %s", mod, modifications); if (modifications == null) { modifications = new LinkedList<WriteCommand>(); } modifications.add(mod); } public void locksAcquired(Collection<Address> nodes) { log.tracef("Adding remote locks on %s. Remote locks are %s", nodes, remoteLockedNodes); if (remoteLockedNodes == null) remoteLockedNodes = new HashSet<Address>(nodes); else remoteLockedNodes.addAll(nodes); } public Collection<Address> getRemoteLocksAcquired() { if (remoteLockedNodes == null) return Collections.emptySet(); return remoteLockedNodes; } public void clearRemoteLocksAcquired() { if (remoteLockedNodes != null) remoteLockedNodes.clear(); } public void markForRollback(boolean markForRollback) { isMarkedForRollback = markForRollback; } public final boolean isMarkedForRollback() { return isMarkedForRollback; } public Transaction getTransaction() { return transaction; } @Override public Map<Object, CacheEntry> getLookedUpEntries() { return lookedUpEntries == null ? Collections.<Object, CacheEntry>emptyMap() : lookedUpEntries; } public boolean isImplicitTransaction() { return implicitTransaction; } @Override public void putLookedUpEntry(Object key, CacheEntry e) { if (isMarkedForRollback()) { throw new CacheException("This transaction is marked for rollback and cannot acquire locks!"); } else if (e == null) { return; //null not allowed in concurrent hash map } if (lookedUpEntries == null) lookedUpEntries = createMapEntries(4); lookedUpEntries.put(key, e); } @Override public void putLookedUpEntries(Map<Object, CacheEntry> entries) { if (isMarkedForRollback()) { throw new CacheException("This transaction is marked for rollback and cannot acquire locks!"); } if (lookedUpEntries == null) { lookedUpEntries = createMapEntries(entries); } else { lookedUpEntries.putAll(entries); } } public boolean isReadOnly() { return (modifications == null || modifications.isEmpty()) && (lookedUpEntries == null || lookedUpEntries.isEmpty()); } public abstract boolean isEnlisted(); @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; LocalTransaction that = (LocalTransaction) o; return tx.getId() == that.tx.getId(); } @Override public int hashCode() { long id = tx.getId(); return (int) (id ^ (id >>> 32)); } @Override public String toString() { return "LocalTransaction{" + "remoteLockedNodes=" + remoteLockedNodes + ", isMarkedForRollback=" + isMarkedForRollback + ", transaction=" + transaction + ", lockedKeys=" + lockedKeys + ", backupKeyLocks=" + backupKeyLocks + ", viewId=" + viewId + "} " + super.toString(); } public void setModifications(List<WriteCommand> modifications) { this.modifications = modifications; } @Override public void addReadKey(Object key) { if (readKeys == null) readKeys = new HashSet<Object>(2); readKeys.add(key); } @Override public boolean keyRead(Object key) { return readKeys != null && readKeys.contains(key); } @Override public final void markPrepareSent() { prepareSent = true; } @Override public final boolean wasPrepareSent() { return prepareSent; } @Override public void setAlreadyReadOnThisNode(boolean value) { if (log.isDebugEnabled()) { log.debugf("[%s] set already read on this node to %s", tx.prettyPrint(), value); } alreadyReadOnThisNode = value; } @Override public boolean hasAlreadyReadOnThisNode() { if (log.isDebugEnabled()) { log.debugf("[%s] has already read on this node? %s", tx.prettyPrint(), alreadyReadOnThisNode); } return alreadyReadOnThisNode; } @Override public Set<Address> getReadFrom() { if (log.isDebugEnabled()) { log.debugf("[%s] get read from %s", tx.prettyPrint(), readFrom); } return readFrom; } @Override public void addReadFrom(Address address) { if (readFrom == null) { readFrom = new HashSet<Address>(16); } if (log.isDebugEnabled()) { log.debugf("[%s] add read from %s to %s", tx.prettyPrint(), address, readFrom); } readFrom.add(address); } @Override public Collection<Object> getReadKeys() { if (log.isDebugEnabled()) { log.debugf("[%s] get read keys %s", tx.prettyPrint(), readKeys); } return readKeys; } }