You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2008/10/14 09:48:24 UTC
svn commit: r704361 [4/5] - in /jackrabbit/trunk:
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/o...
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Tue Oct 14 00:48:22 2008
@@ -73,6 +73,8 @@
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.lock.LockException;
import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
import java.io.InputStream;
/**
@@ -139,17 +141,15 @@
public void save(ItemState state) throws ReferentialIntegrityException,
InvalidItemStateException, RepositoryException {
// shortcut, if no modifications are present
- if (!hasPendingChanges()) {
+ if (!transientStateMgr.hasPendingChanges()) {
return;
}
-
// collect the changes to be saved
- ChangeLog changeLog = getChangeLog(state, true);
+ ChangeLog changeLog = transientStateMgr.getChangeLog(state, true);
if (!changeLog.isEmpty()) {
// only pass changelog if there are transient modifications available
// for the specified item and its decendants.
workspaceItemStateMgr.execute(changeLog);
-
// remove states and operations just processed from the transient ISM
transientStateMgr.dispose(changeLog);
// now its save to clear the changeLog
@@ -168,20 +168,17 @@
* another item needs to be canceled as well in another sub-tree.
*/
public void undo(ItemState itemState) throws ConstraintViolationException, RepositoryException {
- try {
- ChangeLog changeLog = getChangeLog(itemState, false);
- if (!changeLog.isEmpty()) {
- // let changelog revert all changes
- changeLog.undo();
- // remove transient states and related operations from the t-statemanager
- transientStateMgr.dispose(changeLog);
- changeLog.reset();
- }
- } catch (InvalidItemStateException e) {
- // should never get here
- String msg = "Unable to undo item.";
- log.debug(msg);
- throw new RepositoryException(e);
+ // short cut
+ if (!transientStateMgr.hasPendingChanges()) {
+ return;
+ }
+ ChangeLog changeLog = transientStateMgr.getChangeLog(itemState, false);
+ if (!changeLog.isEmpty()) {
+ // let changelog revert all changes
+ changeLog.undo();
+ // remove transient states and related operations from the t-statemanager
+ transientStateMgr.dispose(changeLog);
+ changeLog.reset();
}
}
@@ -262,7 +259,8 @@
NodeState parent = operation.getParentState();
ItemDefinitionProvider defProvider = mgrProvider.getItemDefinitionProvider();
QNodeDefinition def = defProvider.getQNodeDefinition(parent.getAllNodeTypeNames(), operation.getNodeName(), operation.getNodeTypeName());
- addNodeState(parent, operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid(), def, options);
+ List newStates = addNodeState(parent, operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid(), def, options);
+ operation.addedState(newStates);
transientStateMgr.addOperation(operation);
}
@@ -336,11 +334,9 @@
| ItemStateValidator.CHECK_VERSIONING
| ItemStateValidator.CHECK_CONSTRAINTS;
removeItemState(state, options);
- // unless new state got removed remember operation and mark parent modified.
- if (!Status.isTerminal(state.getStatus())) {
- transientStateMgr.addOperation(operation);
- operation.getParentState().markModified();
- }
+
+ transientStateMgr.addOperation(operation);
+ operation.getParentState().markModified();
}
/**
@@ -351,11 +347,12 @@
// NOTE: nodestate is only modified upon save of the changes!
Name[] mixinNames = operation.getMixinNames();
NodeState nState = operation.getNodeState();
- NodeEntry nEntry = (NodeEntry) nState.getHierarchyEntry();
+ NodeEntry nEntry = nState.getNodeEntry();
- // new array of mixinNames to be set on the nodestate (and corresponding property state)
+ // assert the existence of the property entry and set the array of
+ // mixinNames to be set on the corresponding property state
PropertyEntry mixinEntry = nEntry.getPropertyEntry(NameConstants.JCR_MIXINTYPES);
- if (mixinNames != null && mixinNames.length > 0) {
+ if (mixinNames.length > 0) {
// update/create corresponding property state
if (mixinEntry != null) {
// execute value of existing property
@@ -372,22 +369,16 @@
}
nState.markModified();
transientStateMgr.addOperation(operation);
- } else {
+ } else if (mixinEntry != null) {
// remove the jcr:mixinTypes property state if already present
- if (mixinEntry != null) {
- PropertyState pState = mixinEntry.getPropertyState();
- boolean newMixinState = pState.getStatus() == Status.NEW;
- int options = ItemStateValidator.CHECK_LOCK | ItemStateValidator.CHECK_VERSIONING;
- removeItemState(pState, options);
- // only added the remove-mixin operation if it doesn't revert
- // a previous 'add-mixin' (which has been removed automatically
- // upon notification of removing the prop-state).
- if (!newMixinState) {
- nState.markModified();
- transientStateMgr.addOperation(operation);
- }
- }
- }
+ PropertyState pState = mixinEntry.getPropertyState();
+ boolean newMixinState = pState.getStatus() == Status.NEW;
+ int options = ItemStateValidator.CHECK_LOCK | ItemStateValidator.CHECK_VERSIONING;
+ removeItemState(pState, options);
+
+ nState.markModified();
+ transientStateMgr.addOperation(operation);
+ } // else: empty Name array and no mixin-prop-entry (should not occur)
}
/**
@@ -538,40 +529,6 @@
//--------------------------------------------< Internal State Handling >---
/**
*
- * @param itemState
- * @param throwOnStale Throws InvalidItemStateException if either the given
- * <code>ItemState</code> or any of its decendants is stale and the flag is true.
- * @return
- * @throws InvalidItemStateException if a stale <code>ItemState</code> is
- * encountered while traversing the state hierarchy. The <code>changeLog</code>
- * might have been populated with some transient item states. A client should
- * therefore not reuse the <code>changeLog</code> if such an exception is thrown.
- * @throws RepositoryException if <code>state</code> is a new item state.
- */
- private ChangeLog getChangeLog(ItemState itemState, boolean throwOnStale) throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
- // build changelog for affected and decendant states only
- ChangeLog changeLog = new ChangeLog(itemState);
- // fail-fast test: check status of this item's state
- if (itemState.getStatus() == Status.NEW) {
- String msg = "Cannot save/revert an item with status NEW (" +itemState+ ").";
- log.debug(msg);
- throw new RepositoryException(msg);
- }
- if (throwOnStale && Status.isStale(itemState.getStatus())) {
- String msg = "Attempt to save/revert an item, that has been externally modified (" +itemState+ ").";
- log.debug(msg);
- throw new InvalidItemStateException(msg);
- }
- // collect transient/stale states that should be persisted or reverted
- itemState.getHierarchyEntry().collectStates(changeLog, throwOnStale);
-
- changeLog.collectOperations(transientStateMgr.getOperations());
- changeLog.checkIsSelfContained();
- return changeLog;
- }
-
- /**
- *
* @param parent
* @param propertyName
* @param propertyType
@@ -589,17 +546,17 @@
* @throws VersionException
* @throws RepositoryException
*/
- private void addPropertyState(NodeState parent, Name propertyName,
+ private PropertyState addPropertyState(NodeState parent, Name propertyName,
int propertyType, QValue[] values,
QPropertyDefinition pDef, int options)
throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
validator.checkAddProperty(parent, propertyName, pDef, options);
// create property state
- transientStateMgr.createNewPropertyState(propertyName, parent, pDef, values, propertyType);
+ return transientStateMgr.createNewPropertyState(propertyName, parent, pDef, values, propertyType);
}
- private void addNodeState(NodeState parent, Name nodeName, Name nodeTypeName,
+ private List addNodeState(NodeState parent, Name nodeName, Name nodeTypeName,
String uuid, QNodeDefinition definition, int options)
throws RepositoryException, ConstraintViolationException, AccessDeniedException,
UnsupportedRepositoryOperationException, NoSuchNodeTypeException,
@@ -623,14 +580,17 @@
}
}
+ List addedStates = new ArrayList();
+
// create new nodeState. NOTE, that the uniqueID is not added to the
- // state for consistency between 'addNode' and importXML // TODO review
+ // state for consistency between 'addNode' and importXML
NodeState nodeState = transientStateMgr.createNewNodeState(nodeName, null, nodeTypeName, definition, parent);
+ addedStates.add(nodeState);
if (uuid != null) {
QValue[] value = getQValues(uuid, qValueFactory);
ItemDefinitionProvider defProvider = mgrProvider.getItemDefinitionProvider();
QPropertyDefinition pDef = defProvider.getQPropertyDefinition(NameConstants.MIX_REFERENCEABLE, NameConstants.JCR_UUID, PropertyType.STRING, false);
- addPropertyState(nodeState, NameConstants.JCR_UUID, PropertyType.STRING, value, pDef, 0);
+ addedStates.add(addPropertyState(nodeState, NameConstants.JCR_UUID, PropertyType.STRING, value, pDef, 0));
}
// add 'auto-create' properties defined in node type
@@ -642,7 +602,7 @@
if (autoValue != null) {
int propOptions = ItemStateValidator.CHECK_NONE;
// execute 'addProperty' without adding operation.
- addPropertyState(nodeState, pd.getName(), pd.getRequiredType(), autoValue, pd, propOptions);
+ addedStates.add(addPropertyState(nodeState, pd.getName(), pd.getRequiredType(), autoValue, pd, propOptions));
}
}
}
@@ -653,8 +613,9 @@
QNodeDefinition nd = nda[i];
// execute 'addNode' without adding the operation.
int opt = ItemStateValidator.CHECK_LOCK | ItemStateValidator.CHECK_COLLISION;
- addNodeState(nodeState, nd.getName(), nd.getDefaultPrimaryType(), null, nd, opt);
+ addedStates.addAll(addNodeState(nodeState, nd.getName(), nd.getDefaultPrimaryType(), null, nd, opt));
}
+ return addedStates;
}
private void removeItemState(ItemState itemState, int options) throws RepositoryException {
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java Tue Oct 14 00:48:22 2008
@@ -63,7 +63,7 @@
public static final int MODIFIED = 7;
/**
- * a new state was deleted and is now 'removed'
+ * a new state was removed and is now 'removed'
* or an existing item has been removed by a workspace operation or
* by an external modification.
*/
@@ -137,7 +137,7 @@
*
* @param status the status to check.
* @return <code>true</code> if <code>status</code> indicates that an item
- * state is stale.
+ * state is transiently modified.
*/
public static boolean isTransient(int status) {
return status == EXISTING_MODIFIED || status == EXISTING_REMOVED || status == NEW;
@@ -184,10 +184,10 @@
break;
case STALE_MODIFIED:
case STALE_DESTROYED:
- isValid = (oldStatus == EXISTING_MODIFIED);
+ isValid = (oldStatus == EXISTING_MODIFIED || oldStatus == EXISTING_REMOVED || oldStatus == STALE_MODIFIED);
break;
case REMOVED:
- // external removal always possible -> getNewStatus(int, int)
+ // removal always possible -> getNewStatus(int, int)
isValid = true;
break;
case MODIFIED:
@@ -220,7 +220,6 @@
// temporarily set the state to MODIFIED in order to inform listeners.
newStatus = Status.MODIFIED;
} else if (oldStatus == Status.EXISTING_MODIFIED) {
- // TODO: try to merge changes
newStatus = Status.STALE_MODIFIED;
} else {
// old status is EXISTING_REMOVED (or any other) => ignore.
@@ -229,7 +228,7 @@
}
break;
case Status.REMOVED:
- if (oldStatus == Status.EXISTING_MODIFIED) {
+ if (oldStatus == Status.EXISTING_MODIFIED || oldStatus == Status.STALE_MODIFIED) {
newStatus = Status.STALE_DESTROYED;
} else {
// applies both to NEW or to any other status
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java Tue Oct 14 00:48:22 2008
@@ -23,6 +23,7 @@
import org.apache.jackrabbit.spi.PropertyId;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
import org.apache.jackrabbit.jcr2spi.nodetype.ItemDefinitionProvider;
@@ -71,12 +72,10 @@
* @inheritDoc
* @see TransientItemStateFactory#createNewPropertyState(PropertyEntry, QPropertyDefinition)
*/
- public PropertyState createNewPropertyState(PropertyEntry entry, QPropertyDefinition definition) {
- PropertyState propState = new PropertyState(entry, this, definition, defProvider);
-
+ public PropertyState createNewPropertyState(PropertyEntry entry, QPropertyDefinition definition, QValue[] values, int propertyType) throws RepositoryException {
+ PropertyState propState = new PropertyState(entry, this, definition, defProvider, values, propertyType);
// notify listeners that a property state has been created
notifyCreated(propState);
-
return propState;
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java Tue Oct 14 00:48:22 2008
@@ -21,6 +21,9 @@
import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+
+import javax.jcr.RepositoryException;
/**
* <code>TransientItemStateFactory</code> extends the item state factory and
@@ -46,8 +49,12 @@
*
* @param entry
* @param definition
+ * @param values
+ * @param propertyType
* @return the created <code>PropertyState</code>.
*/
public PropertyState createNewPropertyState(PropertyEntry entry,
- QPropertyDefinition definition);
+ QPropertyDefinition definition,
+ QValue[] values, int propertyType)
+ throws RepositoryException;
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Tue Oct 14 00:48:22 2008
@@ -16,19 +16,26 @@
*/
package org.apache.jackrabbit.jcr2spi.state;
-import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.commons.collections.iterators.IteratorChain;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEntry;
import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.QValue;
-import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemExistsException;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.ConstraintViolationException;
+import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
/**
* <code>TransientItemStateManager</code> adds support for transient changes on
@@ -47,23 +54,40 @@
private static final Logger log = LoggerFactory.getLogger(TransientItemStateManager.class);
/**
- * The change log which keeps track of changes and maintains hard references
- * to changed item states.
+ * Added states
+ */
+ private final Set addedStates = new LinkedHashSet();
+
+ /**
+ * Modified states
+ */
+ private final Set modifiedStates = new LinkedHashSet();
+
+ /**
+ * Removed states
*/
- private final ChangeLog changeLog;
+ private final Set removedStates = new LinkedHashSet();
+ /**
+ * Stale states
+ */
+ private final Set staleStates = new LinkedHashSet();
+
+ /**
+ * Set of operations
+ */
+ private Set operations = new LinkedHashSet();
/**
*
*/
TransientItemStateManager() {
- this.changeLog = new ChangeLog(null);
}
/**
* @return the operations that have been recorded until now.
*/
Iterator getOperations() {
- return changeLog.getOperations();
+ return operations.iterator();
}
/**
@@ -73,14 +97,121 @@
* @param operation
*/
void addOperation(Operation operation) {
- changeLog.addOperation(operation);
+ operations.add(operation);
}
/**
* @return <code>true</code> if this transient ISM has pending changes.
*/
boolean hasPendingChanges() {
- return !changeLog.isEmpty();
+ return !operations.isEmpty();
+ }
+
+ /**
+ * Create the change log for the tree starting at <code>target</code>. This
+ * includes a check if the ChangeLog to be created is totally 'self-contained'
+ * and independant; items within the scope of this update operation (i.e.
+ * below the target) must not have dependencies outside of this tree (e.g.
+ * moving a node requires that the target node including both old and new
+ * parents are saved).
+ *
+ * @param target
+ * @param throwOnStale Throws InvalidItemStateException if either the given
+ * <code>ItemState</code> or any of its decendants is stale and the flag is true.
+ * @return
+ * @throws InvalidItemStateException if a stale <code>ItemState</code> is
+ * encountered while traversing the state hierarchy. The <code>changeLog</code>
+ * might have been populated with some transient item states. A client should
+ * therefore not reuse the <code>changeLog</code> if such an exception is thrown.
+ * @throws RepositoryException if <code>state</code> is a new item state.
+ */
+ ChangeLog getChangeLog(ItemState target, boolean throwOnStale) throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
+ // fail-fast test: check status of this item's state
+ if (target.getStatus() == Status.NEW) {
+ String msg = "Cannot save/revert an item with status NEW (" +target+ ").";
+ log.debug(msg);
+ throw new RepositoryException(msg);
+ }
+ if (throwOnStale && Status.isStale(target.getStatus())) {
+ String msg = "Attempt to save/revert an item, that has been externally modified (" +target+ ").";
+ log.debug(msg);
+ throw new InvalidItemStateException(msg);
+ }
+
+ Set ops = new LinkedHashSet();
+ Set affectedStates = new LinkedHashSet();
+
+ HierarchyEntry he = target.getHierarchyEntry();
+ if (he.getParent() == null) {
+ // the root entry -> the complete change log can be used for
+ // simplicity. collecting ops, states can be omitted.
+ if (throwOnStale && !staleStates.isEmpty()) {
+ String msg = "Cannot save changes: States has been modified externally.";
+ log.debug(msg);
+ throw new InvalidItemStateException(msg);
+ } else {
+ affectedStates.addAll(staleStates);
+ }
+ ops.addAll(operations);
+ affectedStates.addAll(addedStates);
+ affectedStates.addAll(modifiedStates);
+ affectedStates.addAll(removedStates);
+ } else {
+ // not root entry:
+ // - check if there is a stale state in the scope (save only)
+ if (throwOnStale) {
+ for (Iterator it = staleStates.iterator(); it.hasNext();) {
+ ItemState state = (ItemState) it.next();
+ if (containedInTree(target, state)) {
+ String msg = "Cannot save changes: States has been modified externally.";
+ log.debug(msg);
+ throw new InvalidItemStateException(msg);
+ }
+ }
+ }
+ // - collect all affected states within the scope of save/undo
+ Iterator[] its = new Iterator[] {
+ addedStates.iterator(),
+ removedStates.iterator(),
+ modifiedStates.iterator()
+ };
+ IteratorChain chain = new IteratorChain(its);
+ if (!throwOnStale) {
+ chain.addIterator(staleStates.iterator());
+ }
+ while (chain.hasNext()) {
+ ItemState state = (ItemState) chain.next();
+ if (containedInTree(target, state)) {
+ affectedStates.add(state);
+ }
+ }
+ // - collect the set of operations and
+ // check if the affected states listed by the operations are all
+ // listed in the modified,removed or added states collected by this
+ // changelog.
+ for (Iterator it = operations.iterator(); it.hasNext();) {
+ Operation op = (Operation) it.next();
+ Collection opStates = op.getAffectedItemStates();
+ for (Iterator osIt = opStates.iterator(); osIt.hasNext();) {
+ ItemState state = (ItemState) osIt.next();
+ if (affectedStates.contains(state)) {
+ // operation needs to be included
+ if (!affectedStates.containsAll(opStates)) {
+ // incomplete changelog: need to save a parent as well
+ String msg = "ChangeLog is not self contained.";
+ throw new ConstraintViolationException(msg);
+ }
+ // no violation: add operation an stop iteration over
+ // all affected states present in the operation.
+ ops.add(op);
+ break;
+ }
+ }
+ }
+ }
+
+ ChangeLog cl = new ChangeLog(target, ops, affectedStates);
+ return cl;
}
/**
@@ -99,15 +230,14 @@
NodeState createNewNodeState(Name nodeName, String uniqueID, Name nodeTypeName,
QNodeDefinition definition, NodeState parent)
throws RepositoryException {
- NodeState nodeState = ((NodeEntry) parent.getHierarchyEntry()).addNewNodeEntry(nodeName, uniqueID, nodeTypeName, definition);
+ NodeEntry ne = ((NodeEntry) parent.getHierarchyEntry()).addNewNodeEntry(nodeName, uniqueID, nodeTypeName, definition);
try {
parent.markModified();
} catch (RepositoryException e) {
- nodeState.getHierarchyEntry().remove();
+ ne.remove();
throw e;
}
-
- return nodeState;
+ return ne.getNodeState();
}
/**
@@ -129,15 +259,14 @@
throws ItemExistsException, ConstraintViolationException, RepositoryException {
// NOTE: callers must make sure, the property type is not 'undefined'
NodeEntry nodeEntry = (NodeEntry) parent.getHierarchyEntry();
- PropertyState propState = nodeEntry.addNewPropertyEntry(propName, definition);
+ PropertyEntry pe = nodeEntry.addNewPropertyEntry(propName, definition, values, propertyType);
try {
- propState.setValues(values, propertyType);
parent.markModified();
} catch (RepositoryException e) {
- propState.getHierarchyEntry().remove();
+ pe.remove();
throw e;
}
- return propState;
+ return pe.getPropertyState();
}
/**
@@ -145,19 +274,74 @@
* transiently modified item states.
*/
void dispose() {
- changeLog.reset();
+ addedStates.clear();
+ modifiedStates.clear();
+ removedStates.clear();
+ staleStates.clear();
+ // also clear all operations
+ operations.clear();
}
/**
- * Remove the states and operations listed in the changeLog from the
- * internal changeLog.
+ * Remove the states and operations listed in the changeLog from internal
+ * list of modifications.
*
* @param subChangeLog
*/
void dispose(ChangeLog subChangeLog) {
- changeLog.removeAll(subChangeLog);
+ Set affectedStates = subChangeLog.getAffectedStates();
+ addedStates.removeAll(affectedStates);
+ modifiedStates.removeAll(affectedStates);
+ removedStates.removeAll(affectedStates);
+ staleStates.removeAll(affectedStates);
+
+ operations.removeAll(subChangeLog.getOperations());
+ }
+
+ /**
+ * A state has been removed. If the state is not a new state
+ * (not in the collection of added ones), then remove
+ * it from the modified states collection and add it to the
+ * removed states collection.
+ *
+ * @param state state that has been removed
+ */
+ private void removed(ItemState state) {
+ if (!addedStates.remove(state)) {
+ modifiedStates.remove(state);
+ }
+ removedStates.add(state);
}
+ /**
+ *
+ * @param parent
+ * @param state
+ * @return
+ */
+ private static boolean containedInTree(ItemState parent, ItemState state) {
+ HierarchyEntry he = state.getHierarchyEntry();
+ HierarchyEntry pHe = parent.getHierarchyEntry();
+ // short cuts first
+ if (he == pHe || he.getParent() == pHe) {
+ return true;
+ }
+ if (!parent.isNode() || he == pHe.getParent()) {
+ return false;
+ }
+ // none of the simple cases: walk up hierarchy
+ HierarchyEntry pe = he.getParent();
+ while (pe != null) {
+ if (pe == pHe) {
+ return true;
+ }
+ pe = pe.getParent();
+ }
+
+ // state isn't descendant of 'parent'
+ return false;
+ }
+
//-----------------------------------------< ItemStateLifeCycleListener >---
/**
* Depending on status of the given state adapt change log.
@@ -168,28 +352,77 @@
* @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
*/
public void statusChanged(ItemState state, int previousStatus) {
- if (changeLog.isEmpty()) {
- return;
- }
+ /*
+ Update the collections of states that were transiently modified.
+ NOTE: cleanup of operations is omitted here. this is expected to
+ occur upon {@link ChangeLog#save()} and {@link ChangeLog#undo()}.
+ External modifications in contrast that clash with transient modifications
+ render the corresponding states stale.
+ */
switch (state.getStatus()) {
- case Status.EXISTING:
+ case (Status.EXISTING):
+ switch (previousStatus) {
+ case Status.EXISTING_MODIFIED:
+ // was modified and got persisted or reverted
+ modifiedStates.remove(state);
+ break;
+ case Status.EXISTING_REMOVED:
+ // was transiently removed and is now reverted
+ removedStates.remove(state);
+ break;
+ case Status.STALE_MODIFIED:
+ // was modified and stale and is now reverted
+ staleStates.remove(state);
+ break;
+ case Status.NEW:
+ // was new and has been saved now
+ addedStates.remove(state);
+ break;
+ //default:
+ // INVALIDATED, MODIFIED ignore. no effect to transient modifications.
+ // any other status change is invalid -> see Status#isValidStatusChange(int, int
+ }
+ break;
case Status.EXISTING_MODIFIED:
- case Status.EXISTING_REMOVED:
- case Status.REMOVED:
- changeLog.statusChanged(state, previousStatus);
+ // transition from EXISTING to EXISTING_MODIFIED
+ modifiedStates.add(state);
+ break;
+ case (Status.EXISTING_REMOVED):
+ // transition from EXISTING or EXISTING_MODIFIED to EXISTING_REMOVED
+ removed(state);
+ break;
+ case (Status.REMOVED):
+ switch (previousStatus) {
+ case Status.EXISTING_REMOVED:
+ // was transiently removed and removal was persisted.
+ // -> ignore
+ break;
+ case Status.NEW:
+ // a new entry was removed again: remember as removed
+ // in order to keep the operations and the affected
+ // states in sync
+ removed(state);
+ break;
+ }
break;
case Status.STALE_DESTROYED:
case Status.STALE_MODIFIED:
- // state is now stale. keep in modified. wait until refreshed
+ /**
+ state is stale due to external modification -> move it to
+ the collection of stale item states.
+ validation omitted for only 'existing_modified' states can
+ become stale see {@link Status#isValidStatusChange(int, int)}
+ */
+ modifiedStates.remove(state);
+ staleStates.add(state);
+ break;
case Status.MODIFIED:
- // MODIFIED is only possible on EXISTING states -> thus, there
- // must not be any transient modifications for that state.
- // we ignore it.
case Status.INVALIDATED:
- // -> nothing to do here.
+ // MODIFIED, INVALIDATED: ignore.
+ log.debug("Item " + state.getName() + " changed status from " + Status.getName(previousStatus) + " to " + Status.getName(state.getStatus()) + ".");
break;
default:
- log.error("ItemState has invalid status: " + state.getStatus());
+ log.error("ItemState "+ state.getName() + " has invalid status: " + state.getStatus());
}
}
@@ -200,7 +433,7 @@
public void created(ItemState state) {
// new state has been created
if (state.getStatus() == Status.NEW) {
- changeLog.added(state);
+ addedStates.add(state);
}
}
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java Tue Oct 14 00:48:22 2008
@@ -145,7 +145,9 @@
public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
try {
PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
- return createDeepPropertyState(info, anyParent, null);
+ PropertyState propState = createDeepPropertyState(info, anyParent, null);
+ assertValidState(propState, info);
+ return propState;
} catch (PathNotFoundException e) {
throw new ItemNotFoundException(e.getMessage());
}
@@ -210,7 +212,7 @@
// the given NodeEntry -> retrieve NodeState before executing
// validation check.
nodeState = createDeepNodeState(first, entry, infos);
- assertMatchingPath(first, nodeState.getNodeEntry());
+ assertValidState(nodeState, first);
} else {
// 'isDeep' == false -> the given NodeEntry must match to the
// first ItemInfo retrieved from the iterator.
@@ -261,9 +263,10 @@
parent.setUniqueID(uniqueID);
}
- // now build the nodestate itself
- NodeState state = new NodeState(entry, info, this, definitionProvider);
- state.setMixinTypeNames(info.getMixins());
+ if (Status.isTransient(entry.getStatus()) || Status.isStale(entry.getStatus())) {
+ log.debug("Node has pending changes; omit resetting the state.");
+ return entry.getNodeState();
+ }
// update NodeEntry from the information present in the NodeInfo (prop entries)
List propNames = new ArrayList();
@@ -273,7 +276,7 @@
propNames.add(propertyName);
}
try {
- entry.addPropertyEntries(propNames);
+ entry.setPropertyEntries(propNames);
} catch (ItemExistsException e) {
// should not get here
log.warn("Internal error", e);
@@ -286,8 +289,18 @@
entry.setNodeEntries(childInfos);
}
- notifyCreated(state);
- return state;
+ // now build or update the nodestate itself
+ NodeState tmp = new NodeState(entry, info, this, definitionProvider);
+ entry.setItemState(tmp);
+
+ NodeState nState = entry.getNodeState();
+ if (nState == tmp) {
+ // tmp state was used as resolution for the given entry i.e. the
+ // entry was not available before. otherwise the 2 states were
+ // merged. see HierarchyEntryImpl#setItemState
+ notifyCreated(nState);
+ }
+ return nState;
}
/**
@@ -298,7 +311,8 @@
* @param entry
* @return the new <code>PropertyState</code>.
*/
- private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry) {
+ private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry)
+ throws RepositoryException {
// make sure uuid part of id is correct
String uniqueID = info.getId().getUniqueID();
if (uniqueID != null) {
@@ -307,11 +321,23 @@
parent.setUniqueID(uniqueID);
}
- // build the PropertyState
- PropertyState state = new PropertyState(entry, info, this, definitionProvider);
-
- notifyCreated(state);
- return state;
+ if (Status.isTransient(entry.getStatus()) || Status.isStale(entry.getStatus())) {
+ log.debug("Property has pending changes; omit resetting the state.");
+ return entry.getPropertyState();
+ }
+
+ // now build or update the nodestate itself
+ PropertyState tmp = new PropertyState(entry, info, this, definitionProvider);
+ entry.setItemState(tmp);
+
+ PropertyState pState = entry.getPropertyState();
+ if (pState == tmp) {
+ // tmp state was used as resolution for the given entry i.e. the
+ // entry was not available before. otherwise the 2 states were
+ // merged. see HierarchyEntryImpl#setItemState
+ notifyCreated(pState);
+ }
+ return pState;
}
/**
@@ -326,10 +352,15 @@
// node for nodeId exists -> build missing entries in hierarchy
// Note, that the path contained in NodeId does not reveal which
// entries are missing -> calculate relative path.
- Path anyParentPath = anyParent.getPath();
+ Path anyParentPath = anyParent.getWorkspacePath();
Path relPath = anyParentPath.computeRelativePath(info.getPath());
Path.Element[] missingElems = relPath.getElements();
+ if (startsWithIllegalElement(missingElems)) {
+ log.error("Relative path to NodeEntry starts with illegal element -> ignore NodeInfo with path " + info.getPath());
+ return null;
+ }
+
NodeEntry entry = anyParent;
for (int i = 0; i < missingElems.length; i++) {
Name name = missingElems[i].getName();
@@ -360,8 +391,16 @@
Path anyParentPath = anyParent.getWorkspacePath();
Path relPath = anyParentPath.computeRelativePath(info.getPath());
Path.Element[] missingElems = relPath.getElements();
- NodeEntry entry = anyParent;
+ // make sure the missing elements don't start with . or .. in which
+ // case the info is not within the tree as it is expected
+ // (see also JCR-1797)
+ if (startsWithIllegalElement(missingElems)) {
+ log.error("Relative path to PropertyEntry starts with illegal element -> ignore PropertyInfo with path " + info.getPath());
+ return null;
+ }
+
+ NodeEntry entry = anyParent;
int i = 0;
// NodeEntries except for the very last 'missingElem'
while (i < missingElems.length - 1) {
@@ -402,6 +441,23 @@
}
/**
+ * Validation check: make sure the state is not null (was really created)
+ * and matches with the specified ItemInfo (path).
+ *
+ * @param state
+ * @param info
+ * @throws ItemNotFoundException
+ * @throws RepositoryException
+ */
+ private static void assertValidState(ItemState state, ItemInfo info)
+ throws ItemNotFoundException, RepositoryException {
+ if (state == null) {
+ throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
+ }
+ assertMatchingPath(info, state.getHierarchyEntry());
+ }
+
+ /**
* Validation check: Path of the given ItemInfo must match to the Path of
* the HierarchyEntry. This is required for Items that are identified by
* a uniqueID that may move within the hierarchy upon restore or clone.
@@ -421,6 +477,22 @@
}
/**
+ * Returns true if the given <code>missingElems</code> start with a parent (..),
+ * a current (.) or the root element, in which case the info is not within
+ * the tree as it is expected.
+ * See also #JCR-1797 for the corresponding enhancement request.
+ *
+ * @param missingElems
+ * @return
+ */
+ private static boolean startsWithIllegalElement(Path.Element[] missingElems) {
+ if (missingElems.length > 0) {
+ return !missingElems[0].denotesName();
+ }
+ return false;
+ }
+
+ /**
* @param entry
* @param degree
* @return the ancestor entry at the specified degree.
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java Tue Oct 14 00:48:22 2008
@@ -65,9 +65,8 @@
try {
return safeGetJCRPath(itemState.getHierarchyEntry().getPath(), pathResolver);
} catch (RepositoryException e) {
- ItemId id = itemState.getId();
- log.error("failed to convert " + id + " to JCR path.");
- return id.toString();
+ log.error("failed to convert " + itemState.toString() + " to JCR path.");
+ return itemState.toString();
}
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java Tue Oct 14 00:48:22 2008
@@ -16,13 +16,11 @@
*/
package org.apache.jackrabbit.jcr2spi.util;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
import org.apache.jackrabbit.jcr2spi.state.PropertyState;
import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
@@ -33,8 +31,6 @@
*/
public class StateUtility {
- private static Logger log = LoggerFactory.getLogger(StateUtility.class);
-
/**
*
* @param ps
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java Tue Oct 14 00:48:22 2008
@@ -453,19 +453,14 @@
Operation an = AddNode.create(parent, nodeInfo.getName(), ntName, nodeInfo.getUUID());
stateMgr.execute(an);
// retrieve id of state that has been created during execution of AddNode
- NodeState childState;
- List cne = parent.getNodeEntry().getNodeEntries(nodeInfo.getName());
- if (def.allowsSameNameSiblings()) {
- // TODO TOBEFIXED find proper solution. problem with same-name-siblings
- childState = ((NodeEntry)cne.get(cne.size()-1)).getNodeState();
- } else {
- childState = ((NodeEntry)cne.get(0)).getNodeState();
- }
+ NodeState childState = (NodeState) ((AddNode) an).getAddedStates().get(0);
// and set mixin types
- // TODO: missing validation
- Operation sm = SetMixin.create(childState, nodeInfo.getMixinNames());
- stateMgr.execute(sm);
+ Name[] mixinNames = nodeInfo.getMixinNames();
+ if (mixinNames != null && mixinNames.length > 0) {
+ Operation sm = SetMixin.create(childState, nodeInfo.getMixinNames());
+ stateMgr.execute(sm);
+ }
return childState;
}
}
Added: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AddPropertyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AddPropertyTest.java?rev=704361&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AddPropertyTest.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AddPropertyTest.java Tue Oct 14 00:48:22 2008
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jcr2spi;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+import javax.jcr.Property;
+import javax.jcr.Node;
+
+/** <code>AddPropertyTest</code>... */
+public class AddPropertyTest extends AbstractJCRTest {
+
+ private static Logger log = LoggerFactory.getLogger(AddPropertyTest.class);
+
+ private Node testNode;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ testNode = testRootNode.addNode(nodeName1);
+ testNode.setProperty(propertyName1, "existingProp");
+ testRootNode.save();
+ }
+
+ private static void assertItemStatus(Item item, int status) throws NotExecutableException {
+ if (!(item instanceof ItemImpl)) {
+ throw new NotExecutableException("org.apache.jackrabbit.jcr2spi.ItemImpl expected");
+ }
+ int st = ((ItemImpl) item).getItemState().getStatus();
+ assertEquals("Expected status to be " + Status.getName(status) + ", was " + Status.getName(st), status, st);
+ }
+
+ public void testReplacingProperty() throws RepositoryException,
+ NotExecutableException {
+ Property p1 = testNode.setProperty(propertyName1, "value1");
+ p1.remove();
+
+ Property p2 = testNode.setProperty(propertyName1, "value2");
+ p2.remove();
+
+ Property p3 = testNode.setProperty(propertyName1, "value3");
+ testNode.save();
+
+ assertTrue(testNode.hasProperty(propertyName1));
+ assertEquals("value3", testNode.getProperty(propertyName1).getString());
+
+ assertItemStatus(p1, Status.REMOVED);
+ assertItemStatus(p2, Status.REMOVED);
+ assertItemStatus(p3, Status.EXISTING);
+ }
+
+ public void testReplacingProperty2() throws RepositoryException,
+ NotExecutableException {
+ Property p1 = testNode.setProperty(propertyName2, "value1");
+ p1.remove();
+
+ Property p2 = testNode.setProperty(propertyName2, "value2");
+ p2.remove();
+
+ Property p3 = testNode.setProperty(propertyName2, "value3");
+ p3.remove();
+ testNode.save();
+
+ assertFalse(testNode.hasProperty(propertyName2));
+
+ assertItemStatus(p1, Status.REMOVED);
+ assertItemStatus(p2, Status.REMOVED);
+ assertItemStatus(p3, Status.REMOVED);
+ }
+
+ public void testRevertReplacingProperty() throws RepositoryException,
+ NotExecutableException {
+ String val = testNode.getProperty(propertyName1).getString();
+ Property p1 = testNode.setProperty(propertyName1, "value1");
+ p1.remove();
+
+ Property p2 = testNode.setProperty(propertyName1, "value2");
+ p2.remove();
+
+ Property p3 = testNode.setProperty(propertyName1, "value3");
+ testNode.refresh(false);
+
+ assertTrue(testNode.hasProperty(propertyName1));
+ assertEquals(val, p1.getString());
+
+ assertItemStatus(p1, Status.EXISTING);
+ assertItemStatus(p2, Status.REMOVED);
+ assertItemStatus(p3, Status.REMOVED);
+ }
+
+ public void testAddingProperty() throws RepositoryException,
+ NotExecutableException {
+ Property p1 = testNode.setProperty(propertyName2, "value1");
+ p1.remove();
+
+ Property p2 = testNode.setProperty(propertyName2, "value2");
+ p2.remove();
+
+ Property p3 = testNode.setProperty(propertyName2, "value3");
+ testNode.save();
+
+ assertTrue(testNode.hasProperty(propertyName2));
+
+ assertItemStatus(p1, Status.REMOVED);
+ assertItemStatus(p2, Status.REMOVED);
+ assertItemStatus(p3, Status.EXISTING);
+ }
+
+ public void testAddingProperty2() throws RepositoryException,
+ NotExecutableException {
+ Property p1 = testNode.setProperty(propertyName2, "value1");
+ p1.remove();
+
+ Property p2 = testNode.setProperty(propertyName2, "value2");
+ p2.remove();
+
+ Property p3 = testNode.setProperty(propertyName2, "value3");
+ p3.remove();
+ testNode.save();
+
+ assertFalse(testNode.hasProperty(propertyName2));
+
+ assertItemStatus(p1, Status.REMOVED);
+ assertItemStatus(p2, Status.REMOVED);
+ assertItemStatus(p3, Status.REMOVED);
+ }
+
+ public void testRevertAddingProperty() throws RepositoryException,
+ NotExecutableException {
+ Property p1 = testNode.setProperty(propertyName2, "value1");
+ p1.remove();
+
+ Property p2 = testNode.setProperty(propertyName2, "value2");
+ p2.remove();
+
+ Property p3 = testNode.setProperty(propertyName2, "value3");
+ testNode.refresh(false);
+
+ assertFalse(testNode.hasProperty(propertyName2));
+
+ assertItemStatus(p1, Status.REMOVED);
+ assertItemStatus(p2, Status.REMOVED);
+ assertItemStatus(p3, Status.REMOVED);
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AddPropertyTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AddPropertyTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java?rev=704361&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java Tue Oct 14 00:48:22 2008
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jcr2spi;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+
+import javax.jcr.Session;
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Item;
+
+/** <code>ExternalModificationTest</code>... */
+public class ExternalModificationTest extends AbstractJCRTest {
+
+ private static Logger log = LoggerFactory.getLogger(ExternalModificationTest.class);
+
+ private Node destParentNode;
+ private Node refNode;
+ private Session testSession;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // create a referenceable node and destination parent.
+ destParentNode = testRootNode.addNode(nodeName1, testNodeType);
+ refNode = testRootNode.addNode(nodeName2, getProperty("nodetype2"));
+ refNode.addMixin(mixReferenceable);
+ testRootNode.save();
+
+ testSession = helper.getReadWriteSession();
+ }
+
+ protected void tearDown() throws Exception {
+ if (testSession != null) {
+ testSession.logout();
+ }
+ super.tearDown();
+ }
+
+ private static boolean isItemStatus(Item item, int status) throws NotExecutableException {
+ if (!(item instanceof ItemImpl)) {
+ throw new NotExecutableException("org.apache.jackrabbit.jcr2spi.ItemImpl expected");
+ }
+ int st = ((ItemImpl) item).getItemState().getStatus();
+ return st == status;
+ }
+
+ private static void assertItemStatus(Item item, int status) throws NotExecutableException {
+ if (!(item instanceof ItemImpl)) {
+ throw new NotExecutableException("org.apache.jackrabbit.jcr2spi.ItemImpl expected");
+ }
+ int st = ((ItemImpl) item).getItemState().getStatus();
+ assertEquals("Expected status to be " + Status.getName(status) + ", was " + Status.getName(st), status, st);
+ }
+
+ public void testMovedReferenceableNode() throws RepositoryException, NotExecutableException {
+ Node refNode2 = (Node) testSession.getItem(refNode.getPath());
+
+ superuser.move(refNode.getPath(), destParentNode.getPath() + "/" + nodeName2);
+ superuser.save();
+
+ try {
+ // modify some prop of the moved node with session 2
+ refNode2.setProperty(propertyName1, "test");
+ testSession.save();
+ // node has been automatically moved to new place
+ // -> check if the parent is correct.
+ assertTrue(testSession.getItem(destParentNode.getPath()).isSame(refNode.getParent()));
+ } catch (InvalidItemStateException e) {
+ // no automatic move of the externally moved node. ok.
+ log.debug(e.getMessage());
+ }
+ }
+
+ public void testRefreshMovedReferenceableNode() throws RepositoryException, NotExecutableException {
+ Node refNode2 = (Node) testSession.getItem(refNode.getPath());
+
+ superuser.move(refNode.getPath(), destParentNode.getPath() + "/" + nodeName2);
+ superuser.save();
+
+ try {
+ refNode2.refresh(true);
+ Node parent = refNode2.getParent();
+ if (parent.isSame(testSession.getItem(destParentNode.getPath()))) {
+ // node has been automatically moved to new place
+ assertItemStatus(refNode2, Status.EXISTING);
+ } else {
+ assertItemStatus(refNode2, Status.REMOVED);
+ }
+ } catch (InvalidItemStateException e) {
+ // no automatic move of the externally moved node. ok.
+ log.debug(e.getMessage());
+ // since node had no pending changes -> status should be changed
+ // to REMOVED.
+ assertItemStatus(refNode2, Status.REMOVED);
+ }
+ }
+
+ public void testConflictingAddMixin() throws RepositoryException, NotExecutableException {
+ Node refNode2 = (Node) testSession.getItem(refNode.getPath());
+ refNode2.addMixin(mixLockable);
+
+ superuser.move(refNode.getPath(), destParentNode.getPath() + "/" + nodeName2);
+ superuser.save();
+
+ try {
+ refNode2.refresh(true);
+ Node parent = refNode2.getParent();
+ if (parent.isSame(testSession.getItem(destParentNode.getPath()))) {
+ // node has been automatically moved to new place
+ assertItemStatus(refNode2, Status.EXISTING_MODIFIED);
+ } else if (!isItemStatus(refNode2, Status.EXISTING_MODIFIED)) {
+ // external removal was detected either by observation or be
+ // batch-reading the parent -> status must be stale.
+ assertItemStatus(refNode2, Status.STALE_DESTROYED);
+ }
+ } catch (InvalidItemStateException e) {
+ // no automatic move of the externally moved node. ok.
+ log.debug(e.getMessage());
+ // since refNode2 has pending modifications its status should be
+ // changed to STALE_DESTROYED.
+ assertItemStatus(refNode2, Status.STALE_DESTROYED);
+ Node refAgain = testSession.getNodeByUUID(refNode.getUUID());
+ assertTrue(refAgain.getParent().isSame(testSession.getItem(destParentNode.getPath())));
+ assertFalse(refAgain.isNodeType(mixLockable));
+ }
+ }
+
+ public void testStaleDestroyed() throws RepositoryException, NotExecutableException {
+ Node refNode2 = (Node) testSession.getItem(refNode.getPath());
+ refNode2.addMixin(mixLockable);
+
+ superuser.move(refNode.getPath(), destParentNode.getPath() + "/" + nodeName2);
+ superuser.save();
+
+ try {
+ refNode2.refresh(true);
+ Node parent = refNode2.getParent();
+ } catch (InvalidItemStateException e) {
+ }
+
+ if (isItemStatus(refNode2, Status.STALE_DESTROYED)) {
+ try {
+ refNode2.refresh(false);
+ fail();
+ } catch (InvalidItemStateException e) {
+ // correct behaviour
+ }
+ }
+ }
+
+ public void testStaleDestroyed2() throws RepositoryException, NotExecutableException {
+ Node refNode2 = (Node) testSession.getItem(refNode.getPath());
+ refNode2.addMixin(mixLockable);
+
+ superuser.move(refNode.getPath(), destParentNode.getPath() + "/" + nodeName2);
+ superuser.save();
+
+ try {
+ refNode2.refresh(true);
+ Node parent = refNode2.getParent();
+ } catch (InvalidItemStateException e) {
+ }
+
+ if (isItemStatus(refNode2, Status.STALE_DESTROYED)) {
+ testSession.refresh(false);
+ assertItemStatus(refNode2, Status.REMOVED);
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java?rev=704361&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java Tue Oct 14 00:48:22 2008
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jcr2spi;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Session;
+import javax.jcr.RepositoryException;
+
+/** <code>GetPropertyTest</code>... */
+public class GetPropertyTest extends AbstractJCRTest {
+
+ private static Logger log = LoggerFactory.getLogger(GetPropertyTest.class);
+
+ private String prop1Path;
+ private String prop2Path;
+
+ private Session readOnly;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ Node n = testRootNode.addNode(nodeName1, testNodeType);
+ Property p = n.setProperty(propertyName1, "string1");
+ prop1Path = p.getPath();
+
+ p = n.setProperty(propertyName2, "string2");
+ prop2Path = p.getPath();
+
+ testRootNode.save();
+
+ readOnly = helper.getReadOnlySession();
+ }
+
+ protected void tearDown() throws Exception {
+ if (readOnly != null) {
+ readOnly.logout();
+ }
+ super.tearDown();
+ }
+
+ public void testItemExists() throws RepositoryException {
+ assertTrue(readOnly.itemExists(prop1Path));
+ assertTrue(readOnly.itemExists(prop2Path));
+ }
+
+ public void testGetItem() throws RepositoryException {
+ assertFalse(readOnly.getItem(prop1Path).isNode());
+ assertFalse(readOnly.getItem(prop2Path).isNode());
+ }
+
+ public void testHasProperty() throws RepositoryException {
+ String testPath = testRootNode.getPath();
+ Node trn = (Node) readOnly.getItem(testPath);
+
+ assertTrue(trn.hasProperty(prop1Path.substring(testPath.length() + 1)));
+ assertTrue(trn.hasProperty(prop2Path.substring(testPath.length() + 1)));
+ }
+
+ public void testGetProperty() throws RepositoryException {
+ String testPath = testRootNode.getPath();
+ Node trn = (Node) readOnly.getItem(testPath);
+
+ trn.getProperty(prop1Path.substring(testPath.length() + 1));
+ trn.getProperty(prop2Path.substring(testPath.length() + 1));
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java Tue Oct 14 00:48:22 2008
@@ -88,7 +88,11 @@
public void testGetProperties() throws RepositoryException {
Session readSession = helper.getReadOnlySession();
- dump((Node) readSession.getItem(fileNode.getPath()));
+ try {
+ dump((Node) readSession.getItem(fileNode.getPath()));
+ } finally {
+ readSession.logout();
+ }
}
/** Recursively outputs the contents of the given node. */
Added: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MixinModificationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MixinModificationTest.java?rev=704361&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MixinModificationTest.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MixinModificationTest.java Tue Oct 14 00:48:22 2008
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jcr2spi;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.test.NotExecutableException;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Session;
+import javax.jcr.ItemNotFoundException;
+
+/** <code>MixinModificationTest</code>... */
+public class MixinModificationTest extends AbstractJCRTest {
+
+ private static Logger log = LoggerFactory.getLogger(MixinModificationTest.class);
+
+ private static void assertItemStatus(Item item, int status) throws NotExecutableException {
+ if (!(item instanceof ItemImpl)) {
+ throw new NotExecutableException("org.apache.jackrabbit.jcr2spi.ItemImpl expected");
+ }
+ int st = ((ItemImpl) item).getItemState().getStatus();
+ assertEquals("Expected status to be " + Status.getName(status) + ", was " + Status.getName(st), status, st);
+ }
+
+ public void testAddMixin() throws RepositoryException,
+ NotExecutableException {
+ Node n = testRootNode.addNode(nodeName1);
+ testRootNode.save();
+
+ if (n.isNodeType(mixVersionable)) {
+ throw new NotExecutableException();
+ }
+ try {
+ n.addMixin(mixVersionable);
+ } catch (RepositoryException e) {
+ throw new NotExecutableException();
+ }
+
+ assertItemStatus(n, Status.EXISTING_MODIFIED);
+ assertTrue(n.hasProperty(jcrMixinTypes));
+ Property p = n.getProperty(jcrMixinTypes);
+
+ n.save();
+
+ // after saving the affected target node must be marked 'invalidated'.
+ // the property however should be set existing.
+ assertItemStatus(n, Status.INVALIDATED);
+ assertItemStatus(p, Status.EXISTING);
+ }
+
+ public void testAddMixin2() throws RepositoryException,
+ NotExecutableException {
+ Node n;
+ try {
+ n = testRootNode.addNode(nodeName1);
+ n.addMixin(mixVersionable);
+ testRootNode.save();
+ } catch (RepositoryException e) {
+ throw new NotExecutableException();
+ }
+
+ // after saving the affected target node must be marked 'invalidated'
+ // even if adding the node and setting a mixin was achieved in the
+ // same batch.
+ assertItemStatus(n, Status.INVALIDATED);
+ }
+
+ public void testRemoveMixin() throws RepositoryException, NotExecutableException {
+ String nPath;
+ try {
+ Node n = testRootNode.addNode(nodeName1);
+ nPath = n.getPath();
+ n.addMixin(mixReferenceable);
+ testRootNode.save();
+ } catch (RepositoryException e) {
+ throw new NotExecutableException();
+ }
+
+ Session testSession = helper.getReadWriteSession();
+ try {
+ Node n = (Node) testSession.getItem(nPath);
+ String uuid = n.getUUID();
+
+ // remove the mixin again.
+ n.removeMixin(mixReferenceable);
+ assertFalse(n.hasProperty(jcrMixinTypes));
+ n.save();
+
+ // accessing node by uuid should not be possible any more.
+ try {
+ Node n2 = testSession.getNodeByUUID(uuid);
+ fail();
+ } catch (ItemNotFoundException e) {
+ // ok
+ }
+
+ // however: the added node should still be valid. but not referenceable
+ assertItemStatus(n, Status.EXISTING);
+ assertFalse(n.isNodeType(mixReferenceable));
+ assertTrue(testSession.itemExists(nPath));
+
+ try {
+ Node n2 = superuser.getNodeByUUID(uuid);
+ fail();
+ } catch (ItemNotFoundException e) {
+ // ok
+ }
+ } finally {
+ if (testSession != null) {
+ testSession.logout();
+ }
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MixinModificationTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MixinModificationTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveCombinedTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveCombinedTest.java?rev=704361&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveCombinedTest.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveCombinedTest.java Tue Oct 14 00:48:22 2008
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jcr2spi;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+/** <code>MoveCombinedTest</code>... */
+public class MoveCombinedTest extends AbstractMoveTest {
+
+ private static Logger log = LoggerFactory.getLogger(MoveCombinedTest.class);
+
+ private Session testSession;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ testSession = helper.getReadOnlySession();
+ }
+
+ protected void tearDown() throws Exception {
+ if (testSession != null) {
+ testSession.logout();
+ }
+ super.tearDown();
+ }
+
+ protected boolean isSessionMove() {
+ return true;
+ }
+
+ public void testMoveAndAddNode() throws RepositoryException {
+ doMove(moveNode.getPath(), destinationPath);
+ Node n = moveNode.addNode(nodeName3);
+ superuser.save();
+
+ assertTrue(testSession.itemExists(n.getPath()));
+ }
+
+ public void testMoveAndAddProperty() throws RepositoryException {
+ doMove(moveNode.getPath(), destinationPath);
+ Property p = moveNode.setProperty(propertyName1, "someValue");
+ superuser.save();
+
+ assertTrue(testSession.itemExists(p.getPath()));
+ }
+
+ public void testMoveAndSetPropertyValue() throws RepositoryException {
+ Property p = moveNode.setProperty(propertyName1, "someValue");
+ moveNode.save();
+
+ doMove(moveNode.getPath(), destinationPath);
+ p = moveNode.setProperty(propertyName1, "changedValue");
+ superuser.save();
+
+ assertTrue(testSession.itemExists(p.getPath()));
+ }
+
+ public void testMoveAndRemove() throws RepositoryException {
+ Node n = moveNode.addNode(nodeName3);
+ String nPath = n.getPath();
+ superuser.save();
+
+ doMove(moveNode.getPath(), destinationPath);
+ n.remove();
+ superuser.save();
+
+ assertFalse(testSession.itemExists(nPath));
+ assertFalse(testSession.itemExists(destinationPath + "/" + nodeName3));
+ }
+
+ public void testMoveAndSetMixin() throws RepositoryException {
+
+ doMove(moveNode.getPath(), destinationPath);
+ moveNode.addMixin(mixVersionable);
+ superuser.save();
+
+ Node n = (Node) testSession.getItem(destinationPath);
+ assertTrue(n.isNodeType(mixVersionable));
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveCombinedTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveCombinedTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java Tue Oct 14 00:48:22 2008
@@ -22,6 +22,9 @@
import javax.jcr.RepositoryException;
import javax.jcr.Item;
import javax.jcr.PathNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
/**
* <code>MoveTreeTest</code>...
@@ -46,7 +49,6 @@
assertTrue("Moving a node must move all child items as well.", ancestor.isSame(destParentNode));
ancestor = grandChildNode.getAncestor(degree);
assertTrue("Moving a node must move all child items as well.", ancestor.isSame(destParentNode));
-
}
public void testTreeEntries() throws RepositoryException {
@@ -109,4 +111,23 @@
// OK
}
}
+
+ public void testRefreshMovedTree() throws RepositoryException {
+ testRootNode.refresh(true);
+ String msg = "Refresh must not revert a moved tree.";
+
+ assertFalse(msg, superuser.itemExists(srcPath + "/" + nodeName2 + "/" + nodeName3));
+ int degree = destParentNode.getDepth();
+
+ List l = new ArrayList();
+ l.add(childNode);
+ l.add(childProperty);
+ l.add(grandChildNode);
+
+ for (Iterator it = l.iterator(); it.hasNext();) {
+ Item item = (Item) it.next();
+ assertTrue(msg, item.isNew());
+ assertTrue(msg, childNode.getAncestor(degree).isSame(destParentNode));
+ }
+ }
}