From commits-return-6959-apmail-jackrabbit-commits-archive=jackrabbit.apache.org@jackrabbit.apache.org Wed Jan 28 09:51:20 2009 Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 85227 invoked from network); 28 Jan 2009 09:51:20 -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:20 -0000 Received: (qmail 75275 invoked by uid 500); 28 Jan 2009 09:51:20 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 75242 invoked by uid 500); 28 Jan 2009 09:51:20 -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 75233 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 612F12388B52; 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 [2/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.612F12388B52@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionManagerImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionManagerImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionManagerImpl.java Wed Jan 28 09:50:50 2009 @@ -16,69 +16,193 @@ */ package org.apache.jackrabbit.core.retention; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.jackrabbit.api.jsr283.retention.RetentionManager; import org.apache.jackrabbit.api.jsr283.retention.Hold; +import org.apache.jackrabbit.api.jsr283.retention.RetentionManager; import org.apache.jackrabbit.api.jsr283.retention.RetentionPolicy; +import org.apache.jackrabbit.core.NodeImpl; +import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.core.ProtectedItemModifier; +import org.apache.jackrabbit.core.PropertyImpl; +import org.apache.jackrabbit.core.security.authorization.Permission; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.NameFactory; +import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.jcr.PathNotFoundException; import javax.jcr.AccessDeniedException; +import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.version.VersionException; +import javax.jcr.Value; +import javax.jcr.PropertyType; import javax.jcr.lock.LockException; +import javax.jcr.version.VersionException; +import java.util.ArrayList; +import java.util.List; /** * RetentionManagerImpl... */ -public class RetentionManagerImpl implements RetentionManager { +public class RetentionManagerImpl extends ProtectedItemModifier implements RetentionManager { private static Logger log = LoggerFactory.getLogger(RetentionManagerImpl.class); - private final Session session; - - public RetentionManagerImpl(Session session) { + private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance(); + + static final Name REP_RETENTION_MANAGEABLE = NAME_FACTORY.create(Name.NS_REP_URI, "RetentionManageable"); + static final Name REP_HOLD = NAME_FACTORY.create(Name.NS_REP_URI, "hold"); + static final Name REP_RETENTION_POLICY = NAME_FACTORY.create(Name.NS_REP_URI, "retentionPolicy"); + + private final SessionImpl session; + + /** + * + * @param session The editing session. + */ + public RetentionManagerImpl(SessionImpl session) { + super(Permission.RETENTION_MNGMT); this.session = session; } - + + //---------------------------------------------------< RetentionManager >--- + /** + * @see RetentionManager#getHolds(String) + */ public Hold[] getHolds(String absPath) throws PathNotFoundException, AccessDeniedException, RepositoryException { - //TODO - return new Hold[0]; + + NodeImpl n = (NodeImpl) session.getNode(absPath); + session.getAccessManager().checkPermission(session.getQPath(absPath), Permission.RETENTION_MNGMT); + + Hold[] holds; + if (n.isNodeType(REP_RETENTION_MANAGEABLE) && n.hasProperty(REP_HOLD)) { + holds = HoldImpl.createFromProperty(n.getProperty(REP_HOLD), n.getNodeId()); + } else { + holds = new Hold[0]; + } + return holds; } + /** + * @see RetentionManager#addHold(String, String, boolean) + */ public Hold addHold(String absPath, String name, boolean isDeep) throws PathNotFoundException, AccessDeniedException, LockException, VersionException, RepositoryException { - //TODO - throw new UnsupportedOperationException("Not yet implemented"); - } + NodeImpl n = (NodeImpl) session.getNode(absPath); + if (!n.isNodeType(REP_RETENTION_MANAGEABLE)) { + n.addMixin(REP_RETENTION_MANAGEABLE); + } + + HoldImpl hold = new HoldImpl(session.getQName(name), isDeep, n.getNodeId(), session); + Value[] vls; + if (n.hasProperty(REP_HOLD)) { + Value[] vs = n.getProperty(REP_HOLD).getValues(); + // check if the same hold already exists + for (int i = 0; i < vs.length; i++) { + if (hold.equals(HoldImpl.createFromValue(vs[i], n.getNodeId(), session))) { + throw new RepositoryException("Hold already exists."); + } + } + vls = new Value[vs.length + 1]; + System.arraycopy(vs, 0, vls, 0, vs.length); + } else { + vls = new Value[1]; + } + + // add the value of the new hold + vls[vls.length - 1] = hold.toValue(session.getValueFactory()); + setProperty(n, REP_HOLD, vls); + return hold; + } + + /** + * @see RetentionManager#removeHold(String, Hold) + */ public void removeHold(String absPath, Hold hold) throws PathNotFoundException, AccessDeniedException, LockException, VersionException, RepositoryException { - //TODO - throw new UnsupportedOperationException("Not yet implemented"); - } + NodeImpl n = (NodeImpl) session.getNode(absPath); + if (hold instanceof HoldImpl + && n.getNodeId().equals(((HoldImpl) hold).getNodeId()) + && n.isNodeType(REP_RETENTION_MANAGEABLE) + && n.hasProperty(REP_HOLD)) { + + PropertyImpl p = n.getProperty(REP_HOLD); + Value[] vls = p.getValues(); + + List newValues = new ArrayList(vls.length - 1); + for (int i = 0; i < vls.length; i++) { + if (!hold.equals(HoldImpl.createFromValue(vls[i], n.getNodeId(), session))) { + newValues.add(vls[i]); + } + } + if (newValues.size() < vls.length) { + if (newValues.size() == 0) { + removeItem(p); + } else { + setProperty(n, REP_HOLD, (Value[]) newValues.toArray(new Value[newValues.size()])); + } + } else { + // no matching hold. + throw new RepositoryException("Cannot remove '" + hold.getName() + "' at " + absPath + "."); + } + } else { + // invalid hold or no hold at absPath + throw new RepositoryException("Cannot remove '" + hold.getName() + "' at " + absPath + "."); + } + } + + /** + * @see RetentionManager#getRetentionPolicy(String) + */ public RetentionPolicy getRetentionPolicy(String absPath) throws PathNotFoundException, AccessDeniedException, RepositoryException { - // TODO - return null; + + NodeImpl n = (NodeImpl) session.getNode(absPath); + session.getAccessManager().checkPermission(session.getQPath(absPath), Permission.RETENTION_MNGMT); + + RetentionPolicy rPolicy = null; + if (n.isNodeType(REP_RETENTION_MANAGEABLE) && n.hasProperty(REP_RETENTION_POLICY)) { + String jcrName = n.getProperty(REP_RETENTION_POLICY).getString(); + rPolicy = new RetentionPolicyImpl(jcrName, n.getNodeId(), session); + } + + return rPolicy; } + /** + * @see RetentionManager#setRetentionPolicy(String, RetentionPolicy) + */ public void setRetentionPolicy(String absPath, RetentionPolicy retentionPolicy) throws PathNotFoundException, AccessDeniedException, LockException, VersionException, RepositoryException { - //TODO - throw new UnsupportedOperationException("Not yet implemented"); + + NodeImpl n = (NodeImpl) session.getNode(absPath); + if (!(retentionPolicy instanceof RetentionPolicyImpl)) { + throw new RepositoryException("Invalid retention policy."); + } + Value retentionReference = session.getValueFactory().createValue(retentionPolicy.getName(), PropertyType.NAME); + if (!n.isNodeType(REP_RETENTION_MANAGEABLE)) { + n.addMixin(REP_RETENTION_MANAGEABLE); + } + setProperty(n, REP_RETENTION_POLICY, retentionReference); } + /** + * @see RetentionManager#removeRetentionPolicy(String) + */ public void removeRetentionPolicy(String absPath) throws PathNotFoundException, AccessDeniedException, LockException, VersionException, RepositoryException { - //TODO - throw new UnsupportedOperationException("Not yet implemented"); + + NodeImpl n = (NodeImpl) session.getNode(absPath); + if (n.isNodeType(REP_RETENTION_MANAGEABLE) && n.hasProperty(REP_RETENTION_POLICY)) { + removeItem(n.getProperty(REP_RETENTION_POLICY)); + } else { + throw new RepositoryException("Cannot remove retention policy at absPath."); + } } } \ No newline at end of file Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionPolicyImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionPolicyImpl.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionPolicyImpl.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionPolicyImpl.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,93 @@ +/* + * 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.api.jsr283.retention.RetentionPolicy; +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.commons.conversion.NameResolver; +import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException; + +import javax.jcr.RepositoryException; +import javax.jcr.NamespaceException; + +/** + * Basic implementation of the RetentionPolicy interface. + */ +public class RetentionPolicyImpl implements RetentionPolicy { + + private final Name name; + private final NodeId nodeId; + private final NameResolver resolver; + + private int hashCode = 0; + + public RetentionPolicyImpl(String jcrName, NameResolver resolver) throws IllegalNameException, NamespaceException { + this(resolver.getQName(jcrName), null, resolver); + } + + RetentionPolicyImpl(String jcrName, NodeId nodeId, NameResolver resolver) throws IllegalNameException, NamespaceException { + this(resolver.getQName(jcrName), nodeId, resolver); + } + + private RetentionPolicyImpl(Name name, NodeId nodeId, NameResolver resolver) { + this.name = name; + this.nodeId = nodeId; + this.resolver = resolver; + } + + NodeId getNodeId() { + return nodeId; + } + + //----------------------------------------------------< RetentionPolicy >--- + /** + * @see org.apache.jackrabbit.api.jsr283.retention.RetentionPolicy#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(); + hashCode = h; + } + return hashCode; + } + + /** + * @see Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof RetentionPolicyImpl) { + RetentionPolicyImpl other = (RetentionPolicyImpl) obj; + return name.equals(other.name) && ((nodeId == null) ? other.nodeId == null : 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/RetentionPolicyImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionPolicyImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,31 @@ +/* + * 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.Path; + +import javax.jcr.RepositoryException; + +/** + * RetentionEvaluator... + */ +public interface RetentionRegistry { + + public boolean hasEffectiveHold(Path nodePath, boolean checkParent) throws RepositoryException; + + public boolean hasEffectiveRetention(Path nodePath, boolean checkParent) throws RepositoryException; +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistry.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,349 @@ +/* + * 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.commons.io.IOUtils; +import org.apache.jackrabbit.api.jsr283.retention.Hold; +import org.apache.jackrabbit.api.jsr283.retention.RetentionPolicy; +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.NodeImpl; +import org.apache.jackrabbit.core.PropertyId; +import org.apache.jackrabbit.core.PropertyImpl; +import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.core.fs.FileSystem; +import org.apache.jackrabbit.core.fs.FileSystemException; +import org.apache.jackrabbit.core.fs.FileSystemResource; +import org.apache.jackrabbit.core.observation.SynchronousEventListener; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.Path; +import org.apache.jackrabbit.spi.commons.name.PathMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.RepositoryException; +import javax.jcr.Workspace; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventIterator; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * RetentionEvaluatorImpl... + */ +public class RetentionRegistryImpl implements RetentionRegistry, SynchronousEventListener { + + /** + * logger instance + */ + private static final Logger log = LoggerFactory.getLogger(RetentionRegistryImpl.class); + /** + * Name of the file storing the existing retention/holds + */ + private static final String FILE_NAME = "retention"; + + private final PathMap retentionMap = new PathMap(); + private final PathMap holdMap = new PathMap(); + + private final SessionImpl session; + private final FileSystemResource retentionFile; + + private long holdCnt = 0; + private long retentionCnt = 0; + + private boolean initialized = false; + + public RetentionRegistryImpl(SessionImpl session, FileSystem fs) throws RepositoryException { + this.session = session; + this.retentionFile = new FileSystemResource(fs, FileSystem.SEPARATOR + FILE_NAME); + + // start listening to added/changed or removed holds and retention policies. + Workspace wsp = session.getWorkspace(); + // register eventlistener to be informed about new/removed holds and retention plcs. + int types = Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED | Event.PROPERTY_CHANGED; + String[] ntFilter = new String[] {session.getJCRName(RetentionManagerImpl.REP_RETENTION_MANAGEABLE)}; + wsp.getObservationManager().addEventListener(this, types, "/", true, null, ntFilter, false); + + // populate the retentionMap and the holdMap with the effective + // holds and retention plcs present within the content. + try { + readRetentionFile(); + } catch (FileSystemException e) { + throw new RepositoryException("Error while reading retention/holds from '" + retentionFile.getPath() + "'", e); + } catch (IOException e) { + throw new RepositoryException("Error while reading retention/holds from '" + retentionFile.getPath() + "'", e); + } + initialized = true; + } + + /** + * Read the file system resource containing the node ids of those nodes + * contain holds/retention policies and populate the 2 path maps. + * + * If an entry in the retention file doesn't have a corresponding entry + * (either rep:hold property or rep:retentionPolicy property at the + * node identified by the node id entry) or doesn't correspond to an existing + * node, that entry will be ignored. Upon {@link #close()} of this + * manager, the file will be updated to reflect the actual set of holds/ + * retentions present and effective in the content. + * + * @throws IOException + * @throws FileSystemException + */ + private void readRetentionFile() throws IOException, FileSystemException { + if (retentionFile.exists()) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(retentionFile.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + NodeId nodeId = NodeId.valueOf(line); + try { + NodeImpl node = (NodeImpl) session.getItemManager().getItem(nodeId); + Path nodePath = node.getPrimaryPath(); + + if (node.hasProperty(RetentionManagerImpl.REP_HOLD)) { + PropertyImpl prop = node.getProperty(RetentionManagerImpl.REP_HOLD); + addHolds(nodePath, prop); + } + if (node.hasProperty(RetentionManagerImpl.REP_RETENTION_POLICY)) { + PropertyImpl prop = node.getProperty(RetentionManagerImpl.REP_RETENTION_POLICY); + addRetentionPolicy(nodePath, prop); + } + } catch (RepositoryException e) { + // node doesn't exist any more or hold/retention has been removed. + // ignore. upon close() the file will not contain the given nodeId + // any more. + log.warn("Unable to read retention policy / holds from node '" + nodeId + "': " + e.getMessage()); + } + } + } finally { + IOUtils.closeQuietly(reader); + } + } + } + + /** + * Write back the file system resource containing the node ids of those + * nodes containing holds and/or retention policies. Each node id is + * present only once. + */ + private void writeRetentionFile() { + final Set nodeIds = new HashSet(); + + // first look for nodes containing holds + holdMap.traverse(new PathMap.ElementVisitor() { + public void elementVisited(PathMap.Element element) { + List holds = (List) element.get(); + if (!holds.isEmpty()) { + nodeIds.add(((HoldImpl) holds.get(0)).getNodeId()); + } + } + }, false); + + // then collect ids of nodes having an retention policy + retentionMap.traverse(new PathMap.ElementVisitor() { + public void elementVisited(PathMap.Element element) { + RetentionPolicyImpl rp = (RetentionPolicyImpl) element.get(); + nodeIds.add(rp.getNodeId()); + } + }, false); + + if (!nodeIds.isEmpty()) { + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new OutputStreamWriter(retentionFile.getOutputStream())); + for (Iterator it = nodeIds.iterator(); it.hasNext();) { + writer.write(it.next().toString()); + if (it.hasNext()) { + writer.newLine(); + } + } + } catch (FileSystemException fse) { + log.error("Error while saving locks to '" + retentionFile.getPath() + "': " + fse.getMessage()); + } catch (IOException ioe) { + log.error("Error while saving locks to '" + retentionFile.getPath() + "': " + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(writer); + } + } + } + + public void close() { + writeRetentionFile(); + initialized = false; + } + + private void addHolds(Path nodePath, PropertyImpl p) throws RepositoryException { + synchronized (holdMap) { + Hold[] holds = HoldImpl.createFromProperty(p, ((PropertyId) p.getId()).getParentId()); + holdMap.put(nodePath, Arrays.asList(holds)); + holdCnt++; + } + } + + private void removeHolds(Path nodePath) { + synchronized (holdMap) { + PathMap.Element el = holdMap.map(nodePath, true); + if (el != null) { + el.remove(); + holdCnt--; + } // else: no entry for holds on nodePath (should not occur) + } + } + + private void addRetentionPolicy(Path nodePath, PropertyImpl p) throws RepositoryException { + synchronized (retentionMap) { + RetentionPolicy rp = new RetentionPolicyImpl(p.getString(), ((PropertyId) p.getId()).getParentId(), session); + retentionMap.put(nodePath, rp); + retentionCnt++; + } + } + + private void removeRetentionPolicy(Path nodePath) { + synchronized (retentionMap) { + PathMap.Element el = retentionMap.map(nodePath, true); + if (el != null) { + el.remove(); + retentionCnt--; + } // else: no entry for holds on nodePath (should not occur) + } + } + + //--------------------------------------------------< RetentionRegistry >--- + /** + * @see RetentionRegistry#hasEffectiveHold(org.apache.jackrabbit.spi.Path,boolean) + */ + public boolean hasEffectiveHold(Path nodePath, boolean checkParent) throws RepositoryException { + if (!initialized) { + throw new IllegalStateException("Not initialized."); + } + if (holdCnt <= 0) { + return false; + } + PathMap.Element element = holdMap.map(nodePath, false); + List holds = (List) element.get(); + if (holds != null) { + if (element.hasPath(nodePath)) { + // one or more holds on the specified path + return true; + } else if (checkParent && !nodePath.denotesRoot() && + element.hasPath(nodePath.getAncestor(1))) { + // hold present on the parent node whithout checking for being + // a deep hold. + // this required for removal of a node that can be inhibited + // by a hold on the node itself, by a hold on the parent or + // by a deep hold on any ancestor. + return true; + } else { + for (Iterator it = holds.iterator(); it.hasNext();) { + Hold h = (Hold) it.next(); + if (h.isDeep()) { + return true; + } + } + } + } + // no hold at path or no deep hold on parent. + return false; + } + + /** + * @see RetentionRegistry#hasEffectiveRetention(org.apache.jackrabbit.spi.Path,boolean) + */ + public boolean hasEffectiveRetention(Path nodePath, boolean checkParent) throws RepositoryException { + if (!initialized) { + throw new IllegalStateException("Not initialized."); + } + if (retentionCnt <= 0) { + return false; + } + RetentionPolicy rp = null; + PathMap.Element element = retentionMap.map(nodePath, true); + if (element != null) { + rp = (RetentionPolicy) element.get(); + } + if (rp == null && checkParent ) { + element = retentionMap.map(nodePath.getAncestor(1), true); + if (element != null) { + rp = (RetentionPolicy) element.get(); + } + } + return rp != null; + } + + //-------------------------------------------< SynchronousEventListener >--- + /** + * @param events Events reporting hold/retention policy changes. + */ + public void onEvent(EventIterator events) { + while (events.hasNext()) { + Event ev = events.nextEvent(); + try { + Path evPath = session.getQPath(ev.getPath()); + Path nodePath = evPath.getAncestor(1); + Name propName = evPath.getNameElement().getName(); + + if (RetentionManagerImpl.REP_HOLD.equals(propName)) { + // hold changes + switch (ev.getType()) { + case Event.PROPERTY_ADDED: + case Event.PROPERTY_CHANGED: + // build the Hold objects from the rep:hold property + // and put them into the hold map. + PropertyImpl p = (PropertyImpl) session.getProperty(ev.getPath()); + addHolds(nodePath, p); + break; + case Event.PROPERTY_REMOVED: + // all holds present on this node were remove + // -> remove the corresponding entry in the holdMap. + removeHolds(nodePath); + break; + } + } else if (RetentionManagerImpl.REP_RETENTION_POLICY.equals(propName)) { + // retention policy changes + switch (ev.getType()) { + case Event.PROPERTY_ADDED: + case Event.PROPERTY_CHANGED: + // build the RetentionPolicy objects from the rep:retentionPolicy property + // and put it into the retentionMap. + PropertyImpl p = (PropertyImpl) session.getProperty(ev.getPath()); + addRetentionPolicy(nodePath, p); + break; + case Event.PROPERTY_REMOVED: + // retention policy present on this node was remove + // -> remove the corresponding entry in the retentionMap. + removeRetentionPolicy(nodePath); + break; + } + } + // else: not interested in any other property -> ignore. + + } catch (RepositoryException e) { + log.warn("Internal error while processing event.",e.getMessage()); + // ignore. + } + } + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/retention/RetentionRegistryImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java Wed Jan 28 09:50:50 2009 @@ -22,13 +22,14 @@ import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy; import org.apache.jackrabbit.api.jsr283.security.AccessControlList; import org.apache.jackrabbit.core.NodeImpl; -import org.apache.jackrabbit.core.SecurityItemModifier; +import org.apache.jackrabbit.core.ProtectedItemModifier; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.security.authorization.AccessControlConstants; import org.apache.jackrabbit.core.security.authorization.AccessControlEditor; import org.apache.jackrabbit.core.security.authorization.AccessControlUtils; import org.apache.jackrabbit.core.security.authorization.JackrabbitAccessControlEntry; import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry; +import org.apache.jackrabbit.core.security.authorization.Permission; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.conversion.NameException; import org.apache.jackrabbit.spi.commons.conversion.NameParser; @@ -49,7 +50,7 @@ /** * ACLEditor... */ -public class ACLEditor extends SecurityItemModifier implements AccessControlEditor, AccessControlConstants { +public class ACLEditor extends ProtectedItemModifier implements AccessControlEditor, AccessControlConstants { /** * the default logger @@ -67,7 +68,7 @@ private final AccessControlUtils utils; ACLEditor(Session editingSession, AccessControlUtils utils) { - super(true); + super(Permission.MODIFY_AC); if (editingSession instanceof SessionImpl) { session = ((SessionImpl) editingSession); // TODO: review and find better solution @@ -147,7 +148,7 @@ access and removed the explicitely */ if (aclNode != null) { - removeSecurityItem(aclNode); + removeItem(aclNode); } // now (re) create it aclNode = createAclNode(nodePath); @@ -161,16 +162,16 @@ ValueFactory vf = session.getValueFactory(); // create the ACE node - NodeImpl aceNode = addSecurityNode(aclNode, nodeName, ntName); + NodeImpl aceNode = addNode(aclNode, nodeName, ntName); // write the rep:principalName property String principalName = ace.getPrincipal().getName(); - setSecurityProperty(aceNode, P_PRINCIPAL_NAME, vf.createValue(principalName)); + setProperty(aceNode, P_PRINCIPAL_NAME, vf.createValue(principalName)); // ... and the rep:privileges property Privilege[] pvlgs = ace.getPrivileges(); Value[] names = getPrivilegeNames(pvlgs, vf); - setSecurityProperty(aceNode, P_PRIVILEGES, names); + setProperty(aceNode, P_PRIVILEGES, names); } } @@ -183,7 +184,7 @@ NodeImpl aclNode = getAclNode(nodePath); if (aclNode != null) { - removeSecurityItem(aclNode); + removeItem(aclNode); } else { throw new AccessControlException("No policy to remove at " + nodePath); } @@ -265,7 +266,7 @@ if (!protectedNode.isNodeType(NT_REP_ACCESS_CONTROLLABLE)) { protectedNode.addMixin(NT_REP_ACCESS_CONTROLLABLE); } - return addSecurityNode(protectedNode, N_POLICY, NT_REP_ACL); + return addNode(protectedNode, N_POLICY, NT_REP_ACL); } /** Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java Wed Jan 28 09:50:50 2009 @@ -23,11 +23,12 @@ import org.apache.jackrabbit.api.security.principal.PrincipalManager; import org.apache.jackrabbit.api.security.principal.NoSuchPrincipalException; import org.apache.jackrabbit.core.NodeImpl; -import org.apache.jackrabbit.core.SecurityItemModifier; +import org.apache.jackrabbit.core.ProtectedItemModifier; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.security.authorization.AccessControlConstants; import org.apache.jackrabbit.core.security.authorization.AccessControlEditor; import org.apache.jackrabbit.core.security.authorization.JackrabbitAccessControlEntry; +import org.apache.jackrabbit.core.security.authorization.Permission; import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal; import org.apache.jackrabbit.core.security.principal.PrincipalImpl; import org.apache.jackrabbit.spi.Name; @@ -49,7 +50,7 @@ /** * CombinedEditor... */ -public class ACLEditor extends SecurityItemModifier implements AccessControlEditor, AccessControlConstants { +public class ACLEditor extends ProtectedItemModifier implements AccessControlEditor, AccessControlConstants { private static Logger log = LoggerFactory.getLogger(ACLEditor.class); /** @@ -64,7 +65,7 @@ private final String acRootPath; ACLEditor(SessionImpl session, Path acRootPath) throws RepositoryException { - super(true); + super(Permission.MODIFY_AC); this.session = session; this.acRootPath = session.getJCRPath(acRootPath); } @@ -162,10 +163,10 @@ NodeImpl aclNode; if (acNode.hasNode(N_POLICY)) { aclNode = acNode.getNode(N_POLICY); - removeSecurityItem(aclNode); + removeItem(aclNode); } /* now (re) create it */ - aclNode = addSecurityNode(acNode, N_POLICY, NT_REP_ACL); + aclNode = addNode(acNode, N_POLICY, NT_REP_ACL); /* add all entries defined on the template */ AccessControlEntry[] aces = acl.getAccessControlEntries(); @@ -175,25 +176,25 @@ // create the ACE node Name nodeName = getUniqueNodeName(aclNode, "entry"); Name ntName = (ace.isAllow()) ? NT_REP_GRANT_ACE : NT_REP_DENY_ACE; - NodeImpl aceNode = addSecurityNode(aclNode, nodeName, ntName); + NodeImpl aceNode = addNode(aclNode, nodeName, ntName); ValueFactory vf = session.getValueFactory(); // write the rep:principalName property - setSecurityProperty(aceNode, P_PRINCIPAL_NAME, vf.createValue(ace.getPrincipal().getName())); + setProperty(aceNode, P_PRINCIPAL_NAME, vf.createValue(ace.getPrincipal().getName())); // ... and the rep:privileges property Privilege[] privs = ace.getPrivileges(); Value[] vs = new Value[privs.length]; for (int j = 0; j < privs.length; j++) { vs[j] = vf.createValue(privs[j].getName(), PropertyType.NAME); } - setSecurityProperty(aceNode, P_PRIVILEGES, vs); + setProperty(aceNode, P_PRIVILEGES, vs); // store the restrictions: String[] restrNames = ace.getRestrictionNames(); for (int rnIndex = 0; rnIndex < restrNames.length; rnIndex++) { Name pName = session.getQName(restrNames[rnIndex]); Value value = ace.getRestriction(restrNames[rnIndex]); - setSecurityProperty(aceNode, pName, value); + setProperty(aceNode, pName, value); } } } @@ -210,7 +211,7 @@ // build the template in order to have a return value AccessControlPolicy tmpl = createTemplate(acNode); if (tmpl.equals(policy)) { - removeSecurityItem(acNode.getNode(N_POLICY)); + removeItem(acNode.getNode(N_POLICY)); return; } } @@ -248,7 +249,7 @@ throw new RepositoryException("Internal error: Unexpected nodetype " + node.getPrimaryNodeType().getName() + " below /rep:accessControl"); } } else { - node = addSecurityNode(node, nName, NT_REP_ACCESS_CONTROL); + node = addNode(node, nName, NT_REP_ACCESS_CONTROL); } } return node; Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Wed Jan 28 09:50:50 2009 @@ -23,7 +23,7 @@ import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.core.ItemImpl; import org.apache.jackrabbit.core.NodeImpl; -import org.apache.jackrabbit.core.SecurityItemModifier; +import org.apache.jackrabbit.core.ProtectedItemModifier; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal; import org.apache.jackrabbit.core.security.principal.PrincipalImpl; @@ -54,7 +54,7 @@ /** * UserManagerImpl */ -public class UserManagerImpl extends SecurityItemModifier implements UserManager, UserConstants { +public class UserManagerImpl extends ProtectedItemModifier implements UserManager, UserConstants { private static final Logger log = LoggerFactory.getLogger(UserManagerImpl.class); @@ -71,7 +71,7 @@ private final Map idPathMap = new LRUMap(1000); public UserManagerImpl(SessionImpl session, String adminId) throws RepositoryException { - super(false); + super(); this.session = session; this.adminId = adminId; @@ -216,11 +216,11 @@ parent = createParentNode(parentPath); Name nodeName = session.getQName(Text.escapeIllegalJcrChars(userID)); - NodeImpl userNode = addSecurityNode(parent, nodeName, NT_REP_USER); + NodeImpl userNode = addNode(parent, nodeName, NT_REP_USER); - setSecurityProperty(userNode, P_USERID, getValue(userID)); - setSecurityProperty(userNode, P_PASSWORD, getValue(UserImpl.buildPasswordValue(password))); - setSecurityProperty(userNode, P_PRINCIPAL_NAME, getValue(principal.getName())); + setProperty(userNode, P_USERID, getValue(userID), true); + setProperty(userNode, P_PASSWORD, getValue(UserImpl.buildPasswordValue(password)), true); + setProperty(userNode, P_PRINCIPAL_NAME, getValue(principal.getName()), true); parent.save(); log.debug("User created: " + userID + "; " + userNode.getPath()); @@ -271,8 +271,8 @@ parent = createParentNode(parentPath); Name groupID = getGroupId(principal.getName()); - NodeImpl groupNode = addSecurityNode(parent, groupID, NT_REP_GROUP); - setSecurityProperty(groupNode, P_PRINCIPAL_NAME, getValue(principal.getName())); + NodeImpl groupNode = addNode(parent, groupID, NT_REP_GROUP); + setProperty(groupNode, P_PRINCIPAL_NAME, getValue(principal.getName())); parent.save(); log.debug("Group created: " + groupID + "; " + groupNode.getPath()); @@ -303,17 +303,17 @@ } void setProtectedProperty(NodeImpl node, Name propName, Value value) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException { - setSecurityProperty(node, propName, value); + setProperty(node, propName, value); node.save(); } void setProtectedProperty(NodeImpl node, Name propName, Value[] values) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException { - setSecurityProperty(node, propName, values); + setProperty(node, propName, values); node.save(); } void removeProtectedItem(ItemImpl item, Node parent) throws RepositoryException, AccessDeniedException, VersionException { - removeSecurityItem(item); + removeItem(item); parent.save(); } @@ -491,7 +491,7 @@ } else { ntName = NT_REP_AUTHORIZABLE_FOLDER; } - NodeImpl added = addSecurityNode(parent, nName, ntName); + NodeImpl added = addNode(parent, nName, ntName); parent.save(); parent = added; } else { Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java Wed Jan 28 09:50:50 2009 @@ -146,7 +146,7 @@ // create new with new uuid: // check if new node can be added (check access rights & // node type constraints only, assume locking & versioning status - // has already been checked on ancestor) + // and retention/hold has already been checked on ancestor) itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS @@ -192,14 +192,16 @@ BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_VERSIONING - | BatchedItemOperations.CHECK_CONSTRAINTS); + | BatchedItemOperations.CHECK_CONSTRAINTS + | BatchedItemOperations.CHECK_HOLD + | BatchedItemOperations.CHECK_RETENTION); // do remove conflicting (recursive) itemOps.removeNodeState(conflicting); // create new with given uuid: // check if new node can be added (check access rights & // node type constraints only, assume locking & versioning status - // has already been checked on ancestor) + // and retention/hold has already been checked on ancestor) itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS @@ -231,7 +233,9 @@ BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_VERSIONING - | BatchedItemOperations.CHECK_CONSTRAINTS); + | BatchedItemOperations.CHECK_CONSTRAINTS + | BatchedItemOperations.CHECK_HOLD + | BatchedItemOperations.CHECK_RETENTION); // 'replace' is actually a 'remove existing/add new' operation; // this unfortunately changes the order of the parent's @@ -244,13 +248,16 @@ itemOps.removeNodeState(conflicting); // create new with given uuid at same location as conflicting: // check if new node can be added at other location - // (access rights, node type constraints, locking & versioning status) + // (access rights, node type constraints, locking & versioning + // status and retention/hold) itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_VERSIONING - | BatchedItemOperations.CHECK_CONSTRAINTS); + | BatchedItemOperations.CHECK_CONSTRAINTS + | BatchedItemOperations.CHECK_HOLD + | BatchedItemOperations.CHECK_RETENTION); // do create new node node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), @@ -463,7 +470,7 @@ // check if new node can be added (check access rights & // node type constraints only, assume locking & versioning status - // has already been checked on ancestor) + // and retention/hold has already been checked on ancestor) itemOps.checkAddNode(parent, nodeName, ntName, BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_CONSTRAINTS); @@ -496,7 +503,7 @@ // check if new node can be added (check access rights & // node type constraints only, assume locking & versioning status - // has already been checked on ancestor) + // and retention/hold has already been checked on ancestor) itemOps.checkAddNode(parent, nodeName, ntName, BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_CONSTRAINTS); Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd Wed Jan 28 09:50:50 2009 @@ -221,4 +221,13 @@ [rep:AuthorizableFolder] > nt:base, mix:referenceable + * (rep:Authorizable) = rep:User protected version - + * (rep:AuthorizableFolder) = rep:AuthorizableFolder protected version \ No newline at end of file + + * (rep:AuthorizableFolder) = rep:AuthorizableFolder protected version + +// ----------------------------------------------------------------------------- +// J A C K R A B B I T R E T E N T I O N M A N A G E M E N T +// ----------------------------------------------------------------------------- + +[rep:RetentionManageable] + mixin + - rep:hold (undefined) protected multiple ignore + - rep:retentionPolicy (undefined) protected ignore \ No newline at end of file Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml Wed Jan 28 09:50:50 2009 @@ -520,4 +520,11 @@ + + + + + + + Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/SessionRemoveItemTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/SessionRemoveItemTest.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/SessionRemoveItemTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/SessionRemoveItemTest.java Wed Jan 28 09:50:50 2009 @@ -20,11 +20,13 @@ import org.slf4j.LoggerFactory; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; +import org.apache.jackrabbit.test.RepositoryStub; import javax.jcr.InvalidItemStateException; import javax.jcr.AccessDeniedException; import javax.jcr.RepositoryException; import javax.jcr.Property; +import javax.jcr.Value; import javax.jcr.version.VersionException; import javax.jcr.lock.LockException; @@ -129,7 +131,8 @@ public void testRemoveLockedChildItem() throws RepositoryException, NotExecutableException { // add a child property and a child node to test deep lock effect. javax.jcr.Node childN = removeNode.addNode(nodeName2); - Property childP = removeNode.setProperty(propertyName2, "propvalue2"); + Value v = getJcrValue(superuser, RepositoryStub.PROP_PROP_VALUE2, RepositoryStub.PROP_PROP_TYPE2, "propvalue2"); + Property childP = removeNode.setProperty(propertyName2, v); removeNode.save(); if (!removeNode.isNodeType(mixLockable)) { @@ -172,7 +175,8 @@ public void testRemoveCheckedInItem() throws RepositoryException, NotExecutableException { // add a child property and a child node to test deep lock effect. javax.jcr.Node childN = removeNode.addNode(nodeName2); - Property childP = removeNode.setProperty(propertyName2, "propvalue2"); + Value v = getJcrValue(superuser, RepositoryStub.PROP_PROP_VALUE2, RepositoryStub.PROP_PROP_TYPE2, "propvalue2"); + Property childP = removeNode.setProperty(propertyName2, v); removeNode.save(); if (!removeNode.isNodeType(mixVersionable)) { Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/AbstractRetentionTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/AbstractRetentionTest.java?rev=738422&r1=738421&r2=738422&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/AbstractRetentionTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/AbstractRetentionTest.java Wed Jan 28 09:50:50 2009 @@ -16,9 +16,11 @@ */ package org.apache.jackrabbit.api.jsr283.retention; -import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; +import org.apache.jackrabbit.test.RepositoryStub; +import org.apache.jackrabbit.core.retention.RetentionPolicyImpl; +import org.apache.jackrabbit.core.SessionImpl; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -30,22 +32,38 @@ public abstract class AbstractRetentionTest extends AbstractJCRTest { protected RetentionManager retentionMgr; + protected String testNodePath; protected void setUp() throws Exception { super.setUp(); + // TODO: uncomment again. + // checkSupportedOption(Repository.OPTION_RETENTION_SUPPORTED); + retentionMgr = getRetentionManager(superuser); + testNodePath = testRootNode.getPath(); } - protected static RetentionManager getRetentionManager(Session s) throws RepositoryException, NotExecutableException { - // TODO: fix (Replace by Session) test as soon as jackrabbit implements 283 - if (!(s instanceof SessionImpl)) { + protected String getHoldName() throws RepositoryException, NotExecutableException { + String holdName = getProperty(RepositoryStub.PROP_HOLD_NAME); + if (holdName == null) { throw new NotExecutableException(); } - // TODO: uncomment again. - // checkSupportedOption(Repository.OPTION_RETENTION_SUPPORTED); + return holdName; + } + + protected RetentionPolicy getApplicableRetentionPolicy() throws NotExecutableException, RepositoryException { + return getApplicableRetentionPolicy("retentionPolicyName"); + } + + protected RetentionPolicy getApplicableRetentionPolicy(String jcrName) throws NotExecutableException, RepositoryException { + // TODO: move to repositoryStub/helper and adjust accordingly + return new RetentionPolicyImpl(jcrName, (SessionImpl)superuser); + } + + protected static RetentionManager getRetentionManager(Session s) throws RepositoryException, NotExecutableException { try { - return ((SessionImpl) s).getRetentionManager(); + return getJsr283Session(s).getRetentionManager(); } catch (UnsupportedRepositoryOperationException e) { throw new NotExecutableException(); } @@ -56,4 +74,13 @@ throw new NotExecutableException(); } } + + protected static org.apache.jackrabbit.api.jsr283.Session getJsr283Session(Session s) throws NotExecutableException { + // TODO: get rid of method once jsr 283 is released + if (s instanceof org.apache.jackrabbit.api.jsr283.Session) { + return (org.apache.jackrabbit.api.jsr283.Session) s; + } else { + throw new NotExecutableException(); + } + } } Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,225 @@ +/* + * 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.api.jsr283.retention; + +import org.apache.jackrabbit.test.NotExecutableException; +import org.apache.jackrabbit.test.RepositoryStub; + +import javax.jcr.Session; +import javax.jcr.RepositoryException; +import javax.jcr.Property; +import javax.jcr.Node; +import javax.jcr.Value; +import javax.jcr.nodetype.NodeType; + +/** + * HoldEffectTest... + */ +public class HoldEffectTest extends AbstractRetentionTest { + + private Node childN; + private Property childP; + private Session otherS; + + protected void setUp() throws Exception { + super.setUp(); + + childN = testRootNode.addNode(nodeName2); + Value v = getJcrValue(superuser, RepositoryStub.PROP_PROP_VALUE1, RepositoryStub.PROP_PROP_TYPE1, "test"); + childP = testRootNode.setProperty(propertyName1, v); + superuser.save(); + + otherS = helper.getSuperuserSession(); + } + + protected void tearDown() throws Exception { + if (otherS != null) { + otherS.logout(); + } + Hold[] holds = retentionMgr.getHolds(testNodePath); + for (int i = 0; i < holds.length; i++) { + retentionMgr.removeHold(testNodePath, holds[i]); + } + superuser.save(); + + super.tearDown(); + } + + // TODO: test importXML (session/wsp) / move (session/wsp) / copy ... + // TODO: test effect on child items + + public void testTransientShallowHold() throws RepositoryException, NotExecutableException { + retentionMgr.addHold(testNodePath, getHoldName(), false); + + assertNoEffect(testRootNode, nodeName3, propertyName2); + assertNoEffect(childN, nodeName3, propertyName2); + assertNoEffect(childP); + } + + public void testTransientShallowHoldForOtherSession() throws RepositoryException, NotExecutableException { + retentionMgr.addHold(testNodePath, getHoldName(), false); + + assertNoEffect((Node) otherS.getItem(testNodePath), nodeName3, propertyName2); + assertNoEffect((Node) otherS.getItem(childN.getPath()), nodeName3, propertyName2); + assertNoEffect((Property) otherS.getItem(childP.getPath())); + } + + public void testTransientDeepHold() throws RepositoryException, NotExecutableException { + retentionMgr.addHold(testNodePath, getHoldName(), true); + + assertNoEffect(testRootNode, nodeName3, propertyName2); + assertNoEffect(childN, nodeName3, propertyName2); + assertNoEffect(childP); + } + + public void testTransientDeepHoldForOtherSession() throws RepositoryException, NotExecutableException { + retentionMgr.addHold(testNodePath, getHoldName(), true); + + assertNoEffect((Node) otherS.getItem(testNodePath), nodeName3, propertyName2); + assertNoEffect((Node) otherS.getItem(childN.getPath()), nodeName3, propertyName2); + assertNoEffect((Property) otherS.getItem(childP.getPath())); + } + + public void testShallowHold() throws RepositoryException, NotExecutableException { + retentionMgr.addHold(testNodePath, getHoldName(), false); + superuser.save(); + + // check for superuser + assertNoEffect(childN, nodeName3, propertyName2); + assertEffect(testRootNode, childN.getName(), childP.getName(), nodeName3, propertyName2); + } + + public void testShallowHoldForOtherSession() throws RepositoryException, NotExecutableException { + retentionMgr.addHold(testNodePath, getHoldName(), false); + superuser.save(); + + // check for other session + assertNoEffect((Node) otherS.getItem(childN.getPath()), nodeName3, propertyName2); + assertEffect((Node) otherS.getItem(testNodePath), childN.getName(), childP.getName(), nodeName3, propertyName2); + } + + public void testDeepHold() throws RepositoryException, NotExecutableException { + Node n = childN.addNode(nodeName2); + Value v = getJcrValue(superuser, RepositoryStub.PROP_PROP_VALUE1, RepositoryStub.PROP_PROP_TYPE1, "test"); + Property p = childN.setProperty(propertyName1, v); + retentionMgr.addHold(testNodePath, getHoldName(), true); + superuser.save(); + + // check for superuser + assertEffect(testRootNode, childN.getName(), childP.getName(), nodeName3, propertyName2); + assertEffect(childN, n.getName(), p.getName(), nodeName3, propertyName2); + } + + public void testDeepHoldForOtherSession() throws RepositoryException, NotExecutableException { + Node n = childN.addNode(nodeName2); + Value v = getJcrValue(superuser, RepositoryStub.PROP_PROP_VALUE1, RepositoryStub.PROP_PROP_TYPE1, "test"); + Property p = childN.setProperty(propertyName1, v); + retentionMgr.addHold(testNodePath, getHoldName(), true); + superuser.save(); + + // check for other session + assertEffect((Node) otherS.getItem(testNodePath), childN.getName(), childP.getName(), nodeName3, propertyName2); + assertEffect((Node) otherS.getItem(childN.getPath()), n.getName(), p.getName(), nodeName3, propertyName2); + } + + private void assertEffect(Node targetNode, String childName, + String propName, String childName2, + String propName2) throws RepositoryException { + Session s = targetNode.getSession(); + try { + Node child = targetNode.getNode(childName); + child.remove(); + s.save(); + fail("Hold present must prevent a child node from being removed."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + try { + Property p = targetNode.getProperty(propName); + p.remove(); + s.save(); + fail("Hold present must prevent a child property from being removed."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + try { + Property p = targetNode.getProperty(propName); + p.setValue("test2"); + s.save(); + fail("Hold present must prevent the child property from being modified."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + try { + targetNode.addNode(childName2); + s.save(); + fail("Hold present must prevent the target node from having new nodes added."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + try { + Value v = getJcrValue(s, RepositoryStub.PROP_PROP_VALUE2, RepositoryStub.PROP_PROP_TYPE2, "test"); + targetNode.setProperty(propName2, v); + s.save(); + fail("Hold present must prevent the target node from having new properties set."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + + NodeType[] mixins = targetNode.getMixinNodeTypes(); + if (mixins.length > 0) { + try { + targetNode.removeMixin(mixins[0].getName()); + s.save(); + fail("Hold present must prevent the target node from having it's mixin types changed."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + } + try { + targetNode.remove(); + s.save(); + fail("Hold present must prevent the target node from being removed."); + } catch (RepositoryException e) { + // success + s.refresh(false); + } + } + + private void assertNoEffect(Node target, String childName, String propName) throws RepositoryException { + Session s = target.getSession(); + + Node n = target.addNode(childName); + Value v = getJcrValue(s, RepositoryStub.PROP_PROP_VALUE2, RepositoryStub.PROP_PROP_TYPE2, "test"); + Property p = target.setProperty(propName, v); + + n.remove(); + p.remove(); + } + + private void assertNoEffect(Property target) throws RepositoryException { + Session s = target.getSession(); + target.setValue("test3"); + target.remove(); + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldEffectTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java?rev=738422&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java Wed Jan 28 09:50:50 2009 @@ -0,0 +1,425 @@ +/* + * 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.api.jsr283.retention; + +import org.apache.jackrabbit.test.NotExecutableException; + +import javax.jcr.AccessDeniedException; +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.PropertyIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.lock.LockException; +import javax.jcr.version.VersionException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * RetentionManagerTest... + */ +public class HoldTest extends AbstractRetentionTest { + + private static boolean containsHold(Hold[] holds, Hold toTest) throws RepositoryException { + for (int i = 0; i < holds.length; i++) { + if (holds[i].getName().equals(toTest.getName()) && holds[i].isDeep() == toTest.isDeep()) { + return true; + } + } + return false; + } + + public void testAddHold() throws RepositoryException, NotExecutableException { + Hold hold = retentionMgr.addHold(testNodePath, getHoldName(), false); + Hold[] holds = retentionMgr.getHolds(testNodePath); + assertTrue("getHolds must return the hold added before.", holds.length >= 1); + assertTrue("getHolds doesn't return the hold added before", containsHold(holds, hold)); + } + + public void testAddHold2() throws RepositoryException, NotExecutableException { + Hold[] holdsBefore = retentionMgr.getHolds(testNodePath); + Hold hold = retentionMgr.addHold(testNodePath, getHoldName(), false); + assertFalse("The hold added must not have been present before.", containsHold(holdsBefore, hold)); + } + + public void testAddHoldIsTransient() throws RepositoryException, NotExecutableException { + Hold hold = retentionMgr.addHold(testNodePath, getHoldName(), false); + Hold[] holds = retentionMgr.getHolds(testNodePath); + + // revert the changes made + superuser.refresh(false); + Hold[] holds2 = retentionMgr.getHolds(testNodePath); + + assertEquals("Reverting transient changes must revert the hold added.", + holds.length -1, holds2.length); + assertFalse("Reverting transient changes must revert the hold added.", + containsHold(holds2, hold)); + } + + public void testRemoveHold() throws RepositoryException, NotExecutableException { + Hold hold = retentionMgr.addHold(testNodePath, getHoldName(), false); + + Hold[] holds = retentionMgr.getHolds(testNodePath); + + retentionMgr.removeHold(testNodePath, hold); + Hold[] holds2 = retentionMgr.getHolds(testNodePath); + + assertEquals("RetentionManager.removeHold should removed the hold added before.", + holds.length -1, holds2.length); + assertFalse("RetentionManager.removeHold should removed the hold added before.", + containsHold(holds2, hold)); + } + + public void testRemoveHoldIsTransient() throws RepositoryException, NotExecutableException { + Hold hold = retentionMgr.addHold(testNodePath, getHoldName(), false); + superuser.save(); + try { + Hold[] holds = retentionMgr.getHolds(testNodePath); + + retentionMgr.removeHold(testNodePath, hold); + superuser.refresh(false); + + Hold[] holds2 = retentionMgr.getHolds(testNodePath); + assertEquals("Reverting transient hold removal must restore the original state.", + Arrays.asList(holds), Arrays.asList(holds2)); + } finally { + // clear the hold that was permanently added before. + retentionMgr.removeHold(testNodePath, hold); + superuser.save(); + } + } + + public void testRemoveHoldFromChild() throws RepositoryException, NotExecutableException { + String childPath = testRootNode.addNode(nodeName2, testNodeType).getPath(); + Hold hold = retentionMgr.addHold(testNodePath, getHoldName(), false); + + try { + retentionMgr.removeHold(childPath, hold); + fail("Removing hold from another node must fail"); + } catch (RepositoryException e) { + // success + assertTrue(containsHold(retentionMgr.getHolds(testNodePath), hold)); + } + + // check again with persisted hold + superuser.save(); + try { + retentionMgr.removeHold(childPath, hold); + fail("Removing hold from another node must fail"); + } catch (RepositoryException e) { + // success + assertTrue(containsHold(retentionMgr.getHolds(testNodePath), hold)); + } finally { + // clear the hold that was permanently added before. + retentionMgr.removeHold(testNodePath, hold); + superuser.save(); + } + } + + public void testInvalidPath() throws RepositoryException, NotExecutableException { + String invalidPath = testPath; // not an absolute path. + try { + retentionMgr.getHolds(invalidPath); + fail("Accessing holds an invalid path must throw RepositoryException."); + } catch (RepositoryException e) { + // success + } + try { + retentionMgr.addHold(invalidPath, getHoldName(), true); + fail("Adding a hold at an invalid path must throw RepositoryException."); + } catch (RepositoryException e) { + // success + } + try { + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), true); + retentionMgr.removeHold(invalidPath, h); + fail("Removing a hold at an invalid path must throw RepositoryException."); + } catch (RepositoryException e) { + // success + } + } + + public void testNonExistingNodePath() throws RepositoryException, NotExecutableException { + String invalidPath = testNodePath + "/nonexisting"; + int cnt = 0; + while (getJsr283Session(superuser).nodeExists(invalidPath)) { + invalidPath += cnt++; + } + + try { + retentionMgr.getHolds(invalidPath); + fail("Accessing holds from non-existing node must throw PathNotFoundException."); + } catch (PathNotFoundException e) { + // success + } + try { + retentionMgr.addHold(invalidPath, getHoldName(), true); + fail("Adding a hold for a non-existing node must throw PathNotFoundException."); + } catch (PathNotFoundException e) { + // success + } + try { + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), true); + retentionMgr.removeHold(invalidPath, h); + fail("Removing a hold at a non-existing node must throw PathNotFoundException."); + } catch (PathNotFoundException e) { + // success + } + } + + public void testPropertyPath() throws RepositoryException, NotExecutableException { + String propPath = null; + for (PropertyIterator it = testRootNode.getProperties(); it.hasNext();) { + String path = it.nextProperty().getPath(); + if (!getJsr283Session(superuser).nodeExists(path)) { + propPath = path; + break; + } + } + if (propPath == null) { + throw new NotExecutableException(); + } + try { + retentionMgr.getHolds(propPath); + fail("Accessing holds from non-existing node must throw PathNotFoundException."); + } catch (PathNotFoundException e) { + // success + } + try { + retentionMgr.addHold(propPath, getHoldName(), true); + fail("Adding a hold for a non-existing node must throw PathNotFoundException."); + } catch (PathNotFoundException e) { + // success + } + try { + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), true); + retentionMgr.removeHold(propPath, h); + fail("Removing a hold at a non-existing node must throw PathNotFoundException."); + } catch (PathNotFoundException e) { + // success + } + } + + public void testInvalidName() { + try { + String invalidName = "*.[y]"; + retentionMgr.addHold(testNodePath, invalidName, false); + fail("Adding a hold with an invalid JCR name must fail."); + } catch (RepositoryException e) { + // success + } + } + + public void testReadOnlySession() throws NotExecutableException, RepositoryException { + javax.jcr.Session s = helper.getReadOnlySession(); + try { + RetentionManager rmgr = getRetentionManager(s); + try { + rmgr.getHolds(testNodePath); + fail("Read-only session doesn't have sufficient privileges to retrieve holds."); + } catch (AccessDeniedException e) { + // success + } + try { + rmgr.addHold(testNodePath, getHoldName(), false); + fail("Read-only session doesn't have sufficient privileges to retrieve holds."); + } catch (AccessDeniedException e) { + // success + } + } finally { + s.logout(); + } + } + + public void testAddHoldOnLockedNode() throws NotExecutableException, RepositoryException { + Node child = getLockedChildNode(); + // remember current holds for clean up. + List holdsBefore = Arrays.asList(retentionMgr.getHolds(child.getPath())); + + // get another session. + javax.jcr.Session otherS = helper.getSuperuserSession(); + try { + RetentionManager rmgr = getRetentionManager(otherS); + rmgr.addHold(child.getPath(), getHoldName(), false); + otherS.save(); + + fail("Adding hold on a locked node must throw LockException."); + } catch (LockException e) { + // success + } finally { + otherS.logout(); + + // clear holds (in case of test failure) + List holds = new ArrayList(Arrays.asList(retentionMgr.getHolds(child.getPath()))); + if (holds.removeAll(holdsBefore)) { + for (Iterator it = holds.iterator(); it.hasNext();) { + retentionMgr.removeHold(child.getPath(), (Hold) it.next()); + } + } + superuser.save(); + } + } + + public void testRemoveHoldOnLockedNode() throws NotExecutableException, RepositoryException { + Node child = getLockedChildNode(); + Hold h = retentionMgr.addHold(child.getPath(), getHoldName(), false); + testRootNode.save(); + + javax.jcr.Session otherS = helper.getSuperuserSession(); + try { + RetentionManager rmgr = getRetentionManager(otherS); + Hold[] holds = rmgr.getHolds(child.getPath()); + + if (holds.length > 0) { + rmgr.removeHold(child.getPath(), holds[0]); + otherS.save(); + fail("Removing a hold on a locked node must throw LockException."); + } + } catch (LockException e) { + // success + } finally { + otherS.logout(); + + // clear hold added before + try { + retentionMgr.removeHold(child.getPath(), h); + superuser.save(); + } catch (RepositoryException e) { + // should not get here if test is correctly executed. + } + } + } + + private Node getLockedChildNode() throws NotExecutableException, RepositoryException { + checkSupportedOption(superuser, Repository.OPTION_LOCKING_SUPPORTED); + Node child = testRootNode.addNode(nodeName2, testNodeType); + if (!child.isNodeType(mixLockable)) { + if (child.canAddMixin(mixLockable)) { + child.addMixin(mixLockable); + } else { + throw new NotExecutableException(); + } + } + testRootNode.save(); + child.lock(false, true); // session-scoped lock clean upon superuser-logout. + return child; + } + + public void testAddHoldOnCheckedInNode() throws NotExecutableException, RepositoryException { + Node child = getVersionableChildNode(); + child.checkout(); + child.checkin(); + + // get another session. + javax.jcr.Session otherS = helper.getSuperuserSession(); + try { + RetentionManager rmgr = getRetentionManager(otherS); + rmgr.addHold(child.getPath(), getHoldName(), false); + otherS.save(); + + fail("Adding hold on a checked-in node must throw VersionException."); + } catch (VersionException e) { + // success + } finally { + otherS.logout(); + + // clear holds (in case of test failure) + child.checkout(); + Hold[] holds = retentionMgr.getHolds(child.getPath()); + for (int i = 0; i < holds.length; i++) { + retentionMgr.removeHold(child.getPath(), holds[i]); + } + superuser.save(); + } + } + + public void testRemoveHoldOnCheckedInNode() throws NotExecutableException, RepositoryException { + Node vn = getVersionableChildNode(); + vn.checkout(); + Node n = vn.addNode(nodeName2); + Hold h = retentionMgr.addHold(n.getPath(), getHoldName(), false); + superuser.save(); + + // checkin on the parent node make the hold-containing node checked-in. + vn.checkin(); + + javax.jcr.Session otherS = helper.getSuperuserSession(); + try { + RetentionManager rmgr = getRetentionManager(otherS); + Hold[] holds = rmgr.getHolds(n.getPath()); + + if (holds.length > 0) { + rmgr.removeHold(n.getPath(), holds[0]); + otherS.save(); + fail("Removing a hold on a checked-in node must throw VersionException."); + } + } catch (VersionException e) { + // success + } finally { + otherS.logout(); + + // clear hold added before + vn.checkout(); + try { + retentionMgr.removeHold(n.getPath(), h); + superuser.save(); + } catch (RepositoryException e) { + // should not get here if test is correctly executed. + } + } + } + + private Node getVersionableChildNode() throws NotExecutableException, RepositoryException { + checkSupportedOption(superuser, Repository.OPTION_VERSIONING_SUPPORTED); + Node child = testRootNode.addNode(nodeName2, testNodeType); + if (!child.isNodeType(mixVersionable)) { + if (child.canAddMixin(mixVersionable)) { + child.addMixin(mixVersionable); + } else { + throw new NotExecutableException(); + } + } + testRootNode.save(); + return child; + } + + public void testHoldGetName() throws RepositoryException, NotExecutableException { + String holdName = getHoldName(); + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), false); + assertEquals("Hold.getName() must return the specified name.",holdName, h.getName()); + } + + public void testHoldGetName2() throws RepositoryException, NotExecutableException { + String holdName = getHoldName(); + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), true); + assertEquals("Hold.getName() must return the specified name.",holdName, h.getName()); + } + + public void testHoldIsDeep() throws RepositoryException, NotExecutableException { + String holdName = getHoldName(); + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), false); + assertEquals("Hold.isDeep() must reflect the specified flag.", false, h.isDeep()); + } + + public void testHoldIsDeep2() throws RepositoryException, NotExecutableException { + String holdName = getHoldName(); + Hold h = retentionMgr.addHold(testNodePath, getHoldName(), true); + assertEquals("Hold.isDeep() must reflect the specified flag.", true, h.isDeep()); + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/jsr283/retention/HoldTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url