use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class ItemSaveOperation method perform.
public Object perform(SessionContext context) throws RepositoryException {
SessionItemStateManager stateMgr = context.getItemStateManager();
/**
* build list of transient (i.e. new & modified) states that
* should be persisted
*/
Collection<ItemState> dirty;
try {
dirty = getTransientStates(context.getItemStateManager());
} catch (ConcurrentModificationException e) {
String msg = "Concurrent modification; session is closed";
log.error(msg, e);
context.getSessionImpl().logout();
throw e;
}
if (dirty.size() == 0) {
// no transient items, nothing to do here
return this;
}
/**
* build list of transient descendants in the attic
* (i.e. those marked as 'removed')
*/
Collection<ItemState> removed = getRemovedStates(context.getItemStateManager());
// All affected item states. The keys are used to look up whether
// an item is affected, and the values are iterated through below
Map<ItemId, ItemState> affected = new HashMap<ItemId, ItemState>(dirty.size() + removed.size());
for (ItemState state : dirty) {
affected.put(state.getId(), state);
}
for (ItemState state : removed) {
affected.put(state.getId(), state);
}
/**
* make sure that this save operation is totally 'self-contained'
* and independent; items within the scope of this save operation
* must not have 'external' dependencies;
* (e.g. moving a node requires that the target node including both
* old and new parents are saved)
*/
for (ItemState transientState : affected.values()) {
if (transientState.isNode()) {
NodeState nodeState = (NodeState) transientState;
Set<NodeId> dependentIDs = new HashSet<NodeId>();
if (nodeState.hasOverlayedState()) {
NodeState overlayedState = (NodeState) nodeState.getOverlayedState();
NodeId oldParentId = overlayedState.getParentId();
NodeId newParentId = nodeState.getParentId();
if (oldParentId != null) {
if (newParentId == null) {
// to dependencies
if (overlayedState.isShareable()) {
dependentIDs.addAll(overlayedState.getSharedSet());
} else {
dependentIDs.add(oldParentId);
}
} else {
if (!oldParentId.equals(newParentId)) {
// node has been moved to a new location,
// add old and new parent to dependencies
dependentIDs.add(oldParentId);
dependentIDs.add(newParentId);
} else {
// the node has been renamed (JCR-1034)
if (!affected.containsKey(newParentId) && stateMgr.hasTransientItemState(newParentId)) {
try {
NodeState parent = (NodeState) stateMgr.getTransientItemState(newParentId);
// check parent's renamed child node entries
for (ChildNodeEntry cne : parent.getRenamedChildNodeEntries()) {
if (cne.getId().equals(nodeState.getId())) {
// node has been renamed,
// add parent to dependencies
dependentIDs.add(newParentId);
}
}
} catch (ItemStateException ise) {
// should never get here
log.warn("failed to retrieve transient state: " + newParentId, ise);
}
}
}
}
}
}
// removed child node entries
for (ChildNodeEntry cne : nodeState.getRemovedChildNodeEntries()) {
dependentIDs.add(cne.getId());
}
// added child node entries
for (ChildNodeEntry cne : nodeState.getAddedChildNodeEntries()) {
dependentIDs.add(cne.getId());
}
// are within the scope of this save operation
for (NodeId id : dependentIDs) {
if (!affected.containsKey(id)) {
// otherwise ignore them
if (stateMgr.hasTransientItemState(id) || stateMgr.hasTransientItemStateInAttic(id)) {
// need to save dependency as well
String msg = context.getItemManager().safeGetJCRPath(id) + " needs to be saved as well.";
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
}
}
}
// validate access and node type constraints
// (this will also validate child removals)
validateTransientItems(context, dirty, removed);
// start the update operation
try {
stateMgr.edit();
} catch (IllegalStateException e) {
throw new RepositoryException("Unable to start edit operation", e);
}
boolean succeeded = false;
try {
// process transient items marked as 'removed'
removeTransientItems(context.getItemStateManager(), removed);
// process transient items that have change in mixins
processShareableNodes(context.getRepositoryContext().getNodeTypeRegistry(), dirty);
// initialize version histories for new nodes (might generate new transient state)
if (initVersionHistories(context, dirty)) {
// re-build the list of transient states because the previous call
// generated new transient state
dirty = getTransientStates(context.getItemStateManager());
}
// process 'new' or 'modified' transient states
persistTransientItems(context.getItemManager(), dirty);
// item state which is not referenced by any node instance.
for (ItemState transientState : dirty) {
// dispose the transient state, it is no longer used
stateMgr.disposeTransientItemState(transientState);
}
// end update operation
stateMgr.update();
// update operation succeeded
succeeded = true;
} catch (StaleItemStateException e) {
throw new InvalidItemStateException("Unable to update a stale item: " + this, e);
} catch (ItemStateException e) {
throw new RepositoryException("Unable to update item: " + this, e);
} finally {
if (!succeeded) {
// update operation failed, cancel all modifications
stateMgr.cancel();
// JCR-288: if an exception has been thrown during
// update() the transient changes have already been
// applied by persistTransientItems() and we need to
// restore transient state, i.e. undo the effect of
// persistTransientItems()
restoreTransientItems(context, dirty);
}
}
// items in store().
for (ItemState transientState : removed) {
// dispose the transient state, it is no longer used
stateMgr.disposeTransientItemStateInAttic(transientState);
}
return this;
}
use of org.apache.jackrabbit.core.state.NodeState 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);
}
}
}
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class NodeImpl method addNode.
/**
* Same as <code>{@link Node#addNode(String, String)}</code> except that
* this method takes <code>Name</code> arguments instead of
* <code>String</code>s and has an additional <code>uuid</code> argument.
* <p>
* <b>Important Notice:</b> This method is for internal use only! Passing
* already assigned uuid's might lead to unexpected results and
* data corruption in the worst case.
*
* @param nodeName name of the new node
* @param nodeTypeName name of the new node's node type or <code>null</code>
* if it should be determined automatically
* @param id id of the new node or <code>null</code> if a new
* id should be assigned
* @return the newly added node
* @throws RepositoryException if the node can not added
*/
// FIXME: This method should not be public
public synchronized NodeImpl addNode(Name nodeName, Name nodeTypeName, NodeId id) throws RepositoryException {
// check state of this instance
sanityCheck();
Path nodePath = PathFactoryImpl.getInstance().create(getPrimaryPath(), nodeName, true);
// Check the explicitly specified node type (if any)
NodeTypeImpl nt = null;
if (nodeTypeName != null) {
nt = sessionContext.getNodeTypeManager().getNodeType(nodeTypeName);
if (nt.isMixin()) {
throw new ConstraintViolationException("Unable to add a node with a mixin node type: " + sessionContext.getJCRName(nodeTypeName));
} else if (nt.isAbstract()) {
throw new ConstraintViolationException("Unable to add a node with an abstract node type: " + sessionContext.getJCRName(nodeTypeName));
} else {
// adding a node with explicit specifying the node type name
// requires the editing session to have nt_management privilege.
sessionContext.getAccessManager().checkPermission(nodePath, Permission.NODE_TYPE_MNGMT);
}
}
// Get the applicable child node definition for this node.
NodeDefinitionImpl def;
try {
def = getApplicableChildNodeDefinition(nodeName, nodeTypeName);
} catch (RepositoryException e) {
throw new ConstraintViolationException("No child node definition for " + sessionContext.getJCRName(nodeName) + " found in " + this, e);
}
// Use default node type from child node definition if needed
if (nt == null) {
nt = (NodeTypeImpl) def.getDefaultPrimaryType();
}
// check the new name
NodeNameNormalizer.check(nodeName);
// check for name collisions
NodeState thisState = data.getNodeState();
ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1);
if (cne != null) {
// check same-name sibling setting of new node
if (!def.allowsSameNameSiblings()) {
throw new ItemExistsException("This node already exists: " + itemMgr.safeGetJCRPath(nodePath));
}
// check same-name sibling setting of existing node
NodeImpl existing = itemMgr.getNode(cne.getId(), getNodeId());
if (!existing.getDefinition().allowsSameNameSiblings()) {
throw new ItemExistsException("Same-name siblings not allowed for " + existing);
}
}
// check protected flag of parent (i.e. this) node and retention/hold
// make sure this node is checked-out and not locked by another session.
int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION;
sessionContext.getItemValidator().checkModify(this, options, Permission.NONE);
// now do create the child node
return createChildNode(nodeName, nt, id);
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class HierarchyManagerImpl method resolvePath.
//-------------------------------------------------------< implementation >
/**
* Internal implementation that iteratively resolves a path into an item.
*
* @param elements path elements
* @param next index of next item in <code>elements</code> to inspect
* @param id id of item at path <code>elements[0]</code>..<code>elements[next - 1]</code>
* @param typesAllowed one of <code>RETURN_ANY</code>, <code>RETURN_NODE</code>
* or <code>RETURN_PROPERTY</code>
* @return id or <code>null</code>
* @throws ItemStateException if an intermediate item state is not found
* @throws MalformedPathException if building an intermediate path fails
*/
protected ItemId resolvePath(Path.Element[] elements, int next, ItemId id, int typesAllowed) throws ItemStateException, MalformedPathException {
PathBuilder builder = new PathBuilder();
for (int i = 0; i < next; i++) {
builder.addLast(elements[i]);
}
for (int i = next; i < elements.length; i++) {
Path.Element elem = elements[i];
NodeId parentId = (NodeId) id;
id = null;
Name name = elem.getName();
int index = elem.getIndex();
if (index == 0) {
index = 1;
}
int typeExpected = typesAllowed;
if (i < elements.length - 1) {
// intermediate items must always be nodes
typeExpected = RETURN_NODE;
}
NodeState parentState = (NodeState) getItemState(parentId);
if ((typeExpected & RETURN_NODE) != 0) {
ChildNodeEntry nodeEntry = getChildNodeEntry(parentState, name, index);
if (nodeEntry != null) {
id = nodeEntry.getId();
}
}
if (id == null && (typeExpected & RETURN_PROPERTY) != 0) {
if (parentState.hasPropertyName(name) && (index <= 1)) {
// property
id = new PropertyId(parentState.getNodeId(), name);
}
}
if (id == null) {
break;
}
builder.addLast(elements[i]);
pathResolved(id, builder);
}
return id;
}
use of org.apache.jackrabbit.core.state.NodeState in project jackrabbit by apache.
the class HierarchyManagerImpl method getParentIds.
/**
* Return all parents of a node. A shareable node has possibly more than
* one parent.
*
* @param state item state
* @param useOverlayed whether to use overlayed state for shareable nodes
* @return set of parent <code>NodeId</code>s. If state has no parent,
* array has length <code>0</code>.
*/
protected Set<NodeId> getParentIds(ItemState state, boolean useOverlayed) {
if (state.isNode()) {
// if this is a node, quickly check whether it is shareable and
// whether it contains more than one parent
NodeState ns = (NodeState) state;
if (ns.isShareable() && useOverlayed && ns.hasOverlayedState()) {
ns = (NodeState) ns.getOverlayedState();
}
Set<NodeId> s = ns.getSharedSet();
if (s.size() > 1) {
return s;
}
}
NodeId parentId = getParentId(state);
if (parentId != null) {
LinkedHashSet<NodeId> s = new LinkedHashSet<NodeId>();
s.add(parentId);
return s;
}
return Collections.emptySet();
}
Aggregations