/* * 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; import static org.jboss.cache.AbstractNode.NodeFlags.VALID; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.lock.LockStrategyFactory; import org.jboss.cache.marshall.MarshalledValue; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.util.FastCopyHashMap; import org.jboss.cache.util.Immutables; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * UnversionedNode specific to pessimistic locking, with legacy code. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @SuppressWarnings("deprecation") @Deprecated public class PessimisticUnversionedNode extends UnversionedNode { /** * Lock manager that manages locks to be acquired when accessing the node inside a transaction. Lazy set just in case * locking is not needed. */ protected transient IdentityLock lock = null; protected LockStrategyFactory lockStrategyFactory; CommandsFactory commandsFactory; protected NodeFactory nodeFactory; public PessimisticUnversionedNode(Object name, Fqn fqn, Map data, CacheSPI cache) { super(fqn, cache, false); if (!fqn.isRoot() && !name.equals(fqn.getLastElement())) throw new IllegalArgumentException("Child " + name + " must be last part of " + fqn); if (data != null && !data.isEmpty()) setInternalState(data); else this.data = new FastCopyHashMap(); setLockForChildInsertRemove(cache != null && cache.getConfiguration() != null && cache.getConfiguration().isLockParentForChildInsertRemove()); } /** * @return a genericized version of the child map. */ @SuppressWarnings("unchecked") private ConcurrentMap> children() { return children; } // ------ lock-per-node paradigm public void injectLockStrategyFactory(LockStrategyFactory lockStrategyFactory) { this.lockStrategyFactory = lockStrategyFactory; } public void injectDependencies(CacheSPI spi, CommandsFactory commandsFactory, NodeFactory nodeFactory) { this.cache = spi; this.commandsFactory = commandsFactory; this.nodeFactory = nodeFactory; } protected synchronized void initLock() { if (lock == null) { lock = new IdentityLock(lockStrategyFactory, delegate); } } @Override public IdentityLock getLock() { initLock(); return lock; } @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); if (lock != null) { if (lock.isReadLocked()) { sb.append(" RL"); } if (lock.isWriteLocked()) { sb.append(" WL"); } } return sb.toString(); } @SuppressWarnings("unchecked") @Override public InternalNode copy() { PessimisticUnversionedNode n = new PessimisticUnversionedNode(fqn.getLastElement(), fqn, data, cache); copyInternals(n); n.children = children; n.lockStrategyFactory = lockStrategyFactory; n.commandsFactory = commandsFactory; n.nodeFactory = nodeFactory; return n; } // ------ legacy addChild methods that used a lot of implicit locks. @Override public void addChildDirect(NodeSPI child) { Fqn childFqn = child.getFqn(); if (childFqn.isDirectChildOf(fqn)) { synchronized (this) { children().put(child.getFqn().getLastElement(), child); } } else throw new CacheException("Attempting to add a child [" + child.getFqn() + "] to [" + getFqn() + "]. Can only add direct children."); } private NodeSPI getOrCreateChild(Object childName, GlobalTransaction gtx, boolean createIfNotExists, boolean notify) { NodeSPI child; if (childName == null) { throw new IllegalArgumentException("null child name"); } child = (NodeSPI) children().get(childName); InvocationContext ctx = cache.getInvocationContext(); if (createIfNotExists && child == null) { // construct the new child outside the synchronized block to avoid // spending any more time than necessary in the synchronized section Fqn childFqn = Fqn.fromRelativeElements(fqn, childName); NodeSPI newChild = nodeFactory.createNode(childFqn, delegate); if (newChild == null) { throw new IllegalStateException(); } synchronized (this) { // check again to see if the child exists // after acquiring exclusive lock child = (NodeSPI) children().get(childName); if (child == null) { if (notify) cache.getNotifier().notifyNodeCreated(childFqn, true, ctx); child = newChild; children().put(childName, child); } } // notify if we actually created a new child if (newChild == child) { if (trace) log.trace("created child: fqn=" + childFqn); if (gtx != null) { CreateNodeCommand createNodeCommand = commandsFactory.buildCreateNodeCommand(childFqn); ctx.getTransactionContext().addLocalModification(createNodeCommand); } if (notify) cache.getNotifier().notifyNodeCreated(childFqn, false, ctx); } } return child; } @Override public NodeSPI addChildDirect(Fqn f, boolean notify) { if (f.size() == 1) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(f.getLastElement(), gtx, true, notify); } else { throw new UnsupportedOperationException("Cannot directly create children which aren't directly under the current node."); } } @Override public NodeSPI addChildDirect(Object o, boolean notify) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(o, gtx, true, notify); } @Override public NodeSPI getChildDirect(Fqn fqn) { if (fqn.size() == 1) { return getChildDirect(fqn.getLastElement()); } else { NodeSPI currentNode = delegate; for (int i = 0; i < fqn.size(); i++) { Object nextChildName = fqn.get(i); currentNode = currentNode.getChildDirect(nextChildName); if (currentNode == null) return null; } return currentNode; } } @Override public NodeSPI getChildDirect(Object childName) { if (childName == null) return null; return (NodeSPI) children().get(childName); } @Override public Set> getChildrenDirect() { // strip out deleted child nodes... if (children == null || children.size() == 0) return Collections.emptySet(); Set> exclDeleted = new HashSet>(); for (Node n : children().values()) { NodeSPI spi = (NodeSPI) n; if (!spi.isDeleted()) exclDeleted.add(spi); } exclDeleted = Collections.unmodifiableSet(exclDeleted); return exclDeleted; } @Override public Map> getChildrenMapDirect() { return children(); } @Override public void setChildrenMapDirect(Map> children) { if (children == null) this.children = null; else { this.children.clear(); this.children().putAll(children); } } @Override public void addChildDirect(Object nodeName, Node nodeToAdd) { if (nodeName != null) { children().put(nodeName, nodeToAdd); } } @SuppressWarnings("unchecked") @Override public Set> getChildrenDirect(boolean includeMarkedForRemoval) { if (includeMarkedForRemoval) { if (children != null && !children.isEmpty()) { return Immutables.immutableSetConvert(children.values()); } else { return Collections.emptySet(); } } else { return getChildrenDirect(); } } @Override public NodeSPI addChildDirect(Fqn f) { return addChildDirect(f, true); } @Override public NodeSPI getOrCreateChild(Object childName, GlobalTransaction gtx) { return getOrCreateChild(childName, gtx, true, true); } @Override public void markAsRemoved(boolean marker, boolean recursive) { setFlag(NodeFlags.REMOVED, marker); if (recursive && children != null) { synchronized (this) { for (Node child : children().values()) { ((NodeSPI) child).markAsDeleted(marker, true); } } } } @Override public void setValid(boolean valid, boolean recursive) { setFlag(VALID, valid); if (trace) log.trace("Marking node " + getFqn() + " as " + (valid ? "" : "in") + "valid"); if (recursive) { for (Node child : children().values()) { ((NodeSPI) child).setValid(valid, recursive); } } } /** * Adds details of the node into a map as strings. */ @Override protected void printDetailsInMap(StringBuilder sb, int indent) { printIndent(sb, indent); indent += 2;// increse it sb.append(Fqn.SEPARATOR); if (!fqn.isRoot()) sb.append(fqn.getLastElement()); sb.append(" "); sb.append(data); for (Node n : children().values()) { sb.append("\n"); ((NodeSPI) n).printDetails(sb, indent); } } @Override public void setFqn(Fqn fqn) { if (trace) { log.trace(getFqn() + " set FQN " + fqn); } this.fqn = fqn; if (children == null) { return; } // invoke children for (Map.Entry> me : children().entrySet()) { NodeSPI n = (NodeSPI) me.getValue(); Fqn cfqn = Fqn.fromRelativeElements(fqn, me.getKey()); n.setFqn(cfqn); } } @Override public void releaseObjectReferences(boolean recursive) { if (recursive && children != null) { for (Node child : children().values()) { child.releaseObjectReferences(recursive); } } if (data != null) { for (K key : data.keySet()) { // get the key first, before attempting to serialize stuff since data.get() may deserialize the key if doing // a hashcode() or equals(). Object value = data.get(key); if (key instanceof MarshalledValue) { ((MarshalledValue) key).compact(true, true); } if (value instanceof MarshalledValue) { ((MarshalledValue) value).compact(true, true); } } } } }