Search in sources :

Example 1 with NodeTypeRegistry

use of org.apache.jackrabbit.core.nodetype.NodeTypeRegistry 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;
}
Also used : NodeState(org.apache.jackrabbit.core.state.NodeState) NodeTypeImpl(org.apache.jackrabbit.core.nodetype.NodeTypeImpl) HashMap(java.util.HashMap) NodeTypeConflictException(org.apache.jackrabbit.core.nodetype.NodeTypeConflictException) NodeDefinition(javax.jcr.nodetype.NodeDefinition) Name(org.apache.jackrabbit.spi.Name) NodeDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) NodeTypeManagerImpl(org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl) HashSet(java.util.HashSet) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) PropertyDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) RepositoryException(javax.jcr.RepositoryException) PropertyDefinition(javax.jcr.nodetype.PropertyDefinition) NoSuchNodeTypeException(javax.jcr.nodetype.NoSuchNodeTypeException) PropertyId(org.apache.jackrabbit.core.id.PropertyId) PropertyState(org.apache.jackrabbit.core.state.PropertyState) ItemStateException(org.apache.jackrabbit.core.state.ItemStateException) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) Value(javax.jcr.Value) ValueFormatException(javax.jcr.ValueFormatException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry) SessionItemStateManager(org.apache.jackrabbit.core.state.SessionItemStateManager) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with NodeTypeRegistry

use of org.apache.jackrabbit.core.nodetype.NodeTypeRegistry in project jackrabbit by apache.

the class ItemValidator method validate.

/**
     * Checks whether the given node state satisfies the constraints specified
     * by its primary and mixin node types. The following validations/checks are
     * performed:
     * <ul>
     * <li>check if its node type satisfies the 'required node types' constraint
     * specified in its definition</li>
     * <li>check if all 'mandatory' child items exist</li>
     * <li>for every property: check if the property value satisfies the
     * value constraints specified in the property's definition</li>
     * </ul>
     *
     * @param nodeState state of node to be validated
     * @throws ConstraintViolationException if any of the validations fail
     * @throws RepositoryException          if another error occurs
     */
public void validate(NodeState nodeState) throws ConstraintViolationException, RepositoryException {
    // effective primary node type
    NodeTypeRegistry registry = context.getNodeTypeRegistry();
    EffectiveNodeType entPrimary = registry.getEffectiveNodeType(nodeState.getNodeTypeName());
    // effective node type (primary type incl. mixins)
    EffectiveNodeType entPrimaryAndMixins = getEffectiveNodeType(nodeState);
    QNodeDefinition def = context.getItemManager().getDefinition(nodeState).unwrap();
    // check if primary type satisfies the 'required node types' constraint
    for (Name requiredPrimaryType : def.getRequiredPrimaryTypes()) {
        if (!entPrimary.includesNodeType(requiredPrimaryType)) {
            String msg = safeGetJCRPath(nodeState.getNodeId()) + ": missing required primary type " + requiredPrimaryType;
            log.debug(msg);
            throw new ConstraintViolationException(msg);
        }
    }
    // mandatory properties
    for (QPropertyDefinition pd : entPrimaryAndMixins.getMandatoryPropDefs()) {
        if (!nodeState.hasPropertyName(pd.getName())) {
            String msg = safeGetJCRPath(nodeState.getNodeId()) + ": mandatory property " + pd.getName() + " does not exist";
            log.debug(msg);
            throw new ConstraintViolationException(msg);
        }
    }
    // mandatory child nodes
    for (QItemDefinition cnd : entPrimaryAndMixins.getMandatoryNodeDefs()) {
        if (!nodeState.hasChildNodeEntry(cnd.getName())) {
            String msg = safeGetJCRPath(nodeState.getNodeId()) + ": mandatory child node " + cnd.getName() + " does not exist";
            log.debug(msg);
            throw new ConstraintViolationException(msg);
        }
    }
}
Also used : EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) QPropertyDefinition(org.apache.jackrabbit.spi.QPropertyDefinition) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry) QNodeDefinition(org.apache.jackrabbit.spi.QNodeDefinition) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) Name(org.apache.jackrabbit.spi.Name)

Example 3 with NodeTypeRegistry

use of org.apache.jackrabbit.core.nodetype.NodeTypeRegistry in project jackrabbit by apache.

the class NodeImpl method setPrimaryType.

/**
     * {@inheritDoc}
     */
