You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2005/06/13 10:45:50 UTC
svn commit: r190372 [2/2] - in /incubator/jackrabbit/trunk/src:
java/org/apache/jackrabbit/core/ java/org/apache/jackrabbit/core/lock/
java/org/apache/jackrabbit/core/state/
test/org/apache/jackrabbit/test/api/lock/
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java Mon Jun 13 01:45:49 2005
@@ -19,6 +19,8 @@
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.QName;
import org.apache.jackrabbit.core.nodetype.NodeDefId;
+import org.apache.commons.collections.map.ReferenceMap;
+import org.apache.commons.collections.MapIterator;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -30,8 +32,8 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
+import java.util.Map;
/**
* <code>NodeState</code> represents the state of a <code>Node</code>.
@@ -62,6 +64,12 @@
protected List propertyEntries = new ArrayList();
/**
+ * Listeners (weak references)
+ */
+ private final transient ReferenceMap listeners =
+ new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
+
+ /**
* Constructor
*
* @param overlayedState the backing node state being overlayed
@@ -70,8 +78,9 @@
*/
public NodeState(NodeState overlayedState, int initialStatus,
boolean isTransient) {
- super(overlayedState, initialStatus, isTransient);
+ super(initialStatus, isTransient);
+ connect(overlayedState);
pull();
}
@@ -400,7 +409,30 @@
* @return the newly added <code>ChildNodeEntry<code>
*/
public synchronized ChildNodeEntry addChildNodeEntry(QName nodeName, String uuid) {
- return childNodeEntries.add(nodeName, uuid);
+ ChildNodeEntry entry = childNodeEntries.add(nodeName, uuid);
+ notifyNodeAdded(entry);
+ return entry;
+ }
+
+ /**
+ * Renames a new <code>ChildNodeEntry<code>.
+ *
+ * @param oldName <code>QName<code> object specifying the entry's old name
+ * @param index 1-based index if there are same-name child node entries
+ * @param newName <code>QName<code> object specifying the entry's new name
+ * @return <code>true</code> if the entry was sucessfully renamed;
+ * otherwise <code>false</code>
+ */
+ public synchronized boolean renameChildNodeEntry(QName oldName, int index,
+ QName newName) {
+ ChildNodeEntry oldEntry = childNodeEntries.remove(oldName, index);
+ if (oldEntry != null) {
+ ChildNodeEntry newEntry = addChildNodeEntry(newName, oldEntry.getUUID());
+ notifyNodeAdded(newEntry);
+ notifyNodeRemoved(oldEntry);
+ return true;
+ }
+ return false;
}
/**
@@ -412,7 +444,11 @@
* in the list of child node entries and could be removed.
*/
public synchronized boolean removeChildNodeEntry(QName nodeName, int index) {
- return childNodeEntries.remove(nodeName, index);
+ ChildNodeEntry entry = childNodeEntries.remove(nodeName, index);
+ if (entry != null) {
+ notifyNodeRemoved(entry);
+ }
+ return entry != null;
}
/**
@@ -429,6 +465,7 @@
public synchronized void setChildNodeEntries(List nodeEntries) {
childNodeEntries.removeAll();
childNodeEntries.addAll(nodeEntries);
+ notifyNodesReplaced();
}
/**
@@ -715,6 +752,85 @@
this.parentUUID = parentUUID;
}
+ /**
+ * @see ItemState#addListener
+ *
+ * If the listener passed is at the same time a <code>NodeStateListener</code>
+ * we add it to our list of specialized listeners.
+ */
+ public void addListener(ItemStateListener listener) {
+ if (listener instanceof NodeStateListener) {
+ synchronized (listeners) {
+ if (!listeners.containsKey(listener)) {
+ listeners.put(listener, listener);
+ }
+ }
+ }
+ super.addListener(listener);
+ }
+
+ /**
+ * @see ItemState#removeListener
+ *
+ * If the listener passed is at the same time a <code>NodeStateListener</code>
+ * we remove it from our list of specialized listeners.
+ */
+ public void removeListener(ItemStateListener listener) {
+ if (listener instanceof NodeStateListener) {
+ synchronized (listeners) {
+ listeners.remove(listener);
+ }
+ }
+ super.removeListener(listener);
+ }
+
+ /**
+ * Notify the listeners that some child node was added
+ */
+ protected void notifyNodeAdded(ChildNodeEntry added) {
+ synchronized (listeners) {
+ MapIterator iter = listeners.mapIterator();
+ while (iter.hasNext()) {
+ NodeStateListener l = (NodeStateListener) iter.next();
+ if (l != null) {
+ l.nodeAdded(this, added.getName(),
+ added.getIndex(), added.getUUID());
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify the listeners that the children nodes were replaced
+ */
+ protected void notifyNodesReplaced() {
+ synchronized (listeners) {
+ MapIterator iter = listeners.mapIterator();
+ while (iter.hasNext()) {
+ NodeStateListener l = (NodeStateListener) iter.next();
+ if (l != null) {
+ l.nodesReplaced(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify the listeners that some child node was removed
+ */
+ protected void notifyNodeRemoved(ChildNodeEntry removed) {
+ synchronized (listeners) {
+ MapIterator iter = listeners.mapIterator();
+ while (iter.hasNext()) {
+ NodeStateListener l = (NodeStateListener) iter.next();
+ if (l != null) {
+ l.nodeRemoved(this, removed.getName(),
+ removed.getIndex(), removed.getUUID());
+ }
+ }
+ }
+ }
+
//-------------------------------------------------< Serializable support >
private void writeObject(ObjectOutputStream out) throws IOException {
// delegate to default implementation
@@ -776,19 +892,19 @@
}
public boolean remove(ChildNodeEntry entry) {
- return remove(entry.getName(), entry.getIndex());
+ return remove(entry.getName(), entry.getIndex()) != null;
}
- public boolean remove(QName nodeName, int index) {
+ public ChildNodeEntry remove(QName nodeName, int index) {
if (index < 1) {
throw new IllegalArgumentException("index is 1-based");
}
List siblings = (List) names.get(nodeName);
if (siblings == null) {
- return false;
+ return null;
}
if (index > siblings.size()) {
- return false;
+ return null;
}
// remove from siblings list
ChildNodeEntry removedEntry = (ChildNodeEntry) siblings.remove(index - 1);
@@ -798,7 +914,7 @@
if (siblings.size() == 0) {
// short cut
names.remove(nodeName);
- return true;
+ return removedEntry;
}
// update indices of subsequent same-name siblings
@@ -811,7 +927,7 @@
entries.set(entries.indexOf(oldEntry), newEntry);
}
- return true;
+ return removedEntry;
}
List get(QName nodeName) {
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeStateListener.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeStateListener.java?rev=190372&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeStateListener.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeStateListener.java Mon Jun 13 01:45:49 2005
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * 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.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.QName;
+
+/**
+ * Extends the <code>ItemStateListener</code> allowing a client to be
+ * additionally informed about changes on a <code>NodeState</code>.
+ *
+ * @see NodeState#addListener
+ */
+public interface NodeStateListener extends ItemStateListener {
+
+ /**
+ * Called when a child node has been added
+ * @param state node state that changed
+ * @param name name of node that was added
+ * @param index index of new node
+ * @param uuid uuid of new node
+ */
+ public void nodeAdded(NodeState state,
+ QName name, int index, String uuid);
+
+ /**
+ * Called when the children nodes were replaced by other nodes, typically
+ * as result of a reorder operation.
+ * @param state node state that changed
+ */
+ public void nodesReplaced(NodeState state);
+
+ /**
+ * Called when a child node has been removed
+ * @param state node state that changed
+ * @param name name of node that was removed
+ * @param index index of removed node
+ * @param uuid uuid of removed node
+ */
+ public void nodeRemoved(NodeState state,
+ QName name, int index, String uuid);
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeStateListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java Mon Jun 13 01:45:49 2005
@@ -56,8 +56,9 @@
*/
public PropertyState(PropertyState overlayedState, int initialStatus,
boolean isTransient) {
- super(overlayedState, initialStatus, isTransient);
+ super(initialStatus, isTransient);
+ connect(overlayedState);
pull();
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java Mon Jun 13 01:45:49 2005
@@ -19,7 +19,6 @@
import org.apache.jackrabbit.core.CachingHierarchyManager;
import org.apache.jackrabbit.core.Constants;
import org.apache.jackrabbit.core.HierarchyManager;
-import org.apache.jackrabbit.core.HierarchyManagerImpl;
import org.apache.jackrabbit.core.ItemId;
import org.apache.jackrabbit.core.MalformedPathException;
import org.apache.jackrabbit.core.NamespaceResolver;
@@ -63,7 +62,7 @@
/**
* Hierarchy manager
*/
- private HierarchyManager hierMgr;
+ private CachingHierarchyManager hierMgr;
/**
* Creates a new <code>SessionItemStateManager</code> instance.
@@ -81,33 +80,18 @@
// create transient item state manager
transientStateMgr = new TransientItemStateManager();
// create hierarchy manager that uses both transient and persistent state
- hierMgr = new HierarchyManagerImpl(rootNodeUUID, this, nsResolver,
- transientStateMgr.getAttic());
+ hierMgr = new CachingHierarchyManager(rootNodeUUID, this,
+ nsResolver, transientStateMgr.getAttic());
}
/**
* En-/Disable chaching of path values.
* <p/>
- * Please keep in mind that the cache of <code>Path</code>s is not automatically
- * updated when the underlying hierarchy is changing. Therefore it should only be
- * turned on with caution and in special situations (usually only locally
- * within a narrow scope) where the underlying hierarchy is not expected to
- * change.
- *
- * @param enable
+ * Paths are always cached, therefore this method has no implementation.
+ * @param enable <code>true</code> to enable caching;
+ * <code>false</code> to disable
*/
public void enablePathCaching(boolean enable) {
- if (enable) {
- if (!(hierMgr instanceof CachingHierarchyManager)) {
- hierMgr = new CachingHierarchyManager(hierMgr);
- }
- } else {
- if (hierMgr instanceof CachingHierarchyManager) {
- CachingHierarchyManager chm = (CachingHierarchyManager) hierMgr;
- chm.clearCache();
- hierMgr = chm.unwrap();
- }
- }
}
/**
@@ -231,6 +215,20 @@
}
/**
+ * Customized variant of {@link #createNew(String, QName, String)} that
+ * connects the newly created persistent state with the transient state.
+ */
+ public NodeState createNew(NodeState transientState)
+ throws IllegalStateException {
+
+ NodeState persistentState = createNew(transientState.getUUID(),
+ transientState.getNodeTypeName(),
+ transientState.getParentUUID());
+ transientState.connect(persistentState);
+ return persistentState;
+ }
+
+ /**
* {@inheritDoc}
*/
public PropertyState createNew(QName propName, String parentUUID)
@@ -239,6 +237,20 @@
}
/**
+ * Customized variant of {@link #createNew(String, QName, String)} that
+ * connects the newly created persistent state with the transient state.
+ */
+ public PropertyState createNew(PropertyState transientState)
+ throws IllegalStateException {
+
+ PropertyState persistentState = createNew(transientState.getName(),
+ transientState.getParentUUID());
+ transientState.connect(persistentState);
+ return persistentState;
+ }
+
+
+ /**
* {@inheritDoc}
*/
public void store(ItemState state) throws IllegalStateException {
@@ -566,7 +578,11 @@
*/
public NodeState createTransientNodeState(NodeState overlayedState, int initialStatus)
throws ItemStateException {
- return transientStateMgr.createNodeState(overlayedState, initialStatus);
+
+ NodeState state = transientStateMgr.createNodeState(
+ overlayedState, initialStatus);
+ hierMgr.stateOverlaid(state);
+ return state;
}
/**
@@ -589,7 +605,22 @@
*/
public PropertyState createTransientPropertyState(PropertyState overlayedState, int initialStatus)
throws ItemStateException {
- return transientStateMgr.createPropertyState(overlayedState, initialStatus);
+
+ PropertyState state = transientStateMgr.createPropertyState(
+ overlayedState, initialStatus);
+ hierMgr.stateOverlaid(state);
+ return state;
+ }
+
+ /**
+ * Disconnect a transient item state from its underlying persistent state.
+ * Notifies the <code>HierarchyManager</code> about the changed identity.
+ * @param state the transient <code>ItemState</code> instance that should
+ * be disconnected
+ */
+ public void disconnectTransientItemState(ItemState state) {
+ hierMgr.stateUncovered(state);
+ state.disconnect();
}
/**
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java Mon Jun 13 01:45:49 2005
@@ -580,6 +580,35 @@
}
/**
+ * Tests if locks are maintained when child nodes are reordered
+ */
+ public void testReorder2() throws Exception {
+ // create three lockable nodes with same name
+ Node testNode = testRootNode.addNode(nodeName1);
+ testNode.addMixin(mixLockable);
+ testNode = testRootNode.addNode(nodeName1);
+ testNode.addMixin(mixLockable);
+ testNode = testRootNode.addNode(nodeName1);
+ testNode.addMixin(mixLockable);
+ testRootNode.save();
+
+ // lock first node (1)
+ testRootNode.getNode(nodeName1 + "[1]").lock(false, true);
+
+ // assert: first node locked
+ assertTrue("First child node locked",
+ testRootNode.getNode(nodeName1 + "[1]").isLocked());
+
+ // move first node to last
+ testRootNode.orderBefore(nodeName1 + "[1]", null);
+ testRootNode.save();
+
+ // assert: third node locked
+ assertTrue("Third child node locked",
+ testRootNode.getNode(nodeName1 + "[3]").isLocked());
+ }
+
+ /**
* Return a flag indicating whether the indicated session contains
* a specific lock token
*/