use of org.apache.jackrabbit.core.state.PropertyState 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.state.PropertyState in project jackrabbit by apache.
the class VersionManagerImplRestore method internalRestoreFrozen.
/**
* Restores the properties and child nodes from the frozen state.
*
* @param state state to restore
* @param freeze the frozen node
* @param vsel version selector
* @param restored set of restored versions
* @param removeExisting remove existing flag
* @param copy if <code>true</code> a pure copy is performed
* @throws RepositoryException if an error occurs
* @throws ItemStateException if an error occurs
*/
protected void internalRestoreFrozen(NodeStateEx state, InternalFrozenNode freeze, VersionSelector vsel, Set<InternalVersion> restored, boolean removeExisting, boolean copy) throws RepositoryException, ItemStateException {
// check uuid
if (state.getEffectiveNodeType().includesNodeType(NameConstants.MIX_REFERENCEABLE)) {
if (!state.getNodeId().equals(freeze.getFrozenId())) {
String msg = "Unable to restore version of " + safeGetJCRPath(state) + ". UUID changed.";
log.error(msg);
throw new ItemExistsException(msg);
}
}
// check primary type
if (!freeze.getFrozenPrimaryType().equals(state.getState().getNodeTypeName())) {
// todo: implement
String msg = "Unable to restore version of " + safeGetJCRPath(state) + ". PrimaryType change not supported yet.";
log.error(msg);
throw new UnsupportedRepositoryOperationException(msg);
}
// adjust mixins
state.setMixins(freeze.getFrozenMixinTypes());
// For each property P present on F (other than jcr:frozenPrimaryType,
// jcr:frozenMixinTypes and jcr:frozenUuid):
// - If P has an OPV of COPY or VERSION then F/P is copied to N/P,
// replacing any existing N/P.
// - F will never have a property with an OPV of IGNORE, INITIALIZE, COMPUTE
// or ABORT (see 15.2 Check-In: Creating a Version).
Set<Name> propNames = new HashSet<Name>();
PropertyState[] props = freeze.getFrozenProperties();
for (PropertyState prop : props) {
// don't restore jcr:activity
Name name = prop.getName();
if (!name.equals(NameConstants.JCR_ACTIVITY)) {
state.copyFrom(prop);
propNames.add(name);
}
}
// remove properties that do not exist in the frozen representation
for (PropertyState prop : state.getProperties()) {
Name propName = prop.getName();
if (!propNames.contains(propName)) {
int opv = state.getDefinition(prop).getOnParentVersion();
if (opv == OnParentVersionAction.COPY || opv == OnParentVersionAction.VERSION || opv == OnParentVersionAction.ABORT) {
state.removeProperty(propName);
} else if (opv == OnParentVersionAction.INITIALIZE) {
InternalValue[] values = computeAutoValues(state, state.getDefinition(prop), true);
if (values != null) {
state.setPropertyValues(propName, prop.getType(), values, prop.isMultiValued());
}
} else if (opv == OnParentVersionAction.COMPUTE) {
InternalValue[] values = computeAutoValues(state, state.getDefinition(prop), false);
if (values != null) {
state.setPropertyValues(propName, prop.getType(), values, prop.isMultiValued());
}
}
}
}
// add 'auto-create' properties that do not exist yet
for (QPropertyDefinition def : state.getEffectiveNodeType().getAutoCreatePropDefs()) {
if (!state.hasProperty(def.getName())) {
InternalValue[] values = computeAutoValues(state, def, true);
if (values != null) {
state.setPropertyValues(def.getName(), def.getRequiredType(), values, def.isMultiple());
}
}
}
// For each child node C present on N but not on F:
// - If C has an OPV of COPY, VERSION or ABORT then N/C is removed.
// Note that while a node with a child item of OPV ABORT cannot be
// versioned, it is legal for a previously versioned node to have such
// a child item added to it and then for it to be restored to the state
// that it had before that item was added, as this step indicates.
// - If C has an OPV of IGNORE then no change is made to N/C.
// - If C has an OPV of INITIALIZE then N/C is re-initialized as if it
// were newly created, as defined in its node type.
// - If C has an OPV of COMPUTE then N/C may be re-initialized according
// to an implementation-specific mechanism.
LinkedList<ChildNodeEntry> toDelete = new LinkedList<ChildNodeEntry>();
for (ChildNodeEntry entry : state.getState().getChildNodeEntries()) {
if (!freeze.hasFrozenChildNode(entry.getName(), entry.getIndex())) {
NodeStateEx child = state.getNode(entry.getName(), entry.getIndex());
int opv = child.getDefinition().getOnParentVersion();
if (copy || opv == OnParentVersionAction.COPY || opv == OnParentVersionAction.VERSION || opv == OnParentVersionAction.ABORT) {
toDelete.addFirst(entry);
} else if (opv == OnParentVersionAction.INITIALIZE) {
log.warn("OPV.INITIALIZE not supported yet on restore of existing child nodes: " + safeGetJCRPath(child));
} else if (opv == OnParentVersionAction.COMPUTE) {
log.warn("OPV.COMPUTE not supported yet on restore of existing child nodes: " + safeGetJCRPath(child));
}
}
}
for (ChildNodeEntry entry : toDelete) {
state.removeNode(entry.getName(), entry.getIndex());
}
// need to sync with state manager
state.store();
// create a map that contains a int->NodeStateEx mapping for each child name
Map<Name, Map<Integer, NodeStateEx>> entryToNodeStateExMapping = new HashMap<Name, Map<Integer, NodeStateEx>>();
for (ChildNodeEntry entry : state.getState().getChildNodeEntries()) {
Map<Integer, NodeStateEx> id2stateMap = entryToNodeStateExMapping.get(entry.getName());
if (id2stateMap == null) {
id2stateMap = new HashMap<Integer, NodeStateEx>();
}
id2stateMap.put(entry.getIndex(), state.getNode(entry.getName(), entry.getIndex()));
entryToNodeStateExMapping.put(entry.getName(), id2stateMap);
}
// COMPUTE or ABORT (see 15.2 Check-In: Creating a Version).
for (ChildNodeEntry entry : freeze.getFrozenChildNodes()) {
InternalFreeze child = freeze.getFrozenChildNode(entry.getName(), entry.getIndex());
NodeStateEx restoredChild = null;
if (child instanceof InternalFrozenNode) {
// - If C has an OPV of COPY or VERSION:
// - B is true, then F/C and its subgraph is copied to N/C, replacing
// any existing N/C and its subgraph and any node in the workspace
// with the same identifier as C or a node in the subgraph of C is
// removed.
// - B is false, then F/C and its subgraph is copied to N/C, replacing
// any existing N/C and its subgraph unless there exists a node in the
// workspace with the same identifier as C, or a node in the subgraph
// of C, in which case an ItemExistsException is thrown , all
// changes made by the restore are rolled back leaving N unchanged.
InternalFrozenNode f = (InternalFrozenNode) child;
// if node is present, remove it
Map<Integer, NodeStateEx> id2stateMap = entryToNodeStateExMapping.get(entry.getName());
if (id2stateMap != null && id2stateMap.containsKey(entry.getIndex())) {
state.removeNode(id2stateMap.get(entry.getIndex()));
}
// check for existing
if (f.getFrozenId() != null) {
if (stateMgr.hasItemState(f.getFrozenId())) {
NodeStateEx existing = state.getNode(f.getFrozenId());
if (removeExisting) {
NodeStateEx parent = existing.getParent();
parent.removeNode(existing);
parent.store();
} else if (existing.getState().isShareable()) {
// if existing node is shareable, then clone it
restoredChild = state.moveFrom(existing, existing.getName(), true);
} else if (!existing.hasAncestor(state.getNodeId())) {
String msg = "Unable to restore node, item already exists " + "outside of restored tree: " + safeGetJCRPath(existing);
log.error(msg);
throw new ItemExistsException(msg);
}
}
}
if (restoredChild == null) {
restoredChild = state.addNode(f.getName(), f.getFrozenPrimaryType(), f.getFrozenId());
restoredChild.setMixins(f.getFrozenMixinTypes());
}
internalRestoreFrozen(restoredChild, f, vsel, restored, removeExisting, true);
} else if (child instanceof InternalFrozenVersionHistory) {
// Each child node C of N where C has an OPV of VERSION and C is
// mix:versionable, is represented in F not as a copy of N/C but as
// special node containing a reference to the version history of
// C. On restore, the following occurs:
// - If the workspace currently has an already existing node corresponding
// to C's version history and the removeExisting flag of the restore is
// set to true, then that instance of C becomes the child of the restored N.
// - If the workspace currently has an already existing node corresponding
// to C's version history and the removeExisting flag of the restore is
// set to false then an ItemExistsException is thrown.
// - If the workspace does not have an instance of C then one is restored from
// C's version history:
// - If the restore was initiated through a restoreByLabel where L is
// the specified label and there is a version of C with the label L then
// that version is restored.
// - If the version history of C does not contain a version with the label
// L or the restore was initiated by a method call that does not specify
// a label then the workspace in which the restore is being performed
// will determine which particular version of C will be restored. This
// determination depends on the configuration of the workspace and
// is outside the scope of this specification.
InternalFrozenVersionHistory fh = (InternalFrozenVersionHistory) child;
InternalVersionHistory vh = vMgr.getVersionHistory(fh.getVersionHistoryId());
// get desired version from version selector
InternalVersion v = vsel.select(vh);
Name oldVersion = null;
// check if representing versionable already exists somewhere
NodeId nodeId = vh.getVersionableId();
if (stateMgr.hasItemState(nodeId)) {
restoredChild = state.getNode(nodeId);
if (restoredChild.getParentId().equals(state.getNodeId())) {
// if same parent, ignore
} else if (removeExisting) {
NodeStateEx parent = restoredChild.getNode(restoredChild.getParentId());
state.moveFrom(restoredChild, fh.getName(), false);
parent.store();
// get old version name
oldVersion = getBaseVersion(restoredChild).getName();
} else {
// since we delete the OPV=Copy children beforehand, all
// found nodes must be outside of this tree
String msg = "Unable to restore node, item already exists " + "outside of restored tree: " + safeGetJCRPath(restoredChild);
log.error(msg);
throw new ItemExistsException(msg);
}
}
// check existing version of item exists
if (restoredChild == null) {
if (v == null) {
// if version selector was unable to select version,
// choose the initial one
List<InternalVersion> vs = vh.getRootVersion().getSuccessors();
if (vs.isEmpty()) {
String msg = "Unable to select appropriate version for " + child.getName() + " using " + vsel;
log.error(msg);
throw new VersionException(msg);
}
v = vs.get(0);
}
InternalFrozenNode f = v.getFrozenNode();
restoredChild = state.addNode(fh.getName(), f.getFrozenPrimaryType(), f.getFrozenId());
restoredChild.setMixins(f.getFrozenMixinTypes());
} else {
if (v == null || oldVersion == null || v.getName().equals(oldVersion)) {
v = null;
}
}
if (v != null) {
try {
internalRestore(restoredChild, v, vsel, removeExisting);
} catch (RepositoryException e) {
log.error("Error while restoring node: " + e);
log.error(" child path: " + restoredChild);
log.error(" selected version: " + v.getName());
StringBuilder avail = new StringBuilder();
for (Name name : vh.getVersionNames()) {
avail.append(name);
avail.append(", ");
}
log.error(" available versions: " + avail);
log.error(" versionselector: " + vsel);
throw e;
}
// add this version to set
restored.add(v);
}
}
if (restoredChild != null && state.getEffectiveNodeType().hasOrderableChildNodes()) {
// In a repository that supports orderable child nodes, the relative
// ordering of the set of child nodes C that are copied from F is
// preserved.
// order at end
ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(state.getState().getChildNodeEntries());
ChildNodeEntry toReorder = null;
boolean isLast = true;
for (ChildNodeEntry e : list) {
if (e.getId().equals(restoredChild.getNodeId())) {
toReorder = e;
} else if (toReorder != null) {
isLast = false;
}
}
if (toReorder != null && !isLast) {
list.remove(toReorder);
list.add(toReorder);
state.getState().setChildNodeEntries(list);
}
}
}
}
use of org.apache.jackrabbit.core.state.PropertyState in project jackrabbit by apache.
the class NodeStateEx method setPropertyValues.
/**
* Sets the property values
*
* @param name name of the property
* @param type type of the values
* @param values values to set
* @param multiple <code>true</code>for MV properties
* @return the modified property state
* @throws RepositoryException if an error occurs
*/
public PropertyState setPropertyValues(Name name, int type, InternalValue[] values, boolean multiple) throws RepositoryException {
PropertyId propId = new PropertyId(nodeState.getNodeId(), name);
if (stateMgr.hasItemState(propId)) {
try {
PropertyState propState = (PropertyState) stateMgr.getItemState(propId);
if (propState.getStatus() == ItemState.STATUS_EXISTING) {
propState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
// although this is not quite correct, we mark node as modified as well
if (nodeState.getStatus() == ItemState.STATUS_EXISTING) {
nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
propState.setType(type);
propState.setMultiValued(multiple);
propState.setValues(values);
return propState;
} catch (ItemStateException e) {
throw new RepositoryException("Unable to create property: " + e.toString());
}
} else {
PropertyState propState = stateMgr.createNew(name, nodeState.getNodeId());
propState.setType(type);
propState.setMultiValued(multiple);
propState.setValues(values);
// need to store node state
nodeState.addPropertyName(name);
if (nodeState.getStatus() == ItemState.STATUS_EXISTING) {
nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
}
return propState;
}
}
use of org.apache.jackrabbit.core.state.PropertyState in project jackrabbit by apache.
the class NodeStateEx method getProperties.
/**
* Returns the properties of this node
*
* @return the properties of this node
* @throws ItemStateException if an error occurs
*/
public PropertyState[] getProperties() throws ItemStateException {
Set<Name> set = nodeState.getPropertyNames();
PropertyState[] props = new PropertyState[set.size()];
int i = 0;
for (Name propName : set) {
PropertyId propId = new PropertyId(nodeState.getNodeId(), propName);
props[i++] = (PropertyState) stateMgr.getItemState(propId);
}
return props;
}
use of org.apache.jackrabbit.core.state.PropertyState 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);
}
Aggregations