public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
    // check state of this instance
    sanityCheck();
    // make sure this node is checked-out, neither protected nor locked and
    // the editing session has sufficient permission to change the primary type.
    int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
    sessionContext.getItemValidator().checkModify(this, options, Permission.NODE_TYPE_MNGMT);
    final NodeState state = data.getNodeState();
    if (state.getParentId() == null) {
        String msg = "changing the primary type of the root node is not supported";
        log.debug(msg);
        throw new RepositoryException(msg);
    }
    Name ntName = sessionContext.getQName(nodeTypeName);
    if (ntName.equals(state.getNodeTypeName())) {
        log.debug("Node already has " + nodeTypeName + " as primary node type.");
        return;
    }
    NodeTypeManagerImpl ntMgr = sessionContext.getNodeTypeManager();
    NodeType nt = ntMgr.getNodeType(ntName);
    if (nt.isMixin()) {
        throw new ConstraintViolationException(nodeTypeName + ": not a primary node type.");
    } else if (nt.isAbstract()) {
        throw new ConstraintViolationException(nodeTypeName + ": is an abstract node type.");
    }
    // build effective node type of new primary type & existing mixin's
    // in order to detect conflicts
    NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
    EffectiveNodeType entNew, entOld, entAll;
    try {
        entNew = ntReg.getEffectiveNodeType(ntName);
        entOld = ntReg.getEffectiveNodeType(state.getNodeTypeName());
        // try to build new effective node type (will throw in case of conflicts)
        entAll = ntReg.getEffectiveNodeType(ntName, state.getMixinTypeNames());
    } catch (NodeTypeConflictException ntce) {
        throw new ConstraintViolationException(ntce.getMessage());
    }
    // get applicable definition for this node using new primary type
    QNodeDefinition nodeDef;
    try {
        NodeImpl parent = (NodeImpl) getParent();
        nodeDef = parent.getApplicableChildNodeDefinition(getQName(), ntName).unwrap();
    } catch (RepositoryException re) {
        String msg = this + ": no applicable definition found in parent node's node type";
        log.debug(msg);
        throw new ConstraintViolationException(msg, re);
    }
    if (!nodeDef.equals(itemMgr.getDefinition(state).unwrap())) {
        onRedefine(nodeDef);
    }
    Set<QItemDefinition> oldDefs = new HashSet<QItemDefinition>(Arrays.asList(entOld.getAllItemDefs()));
    Set<QItemDefinition> newDefs = new HashSet<QItemDefinition>(Arrays.asList(entNew.getAllItemDefs()));
    Set<QItemDefinition> allDefs = new HashSet<QItemDefinition>(Arrays.asList(entAll.getAllItemDefs()));
    // added child item definitions
    Set<QItemDefinition> addedDefs = new HashSet<QItemDefinition>(newDefs);
    addedDefs.removeAll(oldDefs);
    // referential integrity check
    boolean referenceableOld = entOld.includesNodeType(NameConstants.MIX_REFERENCEABLE);
    boolean referenceableNew = entNew.includesNodeType(NameConstants.MIX_REFERENCEABLE);
    if (referenceableOld && !referenceableNew) {
        // node would become non-referenceable;
        // make sure no references exist
        PropertyIterator iter = getReferences();
        if (iter.hasNext()) {
            throw new ConstraintViolationException("the new primary type cannot be set as it would render " + "this node 'non-referenceable' while it is still being " + "referenced through at least one property of type REFERENCE");
        }
    }
    // do the actual modifications in content as mandated by the new primary type
    // modify the state of this node
    NodeState thisState = (NodeState) getOrCreateTransientItemState();
    thisState.setNodeTypeName(ntName);
    // set jcr:primaryType property
    internalSetProperty(NameConstants.JCR_PRIMARYTYPE, InternalValue.create(ntName));
    // walk through properties and child nodes and change definition as necessary
    // use temp set to avoid ConcurrentModificationException
    HashSet<Name> set = new HashSet<Name>(thisState.getPropertyNames());
    for (Name propName : set) {
        try {
            PropertyState propState = (PropertyState) stateMgr.getItemState(new PropertyId(thisState.getNodeId(), propName));
            if (!allDefs.contains(itemMgr.getDefinition(propState).unwrap())) {
                // redefine property if possible
                try {
                    PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propState.getId());
                    if (prop.getDefinition().isProtected()) {
                        // remove 'orphaned' protected properties immediately
                        removeChildProperty(propName);
                        continue;
                    }
                    PropertyDefinitionImpl pdi = getApplicablePropertyDefinition(propName, propState.getType(), propState.isMultiValued(), false);
                    if (pdi.getRequiredType() != PropertyType.UNDEFINED && pdi.getRequiredType() != propState.getType()) {
                        // value conversion required
                        if (propState.isMultiValued()) {
                            // convert value
                            Value[] values = ValueHelper.convert(prop.getValues(), pdi.getRequiredType(), getSession().getValueFactory());
                            // redefine property
                            prop.onRedefine(pdi.unwrap());
                            // set converted values
                            prop.setValue(values);
                        } else {
                            // convert value
                            Value value = ValueHelper.convert(prop.getValue(), pdi.getRequiredType(), getSession().getValueFactory());
                            // redefine property
                            prop.onRedefine(pdi.unwrap());
                            // set converted values
                            prop.setValue(value);
                        }
                    } else {
                        // redefine property
                        prop.onRedefine(pdi.unwrap());
                    }
                    // update collection of added definitions
                    addedDefs.remove(pdi.unwrap());
                } catch (ValueFormatException vfe) {
                    // value conversion failed, remove it
                    removeChildProperty(propName);
                } catch (ConstraintViolationException cve) {
                    // no suitable definition found for this property,
                    // remove it
                    removeChildProperty(propName);
                }
            }
        } catch (ItemStateException ise) {
            String msg = propName + ": failed to retrieve property state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // use temp array to avoid ConcurrentModificationException
    ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(thisState.getChildNodeEntries());
    // start from tail to avoid problems with same-name siblings
    for (int i = list.size() - 1; i >= 0; i--) {
        ChildNodeEntry entry = list.get(i);
        try {
            NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
            if (!allDefs.contains(itemMgr.getDefinition(nodeState).unwrap())) {
                // redefine node if possible
                try {
                    NodeImpl node = (NodeImpl) itemMgr.getItem(nodeState.getId());
                    if (node.getDefinition().isProtected()) {
                        // remove 'orphaned' protected child node immediately
                        removeChildNode(entry.getId());
                        continue;
                    }
                    NodeDefinitionImpl ndi = getApplicableChildNodeDefinition(entry.getName(), nodeState.getNodeTypeName());
                    // redefine node
                    node.onRedefine(ndi.unwrap());
                    // update collection of added definitions
                    addedDefs.remove(ndi.unwrap());
                } catch (ConstraintViolationException cve) {
                    // no suitable definition found for this child node,
                    // remove it
                    removeChildNode(entry.getId());
                }
            }
        } catch (ItemStateException ise) {
            String msg = entry.getName() + ": failed to retrieve node state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // type and at the same time were not present with the old nt
    for (QItemDefinition def : addedDefs) {
        if (def.isAutoCreated()) {
            if (def.definesNode()) {
                NodeDefinitionImpl ndi = ntMgr.getNodeDefinition((QNodeDefinition) def);
                createChildNode(def.getName(), (NodeTypeImpl) ndi.getDefaultPrimaryType(), null);
            } else {
                PropertyDefinitionImpl pdi = ntMgr.getPropertyDefinition((QPropertyDefinition) def);
                createChildProperty(pdi.unwrap().getName(), pdi.getRequiredType(), pdi);
            }
        }
    }
}
Also used : NodeState(org.apache.jackrabbit.core.state.NodeState) NodeTypeConflictException(org.apache.jackrabbit.core.nodetype.NodeTypeConflictException) ArrayList(java.util.ArrayList) Name(org.apache.jackrabbit.spi.Name) NodeDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) NodeTypeManagerImpl(org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl) HashSet(java.util.HashSet) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) PropertyIterator(javax.jcr.PropertyIterator) PropertyDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) RepositoryException(javax.jcr.RepositoryException) QNodeDefinition(org.apache.jackrabbit.spi.QNodeDefinition) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) PropertyState(org.apache.jackrabbit.core.state.PropertyState) PropertyId(org.apache.jackrabbit.core.id.PropertyId) InvalidItemStateException(javax.jcr.InvalidItemStateException) ItemStateException(org.apache.jackrabbit.core.state.ItemStateException) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) NodeType(javax.jcr.nodetype.NodeType) Value(javax.jcr.Value) InternalValue(org.apache.jackrabbit.core.value.InternalValue) ValueFormatException(javax.jcr.ValueFormatException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry)

