use of org.apache.jackrabbit.core.state.ChildNodeEntry in project jackrabbit by apache.
the class NodeStateEx method removeNode.
/**
* removes recursively the node with the given id
*
* @param id node id
* @throws ItemStateException if an error occurs
*/
private void removeNode(NodeId id) throws ItemStateException {
NodeState state = (NodeState) stateMgr.getItemState(id);
// remove properties
for (Name name : state.getPropertyNames()) {
PropertyId propId = new PropertyId(id, name);
PropertyState propState = (PropertyState) stateMgr.getItemState(propId);
stateMgr.destroy(propState);
}
state.removeAllPropertyNames();
// remove child nodes
for (ChildNodeEntry entry : state.getChildNodeEntries()) {
removeNode(entry.getId());
}
state.removeAllChildNodeEntries();
// destroy the state itself
stateMgr.destroy(state);
}
use of org.apache.jackrabbit.core.state.ChildNodeEntry in project jackrabbit by apache.
the class VersionManagerImplMerge method getCorrespondingNode.
/**
* Returns the corresponding node in the workspace of the given session.
* <p>
* Given a node N1 in workspace W1, its corresponding node N2 in workspace
* W2 is defined as follows:
* <ul>
* <li>If N1 is the root node of W1 then N2 is the root node of W2.
* <li>If N1 is referenceable (has a UUID) then N2 is the node in W2 with
* the same UUID.
* <li>If N1 is not referenceable (does not have a UUID) then there is some
* node M1 which is either the nearest ancestor of N1 that is
* referenceable, or is the root node of W1. If the corresponding node
* of M1 is M2 in W2, then N2 is the node with the same relative path
* from M2 as N1 has from M1.
* </ul>
*
* @param state N1
* @param srcRoot the root node state of W2
* @return the corresponding node or <code>null</code> if no corresponding
* node exists.
* @throws RepositoryException If another error occurs.
*/
private NodeStateEx getCorrespondingNode(NodeStateEx state, NodeStateEx srcRoot) throws RepositoryException {
// search nearest ancestor that is referenceable
NodeStateEx m1 = state;
LinkedList<ChildNodeEntry> elements = new LinkedList<ChildNodeEntry>();
while (m1.getParentId() != null && !m1.getEffectiveNodeType().includesNodeType(NameConstants.MIX_REFERENCEABLE)) {
NodeStateEx parent = m1.getParent();
elements.addFirst(parent.getState().getChildNodeEntry(m1.getNodeId()));
m1 = parent;
}
// check if corresponding ancestor exists
if (srcRoot.hasNode(m1.getNodeId())) {
NodeStateEx m2 = srcRoot.getNode(m1.getNodeId());
Iterator<ChildNodeEntry> iter = elements.iterator();
while (iter.hasNext() && m2 != null) {
ChildNodeEntry e = iter.next();
m2 = m2.getNode(e.getName(), e.getIndex());
}
return m2;
} else {
return null;
}
}
use of org.apache.jackrabbit.core.state.ChildNodeEntry in project jackrabbit by apache.
the class VersionManagerImplMerge method internalMerge.
/**
* Merges/Updates this node with its corresponding ones
*
* @param state state to merge or update
* @param srcRoot src workspace root node
* @param failedIds list of failed ids
* @param bestEffort best effort flag
* @param shallow is shallow flag
* @throws RepositoryException if an error occurs
* @throws ItemStateException if an error occurs
*/
private void internalMerge(NodeStateEx state, NodeStateEx srcRoot, List<ItemId> failedIds, boolean bestEffort, boolean shallow) throws RepositoryException, ItemStateException {
NodeStateEx srcNode = doMergeTest(state, srcRoot, failedIds, bestEffort);
if (srcNode == null) {
if (!shallow) {
// nodes (see JCR-1046)
for (NodeStateEx n : state.getChildNodes()) {
if (n.getEffectiveNodeType().includesNodeType(NameConstants.MIX_VERSIONABLE)) {
internalMerge(n, srcRoot, failedIds, bestEffort, shallow);
}
}
}
return;
}
// check lock and hold status if node exists
checkModify(state, ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD, Permission.NONE);
// remove all properties that are not present in srcNode
for (PropertyState prop : state.getProperties()) {
if (!srcNode.hasProperty(prop.getName())) {
state.removeProperty(prop.getName());
}
}
// update all properties from the src node
for (PropertyState prop : srcNode.getProperties()) {
Name propName = prop.getName();
// ignore system types
if (propName.equals(NameConstants.JCR_PRIMARYTYPE) || propName.equals(NameConstants.JCR_MIXINTYPES) || propName.equals(NameConstants.JCR_UUID)) {
continue;
}
state.copyFrom(prop);
}
// update the mixin types
state.setMixins(srcNode.getState().getMixinTypeNames());
// remove the child nodes in N but not in N'
LinkedList<ChildNodeEntry> toDelete = new LinkedList<ChildNodeEntry>();
for (ChildNodeEntry entry : state.getState().getChildNodeEntries()) {
if (!srcNode.getState().hasChildNodeEntry(entry.getName(), entry.getIndex())) {
toDelete.add(entry);
}
}
for (ChildNodeEntry entry : toDelete) {
state.removeNode(entry.getName(), entry.getIndex());
}
state.store();
// add source ones
for (ChildNodeEntry entry : srcNode.getState().getChildNodeEntries()) {
NodeStateEx child = state.getNode(entry.getName(), entry.getIndex());
if (child == null) {
// if destination workspace already has such an node, remove it
if (state.hasNode(entry.getId())) {
child = state.getNode(entry.getId());
NodeStateEx parent = child.getParent();
parent.removeNode(child);
parent.store();
}
// create new child
NodeStateEx srcChild = srcNode.getNode(entry.getId());
child = state.addNode(entry.getName(), srcChild.getState().getNodeTypeName(), srcChild.getNodeId());
child.setMixins(srcChild.getState().getMixinTypeNames());
// copy src child
state.store();
internalMerge(child, srcRoot, null, bestEffort, false);
} else if (!shallow) {
// recursively merge
internalMerge(child, srcRoot, failedIds, bestEffort, false);
}
}
}
use of org.apache.jackrabbit.core.state.ChildNodeEntry in project jackrabbit by apache.
the class WorkspaceImporter method resolveUUIDConflict.
/**
* @param parent parent node state
* @param conflicting conflicting node state
* @param nodeInfo the node info
* @return the resolved node state
* @throws RepositoryException if an error occurs
*/
protected NodeState resolveUUIDConflict(NodeState parent, NodeState conflicting, NodeInfo nodeInfo) throws RepositoryException {
NodeState node;
if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW) {
// create new with new uuid:
// check if new node can be added (check access rights &
// node type constraints only, assume locking & versioning status
// and retention/hold has already been checked on ancestor)
itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_CONSTRAINTS);
node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), null);
// remember uuid mapping
EffectiveNodeType ent = itemOps.getEffectiveNodeType(node);
if (ent.includesNodeType(NameConstants.MIX_REFERENCEABLE)) {
refTracker.mappedId(nodeInfo.getId(), node.getNodeId());
}
} else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW) {
// new node and share with existing
if (conflicting.isShareable()) {
itemOps.clone(conflicting, parent, nodeInfo.getName());
return null;
}
String msg = "a node with uuid " + nodeInfo.getId() + " already exists!";
log.debug(msg);
throw new ItemExistsException(msg);
} else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING) {
// make sure conflicting node is not importTarget or an ancestor thereof
Path p0 = hierMgr.getPath(importTarget.getNodeId());
Path p1 = hierMgr.getPath(conflicting.getNodeId());
try {
if (p1.equals(p0) || p1.isAncestorOf(p0)) {
String msg = "cannot remove ancestor node";
log.debug(msg);
throw new ConstraintViolationException(msg);
}
} catch (MalformedPathException mpe) {
// should never get here...
String msg = "internal error: failed to determine degree of relationship";
log.error(msg, mpe);
throw new RepositoryException(msg, mpe);
}
// remove conflicting:
// check if conflicting can be removed
// (access rights, node type constraints, locking & versioning status)
itemOps.checkRemoveNode(conflicting, BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_CHECKED_OUT | BatchedItemOperations.CHECK_CONSTRAINTS | BatchedItemOperations.CHECK_HOLD | BatchedItemOperations.CHECK_RETENTION);
// do remove conflicting (recursive)
itemOps.removeNodeState(conflicting);
// create new with given uuid:
// check if new node can be added (check access rights &
// node type constraints only, assume locking & versioning status
// and retention/hold has already been checked on ancestor)
itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_CONSTRAINTS);
// do create new node
node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), nodeInfo.getId());
} else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING) {
NodeId parentId = conflicting.getParentId();
if (parentId == null) {
String msg = "root node cannot be replaced";
log.debug(msg);
throw new RepositoryException(msg);
}
// 'replace' current parent with parent of conflicting
try {
parent = itemOps.getNodeState(parentId);
} catch (ItemNotFoundException infe) {
// should never get here...
String msg = "internal error: failed to retrieve parent state";
log.error(msg, infe);
throw new RepositoryException(msg, infe);
}
// remove conflicting:
// check if conflicting can be removed
// (access rights, node type constraints, locking & versioning status)
itemOps.checkRemoveNode(conflicting, BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_CHECKED_OUT | BatchedItemOperations.CHECK_CONSTRAINTS | BatchedItemOperations.CHECK_HOLD | BatchedItemOperations.CHECK_RETENTION);
// 'replace' is actually a 'remove existing/add new' operation;
// this unfortunately changes the order of the parent's
// child node entries (JCR-1055);
// => backup list of child node entries beforehand in order
// to restore it afterwards
ChildNodeEntry cneConflicting = parent.getChildNodeEntry(nodeInfo.getId());
List<ChildNodeEntry> cneList = new ArrayList<ChildNodeEntry>(parent.getChildNodeEntries());
// do remove conflicting (recursive)
itemOps.removeNodeState(conflicting);
// create new with given uuid at same location as conflicting:
// check if new node can be added at other location
// (access rights, node type constraints, locking & versioning
// status and retention/hold)
itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_CHECKED_OUT | BatchedItemOperations.CHECK_CONSTRAINTS | BatchedItemOperations.CHECK_HOLD | BatchedItemOperations.CHECK_RETENTION);
// do create new node
node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), nodeInfo.getId());
// restore list of child node entries (JCR-1055)
if (cneConflicting.getName().equals(nodeInfo.getName())) {
// restore original child node list
parent.setChildNodeEntries(cneList);
} else {
// replace child node entry with different name
// but preserving original position
parent.removeAllChildNodeEntries();
for (ChildNodeEntry cne : cneList) {
if (cne.getId().equals(nodeInfo.getId())) {
// replace entry with different name
parent.addChildNodeEntry(nodeInfo.getName(), nodeInfo.getId());
} else {
parent.addChildNodeEntry(cne.getName(), cne.getId());
}
}
}
} else {
String msg = "unknown uuidBehavior: " + uuidBehavior;
log.debug(msg);
throw new RepositoryException(msg);
}
return node;
}
use of org.apache.jackrabbit.core.state.ChildNodeEntry in project jackrabbit by apache.
the class ItemSaveOperation method validateTransientItems.
/**
* the following validations/checks are performed on transient items:
*
* for every transient item:
* - if it is 'modified' or 'new' check the corresponding write permission.
* - if it is 'removed' check the REMOVE permission
*
* for every transient node:
* - if it is 'new' check that its node type satisfies the
* 'required node type' constraint specified in its definition
* - check if 'mandatory' child items exist
*
* for every transient property:
* - check if the property value satisfies the value constraints
* specified in the property's definition
*
* note that the protected flag is checked in Node.addNode/Node.remove
* (for adding/removing child entries of a node), in
* Node.addMixin/removeMixin/setPrimaryType (for type changes on nodes)
* and in Property.setValue (for properties to be modified).
*/
private void validateTransientItems(SessionContext context, Iterable<ItemState> dirty, Iterable<ItemState> removed) throws RepositoryException {
SessionImpl session = context.getSessionImpl();
ItemManager itemMgr = context.getItemManager();
SessionItemStateManager stateMgr = context.getItemStateManager();
AccessManager accessMgr = context.getAccessManager();
NodeTypeManagerImpl ntMgr = context.getNodeTypeManager();
// walk through list of dirty transient items and validate each
for (ItemState itemState : dirty) {
ItemDefinition def;
if (itemState.isNode()) {
def = itemMgr.getDefinition((NodeState) itemState);
} else {
def = itemMgr.getDefinition((PropertyState) itemState);
}
/* check permissions for non-protected items. protected items are
only added through API methods which need to assert that
permissions are not violated.
*/
if (!def.isProtected()) {
/* detect the effective set of modification:
- new added node -> add_node perm on the child
- new property added -> set_property permission
- property modified -> set_property permission
- modified nodes can be ignored for changes only included
child-item addition or removal or changes of protected
properties such as mixin-types which are covered separately
note: removed items are checked later on.
note: reordering of child nodes has been covered upfront as
this information isn't available here.
*/
Path path = stateMgr.getHierarchyMgr().getPath(itemState.getId());
boolean isGranted = true;
if (itemState.isNode()) {
if (itemState.getStatus() == ItemState.STATUS_NEW) {
isGranted = accessMgr.isGranted(path, Permission.ADD_NODE);
}
// else: modified node (see comment above)
} else {
// modified or new property: set_property permission
isGranted = accessMgr.isGranted(path, Permission.SET_PROPERTY);
}
if (!isGranted) {
String msg = itemMgr.safeGetJCRPath(path) + ": not allowed to add or modify item";
log.debug(msg);
throw new AccessDeniedException(msg);
}
}
if (itemState.isNode()) {
// the transient item is a node
NodeState nodeState = (NodeState) itemState;
ItemId id = nodeState.getNodeId();
NodeDefinition nodeDef = (NodeDefinition) def;
// primary type
NodeTypeImpl pnt = ntMgr.getNodeType(nodeState.getNodeTypeName());
// effective node type (primary type incl. mixins)
EffectiveNodeType ent = getEffectiveNodeType(context.getRepositoryContext().getNodeTypeRegistry(), nodeState);
/**
* if the transient node was added (i.e. if it is 'new') or if
* its primary type has changed, check its node type against the
* required node type in its definition
*/
boolean primaryTypeChanged = nodeState.getStatus() == ItemState.STATUS_NEW;
if (!primaryTypeChanged) {
NodeState overlaid = (NodeState) nodeState.getOverlayedState();
if (overlaid != null) {
Name newName = nodeState.getNodeTypeName();
Name oldName = overlaid.getNodeTypeName();
primaryTypeChanged = !newName.equals(oldName);
}
}
if (primaryTypeChanged) {
for (NodeType ntReq : nodeDef.getRequiredPrimaryTypes()) {
Name ntName = ((NodeTypeImpl) ntReq).getQName();
if (!(pnt.getQName().equals(ntName) || pnt.isDerivedFrom(ntName))) {
/**
* the transient node's primary node type does not
* satisfy the 'required primary types' constraint
*/
String msg = itemMgr.safeGetJCRPath(id) + " must be of node type " + ntReq.getName();
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
}
// mandatory child properties
for (QPropertyDefinition pd : ent.getMandatoryPropDefs()) {
if (pd.getDeclaringNodeType().equals(NameConstants.MIX_VERSIONABLE) || pd.getDeclaringNodeType().equals(NameConstants.MIX_SIMPLE_VERSIONABLE)) {
/**
* todo FIXME workaround for mix:versionable:
* the mandatory properties are initialized at a
* later stage and might not exist yet
*/
continue;
}
String msg = itemMgr.safeGetJCRPath(id) + ": mandatory property " + pd.getName() + " does not exist";
if (!nodeState.hasPropertyName(pd.getName())) {
log.debug(msg);
throw new ConstraintViolationException(msg);
} else {
/*
there exists a property with the mandatory-name.
make sure the property really has the expected mandatory
property definition (and not another non-mandatory def,
such as e.g. multivalued residual instead of single-value
mandatory, named def).
*/
PropertyId pi = new PropertyId(nodeState.getNodeId(), pd.getName());
ItemData childData = itemMgr.getItemData(pi, null, false);
if (!childData.getDefinition().isMandatory()) {
throw new ConstraintViolationException(msg);
}
}
}
// mandatory child nodes
for (QItemDefinition cnd : ent.getMandatoryNodeDefs()) {
String msg = itemMgr.safeGetJCRPath(id) + ": mandatory child node " + cnd.getName() + " does not exist";
if (!nodeState.hasChildNodeEntry(cnd.getName())) {
log.debug(msg);
throw new ConstraintViolationException(msg);
} else {
/*
there exists a child node with the mandatory-name.
make sure the node really has the expected mandatory
node definition.
*/
boolean hasMandatoryChild = false;
for (ChildNodeEntry cne : nodeState.getChildNodeEntries(cnd.getName())) {
ItemData childData = itemMgr.getItemData(cne.getId(), null, false);
if (childData.getDefinition().isMandatory()) {
hasMandatoryChild = true;
break;
}
}
if (!hasMandatoryChild) {
throw new ConstraintViolationException(msg);
}
}
}
} else {
// the transient item is a property
PropertyState propState = (PropertyState) itemState;
ItemId propId = propState.getPropertyId();
org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl propDef = (org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) def;
/**
* check value constraints
* (no need to check value constraints of protected properties
* as those are set by the implementation only, i.e. they
* cannot be set by the user through the api)
*/
if (!def.isProtected()) {
String[] constraints = propDef.getValueConstraints();
if (constraints != null) {
InternalValue[] values = propState.getValues();
try {
EffectiveNodeType.checkSetPropertyValueConstraints(propDef.unwrap(), values);
} catch (RepositoryException e) {
// repack exception for providing more verbose error message
String msg = itemMgr.safeGetJCRPath(propId) + ": " + e.getMessage();
log.debug(msg);
throw new ConstraintViolationException(msg);
}
/**
* need to manually check REFERENCE value constraints
* as this requires a session (target node needs to
* be checked)
*/
if (constraints.length > 0 && (propDef.getRequiredType() == PropertyType.REFERENCE || propDef.getRequiredType() == PropertyType.WEAKREFERENCE)) {
for (InternalValue internalV : values) {
boolean satisfied = false;
String constraintViolationMsg = null;
try {
NodeId targetId = internalV.getNodeId();
if (propDef.getRequiredType() == PropertyType.WEAKREFERENCE && !itemMgr.itemExists(targetId)) {
// target of weakref doesn;t exist, skip
continue;
}
Node targetNode = session.getNodeById(targetId);
/**
* constraints are OR-ed, i.e. at least one
* has to be satisfied
*/
for (String constrNtName : constraints) {
/**
* a [WEAK]REFERENCE value constraint specifies
* the name of the required node type of
* the target node
*/
if (targetNode.isNodeType(constrNtName)) {
satisfied = true;
break;
}
}
if (!satisfied) {
NodeType[] mixinNodeTypes = targetNode.getMixinNodeTypes();
String[] targetMixins = new String[mixinNodeTypes.length];
for (int j = 0; j < mixinNodeTypes.length; j++) {
targetMixins[j] = mixinNodeTypes[j].getName();
}
String targetMixinsString = Text.implode(targetMixins, ", ");
String constraintsString = Text.implode(constraints, ", ");
constraintViolationMsg = itemMgr.safeGetJCRPath(propId) + ": is constraint to [" + constraintsString + "] but references [primaryType=" + targetNode.getPrimaryNodeType().getName() + ", mixins=" + targetMixinsString + "]";
}
} catch (RepositoryException re) {
String msg = itemMgr.safeGetJCRPath(propId) + ": failed to check " + ((propDef.getRequiredType() == PropertyType.REFERENCE) ? "REFERENCE" : "WEAKREFERENCE") + " value constraint";
log.debug(msg);
throw new ConstraintViolationException(msg, re);
}
if (!satisfied) {
log.debug(constraintViolationMsg);
throw new ConstraintViolationException(constraintViolationMsg);
}
}
}
}
}
/**
* no need to check the protected flag as this is checked
* in PropertyImpl.setValue(Value)
*/
}
}
// walk through list of removed transient items and check REMOVE permission
for (ItemState itemState : removed) {
QItemDefinition def;
try {
if (itemState.isNode()) {
def = itemMgr.getDefinition((NodeState) itemState).unwrap();
} else {
def = itemMgr.getDefinition((PropertyState) itemState).unwrap();
}
} catch (ConstraintViolationException e) {
// of a mixin (see also JCR-2130 & JCR-2408)
continue;
}
if (!def.isProtected()) {
Path path = stateMgr.getAtticAwareHierarchyMgr().getPath(itemState.getId());
// check REMOVE permission
int permission = (itemState.isNode()) ? Permission.REMOVE_NODE : Permission.REMOVE_PROPERTY;
if (!accessMgr.isGranted(path, permission)) {
String msg = itemMgr.safeGetJCRPath(path) + ": not allowed to remove item";
log.debug(msg);
throw new AccessDeniedException(msg);
}
}
}
}
Aggregations