You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2007/02/28 11:42:14 UTC

svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Author: jukka
Date: Wed Feb 28 02:42:13 2007
New Revision: 512695

URL: http://svn.apache.org/viewvc?view=rev&rev=512695
Log:
JCR-742: Added abstract base classes for nodes and properties.

Added:
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractItem.java   (with props)
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractNode.java   (with props)
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractProperty.java   (with props)

Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractItem.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractItem.java?view=auto&rev=512695
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractItem.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractItem.java Wed Feb 28 02:42:13 2007
@@ -0,0 +1,82 @@
+/*
+ * 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.commons;
+
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.RepositoryException;
+
+/**
+ * Abstract base class for implementing the JCR {@link Item} interface.
+ * <p>
+ * {@link Item} methods <em>without</em> a default implementation:
+ * <ul>
+ *   <li>{@link Item#accept(javax.jcr.ItemVisitor)}</li>
+ *   <li>{@link Item#getName()}</li>
+ *   <li>{@link Item#getParent()}</li>
+ *   <li>{@link Item#getPath()}</li>
+ *   <li>{@link Item#getSession()}</li>
+ *   <li>{@link Item#isModified()}</li>
+ *   <li>{@link Item#isNew()}</li>
+ *   <li>{@link Item#isNode()}</li>
+ *   <li>{@link Item#isSame(Item)}</li>
+ *   <li>{@link Item#refresh(boolean)}</li>
+ *   <li>{@link Item#remove()}</li>
+ *   <li>{@link Item#save()}</li>
+ * </ul>
+ */
+public abstract class AbstractItem implements Item {
+
+    /**
+     * Returns the ancestor of this item at the given depth from the
+     * root node.
+     * <p>
+     * The default implementation returns this item if the given depth
+     * equals the return value of the {@link Item#getDepth()} method.
+     * Otherwise calls the method recursively on the parent node.
+     *
+     * @param depth depth of the returned ancestor item
+     * @return ancestor item
+     * @throws RepositoryException if an error occurs
+     */
+    public Item getAncestor(int depth) throws RepositoryException {
+        if (getDepth() == depth) {
+            return this;
+        } else {
+            return getParent().getAncestor(depth);
+        }
+    }
+
+    /**
+     * Returns the depth of this item.
+     * <p>
+     * Recursively calls the method on the parent node and increments
+     * the return value to get the depth of this item. Returns zero if
+     * the parent node is not available (i.e. this is the root node).
+     *
+     * @return depth of this item
+     * @throws RepositoryException if an error occurs
+     */
+    public int getDepth() throws RepositoryException {
+        try {
+            return getParent().getDepth() + 1;
+        } catch (ItemNotFoundException e) {
+            return 0;
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractItem.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractNode.java?view=auto&rev=512695
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractNode.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractNode.java Wed Feb 28 02:42:13 2007
@@ -0,0 +1,770 @@
+/*
+ * 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.commons;
+
+import java.io.InputStream;
+import java.util.Calendar;
+
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.ItemVisitor;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+
+import org.apache.jackrabbit.name.QName;
+
+/**
+ * Abstract base class for implementing the JCR {@link Node} interface.
+ * <p>
+ * {@link Item} methods <em>without</em> a default implementation:
+ * <ul>
+ *   <li>{@link Item#accept(javax.jcr.ItemVisitor)}</li>
+ *   <li>{@link Item#getName()}</li>
+ *   <li>{@link Item#getParent()}</li>
+ *   <li>{@link Item#getSession()}</li>
+ *   <li>{@link Item#isModified()}</li>
+ *   <li>{@link Item#isNew()}</li>
+ *   <li>{@link Item#isSame(Item)}</li>
+ *   <li>{@link Item#refresh(boolean)}</li>
+ *   <li>{@link Item#remove()}</li>
+ *   <li>{@link Item#save()}</li>
+ * </ul>
+ * <p>
+ * {@link Node} methods <em>without</em> a default implementation:
+ * <ul>
+ *   <li>{@link Node#addMixin(String)}</li>
+ *   <li>{@link Node#addNode(String)}</li>
+ *   <li>{@link Node#addNode(String, String))}</li>
+ *   <li>{@link Node#canAddMixin(String)}</li>
+ *   <li>{@link Node#cancelMerge(Version)}</li>
+ *   <li>{@link Node#checkin()}</li>
+ *   <li>{@link Node#checkout()}</li>
+ *   <li>{@link Node#doneMerge(Version)}</li>
+ *   <li>{@link Node#getBaseVersion()}</li>
+ *   <li>{@link Node#getCorrespondingNodePath(String)}</li>
+ *   <li>{@link Node#getDefinition()}</li>
+ *   <li>{@link Node#getIndex()}</li>
+ *   <li>{@link Node#getLock()}</li>
+ *   <li>{@link Node#getNode(String)}</li>
+ *   <li>{@link Node#getNodes()}</li>
+ *   <li>{@link Node#getNodes(String)}</li>
+ *   <li>{@link Node#getPrimaryItem()}</li>
+ *   <li>{@link Node#getProperties()}</li>
+ *   <li>{@link Node#getProperties(String)}</li>
+ *   <li>{@link Node#getReferences()}</li>
+ *   <li>{@link Node#lock(boolean, boolean)}</li>
+ *   <li>{@link Node#merge(String, boolean)}</li>
+ *   <li>{@link Node#orderBefore(String, String)}</li>
+ *   <li>{@link Node#removeMixin(String)}</li>
+ *   <li>{@link Node#restore(Version, String, boolean)}</li>
+ *   <li>{@link Node#setProperty(String, Value)}</li>
+ *   <li>{@link Node#setProperty(String, Value[])}</li>
+ *   <li>{@link Node#unlock()}</li>
+ *   <li>{@link Node#update(String)}</li>
+ * </ul>
+ */
+public abstract class AbstractNode extends AbstractItem implements Node {
+
+    //----------------------------------------------------------------< Item >
+
+    /**
+     * Accepts the given item visitor.
+     * <p>
+     * The default implementation calls {@link ItemVisitor#visit(Node)} on
+     * the given visitor with this node as the argument.
+     *
+     * @param visitor item visitor
+     * @throws RepositoryException if an error occurs
+     */
+    public void accept(ItemVisitor visitor) throws RepositoryException {
+        visitor.visit(this);
+    }
+
+    /**
+     * Returns the path of this node.
+     * <p>
+     * The default implementation recursively calls this method on the
+     * parent node and appends the name and optionally the index of this
+     * node to construct the full path. Returns "/" if the parent node is
+     * not available (i.e. this is the root node).
+     *
+     * @return node path
+     * @throws RepositoryException if an error occurs
+     */
+    public String getPath() throws RepositoryException {
+        try {
+            StringBuffer buffer = new StringBuffer(getParent().getPath());
+            if (buffer.length() > 1) {
+                buffer.append('/');
+            }
+            buffer.append(getName());
+            int index = getIndex();
+            if (index != 1) {
+                buffer.append('[');
+                buffer.append(index);
+                buffer.append(']');
+            }
+            return buffer.toString();
+        } catch (ItemNotFoundException e) {
+            return "/";
+        }
+    }
+
+    /**
+     * Returns <code>true</code>.
+     *
+     * @return <code>true</code>
+     */
+    public boolean isNode() {
+        return true;
+    }
+
+    //----------------------------------------------------------------< Node >
+
+    /**
+     * Returns the declared mixin node types of this node.
+     * <p>
+     * The default implementation uses the values of the
+     * <code>jcr:mixinTypes</code> property to look up the mixin node types
+     * from the {@link NodeTypeManager} of the current workspace.
+     *
+     * @return mixin node types
+     * @throws RepositoryException if an error occurs
+     */
+    public NodeType[] getMixinNodeTypes() throws RepositoryException {
+        try {
+            NodeTypeManager manager =
+                getSession().getWorkspace().getNodeTypeManager();
+            Property property = getProperty(getName(QName.JCR_MIXINTYPES));
+            Value[] values = property.getValues();
+            NodeType[] types = new NodeType[values.length];
+            for (int i = 0; i < values.length; i++) {
+                types[i] = manager.getNodeType(values[i].getString());
+            }
+            return types;
+        } catch (PathNotFoundException e) {
+            // jcr:mixinTypes does not exist, i.e. no mixin types on this node
+            return new NodeType[0];
+        }
+    }
+
+    /**
+     * Returns the primary node type of this node.
+     * <p>
+     * The default implementation uses the value of the
+     * <code>jcr:primaryType</code> property to look up the primary
+     * node type from the {@link NodeTypeManager} of the current workspace.
+     *
+     * @return primary node type
+     * @throws RepositoryException if an error occurs
+     */
+    public NodeType getPrimaryNodeType() throws RepositoryException {
+        NodeTypeManager manager =
+            getSession().getWorkspace().getNodeTypeManager();
+        Property property = getProperty(getName(QName.JCR_PRIMARYTYPE));
+        return manager.getNodeType(property.getString());
+    }
+
+    /**
+     * Returns the property at the given relative path from this node.
+     * <p>
+     * The default implementation looks up the parent node of the given
+     * relative path and iterates through the properties of that node to
+     * find and return the identified property.
+     *
+     * @param relPath relative path of the property
+     * @return property
+     * @throws PathNotFoundException if the property is not found
+     * @throws RepositoryException if an error occurs
+     */
+    public Property getProperty(String relPath)
+            throws PathNotFoundException, RepositoryException {
+        // Corner case, remove any "/." self references at the end of the path
+        while (relPath.endsWith("/.")) {
+            relPath = relPath.substring(0, relPath.length() - 2);
+        }
+
+        // Find the parent node of the identified property
+        Node node = this;
+        int slash = relPath.lastIndexOf('/');
+        if (slash == 0) {
+            node = getSession().getRootNode();
+            relPath = relPath.substring(1);
+        } else if (slash > 0) {
+            node = getNode(relPath.substring(0, slash));
+            relPath = relPath.substring(slash + 1);
+        }
+
+        // Look for the named property. Must iterate and re-check for the name
+        // since the client could have used an invalid path like "./a|b".
+        PropertyIterator properties = node.getProperties(relPath);
+        while (properties.hasNext()) {
+            Property property = (Property) properties.next();
+            if (relPath.equals(property.getName())) {
+                return property;
+            }
+        }
+
+        throw new PathNotFoundException("Property not found: " + relPath);
+    }
+
+    /**
+     * Returns the UUID of this node.
+     * <p>
+     * The default implementation checks if this node is referenceable (i.e. of
+     * type <code>mix:referenceable</code>) and returns the contents of the
+     * <code>jcr:uuid</code> property if it is.
+     *
+     * @return node UUID
+     * @throws UnsupportedRepositoryOperationException
+     *         if this node is not referenceable
+     * @throws RepositoryException if an error occurs
+     */
+    public String getUUID()
+            throws UnsupportedRepositoryOperationException, RepositoryException {
+        if (isNodeType(getName(QName.MIX_REFERENCEABLE))) {
+            return getProperty(getName(QName.JCR_UUID)).getString();
+        } else {
+            throw new UnsupportedRepositoryOperationException(
+                    "This node is not referenceable: " + getPath());
+        }
+    }
+
+    /**
+     * Returns the version history of this node.
+     * <p>
+     * The default implementation returns the containing version history of
+     * the base version of this node.
+     *
+     * @return version history
+     * @throws RepositoryException if an error occurs
+     */
+    public VersionHistory getVersionHistory() throws RepositoryException {
+        return getBaseVersion().getContainingHistory();
+    }
+
+    /**
+     * Checks whether a node at the given relative path exists.
+     * <p>
+     * The default implementation looks up the node using
+     * {@link Node#getNode(String)} and returns <code>true</code> if
+     * a {@link PathNotFoundException} is not thrown.
+     *
+     * @param relPath relative path
+     * @return <code>true</code> if a node exists at the given path,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean hasNode(String relPath) throws RepositoryException {
+        try {
+            getNode(relPath);
+            return true;
+        } catch (PathNotFoundException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks if this node has one or more properties.
+     * <p>
+     * The default implementation calls {@link Node#getNodes()} and returns
+     * <code>true</code> iff returned iterator has at least one element.
+     *
+     * @return <code>true</code> if this node has child nodes,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean hasNodes() throws RepositoryException {
+        return getNodes().hasNext();
+    }
+
+    /**
+     * Checks if this node has one or more properties.
+     * <p>
+     * The default implementation calls {@link Node#getProperties()} and
+     * returns <code>true</code> iff returned iterator has at least one element.
+     * <p>
+     * Note that in normal circumstances (i.e. no weird access controls) this
+     * method will always return <code>true</code> since all nodes always have
+     * at least the <code>jcr:primaryType</code> property.
+     *
+     * @return <code>true</code> if this node has properties,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean hasProperties() throws RepositoryException {
+        return getProperties().hasNext();
+    }
+
+    /**
+     * Checks whether a property at the given relative path exists.
+     * <p>
+     * The default implementation looks up the property using
+     * {@link Node#getProperty(String)} and returns <code>true</code> if
+     * a {@link PathNotFoundException} is not thrown.
+     *
+     * @param relPath relative path
+     * @return <code>true</code> if a property exists at the given path,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean hasProperty(String relPath) throws RepositoryException {
+        try {
+            getProperty(relPath);
+            return true;
+        } catch (PathNotFoundException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks if this node holds a lock.
+     * <p>
+     * The default implementation calls {@link Node#getLock()} and returns
+     * <code>true</code> iff the holding node of the lock is the same as this
+     * node.
+     *
+     * @return <code>true</code> if this node holds a lock,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean holdsLock() throws RepositoryException {
+        try {
+            return isSame(getLock().getNode());
+        } catch (LockException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks whether this node is checked out.
+     * <p>
+     * The default implementation checks the <code>jcr:isCheckedOut</code>
+     * property if this node is versionable, and recursively calls this method
+     * on the parent node if this node is not versionable. A non-versionable
+     * root node always returns <code>true</code> from this method.
+     *
+     * @return <code>true</code> if this node is checked out,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean isCheckedOut() throws RepositoryException {
+        if (isNodeType(getName(QName.MIX_VERSIONABLE))) {
+            // This node is versionable, check the jcr:isCheckedOut property
+            return getProperty(getName(QName.JCR_ISCHECKEDOUT)).getBoolean();
+        } else {
+            try {
+                // This node is not versionable, is the parent checked out?
+                return getParent().isCheckedOut();
+            } catch (ItemNotFoundException e) {
+                // This node is the root node, always checked out
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Checks if this node is locked.
+     * <p>
+     * The default implementation calls {@link Node#getLock()} and returns
+     * <code>true</code> iff a {@link LockException} is not thrown.
+     *
+     * @return <code>true</code> if this node is locked,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean isLocked() throws RepositoryException {
+        try {
+            getLock();
+            return true;
+        } catch (LockException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks whether this node is of the given type.
+     * <p>
+     * The default implementation iterates through the primary and mixin
+     * types and all the supertypes of this node, returning <code>true</code>
+     * if a type with the given name is encountered. Returns <code>false</code>
+     * if none of the types matches.
+     *
+     * @param name type name
+     * @return <code>true</code> if this node is of the given type,
+     *         <code>false</code> otherwise
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean isNodeType(String name) throws RepositoryException {
+        NodeType type = getPrimaryNodeType();
+        if (name.equals(type.getName())) {
+            return true;
+        }
+        NodeType[] supertypes = type.getSupertypes();
+        for (int i = 0; i < supertypes.length; i++) {
+            if (name.equals(supertypes[i].getName())) {
+                return true;
+            }
+        }
+
+        NodeType[] mixins = getMixinNodeTypes();
+        for (int i = 0; i < mixins.length; i++) {
+            if (name.equals(mixins[i].getName())) {
+                return true;
+            }
+            supertypes = mixins[i].getSupertypes();
+            for (int j = 0; j < supertypes.length; j++) {
+                if (name.equals(supertypes[j].getName())) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Restores this node to the version with the given name.
+     * <p>
+     * The default implement retrieves the named {@link Version} from the
+     * associated {@link VersionHistory} and forwards the call to the
+     * {@link Node#restore(Version, boolean)} method.
+     *
+     * @param versionName version name
+     * @param removeExisting passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void restore(String versionName, boolean removeExisting)
+            throws RepositoryException {
+        restore(getVersionHistory().getVersion(versionName), removeExisting);
+    }
+
+    /**
+     * Restores this node to the given version.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#restore(Version, String, boolean)} method using the
+     * relative path ".".
+     *
+     * @param version passed through
+     * @param removeExisting passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void restore(Version version, boolean removeExisting)
+            throws RepositoryException {
+        restore(version, ".", removeExisting);
+    }
+
+    /**
+     * Restores this node to the version with the given label.
+     * <p>
+     * The default implement retrieves the labeled {@link Version} from the
+     * associated {@link VersionHistory} and forwards the call to the
+     * {@link Node#restore(Version, boolean)} method.
+     *
+     * @param versionLabel version label
+     * @param removeExisting passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void restoreByLabel(String versionLabel, boolean removeExisting)
+            throws RepositoryException {
+        restore(getVersionHistory().getVersionByLabel(versionLabel),
+                removeExisting);
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instances from
+     * the given string values and forwards the call to the
+     * {@link Node#setProperty(String, Value[])} method.
+     *
+     * @param name property name
+     * @param strings string values
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, String[] strings)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        Value[] values = new Value[strings.length];
+        for (int i = 0; i < strings.length; i++) {
+            values[i] = factory.createValue(strings[i]);
+        }
+        return setProperty(name, values);
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given string value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value string value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, String value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given binary value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value binary value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, InputStream value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given boolean value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value boolean value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, boolean value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given double value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value double value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, double value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given long value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value long value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, long value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given date value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value date value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, Calendar value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance from
+     * the given reference value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value reference value
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, Node value)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value));
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to convert the given value to the given
+     * type and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value property value
+     * @param type property type
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, Value value, int type)
+            throws RepositoryException {
+        if (value.getType() != type) {
+            ValueFactory factory = getSession().getValueFactory();
+            value = factory.createValue(value.getString(), type);
+        }
+        return setProperty(name, value);
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to convert the given values to the given
+     * type and forwards the call to the
+     * {@link Node#setProperty(String, Value[])} method.
+     *
+     * @param name property name
+     * @param values property values
+     * @param type property type
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, Value[] values, int type)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        Value[] converted = new Value[values.length];
+        for (int i = 0; i < values.length; i++) {
+            if (values[i].getType() != type) {
+                converted[i] = factory.createValue(values[i].getString(), type);
+            } else {
+                converted[i] = values[i];
+            }
+        }
+        return setProperty(name, converted);
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create {@link Value} instances of the
+     * given type from the given string values and forwards the call to the
+     * {@link Node#setProperty(String, Value[])} method.
+     *
+     * @param name property name
+     * @param strings string values
+     * @param type property type
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, String[] strings, int type)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        Value[] values = new Value[strings.length];
+        for (int i = 0; i < strings.length; i++) {
+            values[i] =  factory.createValue(strings[i], type);
+        }
+        return setProperty(name, values);
+    }
+
+    /**
+     * Sets the value of the named property.
+     * <p>
+     * The default implementation uses the {@link ValueFactory} of the
+     * current {@link Session} to create a {@link Value} instance of the
+     * given type from the given string value and forwards the call to the
+     * {@link Node#setProperty(String, Value)} method.
+     *
+     * @param name property name
+     * @param value string value
+     * @param type property type
+     * @return modified property
+     * @throws RepositoryException if an error occurs
+     */
+    public Property setProperty(String name, String value, int type)
+            throws RepositoryException {
+        ValueFactory factory = getSession().getValueFactory();
+        return setProperty(name, factory.createValue(value, type));
+    }
+
+    //-------------------------------------------------------------< private >
+
+    /**
+     * Returns the prefixed JCR name for the given {@link QName} using the
+     * current namespace mappings. The given name is assumed <em>not</em>
+     * to be in the default namespace, i.e. there will always be a non-empty
+     * prefix for the name.
+     *
+     * @param name namespaced name
+     * @return prefixed JCR name
+     * @throws RepositoryException if an error occurs
+     */
+    private String getName(QName name) throws RepositoryException {
+        assert name.getNamespaceURI().length() > 0;
+        String prefix = getSession().getNamespacePrefix(name.getNamespaceURI());
+        return prefix + ":" + name.getLocalName();
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractProperty.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractProperty.java?view=auto&rev=512695
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractProperty.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractProperty.java Wed Feb 28 02:42:13 2007
@@ -0,0 +1,426 @@
+/*
+ * 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.commons;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+
+import javax.jcr.Item;
+import javax.jcr.ItemVisitor;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+/**
+ * Abstract base class for implementing the JCR {@link Property} interface.
+ * <p>
+ * {@link Item} methods <em>without</em> a default implementation:
+ * <ul>
+ *   <li>{@link Item#getName()}</li>
+ *   <li>{@link Item#getParent()}</li>
+ *   <li>{@link Item#getSession()}</li>
+ *   <li>{@link Item#isModified()}</li>
+ *   <li>{@link Item#isNew()}</li>
+ *   <li>{@link Item#isSame(Item)}</li>
+ *   <li>{@link Item#refresh(boolean)}</li>
+ *   <li>{@link Item#remove()}</li>
+ *   <li>{@link Item#save()}</li>
+ * </ul>
+ * <p>
+ * {@link Property} methods <em>without</em> a default implementation:
+ * <ul>
+ *   <li>{@link Property#getDefinition()}</li>
+ *   <li>{@link Property#getValue()}</li>
+ *   <li>{@link Property#getValues()}</li>
+ * </ul>
+ */
+public abstract class AbstractProperty extends AbstractItem
+        implements Property {
+
+    //----------------------------------------------------------------< Item >
+
+    /**
+     * Accepts the given item visitor.
+     * <p>
+     * The default implementation calls {@link ItemVisitor#visit(Property)}
+     * on the given visitor with this property as the argument.
+     *
+     * @param visitor item visitor
+     * @throws RepositoryException if an error occurs
+     */
+    public void accept(ItemVisitor visitor) throws RepositoryException {
+        visitor.visit(this);
+    }
+
+    /**
+     * Returns the path of this property.
+     * <p>
+     * The default implementation constructs the path from the path of the
+     * parent node and the name of this property.
+     *
+     * @return property path
+     * @throws RepositoryException if an error occurs
+     */
+    public String getPath() throws RepositoryException {
+        StringBuffer buffer = new StringBuffer(getParent().getPath());
+        if (buffer.length() > 1) {
+            buffer.append('/');
+        }
+        buffer.append(getName());
+        return buffer.toString();
+    }
+
+    /**
+     * Returns <code>false</code>.
+     *
+     * @return <code>false</code>
+     */
+    public boolean isNode() {
+        return false;
+    }
+
+    //------------------------------------------------------------< Property >
+
+    /**
+     * Returns the boolean value of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return boolean value
+     * @throws RepositoryException if an error occurs
+     */
+    public boolean getBoolean() throws RepositoryException {
+        return getValue().getBoolean();
+    }
+
+    /**
+     * Returns the date value of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return date value
+     * @throws RepositoryException if an error occurs
+     */
+    public Calendar getDate() throws RepositoryException {
+        return getValue().getDate();
+    }
+
+    /**
+     * Returns the double value of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return double value
+     * @throws RepositoryException if an error occurs
+     */
+    public double getDouble() throws RepositoryException {
+        return getValue().getDouble();
+    }
+
+    /**
+     * Returns the length of the value of this property.
+     * <p>
+     * The default implementation measures the length of the {@link Value}
+     * instance returned by the generic {@link Property#getValue()} method.
+     *
+     * @return length of the property value
+     * @throws RepositoryException if an error occurs
+     */
+    public long getLength() throws RepositoryException {
+        return getLength(getValue());
+    }
+
+    /**
+     * Returns the lengths of the values of this property.
+     * <p>
+     * The default implementation measures the lengths of the {@link Value}
+     * instances returned by the generic {@link Property#getValues()} method.
+     *
+     * @return lengths of the property values
+     * @throws RepositoryException if an error occurs
+     */
+    public long[] getLengths() throws RepositoryException {
+        Value[] values = getValues();
+        long[] lengths = new long[values.length];
+        for (int i = 0; i < values.length; i++) {
+            lengths[i] = getLength(values[i]);
+        }
+        return lengths;
+    }
+
+    /**
+     * Returns the long value of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return long value
+     * @throws RepositoryException if an error occurs
+     */
+    public long getLong() throws RepositoryException {
+        return getValue().getLong();
+    }
+
+    /**
+     * Returns the node referenced by this property.
+     * <p>
+     * The default implementation checks that this property is a reference
+     * property (or tries to convert the property value to a reference) and
+     * uses {@link Session#getNodeByUUID(String)} to retrieve the
+     * referenced node.
+     *
+     * @return node referenced by this property
+     * @throws RepositoryException if an error occurs
+     */
+    public Node getNode() throws RepositoryException {
+        Session session = getSession();
+        String uuid = getString();
+        if (getType() != PropertyType.REFERENCE) {
+            uuid = session.getValueFactory().createValue(
+                    uuid, PropertyType.REFERENCE).getString();
+        }
+        return session.getNodeByUUID(uuid);
+    }
+
+    /**
+     * Returns the binary value of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return binary value
+     * @throws RepositoryException if an error occurs
+     */
+    public InputStream getStream() throws RepositoryException {
+        return getValue().getStream();
+    }
+
+    /**
+     * Returns the string value of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return string value
+     * @throws RepositoryException if an error occurs
+     */
+    public String getString() throws RepositoryException {
+        return getValue().getString();
+    }
+
+    /**
+     * Returns the type of this property.
+     * <p>
+     * The default implementation forwards the method call to the
+     * {@link Value} instance returned by the generic
+     * {@link Property#getValue()} method.
+     *
+     * @return property type
+     * @throws RepositoryException if an error occurs
+     */
+    public int getType() throws RepositoryException {
+        return getValue().getType();
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, Value)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(Value value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the values of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, Value[])} method of the parent node
+     * using the name of this property.
+     *
+     * @param values passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(Value[] values) throws RepositoryException {
+        getParent().setProperty(getName(), values);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, String)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(String value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the values of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, String[])} method of the parent node
+     * using the name of this property.
+     *
+     * @param values passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(String[] values) throws RepositoryException {
+        getParent().setProperty(getName(), values);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, InputStream)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(InputStream value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, long)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(long value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, double)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(double value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, Calendar)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(Calendar value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, boolean)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(boolean value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    /**
+     * Sets the value of this property.
+     * <p>
+     * The default implementation forwards the call to the
+     * {@link Node#setProperty(String, Node)} method of the parent node
+     * using the name of this property.
+     *
+     * @param value passed through
+     * @throws RepositoryException if an error occurs
+     */
+    public void setValue(Node value) throws RepositoryException {
+        getParent().setProperty(getName(), value);
+    }
+
+    //-------------------------------------------------------------< private >
+
+    /**
+     * Returns the length of the given value.
+     *
+     * @param value value
+     * @return length of the value
+     * @throws RepositoryException if an error occurs
+     */
+    private long getLength(Value value) throws RepositoryException {
+        if (value.getType() != PropertyType.BINARY) {
+            return value.getString().length();
+        } else {
+            try {
+                InputStream stream = value.getStream();
+                try {
+                    long length = 0;
+                    byte[] buffer = new byte[4096];
+                    int n = stream.read(buffer);
+                    while (n != -1) {
+                        length += n;
+                        n = stream.read(buffer);
+                    }
+                    return length;
+                } finally {
+                    stream.close();
+                }
+            } catch (IOException e) {
+                throw new RepositoryException(
+                        "Failed to count the length of a binary value", e);
+            }
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/AbstractProperty.java
------------------------------------------------------------------------------
    svn:eol-style = native



Re: svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On 3/2/07, Stefan Guggisberg <st...@gmail.com> wrote:
> On 3/2/07, Jukka Zitting <ju...@gmail.com> wrote:
> > The only thing being protected is that the client will not have a
> > reference to a parent or ancestor node. I could always construct a
> > custom Node instance myself that would act just like such a  parent or
> > ancestor node would, throwing AccessControlExceptions for all attempts
> > to access property, child node, or metadata information. How would
> > such an instance differ from something that Item.getParent() or
> > Item.getAncestor() can't return?
>
> it wouldn't be compliant with the spec i guess ;)
>
> you wouldn't expect e.g. getPrimaryType() throwing an AccessDeniedExcpetion.

Fair enough. :-)

> the jsr 170 eg's rationale for the different behaviours of Session.getItem
> and Item.getParent wrt access control is as you assumed correctly:
>
> Session.getItem throws a PathNotFound if access is denied. Item.getParent
> otoh throws an AccessDeniedException in same situation since the existence
> of the requested item is evident. once you have e.g. a reference to a Node
> instance you should imo be able to retrieve its primary/mixin node types.

I guess that's valid. I'll add a note about this and an extra catch
for the AccessDeniedException in the abstract base classes I've been
drafting. I figure all reasonable subclasses would override those
default method implementations in any case, so this shouldn't be much
of a problem.

BR,

Jukka Zitting

Re: svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Posted by Stefan Guggisberg <st...@gmail.com>.
On 3/2/07, Jukka Zitting <ju...@gmail.com> wrote:
> Hi,
>
> On 2/28/07, Marcel Reutegger <ma...@gmx.net> wrote:
> > according to the spec Item.getPath() is different in this respect. that is, you
> > can get the path of an item even though an ancestor of an item is not 'visible'
> > to you. I put visible in quotes because at least the name of the ancestor
> > where a session does not have read right is disclosed.
>
> Looking deeper into this I find it curious that AccessDeniedException
> is declared for Item.getParent() and Item.getAncestor(), but not for
> things like Session.getItem(). I assume this is because throwing an
> AccessDeniedException from Session.getItem() would actually expose the
> fact that such an item exists, and thus it is more correct to throw a
> PathNotFoundException. However, with Item.getParent() or
> Item.getAncestor() the existence of the parent or ancestor node is
> already known, so throwing an exception doesn't seem to serve any
> security purpose.
>
> The only thing being protected is that the client will not have a
> reference to a parent or ancestor node. I could always construct a
> custom Node instance myself that would act just like such a  parent or
> ancestor node would, throwing AccessControlExceptions for all attempts
> to access property, child node, or metadata information. How would
> such an instance differ from something that Item.getParent() or
> Item.getAncestor() can't return?

it wouldn't be compliant with the spec i guess ;)

you wouldn't expect e.g. getPrimaryType() throwing an AccessDeniedExcpetion.

the jsr 170 eg's rationale for the different behaviours of Session.getItem
and Item.getParent wrt access control is as you assumed correctly:

Session.getItem throws a PathNotFound if access is denied. Item.getParent
otoh throws an AccessDeniedException in same situation since the existence
of the requested item is evident. once you have e.g. a reference to a Node
instance you should imo be able to retrieve its primary/mixin node types.

cheers
stefan

>
> > well, I guess this is just a detail...
>
> An interesting detail, as it exposes some of the underlying access
> control assumptions in JCR... Perhaps a topic to take up at the
> JSR-283 EG.
>
> BR,
>
> Jukka Zitting
>

Re: svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On 2/28/07, Marcel Reutegger <ma...@gmx.net> wrote:
> according to the spec Item.getPath() is different in this respect. that is, you
> can get the path of an item even though an ancestor of an item is not 'visible'
> to you. I put visible in quotes because at least the name of the ancestor
> where a session does not have read right is disclosed.

Looking deeper into this I find it curious that AccessDeniedException
is declared for Item.getParent() and Item.getAncestor(), but not for
things like Session.getItem(). I assume this is because throwing an
AccessDeniedException from Session.getItem() would actually expose the
fact that such an item exists, and thus it is more correct to throw a
PathNotFoundException. However, with Item.getParent() or
Item.getAncestor() the existence of the parent or ancestor node is
already known, so throwing an exception doesn't seem to serve any
security purpose.

The only thing being protected is that the client will not have a
reference to a parent or ancestor node. I could always construct a
custom Node instance myself that would act just like such a  parent or
ancestor node would, throwing AccessControlExceptions for all attempts
to access property, child node, or metadata information. How would
such an instance differ from something that Item.getParent() or
Item.getAncestor() can't return?

> well, I guess this is just a detail...

An interesting detail, as it exposes some of the underlying access
control assumptions in JCR... Perhaps a topic to take up at the
JSR-283 EG.

BR,

Jukka Zitting

Re: svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Posted by Marcel Reutegger <ma...@gmx.net>.
Jukka Zitting wrote:
> And Item.getPath(). In fact, I'd argue that if getPath() is allowed to
> return the path of an item, then there is no good reason to prevent
> getParent() since the getPath() call already discloses more
> information about the parent node than what getParent() without any
> other method calls (which would of course be access controlled) does.

according to the spec Item.getPath() is different in this respect. that is, you 
can get the path of an item even though an ancestor of an item is not 'visible' 
to you. I put visible in quotes because at least the name of the ancestor where 
a session does not have read right is disclosed. whenever an item is not 
accessible through a method that call throws a AccessDeniedException throughout 
the api:

http://www.day.com/maven/jsr170/javadocs/jcr-1.0/javax/jcr/class-use/AccessDeniedException.html

hence, I assume that Item.getPath() should not throw an AccessDeniedException, 
but of course there might be implementations that still do.

well, I guess this is just a detail...

regards
  marcel

Re: svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On 2/28/07, Marcel Reutegger <ma...@gmx.net> wrote:
> please note that getParent() may also throw an access denied exception, in case
> the session does not have sufficient access rights for an ancestor. however
> getDepth() should still succeed in that case.

Hmm, good point. I think it's a valid option to let getDepth() throw a
RepositoryException (by just passing through the
AccessDeniedException) to indicate that the depth of the item can not
be calculcated in such cases.

> iirc the same also applies to getAncestor(int depth).

And Item.getPath(). In fact, I'd argue that if getPath() is allowed to
return the path of an item, then there is no good reason to prevent
getParent() since the getPath() call already discloses more
information about the parent node than what getParent() without any
other method calls (which would of course be access controlled) does.

> > +    public int getDepth() throws RepositoryException {
> > +        try {
> > +            return getParent().getDepth() + 1;
> > +        } catch (ItemNotFoundException e) {
> > +            return 0;
> > +        }
> > +    }

BR,

Jukka Zitting

Re: svn commit: r512695 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons: AbstractItem.java AbstractNode.java AbstractProperty.java

Posted by Marcel Reutegger <ma...@gmx.net>.
Hi Jukka,

please note that getParent() may also throw an access denied exception, in case 
the session does not have sufficient access rights for an ancestor. however 
getDepth() should still succeed in that case.

iirc the same also applies to getAncestor(int depth).

regards
  marcel

jukka@apache.org wrote:
> Author: jukka
> Date: Wed Feb 28 02:42:13 2007
> New Revision: 512695
> 
> +
> +    /**
> +     * Returns the depth of this item.
> +     * <p>
> +     * Recursively calls the method on the parent node and increments
> +     * the return value to get the depth of this item. Returns zero if
> +     * the parent node is not available (i.e. this is the root node).
> +     *
> +     * @return depth of this item
> +     * @throws RepositoryException if an error occurs
> +     */
> +    public int getDepth() throws RepositoryException {
> +        try {
> +            return getParent().getDepth() + 1;
> +        } catch (ItemNotFoundException e) {
> +            return 0;
> +        }
> +    }
> +
> +}