Example 4 with NodeTypeRegistry

use of org.apache.jackrabbit.core.nodetype.NodeTypeRegistry in project jackrabbit by apache.

the class NodeImpl method setMixins.

/**
     * {@inheritDoc}
     */
public void setMixins(String[] mixinNames) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
    // check state of this instance
    sanityCheck();
    NodeTypeManagerImpl ntMgr = sessionContext.getNodeTypeManager();
    Set<Name> newMixins = new HashSet<Name>();
    for (String name : mixinNames) {
        Name qName = sessionContext.getQName(name);
        if (!ntMgr.getNodeType(qName).isMixin()) {
            throw new RepositoryException(sessionContext.getJCRName(qName) + " is not a mixin node type");
        }
        newMixins.add(qName);
    }
    // make sure this node is checked-out, neither protected nor locked and
    // the editing session has sufficient permission to change the mixin types.
    // special handling of mix:(simple)versionable. since adding the
    // mixin alters the version storage jcr:versionManagement privilege
    // is required in addition.
    int permissions = Permission.NODE_TYPE_MNGMT;
    if (newMixins.contains(MIX_VERSIONABLE) || newMixins.contains(MIX_SIMPLE_VERSIONABLE)) {
        permissions |= Permission.VERSION_MNGMT;
    }
    int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
    sessionContext.getItemValidator().checkModify(this, options, permissions);
    final NodeState state = data.getNodeState();
    // build effective node type of primary type & new mixin's
    // in order to detect conflicts
    NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
    EffectiveNodeType entNew, entOld, entAll;
    try {
        entNew = ntReg.getEffectiveNodeType(newMixins);
        entOld = ntReg.getEffectiveNodeType(state.getMixinTypeNames());
        // try to build new effective node type (will throw in case of conflicts)
        entAll = ntReg.getEffectiveNodeType(state.getNodeTypeName(), newMixins);
    } catch (NodeTypeConflictException ntce) {
        throw new ConstraintViolationException(ntce.getMessage());
    }
    // added child item definitions
    Set<QItemDefinition> addedDefs = new HashSet<QItemDefinition>(Arrays.asList(entNew.getAllItemDefs()));
    addedDefs.removeAll(Arrays.asList(entOld.getAllItemDefs()));
    // referential integrity check
    boolean referenceableOld = getEffectiveNodeType().includesNodeType(NameConstants.MIX_REFERENCEABLE);
    boolean referenceableNew = entAll.includesNodeType(NameConstants.MIX_REFERENCEABLE);
    if (referenceableOld && !referenceableNew) {
        // node would become non-referenceable;
        // make sure no references exist
        PropertyIterator iter = getReferences();
        if (iter.hasNext()) {
            throw new ConstraintViolationException("the new mixin types cannot be set as it would render " + "this node 'non-referenceable' while it is still being " + "referenced through at least one property of type REFERENCE");
        }
    }
    // gather currently assigned definitions *before* doing actual modifications
    Map<ItemId, ItemDefinition> oldDefs = new HashMap<ItemId, ItemDefinition>();
    for (Name name : getNodeState().getPropertyNames()) {
        PropertyId id = new PropertyId(getNodeId(), name);
        try {
            PropertyState propState = (PropertyState) stateMgr.getItemState(id);
            oldDefs.put(id, itemMgr.getDefinition(propState));
        } catch (ItemStateException ise) {
            String msg = name + ": failed to retrieve property state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    for (ChildNodeEntry cne : getNodeState().getChildNodeEntries()) {
        try {
            NodeState nodeState = (NodeState) stateMgr.getItemState(cne.getId());
            oldDefs.put(cne.getId(), itemMgr.getDefinition(nodeState));
        } catch (ItemStateException ise) {
            String msg = cne + ": failed to retrieve node state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // now do the actual modifications in content as mandated by the new mixins
    // modify the state of this node
    NodeState thisState = (NodeState) getOrCreateTransientItemState();
    thisState.setMixinTypeNames(newMixins);
    // set jcr:mixinTypes property
    setMixinTypesProperty(newMixins);
    // walk through properties and child nodes and change definition as necessary
    // use temp set to avoid ConcurrentModificationException
    HashSet<Name> set = new HashSet<Name>(thisState.getPropertyNames());
    for (Name propName : set) {
        PropertyState propState = null;
        try {
            propState = (PropertyState) stateMgr.getItemState(new PropertyId(thisState.getNodeId(), propName));
            // the following call triggers ConstraintViolationException
            // if there isn't any suitable definition anymore
            itemMgr.getDefinition(propState);
        } catch (ConstraintViolationException cve) {
            // redefine property if possible
            try {
                if (oldDefs.get(propState.getId()).isProtected()) {
                    // remove 'orphaned' protected properties immediately
                    removeChildProperty(propName);
                    continue;
                }
                PropertyDefinitionImpl pdi = getApplicablePropertyDefinition(propName, propState.getType(), propState.isMultiValued(), false);
                PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propState.getId());
                if (pdi.getRequiredType() != PropertyType.UNDEFINED && pdi.getRequiredType() != propState.getType()) {
                    // value conversion required
                    if (propState.isMultiValued()) {
                        // convert value
                        Value[] values = ValueHelper.convert(prop.getValues(), pdi.getRequiredType(), getSession().getValueFactory());
                        // redefine property
                        prop.onRedefine(pdi.unwrap());
                        // set converted values
                        prop.setValue(values);
                    } else {
                        // convert value
                        Value value = ValueHelper.convert(prop.getValue(), pdi.getRequiredType(), getSession().getValueFactory());
                        // redefine property
                        prop.onRedefine(pdi.unwrap());
                        // set converted values
                        prop.setValue(value);
                    }
                } else {
                    // redefine property
                    prop.onRedefine(pdi.unwrap());
                }
                // update collection of added definitions
                addedDefs.remove(pdi.unwrap());
            } catch (ValueFormatException vfe) {
                // value conversion failed, remove it
                removeChildProperty(propName);
            } catch (ConstraintViolationException cve1) {
                // no suitable definition found for this property,
                // remove it
                removeChildProperty(propName);
            }
        } catch (ItemStateException ise) {
            String msg = propName + ": failed to retrieve property state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // use temp array to avoid ConcurrentModificationException
    ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(thisState.getChildNodeEntries());
    // start from tail to avoid problems with same-name siblings
    for (int i = list.size() - 1; i >= 0; i--) {
        ChildNodeEntry entry = list.get(i);
        NodeState nodeState = null;
        try {
            nodeState = (NodeState) stateMgr.getItemState(entry.getId());
            // the following call triggers ConstraintViolationException
            // if there isn't any suitable definition anymore
            itemMgr.getDefinition(nodeState);
        } catch (ConstraintViolationException cve) {
            // redefine node if possible
            try {
                if (oldDefs.get(nodeState.getId()).isProtected()) {
                    // remove 'orphaned' protected child node immediately
                    removeChildNode(entry.getId());
                    continue;
                }
                NodeDefinitionImpl ndi = getApplicableChildNodeDefinition(entry.getName(), nodeState.getNodeTypeName());
                NodeImpl node = (NodeImpl) itemMgr.getItem(nodeState.getId());
                // redefine node
                node.onRedefine(ndi.unwrap());
                // update collection of added definitions
                addedDefs.remove(ndi.unwrap());
            } catch (ConstraintViolationException cve1) {
                // no suitable definition found for this child node,
                // remove it
                removeChildNode(entry.getId());
            }
        } catch (ItemStateException ise) {
            String msg = entry + ": failed to retrieve node state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // and at the same time were not present with the old mixins
    for (QItemDefinition def : addedDefs) {
        if (def.isAutoCreated()) {
            if (def.definesNode()) {
                NodeDefinitionImpl ndi = ntMgr.getNodeDefinition((QNodeDefinition) def);
                createChildNode(def.getName(), (NodeTypeImpl) ndi.getDefaultPrimaryType(), null);
            } else {
                PropertyDefinitionImpl pdi = ntMgr.getPropertyDefinition((QPropertyDefinition) def);
                createChildProperty(pdi.unwrap().getName(), pdi.getRequiredType(), pdi);
            }
        }
    }
}
Also used : NodeState(org.apache.jackrabbit.core.state.NodeState) HashMap(java.util.HashMap) NodeTypeConflictException(org.apache.jackrabbit.core.nodetype.NodeTypeConflictException) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) ItemDefinition(javax.jcr.nodetype.ItemDefinition) ArrayList(java.util.ArrayList) ItemId(org.apache.jackrabbit.core.id.ItemId) Name(org.apache.jackrabbit.spi.Name) NodeDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) NodeTypeManagerImpl(org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl) HashSet(java.util.HashSet) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) PropertyIterator(javax.jcr.PropertyIterator) PropertyDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) RepositoryException(javax.jcr.RepositoryException) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) PropertyId(org.apache.jackrabbit.core.id.PropertyId) PropertyState(org.apache.jackrabbit.core.state.PropertyState) InvalidItemStateException(javax.jcr.InvalidItemStateException) ItemStateException(org.apache.jackrabbit.core.state.ItemStateException) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) Value(javax.jcr.Value) InternalValue(org.apache.jackrabbit.core.value.InternalValue) ValueFormatException(javax.jcr.ValueFormatException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry)

