Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 85284 invoked from network); 28 Jan 2009 09:51:21 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 28 Jan 2009 09:51:21 -0000 Received: (qmail 75449 invoked by uid 500); 28 Jan 2009 09:51:21 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 75359 invoked by uid 500); 28 Jan 2009 09:51:21 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 75350 invoked by uid 99); 28 Jan 2009 09:51:20 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 28 Jan 2009 01:51:20 -0800 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 28 Jan 2009 09:51:13 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 579002388A23; Wed, 28 Jan 2009 09:50:53 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r738422 [1/3] - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/lock/ main/java/org/apache/jackrabbit/core/retention/ main/java/org/apache/jackrabbit/core/security/authori... Date: Wed, 28 Jan 2009 09:50:51 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090128095053.579002388A23@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: angela Date: Wed Jan 28 09:50:50 2009 New Revision: 738422 URL: http://svn.apache.org/viewvc?rev=738422&view=rev Log: JCR-1589: JSR 283 Retention & Hold Management (work in progress) JCR-1957: Move common validation checks to a single place Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java (with props) jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java (with props) jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionPolicyImpl.java (with props) jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java (with props) jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/RetentionPolicyEffectTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/RetentionPolicyTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/TestAll.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/RetentionRegistryImplTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/retention/ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/retention/HoldTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/retention/RetentionPolicyTest.java (with props) jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/retention/TestAll.java (with props) Removed: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SecurityItemModifier.java Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemValidator.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/SessionLockManager.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionManagerImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/SessionRemoveItemTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/AbstractRetentionTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/security/RSessionAccessControlTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/TestAll.java jackrabbit/trunk/jackrabbit-core/src/test/resources/repositoryStubImpl.properties Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java Wed Jan 28 09:50:50 2009 @@ -81,45 +81,11 @@ protected static final int CLONE_REMOVE_EXISTING = 2; /** - * option for {@link #checkAddNode} and - * {@link #checkRemoveNode} methods:

- * check access rights - */ - public static final int CHECK_ACCESS = 1; - /** - * option for {@link #checkAddNode} and - * {@link #checkRemoveNode} methods:

- * check lock status - */ - public static final int CHECK_LOCK = 2; - /** - * option for {@link #checkAddNode} and - * {@link #checkRemoveNode} methods:

- * check checked-out status - */ - public static final int CHECK_VERSIONING = 4; - /** - * option for {@link #checkAddNode} and - * {@link #checkRemoveNode} methods:

- * check constraints defined in node type - */ - public static final int CHECK_CONSTRAINTS = 16; - /** - * option for {@link #checkRemoveNode} method:

- * check that target node is not being referenced - */ - public static final int CHECK_REFERENCES = 8; - - /** * wrapped item state manager */ protected final UpdatableItemStateManager stateMgr; /** - * lock manager used for checking locking status - */ - protected final LockManager lockMgr; - /** - * current session used for checking access rights and locking status + * current session used for checking access rights */ protected final SessionImpl session; @@ -131,15 +97,15 @@ * @param lockMgr lock manager * @param session current session * @param hierMgr hierarchy manager + * @throws RepositoryException */ public BatchedItemOperations(UpdatableItemStateManager stateMgr, NodeTypeRegistry ntReg, LockManager lockMgr, SessionImpl session, - HierarchyManager hierMgr) { - super(ntReg, hierMgr, session); + HierarchyManager hierMgr) throws RepositoryException { + super(ntReg, hierMgr, session, lockMgr, session.getAccessManager(), session.getRetentionRegistry()); this.stateMgr = stateMgr; - this.lockMgr = lockMgr; this.session = session; } @@ -301,7 +267,7 @@ // 2. check access rights, lock status, node type constraints, etc. checkAddNode(destParentState, destName, srcState.getNodeTypeName(), CHECK_ACCESS | CHECK_LOCK - | CHECK_VERSIONING | CHECK_CONSTRAINTS); + | CHECK_VERSIONING | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION); // 3. verify that source has mixin mix:shareable if (!isShareable(srcState)) { @@ -435,7 +401,7 @@ checkAddNode(destParentState, destName.getName(), srcState.getNodeTypeName(), CHECK_ACCESS | CHECK_LOCK - | CHECK_VERSIONING | CHECK_CONSTRAINTS); + | CHECK_VERSIONING | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION); // check read access right on source node using source access manager try { if (!srcAccessMgr.isGranted(srcPath, Permission.READ)) { @@ -580,10 +546,11 @@ // 2. check if target state can be removed from old/added to new parent checkRemoveNode(target, srcParent.getNodeId(), - CHECK_ACCESS | CHECK_LOCK | CHECK_VERSIONING | CHECK_CONSTRAINTS); + CHECK_ACCESS | CHECK_LOCK | CHECK_VERSIONING | CHECK_CONSTRAINTS | + CHECK_HOLD | CHECK_RETENTION); checkAddNode(destParent, destName.getName(), target.getNodeTypeName(), CHECK_ACCESS | CHECK_LOCK - | CHECK_VERSIONING | CHECK_CONSTRAINTS); + | CHECK_VERSIONING | CHECK_CONSTRAINTS | CHECK_HOLD | CHECK_RETENTION); // 3. do move operation (modify and store affected states) boolean renameOnly = srcParent.getNodeId().equals(destParent.getNodeId()); @@ -670,7 +637,7 @@ // 2. check if target state can be removed from parent checkRemoveNode(target, parentId, CHECK_ACCESS | CHECK_LOCK | CHECK_VERSIONING - | CHECK_CONSTRAINTS | CHECK_REFERENCES); + | CHECK_CONSTRAINTS | CHECK_REFERENCES | CHECK_HOLD | CHECK_RETENTION); // 3. do remove operation removeNodeState(target); @@ -697,7 +664,8 @@ * parent node is checked-out *

  • {@link #CHECK_CONSTRAINTS}: * make sure no node type constraints would be violated
  • - *
  • {@link #CHECK_REFERENCES}
  • + *
  • {@link #CHECK_HOLD}: check for effective holds preventing the add operation
  • + *
  • {@link #CHECK_RETENTION}: check for effective retention policy preventing the add operation
  • * * @throws ConstraintViolationException * @throws AccessDeniedException @@ -732,7 +700,6 @@ // 3. access rights if ((options & CHECK_ACCESS) == CHECK_ACCESS) { - AccessManager accessMgr = session.getAccessManager(); // make sure current session is granted read access on parent node if (!accessMgr.isGranted(parentPath, Permission.READ)) { throw new ItemNotFoundException(safeGetJCRPath(parentState.getNodeId())); @@ -796,6 +763,17 @@ } } } + + if ((options & CHECK_HOLD) == CHECK_HOLD) { + if (retentionReg.hasEffectiveHold(parentPath, false)) { + throw new RepositoryException("Unable to add node. Parent is affected by a hold."); + } + } + if ((options & CHECK_RETENTION) == CHECK_RETENTION) { + if (retentionReg.hasEffectiveRetention(parentPath, false)) { + throw new RepositoryException("Unable to add node. Parent is affected by a retention."); + } + } } /** @@ -816,6 +794,8 @@ * make sure no node type constraints would be violated *
  • {@link #CHECK_REFERENCES}: * make sure no references exist on target node
  • + *
  • {@link #CHECK_HOLD}: check for effective holds preventing the add operation
  • + *
  • {@link #CHECK_RETENTION}
  • * * @throws ConstraintViolationException * @throws AccessDeniedException @@ -852,6 +832,8 @@ * make sure no node type constraints would be violated *
  • {@link #CHECK_REFERENCES}: * make sure no references exist on target node
  • + *
  • {@link #CHECK_HOLD}: check for effective holds preventing the add operation
  • + *
  • {@link #CHECK_RETENTION}: check for effective retention policy preventing the add operation
  • * * @throws ConstraintViolationException * @throws AccessDeniedException @@ -892,7 +874,6 @@ // 3. access rights if ((options & CHECK_ACCESS) == CHECK_ACCESS) { - AccessManager accessMgr = session.getAccessManager(); try { // make sure current session is granted read access on parent node if (!accessMgr.isGranted(targetPath, Permission.READ)) { @@ -952,6 +933,17 @@ } } } + + if ((options & CHECK_HOLD) == CHECK_HOLD) { + if (retentionReg.hasEffectiveHold(targetPath, true)) { + throw new RepositoryException("Unable to perform removal. Node is affected by a hold."); + } + } + if ((options & CHECK_RETENTION) == CHECK_RETENTION) { + if (retentionReg.hasEffectiveRetention(targetPath, true)) { + throw new RepositoryException("Unable to perform removal. Node is affected by a retention."); + } + } } /** @@ -963,6 +955,7 @@ *
  • the node must not be locked by another session
  • *
  • the node must not be checked-in
  • *
  • the node must not be protected
  • + *
  • the node must not be affected by a hold or a retention policy
  • * * * @param nodePath path of node to check @@ -988,7 +981,6 @@ NodeState node = getNodeState(nodePath); // access rights - AccessManager accessMgr = session.getAccessManager(); // make sure current session is granted read access on node if (!accessMgr.isGranted(nodePath, Permission.READ)) { throw new PathNotFoundException(safeGetJCRPath(node.getNodeId())); @@ -1007,6 +999,13 @@ // versioning status verifyCheckedOut(nodePath); + + if (retentionReg.hasEffectiveHold(nodePath, false)) { + throw new RepositoryException("Unable to write. Node is affected by a hold."); + } + if (retentionReg.hasEffectiveRetention(nodePath, false)) { + throw new RepositoryException("Unable to write. Node is affected by a retention."); + } } /** @@ -1027,7 +1026,6 @@ public void verifyCanRead(Path nodePath) throws PathNotFoundException, RepositoryException { // access rights - AccessManager accessMgr = session.getAccessManager(); // make sure current session is granted read access on node if (!accessMgr.isGranted(nodePath, Permission.READ)) { throw new PathNotFoundException(safeGetJCRPath(nodePath)); @@ -1599,13 +1597,17 @@ try { NodeState nodeState = (NodeState) stateMgr.getItemState(nodeId); // check if child node can be removed - // (access rights, locking & versioning status); + // (access rights, locking & versioning status as well + // as retention and hold); // referential integrity (references) is checked // on commit checkRemoveNode(nodeState, targetState.getNodeId(), CHECK_ACCESS | CHECK_LOCK - | CHECK_VERSIONING); + | CHECK_VERSIONING + | CHECK_HOLD + | CHECK_RETENTION + ); // remove child node recursiveRemoveNodeState(nodeState); } catch (ItemStateException ise) { @@ -1728,12 +1730,14 @@ // check if existing can be removed // (access rights, locking & versioning status, - // node type constraints) + // node type constraints and retention/hold) checkRemoveNode(existingState, CHECK_ACCESS | CHECK_LOCK | CHECK_VERSIONING - | CHECK_CONSTRAINTS); + | CHECK_CONSTRAINTS + | CHECK_HOLD + | CHECK_RETENTION); // do remove existing removeNodeState(existingState); } Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java Wed Jan 28 09:50:50 2009 @@ -53,7 +53,6 @@ import javax.jcr.ItemVisitor; import javax.jcr.Node; import javax.jcr.PathNotFoundException; -import javax.jcr.Property; import javax.jcr.PropertyType; import javax.jcr.ReferentialIntegrityException; import javax.jcr.RepositoryException; @@ -814,32 +813,16 @@ } NodeImpl parentNode = (NodeImpl) getParent(); - if (!noChecks) { - // check if protected - ItemDefinition definition; - if (isNode()) { - definition = ((Node) this).getDefinition(); - } else { - definition = ((Property) this).getDefinition(); - } - if (definition.isProtected()) { - throw new ConstraintViolationException( - "Cannot remove a protected item: " + this); - } - - // verify that parent node is checked-out and not protected - if (!parentNode.internalIsCheckedOut()) { - throw new VersionException( - "Cannot remove a child of a checked-in node: " + this); - } - if (parentNode.getDefinition().isProtected()) { - throw new ConstraintViolationException( - "Cannot remove a child of a protected node: " + this); - } - - // check lock status - parentNode.checkLock(); + // check if protected and not under retention/hold + int options = ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | + ItemValidator.CHECK_RETENTION; + session.getValidator().checkRemove(this, options, Permission.NONE); + + // parent node: make sure it is checked-out and not protected nor locked. + options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | + ItemValidator.CHECK_CONSTRAINTS; + session.getValidator().checkModify(parentNode, options, Permission.NONE); } // delegate the removal of the child item to the parent node Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemValidator.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemValidator.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemValidator.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemValidator.java Wed Jan 28 09:50:50 2009 @@ -24,6 +24,10 @@ import org.apache.jackrabbit.core.state.NodeState; import org.apache.jackrabbit.core.state.PropertyState; import org.apache.jackrabbit.core.value.InternalValue; +import org.apache.jackrabbit.core.security.authorization.Permission; +import org.apache.jackrabbit.core.security.AccessManager; +import org.apache.jackrabbit.core.lock.LockManager; +import org.apache.jackrabbit.core.retention.RetentionRegistry; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.conversion.PathResolver; import org.apache.jackrabbit.spi.Name; @@ -34,7 +38,13 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.ItemNotFoundException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.lock.LockException; +import javax.jcr.version.VersionException; import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.ItemDefinition; /** * Utility class for validating an item against constraints @@ -43,6 +53,50 @@ public class ItemValidator { /** + * check access permissions + */ + public static final int CHECK_ACCESS = 1; + + /** + * option to check lock status + */ + public static final int CHECK_LOCK = 2; + /** + * option to check checked-out status + */ + public static final int CHECK_VERSIONING = 4; + + /** + * check for referential integrity upon removal + */ + public static final int CHECK_REFERENCES = 8; + + /** + * option to check if the item is protected by it's nt definition + */ + public static final int CHECK_CONSTRAINTS = 16; + + /** + * option to check for pending changes on the session + */ + public static final int CHECK_PENDING_CHANGES = 32; + + /** + * option to check for pending changes on the specified node + */ + public static final int CHECK_PENDING_CHANGES_ON_NODE = 64; + + /** + * option to check for effective holds + */ + public static final int CHECK_HOLD = 128; + + /** + * option to check for effective retention policies + */ + public static final int CHECK_RETENTION = 256; + + /** * Logger instance for this class */ private static Logger log = LoggerFactory.getLogger(ItemValidator.class); @@ -66,18 +120,49 @@ protected final PathResolver resolver; /** + * + */ + protected final LockManager lockMgr; + + protected final AccessManager accessMgr; + + protected final RetentionRegistry retentionReg; + + /** * Creates a new ItemValidator instance. * * @param ntReg node type registry * @param hierMgr hierarchy manager - * @param resolver path resolver + * @param session session */ public ItemValidator(NodeTypeRegistry ntReg, HierarchyManager hierMgr, - PathResolver resolver) { + SessionImpl session) throws RepositoryException { + this(ntReg, hierMgr, session, session.getLockManager(), session.getAccessManager(), session.getRetentionRegistry()); + } + + /** + * Creates a new ItemValidator instance. + * + * @param ntReg node type registry + * @param hierMgr hierarchy manager + * @param resolver resolver + * @param lockMgr lockMgr + * @param accessMgr accessMgr + * @param retentionReg + */ + public ItemValidator(NodeTypeRegistry ntReg, + HierarchyManager hierMgr, + PathResolver resolver, + LockManager lockMgr, + AccessManager accessMgr, + RetentionRegistry retentionReg) { this.ntReg = ntReg; this.hierMgr = hierMgr; this.resolver = resolver; + this.lockMgr = lockMgr; + this.accessMgr = accessMgr; + this.retentionReg = retentionReg; } /** @@ -178,6 +263,161 @@ EffectiveNodeType.checkSetPropertyValueConstraints(def, values); } + public void checkModify(ItemImpl item, int options, int permissions) throws RepositoryException { + checkCondition(item, options, permissions, false); + } + + public void checkRemove(ItemImpl item, int options, int permissions) throws RepositoryException { + checkCondition(item, options, permissions, true); + } + + private void checkCondition(ItemImpl item, int options, int permissions, boolean isRemoval) throws RepositoryException { + if ((options & CHECK_PENDING_CHANGES) == CHECK_PENDING_CHANGES) { + if (item.getSession().hasPendingChanges()) { + String msg = "Unable to perform operation. Session has pending changes."; + log.debug(msg); + throw new InvalidItemStateException(msg); + } + } + if ((options & CHECK_PENDING_CHANGES_ON_NODE) == CHECK_PENDING_CHANGES_ON_NODE) { + if (item.isNode() && ((NodeImpl) item).hasPendingChanges()) { + String msg = "Unable to perform operation. Session has pending changes."; + log.debug(msg); + throw new InvalidItemStateException(msg); + } + } + if ((options & CHECK_CONSTRAINTS) == CHECK_CONSTRAINTS) { + if (isProtected(item)) { + String msg = "Unable to perform operation. Node is protected."; + log.debug(msg); + throw new ConstraintViolationException(msg); + } + } + if ((options & CHECK_VERSIONING) == CHECK_VERSIONING) { + NodeImpl node = (item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent(); + if (!node.internalIsCheckedOut()) { + String msg = "Unable to perform operation. Node is checked-in."; + log.debug(msg); + throw new VersionException(msg); + } + } + if ((options & CHECK_LOCK) == CHECK_LOCK) { + checkLock(item); + } + + if (permissions > Permission.NONE) { + Path path = item.getPrimaryPath(); + accessMgr.checkPermission(path, permissions); + } + if ((options & CHECK_HOLD) == CHECK_HOLD) { + if (hasHold(item, isRemoval)) { + throw new RepositoryException("Unable to perform operation. Node is affected by a hold."); + } + } + if ((options & CHECK_RETENTION) == CHECK_RETENTION) { + if (hasRetention(item, isRemoval)) { + throw new RepositoryException("Unable to perform operation. Node is affected by a retention."); + } + } + } + + public boolean canModify(ItemImpl item, int options, int permissions) throws RepositoryException { + return hasCondition(item, options, permissions, false); + } + + private boolean hasCondition(ItemImpl item, int options, int permissions, boolean isRemoval) throws RepositoryException { + if ((options & CHECK_PENDING_CHANGES) == CHECK_PENDING_CHANGES) { + if (item.getSession().hasPendingChanges()) { + return false; + } + } + if ((options & CHECK_PENDING_CHANGES_ON_NODE) == CHECK_PENDING_CHANGES_ON_NODE) { + if (item.isNode() && ((NodeImpl) item).hasPendingChanges()) { + return false; + } + } + if ((options & CHECK_CONSTRAINTS) == CHECK_CONSTRAINTS) { + if (isProtected(item)) { + return false; + } + } + if ((options & CHECK_VERSIONING) == CHECK_VERSIONING) { + NodeImpl node = (item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent(); + if (!node.internalIsCheckedOut()) { + return false; + } + } + if ((options & CHECK_LOCK) == CHECK_LOCK) { + try { + checkLock(item); + } catch (LockException e) { + return false; + } + } + if (permissions > Permission.NONE) { + Path path = item.getPrimaryPath(); + if (!accessMgr.isGranted(item.getPrimaryPath(), permissions)) { + return false; + } + } + if ((options & CHECK_HOLD) == CHECK_HOLD) { + if (hasHold(item, isRemoval)) { + return false; + } + } + if ((options & CHECK_RETENTION) == CHECK_RETENTION) { + if (hasRetention(item, isRemoval)) { + return false; + } + } + return true; + } + + private void checkLock(ItemImpl item) throws LockException, RepositoryException { + if (item.isNew()) { + // a new item needs no check + return; + } + NodeImpl node = (item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent(); + lockMgr.checkLock(node); + } + + private boolean isProtected(ItemImpl item) throws RepositoryException { + ItemDefinition def; + if (item.isNode()) { + def = ((Node) item).getDefinition(); + } else { + def = ((Property) item).getDefinition(); + } + return def.isProtected(); + } + + private boolean hasHold(ItemImpl item, boolean isRemoval) throws RepositoryException { + if (item.isNew()) { + return false; + } + Path path = item.getPrimaryPath(); + if (!item.isNode()) { + path = path.getAncestor(1); + } + boolean checkParent = (item.isNode() && isRemoval); + return retentionReg.hasEffectiveHold(path, checkParent); + } + + private boolean hasRetention(ItemImpl item, boolean isRemoval) throws RepositoryException { + if (item.isNew()) { + return false; + } + Path path = item.getPrimaryPath(); + if (!item.isNode()) { + path = path.getAncestor(1); + } + boolean checkParent = (item.isNode() && isRemoval); + return retentionReg.hasEffectiveRetention(path, checkParent); + } + + + //-------------------------------------------------< misc. helper methods > /** * Helper method that builds the effective (i.e. merged and resolved) Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Wed Jan 28 09:50:50 2009 @@ -45,7 +45,6 @@ import org.apache.jackrabbit.core.version.VersionImpl; import org.apache.jackrabbit.core.version.VersionSelector; import org.apache.jackrabbit.core.security.authorization.Permission; -import org.apache.jackrabbit.core.security.AccessManager; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException; @@ -722,15 +721,9 @@ throw new PathNotFoundException(relPath); } - // make sure that parent node is checked-out - if (!parentNode.internalIsCheckedOut()) { - String msg = this + ": cannot add a child to a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check lock status - parentNode.checkLock(); + // make sure that parent node is checked-out and not locked + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING; + session.getValidator().checkModify(parentNode, options, Permission.NONE); // delegate the creation of the child node to the parent node return parentNode.internalAddChildNode(nodeName, nodeType, id); @@ -794,13 +787,10 @@ } } - // check protected flag of parent (i.e. this) node - final NodeDefinition definition = data.getNodeDefinition(); - if (definition.isProtected()) { - String msg = this + ": cannot add a child to a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } + // check protected flag of parent (i.e. this) node and retention/hold + int options = ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | + ItemValidator.CHECK_RETENTION; + session.getValidator().checkModify(this, options, Permission.NONE); // now do create the child node return createChildNode(nodeName, def, nodeType, id); @@ -1007,33 +997,16 @@ // check state of this instance sanityCheck(); - // make sure this node is checked-out - if (!internalIsCheckedOut()) { - String msg = this + ": cannot add a mixin node type to a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check protected flag - final NodeDefinition definition = data.getNodeDefinition(); - if (definition.isProtected()) { - String msg = this + ": cannot add a mixin node type to a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - - // check lock status - checkLock(); - // check permissions - Path p = getPrimaryPath(); - AccessManager acMgr = session.getAccessManager(); - acMgr.checkPermission(p, Permission.NODE_TYPE_MNGMT); + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | + ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD; + int permissions = Permission.NODE_TYPE_MNGMT; // special handling of mix:versionable. since adding the mixin alters // the version storage jcr:versionManagement privilege is required // in addition. if (NameConstants.MIX_VERSIONABLE.equals(mixinName)) { - acMgr.checkPermission(p, Permission.VERSION_MNGMT); + permissions |= Permission.VERSION_MNGMT; } + session.getValidator().checkModify(this, options, permissions); NodeTypeManagerImpl ntMgr = session.getNodeTypeManager(); NodeTypeImpl mixin = ntMgr.getNodeType(mixinName); @@ -1129,27 +1102,10 @@ // check state of this instance sanityCheck(); - // make sure this node is checked-out - if (!internalIsCheckedOut()) { - String msg = - this + ": cannot remove a mixin node type from a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check protected flag - NodeDefinition definition = data.getNodeDefinition(); - if (definition.isProtected()) { - String msg = - this + ": cannot remove a mixin node type from a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - - // check lock status - checkLock(); - // check permission - session.getAccessManager().checkPermission(getPrimaryPath(), Permission.NODE_TYPE_MNGMT); + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | + ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD; + int permissions = Permission.NODE_TYPE_MNGMT; + session.getValidator().checkModify(this, options, permissions); // check if mixin is assigned final NodeState state = data.getNodeState(); @@ -1325,15 +1281,9 @@ */ protected void checkSetProperty() throws VersionException, LockException, RepositoryException { - // make sure this node is checked-out - if (!internalIsCheckedOut()) { - String msg = this + ": cannot set property of a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check lock status - checkLock(); + // make sure this node is checked-out and is not locked + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING; + session.getValidator().checkModify(this, options, Permission.NONE); } /** @@ -1590,15 +1540,9 @@ // check state of this instance sanityCheck(); - // make sure this node is checked-out - if (!internalIsCheckedOut()) { - String msg = this + ": cannot add node to a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check lock status - checkLock(); + // make sure this node is checked-out and not locked by another session. + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING; + session.getValidator().checkModify(this, options, Permission.NONE); NodeTypeImpl nt = null; if (nodeTypeName != null) { @@ -1827,25 +1771,10 @@ this + " has no child node with name " + name); } - // make sure this node is checked-out - if (!internalIsCheckedOut()) { - String msg = - this + ": cannot change child node ordering of a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check protected flag - final NodeDefinition definition = data.getNodeDefinition(); - if (definition.isProtected()) { - String msg = - this + ": cannot change child node ordering of a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - - // check lock status - checkLock(); + // make sure this node is checked-out and neither protected nor locked + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | + ItemValidator.CHECK_CONSTRAINTS; + session.getValidator().checkModify(this, options, Permission.NONE); ArrayList list = new ArrayList(data.getNodeState().getChildNodeEntries()); int srcInd = -1, destInd = -1; @@ -2011,16 +1940,12 @@ } // (1) make sure that parent node is checked-out - if (!internalIsCheckedOut()) { - String msg = this + ": cannot add a child to a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - // (2) check lock status - checkLock(); + // (3) check protected flag of parent (i.e. this) node + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_CONSTRAINTS; + session.getValidator().checkModify(this, options, Permission.NONE); - // (3) check for name collisions + // (4) check for name collisions NodeDefinitionImpl def; try { def = getApplicableChildNodeDefinition(name, null); @@ -2044,14 +1969,6 @@ } } - // (4) check protected flag of parent (i.e. this) node - final NodeDefinition definition = data.getNodeDefinition(); - if (definition.isProtected()) { - String msg = this + ": cannot add a child to a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - // (5) do clone operation NodeId parentId = getNodeId(); src.addShareParent(parentId); @@ -2851,49 +2768,23 @@ // check state of this instance sanityCheck(); - // check checked-out status - if (!internalIsCheckedOut()) { - return false; - } - - // check protected flag - if (data.getNodeDefinition().isProtected()) { - return false; - } - - // check lock status - try { - checkLock(); - } catch (LockException le) { - return false; - } - - Name ntName; - try { - ntName = session.getQName(mixinName); - } catch (NameException e) { - throw new RepositoryException( - "invalid mixin type name: " + mixinName, e); - } - - // check permissions - Path p = getPrimaryPath(); - AccessManager acMgr = session.getAccessManager(); - if (!acMgr.isGranted(p, Permission.NODE_TYPE_MNGMT)) { + Name ntName = session.getQName(mixinName); + NodeTypeManagerImpl ntMgr = session.getNodeTypeManager(); + NodeTypeImpl mixin = ntMgr.getNodeType(ntName); + if (!mixin.isMixin()) { return false; } + + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | + ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD; + int permissions = Permission.NODE_TYPE_MNGMT; // special handling of mix:versionable. since adding the mixin alters // the version storage jcr:versionManagement privilege is required // in addition. if (NameConstants.MIX_VERSIONABLE.equals(ntName)) { - if (!acMgr.isGranted(p, Permission.VERSION_MNGMT)) { - return false; - } + permissions |= Permission.VERSION_MNGMT; } - - NodeTypeManagerImpl ntMgr = session.getNodeTypeManager(); - NodeTypeImpl mixin = ntMgr.getNodeType(ntName); - if (!mixin.isMixin()) { + if (!session.getValidator().canModify(this, options, permissions)) { return false; } @@ -3339,17 +3230,9 @@ return getBaseVersion(); } - // check for pending changes - if (hasPendingChanges()) { - String msg = "Unable to checkin node. Node has pending changes: " + this; - log.debug(msg); - throw new InvalidItemStateException(msg); - } - - // check lock status - checkLock(); - // check permission - session.getAccessManager().checkPermission(getPrimaryPath(), Permission.VERSION_MNGMT); + // check lock status, holds and permissions + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE; + session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT); Version v = session.getVersionManager().checkin(this); boolean success = false; @@ -3392,10 +3275,8 @@ return; } - // check lock status - checkLock(); - // check permission - session.getAccessManager().checkPermission(getPrimaryPath(), Permission.VERSION_MNGMT); + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT); boolean hasPendingChanges = hasPendingChanges(); Property[] props = new Property[2]; @@ -3487,8 +3368,8 @@ // checks sanityCheck(); - checkSessionHasPending(); - checkLock(); + int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.NONE); Version v = getVersionHistory().getVersion(versionName); DateVersionSelector gvs = new DateVersionSelector(v.getCreated()); @@ -3506,9 +3387,9 @@ // do checks sanityCheck(); - checkSessionHasPending(); checkVersionable(); - checkLock(); + int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK| ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.NONE); // check if 'own' version if (!version.getContainingHistory().isSame(getVersionHistory())) { @@ -3529,8 +3410,8 @@ // do checks sanityCheck(); - checkSessionHasPending(); - checkLock(); + int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.NONE); // if node exists, do a 'normal' restore if (hasNode(relPath)) { @@ -3578,8 +3459,8 @@ // do checks sanityCheck(); - checkSessionHasPending(); - checkLock(); + int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK| ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.NONE); Version v = getVersionHistory().getVersionByLabel(versionLabel); if (v == null) { @@ -3642,24 +3523,6 @@ } /** - * Checks if this nodes session has pending changes. - * - * @throws InvalidItemStateException if this nodes session has pending changes - * @throws RepositoryException - */ - private void checkSessionHasPending() - throws InvalidItemStateException, RepositoryException { - // check for pending changes - if (session.hasPendingChanges()) { - String msg = "Unable to perform operation. Session has pending changes."; - log.debug(msg); - throw new InvalidItemStateException(msg); - } - - - } - - /** * Returns the corresponding node in the workspace of the given session. *

    * Given a node N1 in workspace W1, its corresponding node N2 in workspace @@ -3834,28 +3697,12 @@ // check state of this instance sanityCheck(); - // check for pending changes - if (hasPendingChanges()) { - String msg = - "Unable to finish merge. Node has pending changes: " + this; - log.debug(msg); - throw new InvalidItemStateException(msg); - } - // check versionable checkVersionable(); - // check lock - checkLock(); - // check permission - session.getAccessManager().checkPermission(getPrimaryPath(), Permission.VERSION_MNGMT); - - // check if checked out - if (!internalIsCheckedOut()) { - String msg = "Unable to finish merge. Node is checked-in: " + this; - log.error(msg); - throw new VersionException(msg); - } + // check lock, permissions and checkout-status + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE | ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT); // check if version is in mergeFailed list Set failed = internalGetMergeFailed(); @@ -4089,9 +3936,7 @@ // do checks sanityCheck(); - checkSessionHasPending(); - // check permission - session.getAccessManager().checkPermission(getPrimaryPath(), Permission.VERSION_MNGMT); + session.getValidator().checkModify(this, ItemValidator.CHECK_PENDING_CHANGES, Permission.VERSION_MNGMT); // if same workspace, ignore if (srcWorkspaceName.equals(session.getWorkspace().getName())) { @@ -4151,8 +3996,9 @@ return; } - // check lock status - checkLock(); + // check lock and hold status + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.NONE); // update the properties PropertyIterator iter = getProperties(); @@ -4616,6 +4462,7 @@ * * @throws LockException if this node is locked by somebody else * @throws RepositoryException if some other error occurs + * @deprecated */ protected void checkLock() throws LockException, RepositoryException { if (isNew()) { @@ -4687,19 +4534,11 @@ // check state of this instance sanityCheck(); - // make sure this node is checked-out - if (!internalIsCheckedOut()) { - String msg = this + ": cannot set primary type of a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check protected flag - if (data.getDefinition().isProtected()) { - String msg = this + ": cannot set primary type of a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } + // 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_VERSIONING | ItemValidator.CHECK_LOCK | + ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD; + session.getValidator().checkModify(this, options, Permission.NODE_TYPE_MNGMT); final NodeState state = data.getNodeState(); if (state.getParentId() == null) { @@ -4708,20 +4547,9 @@ throw new RepositoryException(msg); } - // check lock status - checkLock(); - // check permission - session.getAccessManager().checkPermission(getPrimaryPath(), Permission.NODE_TYPE_MNGMT); - - Name ntName; - try { - ntName = session.getQName(nodeTypeName); - } catch (NameException e) { - throw new RepositoryException( - "invalid node type name: " + nodeTypeName, e); - } - + Name ntName = session.getQName(nodeTypeName); if (ntName.equals(state.getNodeTypeName())) { + log.debug("Node already has " + nodeTypeName + " as primary node type."); return; } Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java Wed Jan 28 09:50:50 2009 @@ -23,6 +23,7 @@ import org.apache.jackrabbit.core.value.InternalValue; import org.apache.jackrabbit.core.nodetype.PropDefId; import org.apache.jackrabbit.core.nodetype.PropertyDefinitionImpl; +import org.apache.jackrabbit.core.security.authorization.Permission; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.value.ValueHelper; @@ -236,36 +237,22 @@ RepositoryException { NodeImpl parent = (NodeImpl) getParent(); PropertyDefinition definition = data.getPropertyDefinition(); - - // verify that parent node is checked-out - if (!parent.internalIsCheckedOut()) { - throw new VersionException( - "Cannot set a property of a checked-in node: " + this); - } - - // check protected flag - if (definition.isProtected()) { - throw new ConstraintViolationException( - "Cannot set the value of a protected property: " + this); - } - // check multi-value flag - if (multipleValues) { - if (!definition.isMultiple()) { - throw new ValueFormatException( - "Single-valued property can not be set to" - + " an array of values: " + this); - } - } else { - if (definition.isMultiple()) { - throw new ValueFormatException( - "Multivalued property can not be set to a single" - + " value (an array of lenght one is OK): " + this); - } + if (multipleValues != definition.isMultiple()) { + String msg = (multipleValues) ? + "Single-valued property can not be set to an array of values:" : + "Multivalued property can not be set to a single value (an array of lenght one is OK): "; + throw new ValueFormatException(msg + this); } - // check lock status - parent.checkLock(); + // check protected flag and for retention/hold + int options = ItemValidator.CHECK_CONSTRAINTS; + session.getValidator().checkModify(this, options, Permission.NONE); + + // make sure the parent is checked-out and neither locked nor under retention + options = ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_LOCK | + ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION; + session.getValidator().checkModify(parent, options, Permission.NONE); } /** Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core; + +import org.apache.jackrabbit.core.nodetype.NodeDefinitionImpl; +import org.apache.jackrabbit.core.nodetype.NodeTypeImpl; +import org.apache.jackrabbit.core.security.AccessManager; +import org.apache.jackrabbit.core.security.authorization.Permission; +import org.apache.jackrabbit.core.security.authorization.acl.ACLEditor; +import org.apache.jackrabbit.core.security.user.UserManagerImpl; +import org.apache.jackrabbit.core.state.ChildNodeEntry; +import org.apache.jackrabbit.core.state.NodeState; +import org.apache.jackrabbit.core.value.InternalValue; +import org.apache.jackrabbit.core.retention.RetentionManagerImpl; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.Path; + +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ItemExistsException; +import javax.jcr.AccessDeniedException; + +/** + * SecurityItemModifier: An abstract helper class to allow classes + * of the security API residing outside of the core package to modify and remove + * protected items for security. The protected item definitions are required in + * order not to have security relevant content being changed through common + * item operations but forcing the usage of the security API. The latter asserts + * that implementation specific constraints are not violated. + */ +public abstract class ProtectedItemModifier { + + private static final int DEFAULT_PERM_CHECK = -1; + private final int permission; + + protected ProtectedItemModifier() { + this(DEFAULT_PERM_CHECK); + } + + protected ProtectedItemModifier(int permission) { + Class cl = getClass(); + if (!(cl.equals(UserManagerImpl.class) || + cl.equals(RetentionManagerImpl.class) || + cl.equals(ACLEditor.class) || + cl.equals(org.apache.jackrabbit.core.security.authorization.principalbased.ACLEditor.class))) { + throw new IllegalArgumentException("Only UserManagerImpl, RetentionManagerImpl and ACLEditor may extend from the ProtectedItemModifier"); + } + this.permission = permission; + } + + protected NodeImpl addNode(NodeImpl parentImpl, Name name, Name ntName) throws RepositoryException { + checkPermission(parentImpl, name, getPermission(true, false)); + // validation: make sure Node is not locked or checked-in. + parentImpl.checkSetProperty(); + + NodeTypeImpl nodeType = parentImpl.session.getNodeTypeManager().getNodeType(ntName); + NodeDefinitionImpl def = parentImpl.getApplicableChildNodeDefinition(name, ntName); + + // check for name collisions + // TODO: improve. copied from NodeImpl + NodeState thisState = (NodeState) parentImpl.getItemState(); + ChildNodeEntry cne = thisState.getChildNodeEntry(name, 1); + if (cne != null) { + // there's already a child node entry with that name; + // check same-name sibling setting of new node + if (!def.allowsSameNameSiblings()) { + throw new ItemExistsException(); + } + // check same-name sibling setting of existing node + NodeId newId = cne.getId(); + NodeImpl n = (NodeImpl) parentImpl.session.getItemManager().getItem(newId); + if (!n.getDefinition().allowsSameNameSiblings()) { + throw new ItemExistsException(); + } + } + + return parentImpl.createChildNode(name, def, nodeType, null); + } + + protected Property setProperty(NodeImpl parentImpl, Name name, Value value) throws RepositoryException { + return setProperty(parentImpl, name, value, false); + } + + protected Property setProperty(NodeImpl parentImpl, Name name, Value value, boolean ignorePermissions) throws RepositoryException { + if (!ignorePermissions) { + checkPermission(parentImpl, name, getPermission(false, false)); + } + // validation: make sure Node is not locked or checked-in. + parentImpl.checkSetProperty(); + InternalValue intVs = InternalValue.create(value, parentImpl.session); + return parentImpl.internalSetProperty(name, intVs); + } + + protected Property setProperty(NodeImpl parentImpl, Name name, Value[] values) throws RepositoryException { + checkPermission(parentImpl, name, getPermission(false, false)); + // validation: make sure Node is not locked or checked-in. + parentImpl.checkSetProperty(); + InternalValue[] intVs = new InternalValue[values.length]; + for (int i = 0; i < values.length; i++) { + intVs[i] = InternalValue.create(values[i], parentImpl.session); + } + return parentImpl.internalSetProperty(name, intVs); + } + + protected void removeItem(ItemImpl itemImpl) throws RepositoryException { + NodeImpl n; + if (itemImpl.isNode()) { + n = (NodeImpl) itemImpl; + } else { + n = (NodeImpl) itemImpl.getParent(); + } + checkPermission(itemImpl, getPermission(itemImpl.isNode(), true)); + // validation: make sure Node is not locked or checked-in. + n.checkSetProperty(); + itemImpl.internalRemove(true); + } + + private void checkPermission(ItemImpl item, int perm) throws RepositoryException { + if (perm > Permission.NONE) { + SessionImpl sImpl = (SessionImpl) item.getSession(); + AccessManager acMgr = sImpl.getAccessManager(); + + Path path = item.getPrimaryPath(); + acMgr.checkPermission(path, perm); + } + } + + private void checkPermission(NodeImpl node, Name childName, int perm) throws RepositoryException { + if (perm > Permission.NONE) { + SessionImpl sImpl = (SessionImpl) node.getSession(); + AccessManager acMgr = sImpl.getAccessManager(); + + boolean isGranted = acMgr.isGranted(node.getPrimaryPath(), childName, perm); + if (!isGranted) { + throw new AccessDeniedException("Permission denied."); + } + } + } + + private int getPermission(boolean isNode, boolean isRemove) { + if (permission < Permission.NONE) { + if (isNode) { + return (isRemove) ? Permission.REMOVE_NODE : Permission.ADD_NODE; + } else { + return (isRemove) ? Permission.REMOVE_PROPERTY : Permission.SET_PROPERTY; + } + } else { + return permission; + } + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Wed Jan 28 09:50:50 2009 @@ -67,6 +67,8 @@ import org.apache.jackrabbit.core.version.VersionManager; import org.apache.jackrabbit.core.version.VersionManagerImpl; import org.apache.jackrabbit.core.xml.ClonedInputSource; +import org.apache.jackrabbit.core.retention.RetentionRegistry; +import org.apache.jackrabbit.core.retention.RetentionRegistryImpl; import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver; import org.apache.jackrabbit.spi.commons.namespace.RegistryNamespaceResolver; @@ -940,6 +942,21 @@ } /** + * Returns the {@link org.apache.jackrabbit.core.retention.RetentionRegistry} for the workspace with name + * workspaceName + * + * @param workspaceName workspace name + * @return RetentionEvaluator for the workspace + * @throws NoSuchWorkspaceException if such a workspace does not exist + * @throws RepositoryException if some other error occurs + */ + RetentionRegistry getRetentionRegistry(String workspaceName) throws NoSuchWorkspaceException, RepositoryException { + // check sanity of this instance + sanityCheck(); + return getWorkspaceInfo(workspaceName).getRetentionRegistry(); + } + + /** * Returns the {@link SystemSession} for the workspace with name * workspaceName * @@ -1573,6 +1590,12 @@ private LockManagerImpl lockMgr; /** + * internal manager for evaluation of effective retention policies + * and holds + */ + private RetentionRegistryImpl retentionReg; + + /** * flag indicating whether this instance has been initialized. */ private boolean initialized; @@ -1798,6 +1821,24 @@ } /** + * Return manager used for evaluating effect retention and holds. + * + * @return + * @throws RepositoryException + */ + protected RetentionRegistry getRetentionRegistry() throws RepositoryException { + if (!isInitialized()) { + throw new IllegalStateException("workspace '" + getName() + "' not initialized"); + } + synchronized (this) { + if (retentionReg == null) { + retentionReg = new RetentionRegistryImpl(getSystemSession(), fs); + } + return retentionReg; + } + } + + /** * Returns the system session for this workspace. * * @return the system session for this workspace @@ -2043,12 +2084,17 @@ lockMgr = null; } + // close retention registry + if (retentionReg != null) { + retentionReg.close(); + retentionReg = null; + } + // close workspace file system try { fs.close(); } catch (FileSystemException fse) { - log.error("error while closing file system of workspace " - + config.getName(), fse); + log.error("error while closing file system of workspace " + config.getName(), fse); } fs = null; } Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java Wed Jan 28 09:50:50 2009 @@ -49,6 +49,7 @@ import org.apache.jackrabbit.core.xml.ImportHandler; import org.apache.jackrabbit.core.xml.SessionImporter; import org.apache.jackrabbit.core.retention.RetentionManagerImpl; +import org.apache.jackrabbit.core.retention.RetentionRegistry; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; @@ -228,6 +229,12 @@ private RetentionManager retentionManager; /** + * Internal helper class for common validation checks (lock status, checkout + * status, protection etc. etc.) + */ + private ItemValidator validator; + + /** * Protected constructor. * * @param rep @@ -372,6 +379,17 @@ } /** + * @return ItemValidator instance for this session. + * @throws RepositoryException If an error occurs. + */ + public synchronized ItemValidator getValidator() throws RepositoryException { + if (validator == null) { + validator = new ItemValidator(rep.getNodeTypeRegistry(), getHierarchyManager(), this); + } + return validator; + } + + /** * Returns the Subject associated with this session. * * @return the Subject associated with this session @@ -462,6 +480,18 @@ return versionMgr; } + + /** + * Returns the internal retention manager used for evaluation of effective + * retention policies and holds. + * + * @return internal retention manager + * @throws RepositoryException + */ + protected RetentionRegistry getRetentionRegistry() throws RepositoryException { + return wsp.getRetentionRegistry(); + } + /** * Retrieves the referenceable node with the given UUID. * @@ -867,28 +897,6 @@ } /** - * Determines if there are pending unsaved changes either on the passed - * item or on any item in it's subtree. - * - * @param item Item start of the subtree to be tested for pending changes. - * @return true if there are pending unsaved changes, - * false otherwise. - * @throws RepositoryException if an error occurred - */ - public boolean hasPendingChanges(Item item) throws RepositoryException { - if (!(item instanceof ItemImpl) || ((ItemImpl) item).session != this) { - throw new IllegalArgumentException(); - } - sanityCheck(); - ItemImpl itemImpl = (ItemImpl) item; - if (itemImpl.isTransient()) { - return true; - } else { - return item.isNode() && ((NodeImpl) item).hasPendingChanges(); - } - } - - /** * {@inheritDoc} */ public void move(String srcAbsPath, String destAbsPath) @@ -961,20 +969,7 @@ throw new RepositoryException(msg); } - // verify that both source and destination parent nodes are checked-out - if (!srcParentNode.internalIsCheckedOut()) { - String msg = srcAbsPath + ": cannot move a child of a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - if (!destParentNode.internalIsCheckedOut()) { - String msg = destAbsPath + ": cannot move a target to a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - // check for name collisions - NodeImpl existing = null; try { existing = getItemManager().getNode(destPath); @@ -991,8 +986,15 @@ // no name collision, fall through } - // check constraints + // verify for both source and destination parent nodes that + // - they are checked-out + // - are not protected neither by node type constraints nor by retention/hold + int options = ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_LOCK | + ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION; + getValidator().checkRemove(srcParentNode, options, Permission.NONE); + getValidator().checkModify(destParentNode, options, Permission.NONE); + // check constraints // get applicable definition of target node at new location NodeTypeImpl nt = (NodeTypeImpl) targetNode.getPrimaryNodeType(); NodeDefinitionImpl newTargetDef; @@ -1012,22 +1014,6 @@ "Same name siblings not allowed: " + existing); } - // check protected flag of old & new parent - if (destParentNode.getDefinition().isProtected()) { - String msg = destAbsPath + ": cannot add a child node to a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - if (srcParentNode.getDefinition().isProtected()) { - String msg = srcAbsPath + ": cannot remove a child node from a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - - // check lock status - srcParentNode.checkLock(); - destParentNode.checkLock(); - NodeId targetId = targetNode.getNodeId(); int index = srcName.getIndex(); if (index == 0) { @@ -1098,22 +1084,11 @@ throw new PathNotFoundException(parentAbsPath); } - // verify that parent node is checked-out - if (!parent.internalIsCheckedOut()) { - String msg = parentAbsPath + ": cannot add a child to a checked-in node"; - log.debug(msg); - throw new VersionException(msg); - } - - // check protected flag of parent node - if (parent.getDefinition().isProtected()) { - String msg = parentAbsPath + ": cannot add a child to a protected node"; - log.debug(msg); - throw new ConstraintViolationException(msg); - } - - // check lock status - parent.checkLock(); + // verify that parent node is checked-out, not locked and not protected + // by either node type constraints nor by some retention or hold. + int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | + ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION; + getValidator().checkModify(parent, options, Permission.NONE); SessionImporter importer = new SessionImporter(parent, this, uuidBehavior); return new ImportHandler(importer, this); @@ -1522,9 +1497,14 @@ * @see org.apache.jackrabbit.api.jsr283.Session#getRetentionManager() * @since JCR 2.0 */ - public synchronized RetentionManager getRetentionManager() + public RetentionManager getRetentionManager() throws UnsupportedRepositoryOperationException, RepositoryException { + // check sanity of this session + sanityCheck(); if (retentionManager == null) { + // make sure the internal retention manager exists. + getRetentionRegistry(); + // create the api level retention manager. retentionManager = new RetentionManagerImpl(this); } return retentionManager; Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java Wed Jan 28 09:50:50 2009 @@ -37,6 +37,7 @@ import org.apache.jackrabbit.core.xml.WorkspaceImporter; import org.apache.jackrabbit.core.cluster.ClusterNode; import org.apache.jackrabbit.core.security.principal.AdminPrincipal; +import org.apache.jackrabbit.core.retention.RetentionRegistry; import org.apache.jackrabbit.commons.AbstractWorkspace; import org.apache.jackrabbit.spi.commons.conversion.NameException; import org.apache.jackrabbit.spi.Path; @@ -119,9 +120,19 @@ */ protected LockManager lockMgr; + /** + * The API LockManager for this workspace, used to create and release + * locks and determine the lock status. + */ private org.apache.jackrabbit.api.jsr283.lock.LockManager jcr283LockManager; /** + * The internal manager used to evaluate effective retention policies and + * holds. + */ + private RetentionRegistry retentionRegistry; + + /** * Protected constructor. * * @param wspConfig The workspace configuration @@ -520,6 +531,22 @@ return lockMgr; } + /** + * Return the internal effective retention/hold manager for this workspace. + * If not already done, creates a new instance. + * + * @return effective retention/hold manager for this workspace + * @throws RepositoryException if an error occurs + */ + synchronized RetentionRegistry getRetentionRegistry() throws RepositoryException { + // check state of this instance + sanityCheck(); + if (retentionRegistry == null) { + retentionRegistry = rep.getRetentionRegistry(wspConfig.getName()); + } + return retentionRegistry; + } + //------------------------------------------------------------< Workspace > /** * {@inheritDoc} @@ -864,7 +891,7 @@ log.debug(msg); throw new InvalidItemStateException(msg); } - + // TODO: add checks for lock/hold... boolean success = false; try { // now restore all versions that have a node in the ws Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/SessionLockManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/SessionLockManager.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/SessionLockManager.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/SessionLockManager.java Wed Jan 28 09:50:50 2009 @@ -19,6 +19,7 @@ import org.apache.jackrabbit.api.jsr283.lock.Lock; import org.apache.jackrabbit.core.NodeImpl; import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.core.ItemValidator; import org.apache.jackrabbit.core.security.authorization.Permission; import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.slf4j.Logger; @@ -150,14 +151,10 @@ public Lock lock(String absPath, boolean isDeep, boolean isSessionScoped, long timeoutHint, String ownerInfo) throws RepositoryException { NodeImpl node = (NodeImpl) session.getNode(absPath); - - if (session.hasPendingChanges(node)) { - String msg = "Unable to lock node. Node has pending changes: " + this; - log.debug(msg); - throw new InvalidItemStateException(msg); - } + + int options = ItemValidator.CHECK_HOLD | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE; + session.getValidator().checkModify(node, options, Permission.LOCK_MNGMT); checkLockable(node); - session.getAccessManager().checkPermission(session.getQPath(node.getPath()), Permission.LOCK_MNGMT); synchronized (systemLockMgr) { return (Lock) systemLockMgr.lock(node, isDeep, isSessionScoped, timeoutHint, ownerInfo); @@ -173,13 +170,9 @@ RepositoryException { NodeImpl node = (NodeImpl) session.getNode(absPath); - if (session.hasPendingChanges(node)) { - String msg = "Unable to unlock node. Node has pending changes: " + this; - log.debug(msg); - throw new InvalidItemStateException(msg); - } + int options = ItemValidator.CHECK_HOLD | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE; + session.getValidator().checkModify(node, options, Permission.LOCK_MNGMT); checkLockable(node); - session.getAccessManager().checkPermission(session.getQPath(node.getPath()), Permission.LOCK_MNGMT); synchronized (systemLockMgr) { // basic checks if unlock can be called on the node. Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.retention; + +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.NameFactory; +import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; +import org.apache.jackrabbit.spi.commons.conversion.NameResolver; +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.PropertyImpl; +import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.api.jsr283.retention.Hold; + +import javax.jcr.Value; +import javax.jcr.RepositoryException; +import javax.jcr.ValueFactory; + +/** + * HoldImpl... + */ +class HoldImpl implements Hold { + + private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance(); + + private static final String DEEP = "D_"; + private static final String SHALLOW = "S_"; + + private final Name name; + private final boolean isDeep; + private final NodeId nodeId; + + private final NameResolver resolver; + + private int hashCode = 0; + + HoldImpl(Name name, boolean isDeep, NodeId nodeId, NameResolver resolver) { + this.name = name; + this.isDeep = isDeep; + this.nodeId = nodeId; + this.resolver = resolver; + } + + NodeId getNodeId() { + return nodeId; + } + + Value toValue(ValueFactory valueFactory) throws RepositoryException { + String str = ((isDeep) ? DEEP : SHALLOW) + name.toString(); + return valueFactory.createValue(str); + } + + static Hold createFromValue(Value val, NodeId nodeId, NameResolver resolver) throws RepositoryException { + String str = val.getString(); + Name name = NAME_FACTORY.create(str.substring(2)); + boolean isDeep = str.startsWith(DEEP); + return new HoldImpl(name, isDeep, nodeId, resolver); + } + + static Hold[] createFromProperty(PropertyImpl property, NodeId nodeId) throws RepositoryException { + Value[] vs = property.getValues(); + Hold[] holds = new Hold[vs.length]; + for (int i = 0; i < vs.length; i++) { + holds[i] = createFromValue(vs[i], nodeId, (SessionImpl) property.getSession()); + } + return holds; + } + + //-----------------------------------------------------------< Hold >--- + /** + * @see org.apache.jackrabbit.api.jsr283.retention.Hold#isDeep() + */ + public boolean isDeep() throws RepositoryException { + return isDeep; + } + + /** + * @see org.apache.jackrabbit.api.jsr283.retention.Hold#getName() + */ + public String getName() throws RepositoryException { + return resolver.getJCRName(name); + } + + //---------------------------------------------------------< Object >--- + /** + * @see Object#hashCode() + */ + public int hashCode() { + if (hashCode == 0) { + int h = 17; + h = 37 * h + name.hashCode(); + h = 37 * h + nodeId.hashCode(); + h = 37 * h + Boolean.valueOf(isDeep).hashCode(); + hashCode = h; + } + return hashCode; + } + + /** + * @see Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof HoldImpl) { + HoldImpl other = (HoldImpl) obj; + return isDeep == other.isDeep && name.equals(other.name) && nodeId.equals(other.nodeId); + } + return false; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/HoldImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url