* (for adding/removing child entries of a node), in
* Node.addMixin/removeMixin (for mixin changes on nodes)
* and in Property.setValue (for properties to be modified).
*/
AccessManager accessMgr = session.getAccessManager();
// walk through list of dirty transient items and validate each
while (dirtyIter.hasNext()) {
ItemState itemState = (ItemState) dirtyIter.next();
if (itemState.getStatus() != ItemState.STATUS_NEW) {
// transient item is not 'new', therefore it has to be 'modified'
// check WRITE permission
ItemId id = itemState.getId();
if (!accessMgr.isGranted(id, AccessManager.WRITE)) {
String msg = itemMgr.safeGetJCRPath(id)
+ ": not allowed to modify item";
log.debug(msg);
throw new AccessDeniedException(msg);
}
}
if (itemState.isNode()) {
// the transient item is a node
NodeState nodeState = (NodeState) itemState;
ItemId id = nodeState.getNodeId();
NodeImpl node = (NodeImpl) itemMgr.getItem(id);
NodeDefinition def = node.getDefinition();
// primary type
NodeTypeImpl pnt = (NodeTypeImpl) node.getPrimaryNodeType();
// effective node type (primary type incl. mixins)
EffectiveNodeType ent = node.getEffectiveNodeType();
/**
* if the transient node was added (i.e. if it is 'new'),
* check its node's node type against the required node type
* in its definition
*/
if (nodeState.getStatus() == ItemState.STATUS_NEW) {
NodeType[] nta = def.getRequiredPrimaryTypes();
for (int i = 0; i < nta.length; i++) {
NodeTypeImpl ntReq = (NodeTypeImpl) nta[i];
if (!(pnt.getQName().equals(ntReq.getQName())
|| pnt.isDerivedFrom(ntReq.getQName()))) {
/**
* the transient node's primary node type does not
* satisfy the 'required primary types' constraint
*/
String msg = node.safeGetJCRPath()
+ " must be of node type " + ntReq.getName();
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
}
// mandatory child properties
PropDef[] pda = ent.getMandatoryPropDefs();
for (int i = 0; i < pda.length; i++) {
PropDef pd = pda[i];
if (pd.getDeclaringNodeType().equals(QName.MIX_VERSIONABLE)) {
/**
* todo FIXME workaround for mix:versionable:
* the mandatory properties are initialized at a
* later stage and might not exist yet
*/
continue;
}
if (!nodeState.hasPropertyName(pd.getName())) {
String msg = node.safeGetJCRPath()
+ ": mandatory property " + pd.getName()
+ " does not exist";
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
// mandatory child nodes
NodeDef[] cnda = ent.getMandatoryNodeDefs();
for (int i = 0; i < cnda.length; i++) {
NodeDef cnd = cnda[i];
if (!nodeState.hasChildNodeEntry(cnd.getName())) {
String msg = node.safeGetJCRPath()
+ ": mandatory child node " + cnd.getName()
+ " does not exist";
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
} else {
// the transient item is a property
PropertyState propState = (PropertyState) itemState;
ItemId propId = propState.getPropertyId();
PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propId);
PropertyDefinitionImpl def =
(PropertyDefinitionImpl) prop.getDefinition();
/**
* check value constraints
* (no need to check value constraints of protected properties
* as those are set by the implementation only, i.e. they
* cannot be set by the user through the api)
*/
if (!def.isProtected()) {
String[] constraints = def.getValueConstraints();
if (constraints != null) {
InternalValue[] values = propState.getValues();
try {
EffectiveNodeType.checkSetPropertyValueConstraints(
def.unwrap(), values);
} catch (RepositoryException e) {
// repack exception for providing verboser error message
String msg = prop.safeGetJCRPath() + ": " + e.getMessage();
log.debug(msg);
throw new ConstraintViolationException(msg);
}
/**
* need to manually check REFERENCE value constraints
* as this requires a session (target node needs to
* be checked)
*/
if (constraints.length > 0
&& def.getRequiredType() == PropertyType.REFERENCE) {
for (int i = 0; i < values.length; i++) {
boolean satisfied = false;
try {
UUID targetUUID = (UUID) values[i].internalValue();
Node targetNode = session.getNodeByUUID(targetUUID);
/**
* constraints are OR-ed, i.e. at least one
* has to be satisfied
*/
for (int j = 0; j < constraints.length; j++) {
/**
* a REFERENCE value constraint specifies
* the name of the required node type of
* the target node
*/
String ntName = constraints[j];
if (targetNode.isNodeType(ntName)) {
satisfied = true;
break;
}
}
} catch (RepositoryException re) {
String msg = prop.safeGetJCRPath()
+ ": failed to check REFERENCE value constraint";
log.debug(msg);
throw new ConstraintViolationException(msg, re);
}
if (!satisfied) {
String msg = prop.safeGetJCRPath()
+ ": does not satisfy the value constraint "
+ constraints[0]; // just report the 1st
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
}
}
}
/**
* no need to check the protected flag as this is checked
* in PropertyImpl.setValue(Value)
*/
}
}
// walk through list of removed transient items and check REMOVE permission
while (removedIter.hasNext()) {
ItemState itemState = (ItemState) removedIter.next();
ItemId id = itemState.getId();
// check REMOVE permission
if (!accessMgr.isGranted(id, AccessManager.REMOVE)) {
String msg = itemMgr.safeGetJCRPath(id)
+ ": not allowed to remove item";
log.debug(msg);
throw new AccessDeniedException(msg);
}