Example 5 with NodeTypeRegistry

use of org.apache.jackrabbit.core.nodetype.NodeTypeRegistry in project jackrabbit by apache.

the class NodeImpl method canAddMixin.

/**
     * {@inheritDoc}
     */
public boolean canAddMixin(String mixinName) throws NoSuchNodeTypeException, RepositoryException {
    // check state of this instance
    sanityCheck();
    Name ntName = sessionContext.getQName(mixinName);
    NodeTypeManagerImpl ntMgr = sessionContext.getNodeTypeManager();
    NodeTypeImpl mixin = ntMgr.getNodeType(ntName);
    if (!mixin.isMixin()) {
        return false;
    }
    int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
    int permissions = Permission.NODE_TYPE_MNGMT;
    // in addition.
    if (NameConstants.MIX_VERSIONABLE.equals(ntName) || NameConstants.MIX_SIMPLE_VERSIONABLE.equals(ntName)) {
        permissions |= Permission.VERSION_MNGMT;
    }
    if (!sessionContext.getItemValidator().canModify(this, options, permissions)) {
        return false;
    }
    final Name primaryTypeName = data.getNodeState().getNodeTypeName();
    NodeTypeImpl primaryType = ntMgr.getNodeType(primaryTypeName);
    if (primaryType.isDerivedFrom(ntName)) {
        // mixin already inherited -> addMixin is allowed but has no effect.
        return true;
    }
    // build effective node type of mixins & primary type
    // in order to detect conflicts
    NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
    EffectiveNodeType entExisting;
    try {
        // existing mixin's
        Set<Name> mixins = new HashSet<Name>(data.getNodeState().getMixinTypeNames());
        // build effective node type representing primary type including existing mixin's
        entExisting = ntReg.getEffectiveNodeType(primaryTypeName, mixins);
        if (entExisting.includesNodeType(ntName)) {
            // addMixin would succeed without modifying the node.
            return true;
        }
        // add new mixin
        mixins.add(ntName);
        // try to build new effective node type (will throw in case of conflicts)
        ntReg.getEffectiveNodeType(primaryTypeName, mixins);
    } catch (NodeTypeConflictException ntce) {
        return false;
    }
    return true;
}
Also used : EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) NodeTypeImpl(org.apache.jackrabbit.core.nodetype.NodeTypeImpl) NodeTypeConflictException(org.apache.jackrabbit.core.nodetype.NodeTypeConflictException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry) NodeTypeManagerImpl(org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl) Name(org.apache.jackrabbit.spi.Name) HashSet(java.util.HashSet)

