use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class BatchedItemOperations method copy.
/**
* Copies the tree at <code>srcPath</code> retrieved using the specified
* <code>srcStateMgr</code> to the new location at <code>destPath</code>.
* Returns the id of the node at its new position.
* <p>
* <b>Precondition:</b> the state manager needs to be in edit mode.
*
* @param srcPath
* @param srcStateMgr
* @param srcHierMgr
* @param srcAccessMgr
* @param destPath
* @param flag one of
* <ul>
* <li><code>COPY</code></li>
* <li><code>CLONE</code></li>
* <li><code>CLONE_REMOVE_EXISTING</code></li>
* </ul>
* @return the id of the node at its new position
* @throws ConstraintViolationException
* @throws AccessDeniedException
* @throws VersionException
* @throws PathNotFoundException
* @throws ItemExistsException
* @throws LockException
* @throws RepositoryException
* @throws IllegalStateException if the state manager is not in edit mode.
*/
public NodeId copy(Path srcPath, ItemStateManager srcStateMgr, HierarchyManager srcHierMgr, AccessManager srcAccessMgr, Path destPath, int flag) throws ConstraintViolationException, AccessDeniedException, VersionException, PathNotFoundException, ItemExistsException, LockException, RepositoryException, IllegalStateException {
// check precondition
checkInEditMode();
// 1. check paths & retrieve state
NodeState srcState = getNodeState(srcStateMgr, srcHierMgr, srcPath);
Path destParentPath = destPath.getAncestor(1);
NodeState destParentState = getNodeState(destParentPath);
int ind = destPath.getIndex();
if (ind > 0) {
// subscript in name element
String msg = "invalid copy destination path: " + safeGetJCRPath(destPath) + " (subscript in name element is not allowed)";
log.debug(msg);
throw new RepositoryException(msg);
}
// 2. check access rights, lock status, node type constraints, etc.
// JCR-2269: store target node state in changelog early as a
// precautionary measure in order to isolate it from concurrent
// underlying changes while checking preconditions
stateMgr.store(destParentState);
checkAddNode(destParentState, destPath.getName(), srcState.getNodeTypeName(), CHECK_ACCESS | CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION);
// check read access right on source node using source access manager
try {
if (!srcAccessMgr.isGranted(srcPath, Permission.READ)) {
throw new PathNotFoundException(safeGetJCRPath(srcPath));
}
} catch (ItemNotFoundException infe) {
String msg = "internal error: failed to check access rights for " + safeGetJCRPath(srcPath);
log.debug(msg);
throw new RepositoryException(msg, infe);
}
// 3. do copy operation (modify and store affected states)
ReferenceChangeTracker refTracker = new ReferenceChangeTracker();
// create deep copy of source node state
NodeState newState = copyNodeState(srcState, srcPath, srcStateMgr, srcAccessMgr, destParentState.getNodeId(), flag, refTracker);
// add to new parent
destParentState.addChildNodeEntry(destPath.getName(), newState.getNodeId());
// adjust references that refer to uuid's which have been mapped to
// newly generated uuid's on copy/clone
Iterator<Object> iter = refTracker.getProcessedReferences();
while (iter.hasNext()) {
PropertyState prop = (PropertyState) iter.next();
// being paranoid...
if (prop.getType() != PropertyType.REFERENCE && prop.getType() != PropertyType.WEAKREFERENCE) {
continue;
}
boolean modified = false;
InternalValue[] values = prop.getValues();
InternalValue[] newVals = new InternalValue[values.length];
for (int i = 0; i < values.length; i++) {
NodeId adjusted = refTracker.getMappedId(values[i].getNodeId());
if (adjusted != null) {
boolean weak = prop.getType() == PropertyType.WEAKREFERENCE;
newVals[i] = InternalValue.create(adjusted, weak);
modified = true;
} else {
// reference doesn't need adjusting, just copy old value
newVals[i] = values[i];
}
}
if (modified) {
prop.setValues(newVals);
stateMgr.store(prop);
}
}
refTracker.clear();
// store states
stateMgr.store(newState);
stateMgr.store(destParentState);
return newState.getNodeId();
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class BatchedItemOperations method move.
/**
* Moves the tree at <code>srcPath</code> to the new location at
* <code>destPath</code>. Returns the id of the moved node.
* <p>
* <b>Precondition:</b> the state manager needs to be in edit mode.
*
* @param srcPath
* @param destPath
* @return the id of the moved node
* @throws ConstraintViolationException
* @throws VersionException
* @throws AccessDeniedException
* @throws PathNotFoundException
* @throws ItemExistsException
* @throws LockException
* @throws RepositoryException
* @throws IllegalStateException if the state manager is not in edit mode
*/
public NodeId move(Path srcPath, Path destPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException, IllegalStateException {
// check precondition
if (!stateMgr.inEditMode()) {
throw new IllegalStateException("cannot move path " + safeGetJCRPath(srcPath) + " because manager is not in edit mode");
}
try {
if (srcPath.isAncestorOf(destPath)) {
String msg = safeGetJCRPath(destPath) + ": invalid destination path" + " (cannot be descendant of source path)";
log.debug(msg);
throw new RepositoryException(msg);
}
} catch (MalformedPathException mpe) {
String msg = "invalid path for move: " + safeGetJCRPath(destPath);
log.debug(msg);
throw new RepositoryException(msg, mpe);
}
Path srcParentPath = srcPath.getAncestor(1);
NodeState target = getNodeState(srcPath);
NodeState srcParent = getNodeState(srcParentPath);
Path destParentPath = destPath.getAncestor(1);
NodeState destParent = getNodeState(destParentPath);
int ind = destPath.getIndex();
if (ind > 0) {
// subscript in name element
String msg = safeGetJCRPath(destPath) + ": invalid destination path" + " (subscript in name element is not allowed)";
log.debug(msg);
throw new RepositoryException(msg);
}
HierarchyManagerImpl hierMgr = (HierarchyManagerImpl) this.hierMgr;
if (hierMgr.isShareAncestor(target.getNodeId(), destParent.getNodeId())) {
String msg = safeGetJCRPath(destPath) + ": invalid destination path" + " (share cycle detected)";
log.debug(msg);
throw new RepositoryException(msg);
}
// 2. check if target state can be removed from old/added to new parent
checkRemoveNode(target, srcParent.getNodeId(), CHECK_ACCESS | CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION);
checkAddNode(destParent, destPath.getName(), target.getNodeTypeName(), CHECK_ACCESS | CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION);
// 3. do move operation (modify and store affected states)
boolean renameOnly = srcParent.getNodeId().equals(destParent.getNodeId());
int srcNameIndex = srcPath.getIndex();
if (srcNameIndex == 0) {
srcNameIndex = 1;
}
stateMgr.store(target);
if (renameOnly) {
stateMgr.store(srcParent);
// change child node entry
destParent.renameChildNodeEntry(srcPath.getName(), srcNameIndex, destPath.getName());
} else {
// check shareable case
if (target.isShareable()) {
String msg = "Moving a shareable node (" + safeGetJCRPath(srcPath) + ") is not supported.";
log.debug(msg);
throw new UnsupportedRepositoryOperationException(msg);
}
stateMgr.store(srcParent);
stateMgr.store(destParent);
// 1. remove child node entry from old parent
if (srcParent.removeChildNodeEntry(target.getNodeId())) {
// 2. re-parent target node
target.setParentId(destParent.getNodeId());
// 3. add child node entry to new parent
destParent.addChildNodeEntry(destPath.getName(), target.getNodeId());
}
}
return target.getNodeId();
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class BatchedItemOperations method copyNodeState.
/**
* Recursively copies the specified node state including its properties and
* child nodes.
*
* @param srcState
* @param srcPath
* @param srcStateMgr
* @param srcAccessMgr
* @param destParentId
* @param flag one of
* <ul>
* <li><code>COPY</code></li>
* <li><code>CLONE</code></li>
* <li><code>CLONE_REMOVE_EXISTING</code></li>
* </ul>
* @param refTracker tracks uuid mappings and processed reference properties
* @return a deep copy of the given node state and its children
* @throws RepositoryException if an error occurs
*/
private NodeState copyNodeState(NodeState srcState, Path srcPath, ItemStateManager srcStateMgr, AccessManager srcAccessMgr, NodeId destParentId, int flag, ReferenceChangeTracker refTracker) throws RepositoryException {
NodeState newState;
try {
NodeId id = null;
EffectiveNodeType ent = getEffectiveNodeType(srcState);
boolean referenceable = ent.includesNodeType(NameConstants.MIX_REFERENCEABLE);
boolean versionable = ent.includesNodeType(NameConstants.MIX_SIMPLE_VERSIONABLE);
boolean fullVersionable = ent.includesNodeType(NameConstants.MIX_VERSIONABLE);
boolean shareable = ent.includesNodeType(NameConstants.MIX_SHAREABLE);
switch(flag) {
case COPY:
/* if this node is shareable and another node in the same shared set
* has been already been copied and given a new uuid, use this one
* (see section 14.5 of the specification)
*/
if (shareable && refTracker.getMappedId(srcState.getNodeId()) != null) {
NodeId newId = refTracker.getMappedId(srcState.getNodeId());
NodeState sharedState = (NodeState) stateMgr.getItemState(newId);
sharedState.addShare(destParentId);
return sharedState;
}
break;
case CLONE:
if (!referenceable) {
// non-referenceable node: always create new node id
break;
}
// use same uuid as source node
id = srcState.getNodeId();
if (stateMgr.hasItemState(id)) {
if (shareable) {
NodeState sharedState = (NodeState) stateMgr.getItemState(id);
sharedState.addShare(destParentId);
return sharedState;
}
// node with this uuid already exists
throw new ItemExistsException(safeGetJCRPath(id));
}
break;
case CLONE_REMOVE_EXISTING:
if (!referenceable) {
// non-referenceable node: always create new node id
break;
}
// use same uuid as source node
id = srcState.getNodeId();
if (stateMgr.hasItemState(id)) {
NodeState existingState = (NodeState) stateMgr.getItemState(id);
// or an ancestor thereof
if (id.equals(destParentId) || hierMgr.isAncestor(id, destParentId)) {
String msg = "cannot remove node " + safeGetJCRPath(srcPath) + " because it is an ancestor of the destination";
log.debug(msg);
throw new RepositoryException(msg);
}
// check if existing can be removed
// (access rights, locking & versioning status,
// node type constraints and retention/hold)
checkRemoveNode(existingState, CHECK_ACCESS | CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION);
// do remove existing
removeNodeState(existingState);
}
break;
default:
throw new IllegalArgumentException("unknown flag for copying node state: " + flag);
}
newState = stateMgr.createNew(id, srcState.getNodeTypeName(), destParentId);
id = newState.getNodeId();
if (flag == COPY && referenceable) {
// remember uuid mapping
refTracker.mappedId(srcState.getNodeId(), id);
}
// copy node state
newState.setMixinTypeNames(srcState.getMixinTypeNames());
if (shareable) {
// initialize shared set
newState.addShare(destParentId);
}
// copy child nodes
for (ChildNodeEntry entry : srcState.getChildNodeEntries()) {
Path srcChildPath = PathFactoryImpl.getInstance().create(srcPath, entry.getName(), true);
if (!srcAccessMgr.isGranted(srcChildPath, Permission.READ)) {
continue;
}
NodeId nodeId = entry.getId();
NodeState srcChildState = (NodeState) srcStateMgr.getItemState(nodeId);
/**
* If child is shareble and its UUID has already been remapped,
* then simply add a reference to the state with that remapped
* UUID instead of copying the whole subtree.
*/
if (srcChildState.isShareable()) {
NodeId mappedId = refTracker.getMappedId(srcChildState.getNodeId());
if (mappedId != null) {
if (stateMgr.hasItemState(mappedId)) {
NodeState destState = (NodeState) stateMgr.getItemState(mappedId);
if (!destState.isShareable()) {
String msg = "Remapped child (" + safeGetJCRPath(srcPath) + ") is not shareable.";
throw new ItemStateException(msg);
}
if (!destState.addShare(id)) {
String msg = "Unable to add share to node: " + id;
throw new ItemStateException(msg);
}
stateMgr.store(destState);
newState.addChildNodeEntry(entry.getName(), mappedId);
continue;
}
}
}
// recursive copying of child node
NodeState newChildState = copyNodeState(srcChildState, srcChildPath, srcStateMgr, srcAccessMgr, id, flag, refTracker);
// store new child node
stateMgr.store(newChildState);
// add new child node entry to new node
newState.addChildNodeEntry(entry.getName(), newChildState.getNodeId());
}
// init version history if needed
VersionHistoryInfo history = null;
if (versionable && flag == COPY) {
NodeId copiedFrom = null;
if (fullVersionable) {
// base version of copied versionable node is reference value of
// the histories jcr:copiedFrom property
PropertyId propId = new PropertyId(srcState.getNodeId(), NameConstants.JCR_BASEVERSION);
PropertyState prop = (PropertyState) srcStateMgr.getItemState(propId);
copiedFrom = prop.getValues()[0].getNodeId();
}
InternalVersionManager manager = session.getInternalVersionManager();
history = manager.getVersionHistory(session, newState, copiedFrom);
}
// copy properties
for (Name propName : srcState.getPropertyNames()) {
Path propPath = PathFactoryImpl.getInstance().create(srcPath, propName, true);
PropertyId propId = new PropertyId(srcState.getNodeId(), propName);
if (!srcAccessMgr.canRead(propPath, propId)) {
continue;
}
PropertyState srcChildState = (PropertyState) srcStateMgr.getItemState(propId);
/**
* special handling required for properties with special semantics
* (e.g. those defined by mix:referenceable, mix:versionable,
* mix:lockable, et.al.)
*
* todo FIXME delegate to 'node type instance handler'
*/
QPropertyDefinition def = ent.getApplicablePropertyDef(srcChildState.getName(), srcChildState.getType(), srcChildState.isMultiValued());
if (NameConstants.MIX_LOCKABLE.equals(def.getDeclaringNodeType())) {
// skip properties defined by mix:lockable
continue;
}
PropertyState newChildState = copyPropertyState(srcChildState, id, propName, def);
if (history != null) {
if (fullVersionable) {
if (propName.equals(NameConstants.JCR_VERSIONHISTORY)) {
// jcr:versionHistory
InternalValue value = InternalValue.create(history.getVersionHistoryId());
newChildState.setValues(new InternalValue[] { value });
} else if (propName.equals(NameConstants.JCR_BASEVERSION) || propName.equals(NameConstants.JCR_PREDECESSORS)) {
// jcr:baseVersion or jcr:predecessors
InternalValue value = InternalValue.create(history.getRootVersionId());
newChildState.setValues(new InternalValue[] { value });
} else if (propName.equals(NameConstants.JCR_ISCHECKEDOUT)) {
// jcr:isCheckedOut
newChildState.setValues(new InternalValue[] { InternalValue.create(true) });
}
} else {
// version history when we see the jcr:isCheckedOut
if (propName.equals(NameConstants.JCR_ISCHECKEDOUT)) {
// jcr:isCheckedOut
newChildState.setValues(new InternalValue[] { InternalValue.create(true) });
}
}
}
if (newChildState.getType() == PropertyType.REFERENCE || newChildState.getType() == PropertyType.WEAKREFERENCE) {
refTracker.processedReference(newChildState);
}
// store new property
stateMgr.store(newChildState);
// add new property entry to new node
newState.addPropertyName(propName);
}
return newState;
} catch (ItemStateException ise) {
String msg = "internal error: failed to copy state of " + srcState.getNodeId();
log.debug(msg);
throw new RepositoryException(msg, ise);
}
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class BatchedItemOperations method removeNodeState.
/**
* Unlinks the specified node state from its parent and recursively
* removes it including its properties and child nodes.
* <p>
* Note that no checks (access rights etc.) are performed on the specified
* target node state. Those checks have to be performed beforehand by the
* caller. However, the (recursive) removal of target node's child nodes are
* subject to the following checks: access rights, locking, versioning.
*
* @param target
* @throws RepositoryException if an error occurs
*/
public void removeNodeState(NodeState target) throws RepositoryException {
NodeId parentId = target.getParentId();
if (parentId == null) {
String msg = "root node cannot be removed";
log.debug(msg);
throw new RepositoryException(msg);
}
// remove target
recursiveRemoveNodeState(target);
// remove child node entry from parent
NodeState parent = getNodeState(parentId);
parent.removeChildNodeEntry(target.getNodeId());
// store parent
stateMgr.store(parent);
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class AddMixinOperation method perform.
public Object perform(SessionContext context) throws RepositoryException {
int permissions = Permission.NODE_TYPE_MNGMT;
// is required in addition.
if (MIX_VERSIONABLE.equals(mixinName) || MIX_SIMPLE_VERSIONABLE.equals(mixinName)) {
permissions |= Permission.VERSION_MNGMT;
}
context.getItemValidator().checkModify(node, CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD, permissions);
NodeTypeManagerImpl ntMgr = context.getNodeTypeManager();
NodeTypeImpl mixin = ntMgr.getNodeType(mixinName);
if (!mixin.isMixin()) {
throw new RepositoryException(context.getJCRName(mixinName) + " is not a mixin node type");
}
Name primaryTypeName = node.getNodeState().getNodeTypeName();
NodeTypeImpl primaryType = ntMgr.getNodeType(primaryTypeName);
if (primaryType.isDerivedFrom(mixinName)) {
// new mixin is already included in primary type
return this;
}
// build effective node type of mixin's & primary type in order
// to detect conflicts
NodeTypeRegistry ntReg = context.getNodeTypeRegistry();
EffectiveNodeType entExisting;
try {
// existing mixin's
Set<Name> mixins = new HashSet<Name>(node.getNodeState().getMixinTypeNames());
// build effective node type representing primary type including
// existing mixin's
entExisting = ntReg.getEffectiveNodeType(primaryTypeName, mixins);
if (entExisting.includesNodeType(mixinName)) {
// new mixin is already included in existing mixin type(s)
return this;
}
// add new mixin
mixins.add(mixinName);
// try to build new effective node type (will throw in case
// of conflicts)
ntReg.getEffectiveNodeType(primaryTypeName, mixins);
} catch (NodeTypeConflictException e) {
throw new ConstraintViolationException(e.getMessage(), e);
}
// try to revert the changes in case an exception occurs
try {
// modify the state of this node
NodeState thisState = (NodeState) node.getOrCreateTransientItemState();
// add mixin name
Set<Name> mixins = new HashSet<Name>(thisState.getMixinTypeNames());
mixins.add(mixinName);
thisState.setMixinTypeNames(mixins);
// set jcr:mixinTypes property
node.setMixinTypesProperty(mixins);
// add 'auto-create' properties defined in mixin type
for (PropertyDefinition aPda : mixin.getAutoCreatedPropertyDefinitions()) {
PropertyDefinitionImpl pd = (PropertyDefinitionImpl) aPda;
// make sure that the property is not already defined
// by primary type or existing mixin's
NodeTypeImpl declaringNT = (NodeTypeImpl) pd.getDeclaringNodeType();
if (!entExisting.includesNodeType(declaringNT.getQName())) {
node.createChildProperty(pd.unwrap().getName(), pd.getRequiredType(), pd);
}
}
// recursively add 'auto-create' child nodes defined in mixin type
for (NodeDefinition aNda : mixin.getAutoCreatedNodeDefinitions()) {
NodeDefinitionImpl nd = (NodeDefinitionImpl) aNda;
// make sure that the child node is not already defined
// by primary type or existing mixin's
NodeTypeImpl declaringNT = (NodeTypeImpl) nd.getDeclaringNodeType();
if (!entExisting.includesNodeType(declaringNT.getQName())) {
node.createChildNode(nd.unwrap().getName(), (NodeTypeImpl) nd.getDefaultPrimaryType(), null);
}
}
} catch (RepositoryException re) {
// try to undo the modifications by removing the mixin
try {
node.removeMixin(mixinName);
} catch (RepositoryException re1) {
// silently ignore & fall through
}
throw re;
}
return this;
}
Aggregations