use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.
the class RemoveMixinOperation method perform.
public Object perform(SessionContext context) throws RepositoryException {
SessionImpl session = context.getSessionImpl();
ItemManager itemMgr = context.getItemManager();
SessionItemStateManager stateMgr = context.getItemStateManager();
context.getItemValidator().checkModify(node, CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD, Permission.NODE_TYPE_MNGMT);
// check if mixin is assigned
NodeState state = node.getNodeState();
if (!state.getMixinTypeNames().contains(mixinName)) {
throw new NoSuchNodeTypeException("Mixin " + context.getJCRName(mixinName) + " not included in " + node);
}
NodeTypeManagerImpl ntMgr = context.getNodeTypeManager();
NodeTypeRegistry ntReg = context.getNodeTypeRegistry();
// build effective node type of remaining mixin's & primary type
Set<Name> remainingMixins = new HashSet<Name>(state.getMixinTypeNames());
// remove name of target mixin
remainingMixins.remove(mixinName);
EffectiveNodeType entResulting;
try {
// build effective node type representing primary type
// including remaining mixin's
entResulting = ntReg.getEffectiveNodeType(state.getNodeTypeName(), remainingMixins);
} catch (NodeTypeConflictException e) {
throw new ConstraintViolationException(e.getMessage(), e);
}
// mix:referenceable needs special handling because it has
// special semantics:
// it can only be removed if there no more references to this node
NodeTypeImpl mixin = ntMgr.getNodeType(mixinName);
if (isReferenceable(mixin) && !entResulting.includesNodeType(MIX_REFERENCEABLE)) {
if (node.getReferences().hasNext()) {
throw new ConstraintViolationException(mixinName + " can not be removed:" + " the node is being referenced through at least" + " one property of type REFERENCE");
}
}
// currently locked even if the editing session is the lock holder.
if ((NameConstants.MIX_LOCKABLE.equals(mixinName) || mixin.isDerivedFrom(NameConstants.MIX_LOCKABLE)) && !entResulting.includesNodeType(NameConstants.MIX_LOCKABLE) && node.isLocked()) {
throw new ConstraintViolationException(mixinName + " can not be removed: the node is locked.");
}
NodeState thisState = (NodeState) node.getOrCreateTransientItemState();
// collect information about properties and nodes which require further
// action as a result of the mixin removal; we need to do this *before*
// actually changing the assigned mixin types, otherwise we wouldn't
// be able to retrieve the current definition of an item.
Map<PropertyId, PropertyDefinition> affectedProps = new HashMap<PropertyId, PropertyDefinition>();
Map<ChildNodeEntry, NodeDefinition> affectedNodes = new HashMap<ChildNodeEntry, NodeDefinition>();
try {
Set<Name> names = thisState.getPropertyNames();
for (Name propName : names) {
PropertyId propId = new PropertyId(thisState.getNodeId(), propName);
PropertyState propState = (PropertyState) stateMgr.getItemState(propId);
PropertyDefinition oldDef = itemMgr.getDefinition(propState);
// check if property has been defined by mixin type
// (or one of its supertypes)
NodeTypeImpl declaringNT = (NodeTypeImpl) oldDef.getDeclaringNodeType();
if (!entResulting.includesNodeType(declaringNT.getQName())) {
// the resulting effective node type doesn't include the
// node type that declared this property
affectedProps.put(propId, oldDef);
}
}
List<ChildNodeEntry> entries = thisState.getChildNodeEntries();
for (ChildNodeEntry entry : entries) {
NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
NodeDefinition oldDef = itemMgr.getDefinition(nodeState);
// check if node has been defined by mixin type
// (or one of its supertypes)
NodeTypeImpl declaringNT = (NodeTypeImpl) oldDef.getDeclaringNodeType();
if (!entResulting.includesNodeType(declaringNT.getQName())) {
// the resulting effective node type doesn't include the
// node type that declared this child node
affectedNodes.put(entry, oldDef);
}
}
} catch (ItemStateException e) {
throw new RepositoryException("Failed to determine effect of removing mixin " + context.getJCRName(mixinName), e);
}
// modify the state of this node
thisState.setMixinTypeNames(remainingMixins);
// set jcr:mixinTypes property
node.setMixinTypesProperty(remainingMixins);
// process affected nodes & properties:
// 1. try to redefine item based on the resulting
// new effective node type (see JCR-2130)
// 2. remove item if 1. fails
boolean success = false;
try {
for (Map.Entry<PropertyId, PropertyDefinition> entry : affectedProps.entrySet()) {
PropertyId id = entry.getKey();
PropertyImpl prop = (PropertyImpl) itemMgr.getItem(id);
PropertyDefinition oldDef = entry.getValue();
if (oldDef.isProtected()) {
// remove 'orphaned' protected properties immediately
node.removeChildProperty(id.getName());
continue;
}
// redefine property if possible (JCR-2130)
try {
PropertyDefinitionImpl newDef = node.getApplicablePropertyDefinition(id.getName(), prop.getType(), oldDef.isMultiple(), false);
if (newDef.getRequiredType() != PropertyType.UNDEFINED && newDef.getRequiredType() != prop.getType()) {
// value conversion required
if (oldDef.isMultiple()) {
// convert value
Value[] values = ValueHelper.convert(prop.getValues(), newDef.getRequiredType(), session.getValueFactory());
// redefine property
prop.onRedefine(newDef.unwrap());
// set converted values
prop.setValue(values);
} else {
// convert value
Value value = ValueHelper.convert(prop.getValue(), newDef.getRequiredType(), session.getValueFactory());
// redefine property
prop.onRedefine(newDef.unwrap());
// set converted values
prop.setValue(value);
}
} else {
// redefine property
prop.onRedefine(newDef.unwrap());
}
} catch (ValueFormatException vfe) {
// value conversion failed, remove it
node.removeChildProperty(id.getName());
} catch (ConstraintViolationException cve) {
// no suitable definition found for this property,
// remove it
node.removeChildProperty(id.getName());
}
}
for (ChildNodeEntry entry : affectedNodes.keySet()) {
NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
NodeImpl childNode = (NodeImpl) itemMgr.getItem(entry.getId());
NodeDefinition oldDef = affectedNodes.get(entry);
if (oldDef.isProtected()) {
// remove 'orphaned' protected child node immediately
node.removeChildNode(entry.getId());
continue;
}
// redefine node if possible (JCR-2130)
try {
NodeDefinitionImpl newDef = node.getApplicableChildNodeDefinition(entry.getName(), nodeState.getNodeTypeName());
// redefine node
childNode.onRedefine(newDef.unwrap());
} catch (ConstraintViolationException cve) {
// no suitable definition found for this child node,
// remove it
node.removeChildNode(entry.getId());
}
}
success = true;
} catch (ItemStateException e) {
throw new RepositoryException("Failed to clean up child items defined by removed mixin " + context.getJCRName(mixinName), e);
} finally {
if (!success) {
// TODO JCR-1914: revert any changes made so far
}
}
return this;
}
use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.
the class NodeStateEx method moveFrom.
/**
* Moves the source node to this node using the given name.
* @param src shareable source node
* @param name name of new node
* @param createShare if <code>true</code> a share is created instead.
* @return child node
* @throws RepositoryException if an error occurs
*/
public NodeStateEx moveFrom(NodeStateEx src, Name name, boolean createShare) throws RepositoryException {
if (name == null) {
name = src.getName();
}
EffectiveNodeType ent = getEffectiveNodeType();
// (4) check for name collisions
QNodeDefinition def;
try {
def = ent.getApplicableChildNodeDef(name, nodeState.getNodeTypeName(), ntReg);
} catch (RepositoryException re) {
String msg = "no definition found in parent node's node type for new node";
throw new ConstraintViolationException(msg, re);
}
ChildNodeEntry cne = nodeState.getChildNodeEntry(name, 1);
if (cne != null) {
// check same-name sibling setting of new node
if (!def.allowsSameNameSiblings()) {
throw new ItemExistsException(getNodeId() + "/" + name);
}
NodeState existingChild;
try {
// check same-name sibling setting of existing node
existingChild = (NodeState) stateMgr.getItemState(cne.getId());
} catch (ItemStateException e) {
throw new RepositoryException(e);
}
QNodeDefinition existingChildDef = ent.getApplicableChildNodeDef(cne.getName(), existingChild.getNodeTypeName(), ntReg);
if (!existingChildDef.allowsSameNameSiblings()) {
throw new ItemExistsException(existingChild.toString());
}
} else {
// check if 'add' is allowed
if (getDefinition().isProtected()) {
String msg = "not allowed to modify a protected node";
throw new ConstraintViolationException(msg);
}
}
if (createShare) {
// (5) do clone operation
NodeId parentId = getNodeId();
src.addShareParent(parentId);
// attach to this parent
nodeState.addChildNodeEntry(name, src.getNodeId());
if (nodeState.getStatus() == ItemState.STATUS_EXISTING) {
nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
return new NodeStateEx(stateMgr, ntReg, src.getState(), name);
} else {
// detach from parent
NodeStateEx parent = getNode(src.getParentId());
parent.nodeState.removeChildNodeEntry(src.getNodeId());
if (parent.nodeState.getStatus() == ItemState.STATUS_EXISTING) {
parent.nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
// attach to this parent
nodeState.addChildNodeEntry(name, src.getNodeId());
if (nodeState.getStatus() == ItemState.STATUS_EXISTING) {
nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
NodeState srcState = src.getState();
srcState.setParentId(getNodeId());
if (srcState.getStatus() == ItemState.STATUS_EXISTING) {
srcState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
return new NodeStateEx(stateMgr, ntReg, srcState, name);
}
}
use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType 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.nodetype.EffectiveNodeType in project jackrabbit by apache.
the class WorkspaceImporter method postProcessNode.
/**
* Post-process imported node (initialize properties with special
* semantics etc.)
*
* @param node the node state
* @throws RepositoryException if an error occurs
*/
protected void postProcessNode(NodeState node) throws RepositoryException {
/**
* 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'
*/
EffectiveNodeType ent = itemOps.getEffectiveNodeType(node);
if (ent.includesNodeType(NameConstants.MIX_SIMPLE_VERSIONABLE)) {
/**
* check if there's already a version history for that
* node; this would e.g. be the case if a versionable node
* had been exported, removed and re-imported with either
* IMPORT_UUID_COLLISION_REMOVE_EXISTING or
* IMPORT_UUID_COLLISION_REPLACE_EXISTING;
* otherwise create a new version history
*/
VersionHistoryInfo history = versionManager.getVersionHistory(session, node, null);
InternalValue historyId = InternalValue.create(history.getVersionHistoryId());
InternalValue versionId = InternalValue.create(history.getRootVersionId());
// jcr:isCheckedOut
conditionalAddProperty(node, NameConstants.JCR_ISCHECKEDOUT, PropertyType.BOOLEAN, false, InternalValue.create(true));
// set extra properties only for full versionable nodes
if (ent.includesNodeType(NameConstants.MIX_VERSIONABLE)) {
// jcr:versionHistory
conditionalAddProperty(node, NameConstants.JCR_VERSIONHISTORY, PropertyType.REFERENCE, false, historyId);
// jcr:baseVersion
conditionalAddProperty(node, NameConstants.JCR_BASEVERSION, PropertyType.REFERENCE, false, versionId);
// jcr:predecessors
conditionalAddProperty(node, NameConstants.JCR_PREDECESSORS, PropertyType.REFERENCE, true, versionId);
}
}
}
use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.
the class SharedItemStateManager method isShareable.
/**
* Determines whether the specified node is <i>shareable</i>, i.e. whether the mixin type <code>mix:shareable</code>
* is either directly assigned or indirectly inherited.
*
* @param state
* node state to check
* @return true if the specified node is <i>shareable</i>, false otherwise.
* @throws RepositoryException
* if an error occurs
*/
private boolean isShareable(NodeState state) throws RepositoryException {
// shortcut: check some well-known built-in types first
Name primary = state.getNodeTypeName();
Set<Name> mixins = state.getMixinTypeNames();
if (mixins.contains(NameConstants.MIX_SHAREABLE)) {
return true;
}
try {
EffectiveNodeType type = ntReg.getEffectiveNodeType(primary, mixins);
return type.includesNodeType(NameConstants.MIX_SHAREABLE);
} catch (NodeTypeConflictException ntce) {
String msg = "internal error: failed to build effective node type for node " + state.getNodeId();
log.debug(msg);
throw new RepositoryException(msg, ntce);
}
}
Aggregations