Aggregations

NodeTypeRegistry (org.apache.jackrabbit.core.nodetype.NodeTypeRegistry)13 EffectiveNodeType (org.apache.jackrabbit.core.nodetype.EffectiveNodeType)10 Name (org.apache.jackrabbit.spi.Name)10 RepositoryException (javax.jcr.RepositoryException)9 NodeTypeConflictException (org.apache.jackrabbit.core.nodetype.NodeTypeConflictException)9 ConstraintViolationException (javax.jcr.nodetype.ConstraintViolationException)7 NodeTypeManagerImpl (org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl)6 NodeState (org.apache.jackrabbit.core.state.NodeState)6 HashSet (java.util.HashSet)5 ItemStateException (org.apache.jackrabbit.core.state.ItemStateException)5 InvalidItemStateException (javax.jcr.InvalidItemStateException)4 ChildNodeEntry (org.apache.jackrabbit.core.state.ChildNodeEntry)4 NodeDefinitionImpl (org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl)4 PropertyDefinitionImpl (org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl)4 ArrayList (java.util.ArrayList)3 Value (javax.jcr.Value)3 ValueFormatException (javax.jcr.ValueFormatException)3 PropertyId (org.apache.jackrabbit.core.id.PropertyId)3 NodeTypeImpl (org.apache.jackrabbit.core.nodetype.NodeTypeImpl)3 PropertyState (org.apache.jackrabbit.core.state.PropertyState)3