You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ch...@apache.org on 2008/08/02 11:56:04 UTC

svn commit: r681945 [5/9] - in /felix/trunk/org.osgi.compendium: ./ doc/ src/main/java/info/ src/main/java/info/dmtree/ src/main/java/info/dmtree/notification/ src/main/java/info/dmtree/notification/spi/ src/main/java/info/dmtree/registry/ src/main/jav...

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,442 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/security/DmtPermission.java,v 1.10 2006/10/19 13:32:53 tszeredi Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * 
+ * Licensed 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 info.dmtree.security;
+
+import info.dmtree.Acl;
+import info.dmtree.Uri;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.*;
+
+/**
+ * Controls access to management objects in the Device Management Tree (DMT). It
+ * is intended to control local access to the DMT. DmtPermission target string
+ * identifies the management object URI and the action field lists the OMA DM
+ * commands that are permitted on the management object. Example:
+ * 
+ * <pre>
+ * DmtPermission(&quot;./OSGi/bundles&quot;, &quot;Add,Replace,Get&quot;);
+ * </pre>
+ * 
+ * This means that owner of this permission can execute Add, Replace and Get
+ * commands on the ./OSGi/bundles management object. It is possible to use
+ * wildcards in both the target and the actions field. Wildcard in the target
+ * field means that the owner of the permission can access children nodes of the
+ * target node. Example:
+ * 
+ * <pre>
+ * DmtPermission(&quot;./OSGi/bundles/*&quot;, &quot;Get&quot;);
+ * </pre>
+ * 
+ * This means that owner of this permission has Get access on every child node
+ * of ./OSGi/bundles. The asterix does not necessarily have to follow a '/'
+ * character. For example the
+ * <code>&quot;./OSGi/a*&quot;</code> target matches the 
+ * <code>./OSGi/applications</code> subtree.
+ * <p>
+ * If wildcard is present in the actions field, all legal OMA DM commands are 
+ * allowed on the designated nodes(s) by the owner of the permission.  Action
+ * names are interpreted case-insensitively, but the canonical action string
+ * returned by {@link #getActions} uses the forms defined by the action 
+ * constants.
+ */
+public class DmtPermission extends Permission {
+    private static final long serialVersionUID = -1910969921419407809L;
+
+    /**
+     * Holders of DmtPermission with the Add action present can create new nodes
+     * in the DMT, that is they are authorized to execute the
+     * createInteriorNode() and createLeafNode() methods of the DmtSession. This
+     * action is also required for the copy() command, which needs to perform
+     * node creation operations (among others).
+     */
+    public static final String ADD = "Add";
+
+    /**
+     * Holders of DmtPermission with the Delete action present can delete nodes
+     * from the DMT, that is they are authorized to execute the deleteNode()
+     * method of the DmtSession.
+     */
+    public static final String DELETE = "Delete";
+
+    /**
+     * Holders of DmtPermission with the Exec action present can execute nodes
+     * in the DMT, that is they are authorized to call the execute() method of
+     * the DmtSession.
+     */
+    public static final String EXEC = "Exec";
+
+    /**
+     * Holders of DmtPermission with the Get action present can query DMT node
+     * value or properties, that is they are authorized to execute the
+     * isLeafNode(), getNodeAcl(), getEffectiveNodeAcl(), getMetaNode(),
+     * getNodeValue(), getChildNodeNames(), getNodeTitle(), getNodeVersion(),
+     * getNodeTimeStamp(), getNodeSize() and getNodeType() methods of the
+     * DmtSession. This action is also required for the copy() command, which
+     * needs to perform node query operations (among others).
+     */
+    public static final String GET = "Get";
+
+    /**
+     * Holders of DmtPermission with the Replace action present can update DMT
+     * node value or properties, that is they are authorized to execute the
+     * setNodeAcl(), setNodeTitle(), setNodeValue(), setNodeType() and
+     * renameNode() methods of the DmtSession. This action is also be required
+     * for the copy() command if the original node had a title property (which
+     * must be set in the new node).
+     */
+    public static final String REPLACE = "Replace";
+
+    // does this permission have a wildcard at the end?
+    private final boolean prefixPath;
+
+    // the name without the wildcard on the end
+    private final String path;
+
+    // the actions mask
+    private final int mask;
+
+    // the canonical action string (redundant)
+    private final String actions;
+
+    /**
+     * Creates a new DmtPermission object for the specified DMT URI with the
+     * specified actions. The given URI can be:
+     * <ul>
+     * <li> <code>"*"</code>, which matches all valid
+     * (see {@link Uri#isValidUri}) absolute URIs;
+     * <li> the prefix of an absolute URI followed by the <code>*</code> 
+     * character (for example <code>"./OSGi/L*"</code>), which matches all valid
+     * absolute URIs beginning with the given prefix;
+     * <li> a valid absolute URI, which matches itself.
+     * </ul>
+     * <p>
+     * Since the <code>*</code> character is itself a valid URI character, it 
+     * can appear as the last character of a valid absolute URI. To distinguish
+     * this case from using <code>*</code> as a wildcard, the <code>*</code> 
+     * character at the end of the URI must be escaped with the <code>\</code> 
+     * charater. For example the URI <code>"./a*"</code> matches 
+     * <code>"./a"</code>, <code>"./aa"</code>, <code>"./a/b"</code> etc. while
+     * <code>"./a\*"</code> matches <code>"./a*"</code> only.
+     * <p>
+     * The actions string must either be "*" to allow all actions, or it must
+     * contain a non-empty subset of the valid actions, defined as constants in
+     * this class.
+     * 
+     * @param dmtUri URI of the management object (or subtree)
+     * @param actions OMA DM actions allowed
+     * @throws NullPointerException if any of the parameters are
+     *         <code>null</code>
+     * @throws IllegalArgumentException if any of the parameters are invalid
+     */
+    public DmtPermission(String dmtUri, String actions) {
+        super(dmtUri);
+        mask = getMask(actions);
+        this.actions = canonicalActions(mask);
+
+        if (dmtUri == null)
+            throw new NullPointerException("'dmtUri' parameter must not be " +
+                    "null.");
+        
+        prefixPath = dmtUri.endsWith("*") && !dmtUri.endsWith("\\*");
+        
+        if(prefixPath) {
+            dmtUri = dmtUri.substring(0, dmtUri.length() - 1);
+
+            // the single "*" as dmtUri is the only valid non-absolute URI param
+            if(dmtUri.length() == 0) {
+                path = "";
+                return;
+            }
+        }
+        
+        // if URI ends with "/*", remove it before the validity check
+        if(prefixPath && dmtUri.endsWith("/") && !dmtUri.endsWith("\\/"))
+            checkUri(dmtUri.substring(0, dmtUri.length() - 1));
+        else
+            checkUri(dmtUri);
+        
+        // canonicalize URI: remove escapes from non-special characters
+        StringBuffer sb = new StringBuffer(dmtUri);
+        int i = 0;
+        while(i < sb.length()) { // length can decrease during the loop!
+            if(sb.charAt(i) == '\\') {
+                // there must be a next character after a '\' in a valid URI
+                char nextCh = sb.charAt(i+1);
+                if(nextCh != '/' && nextCh != '\\')
+                    sb.deleteCharAt(i); // remove the extra '\'
+                else
+                    i++;
+            }
+            i++;
+        }
+        path = sb.toString();
+    }
+    
+    private void checkUri(String dmtUri) throws IllegalArgumentException {
+        if(!Uri.isValidUri(dmtUri))
+            throw new IllegalArgumentException("'dmtUri' parameter does not " +
+                    "contain a valid URI.");
+        
+        if(!Uri.isAbsoluteUri(dmtUri))
+            throw new IllegalArgumentException("'dmtUri' parameter does not " +
+                    "contain an absolute URI.");
+    }
+
+    /**
+     * Checks whether the given object is equal to this DmtPermission instance.
+     * Two DmtPermission instances are equal if they have the same target string
+     * and the same action mask. The "*" action mask is considered equal to a
+     * mask containing all actions.
+     * 
+     * @param obj the object to compare to this DmtPermission instance
+     * @return <code>true</code> if the parameter represents the same
+     *         permissions as this instance
+     */
+    public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
+
+        if (!(obj instanceof DmtPermission))
+            return false;
+
+        DmtPermission other = (DmtPermission) obj;
+
+        return mask == other.mask && prefixPath == other.prefixPath
+                && path.equals(other.path);
+    }
+
+    /**
+     * Returns the String representation of the action list. The allowed actions
+     * are listed in the following order: Add, Delete, Exec, Get, Replace. The
+     * wildcard character is not used in the returned string, even if the class
+     * was created using the "*" wildcard.
+     * 
+     * @return canonical action list for this permission object
+     */
+    public String getActions() {
+        return actions;
+    }
+
+    /**
+     * Returns the hash code for this permission object. If two DmtPermission
+     * objects are equal according to the {@link #equals} method, then calling
+     * this method on each of the two DmtPermission objects must produce the
+     * same integer result.
+     * 
+     * @return hash code for this permission object
+     */
+    public int hashCode() {
+        return new Integer(mask).hashCode()
+                ^ new Boolean(prefixPath).hashCode() ^ path.hashCode();
+    }
+
+    /**
+     * Checks if this DmtPermission object &quot;implies&quot; the specified
+     * permission. This method returns <code>false</code> if and only if at
+     * least one of the following conditions are fulfilled for the specified
+     * permission:
+     * <ul>
+     * <li>it is not a DmtPermission
+     * <li>its set of actions contains an action not allowed by this permission
+     * <li>the set of nodes defined by its path contains a node not defined by
+     * the path of this permission
+     * </ul>
+     * 
+     * @param p the permission to check for implication
+     * @return true if this DmtPermission instance implies the specified
+     *         permission
+     */
+    public boolean implies(Permission p) {
+        if (!(p instanceof DmtPermission))
+            return false;
+
+        DmtPermission other = (DmtPermission) p;
+
+        if ((mask & other.mask) != other.mask)
+            return false;
+
+        return impliesPath(other);
+    }
+
+    /**
+     * Returns a new PermissionCollection object for storing DmtPermission
+     * objects.
+     * 
+     * @return the new PermissionCollection
+     */
+    public PermissionCollection newPermissionCollection() {
+        return new DmtPermissionCollection();
+    }
+
+    // parses the given action string, and returns the corresponding action mask
+    private static int getMask(String actions) {
+        int mask = 0;
+
+        if (actions == null)
+            throw new NullPointerException(
+                    "'actions' parameter cannot be null.");
+
+        if (actions.equals("*"))
+            return Acl.ALL_PERMISSION;
+
+        // empty tokens (swallowed by StringTokenizer) are not considered errors
+        StringTokenizer st = new StringTokenizer(actions, ",");
+        while (st.hasMoreTokens()) {
+            String action = st.nextToken();
+            if (action.equalsIgnoreCase(GET)) {
+                mask |= Acl.GET;
+            } else if (action.equalsIgnoreCase(ADD)) {
+                mask |= Acl.ADD;
+            } else if (action.equalsIgnoreCase(REPLACE)) {
+                mask |= Acl.REPLACE;
+            } else if (action.equalsIgnoreCase(DELETE)) {
+                mask |= Acl.DELETE;
+            } else if (action.equalsIgnoreCase(EXEC)) {
+                mask |= Acl.EXEC;
+            } else
+                throw new IllegalArgumentException("Invalid action '" + action
+                        + "'");
+        }
+
+        if (mask == 0)
+            throw new IllegalArgumentException("Action mask cannot be empty.");
+
+        return mask;
+    }
+
+    // generates the canonical string representation of the action list
+    private static String canonicalActions(int mask) {
+        StringBuffer sb = new StringBuffer();
+        addAction(sb, mask, Acl.ADD, ADD);
+        addAction(sb, mask, Acl.DELETE, DELETE);
+        addAction(sb, mask, Acl.EXEC, EXEC);
+        addAction(sb, mask, Acl.GET, GET);
+        addAction(sb, mask, Acl.REPLACE, REPLACE);
+        return sb.toString();
+    }
+
+    // if 'flag' appears in 'mask', appends the 'action' string to the contents
+    // of 'sb', separated by a comma if needed
+    private static void addAction(StringBuffer sb, int mask, int flag,
+            String action) {
+        if ((mask & flag) != 0) {
+            if (sb.length() > 0)
+                sb.append(',');
+            sb.append(action);
+        }
+    }
+
+    // used by DmtPermissionCollection to retrieve the action mask
+    int getMask() {
+        return mask;
+    }
+
+    // returns true if the path parameter of the given DmtPermission is
+    // implied by the path of this permission, i.e. this path is a prefix of the
+    // other path, but ends with a *, or the two path strings are equal
+    boolean impliesPath(DmtPermission p) {
+        return prefixPath ? p.path.startsWith(path) : !p.prefixPath
+                && p.path.equals(path);
+    }
+}
+
+/**
+ * Represents a homogeneous collection of DmtPermission objects.
+ */
+final class DmtPermissionCollection extends PermissionCollection {
+    private static final long serialVersionUID = -4172481774562012941L;
+
+    // OPTIMIZE keep a special flag for permissions of "*" path
+
+    private ArrayList perms;
+
+    /**
+     * Create an empty DmtPermissionCollection object.
+     */
+    public DmtPermissionCollection() {
+        perms = new ArrayList();
+    }
+
+    /**
+     * Adds a permission to the DmtPermissionCollection.
+     * 
+     * @param permission the Permission object to add
+     * @exception IllegalArgumentException if the permission is not a
+     *            DmtPermission
+     * @exception SecurityException if this DmtPermissionCollection object has
+     *            been marked readonly
+     */
+    public void add(Permission permission) {
+        if (!(permission instanceof DmtPermission))
+            throw new IllegalArgumentException(
+                    "Cannot add permission, invalid permission type: "
+                            + permission);
+        if (isReadOnly())
+            throw new SecurityException(
+                    "Cannot add permission, collection is marked read-only.");
+
+        // No need to synchronize because all adds are done sequentially
+        // before any implies() calls
+        perms.add(permission);
+    }
+
+    /**
+     * Check whether this set of permissions implies the permission specified in
+     * the parameter.
+     * 
+     * @param permission the Permission object to compare
+     * @return true if the parameter permission is a proper subset of the
+     *         permissions in the collection, false otherwise
+     */
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof DmtPermission))
+            return false;
+
+        DmtPermission other = (DmtPermission) permission;
+
+        int required = other.getMask();
+        int available = 0;
+        int needed = required;
+
+        Iterator i = perms.iterator();
+        while (i.hasNext()) {
+            DmtPermission p = (DmtPermission) i.next();
+            if (((needed & p.getMask()) != 0) && p.impliesPath(other)) {
+                available |= p.getMask();
+                if ((available & required) == required)
+                    return true;
+                needed = (required ^ available);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of all the DmtPermission objects in the container.
+     * The returned value cannot be <code>null</code>.
+     * 
+     * @return an enumeration of all the DmtPermission objects
+     */
+    public Enumeration elements() {
+        // Convert Iterator into Enumeration
+        return Collections.enumeration(perms);
+    }
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,263 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/security/DmtPrincipalPermission.java,v 1.4 2006/07/12 21:21:52 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ *
+ * Licensed 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 info.dmtree.security;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.*;
+
+/**
+ * Indicates the callers authority to create DMT sessions on behalf of a remote
+ * management server. Only protocol adapters communicating with management
+ * servers should be granted this permission.
+ * <p>
+ * <code>DmtPrincipalPermission</code> has a target string which controls the
+ * name of the principal on whose behalf the protocol adapter can act. A
+ * wildcard is allowed at the end of the target string, to allow using any
+ * principal name with the given prefix. The &quot;*&quot; target means the
+ * adapter can create a session in the name of any principal.
+ */
+public class DmtPrincipalPermission extends Permission {
+    private static final long serialVersionUID = 6388752177325038332L;
+
+    // specifies whether the target string had a wildcard at the end
+    private final boolean isPrefix;
+
+    // the target string without the wildcard (if there was one)
+    private final String principal;
+
+    /**
+     * Creates a new <code>DmtPrincipalPermission</code> object with its name
+     * set to the target string. Name must be non-null and non-empty.
+     * 
+     * @param target the name of the principal, can end with <code>*</code> to
+     *        match any principal with the given prefix
+     * @throws NullPointerException if <code>name</code> is <code>null</code>
+     * @throws IllegalArgumentException if <code>name</code> is empty
+     */
+    public DmtPrincipalPermission(String target) {
+        super(target);
+
+        if (target == null)
+            throw new NullPointerException(
+                    "'target' parameter must not be null.");
+
+        if (target.equals(""))
+            throw new IllegalArgumentException(
+                    "'target' parameter must not be empty.");
+
+        isPrefix = target.endsWith("*");
+        if (isPrefix)
+            principal = target.substring(0, target.length() - 1);
+        else
+            principal = target;
+    }
+
+    /**
+     * Creates a new <code>DmtPrincipalPermission</code> object using the
+     * 'canonical' two argument constructor. In this version this class does not
+     * define any actions, the second argument of this constructor must be "*"
+     * so that this class can later be extended in a backward compatible way.
+     * 
+     * @param target the name of the principal, can end with <code>*</code> to
+     *        match any principal with the given prefix
+     * @param actions no actions defined, must be "*" for forward compatibility
+     * @throws NullPointerException if <code>name</code> or
+     *         <code>actions</code> is <code>null</code>
+     * @throws IllegalArgumentException if <code>name</code> is empty or
+     *         <code>actions</code> is not "*"
+     */
+    public DmtPrincipalPermission(String target, String actions) {
+        this(target);
+
+        if (actions == null)
+            throw new NullPointerException(
+                    "'actions' parameter must not be null.");
+
+        if (!actions.equals("*"))
+            throw new IllegalArgumentException(
+                    "'actions' parameter must be \"*\".");
+    }
+
+    /**
+     * Checks whether the given object is equal to this DmtPrincipalPermission
+     * instance. Two DmtPrincipalPermission instances are equal if they have the
+     * same target string.
+     * 
+     * @param obj the object to compare to this DmtPrincipalPermission instance
+     * @return <code>true</code> if the parameter represents the same
+     *         permissions as this instance
+     */
+    public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
+
+        if (!(obj instanceof DmtPrincipalPermission))
+            return false;
+
+        DmtPrincipalPermission other = (DmtPrincipalPermission) obj;
+
+        return isPrefix == other.isPrefix && principal.equals(other.principal);
+    }
+
+    /**
+     * Returns the action list (always <code>*</code> in the current version).
+     * 
+     * @return the action string &quot;*&quot;
+     */
+    public String getActions() {
+        return "*";
+    }
+
+    /**
+     * Returns the hash code for this permission object. If two
+     * DmtPrincipalPermission objects are equal according to the {@link #equals}
+     * method, then calling this method on each of the two
+     * DmtPrincipalPermission objects must produce the same integer result.
+     * 
+     * @return hash code for this permission object
+     */
+    public int hashCode() {
+        return new Boolean(isPrefix).hashCode() ^ principal.hashCode();
+    }
+
+    /**
+     * Checks if this DmtPrincipalPermission object implies the specified
+     * permission. Another DmtPrincipalPermission instance is implied by this
+     * permission either if the target strings are identical, or if this target
+     * can be made identical to the other target by replacing a trailing
+     * &quot;*&quot; with any string.
+     * 
+     * @param p the permission to check for implication
+     * @return true if this DmtPrincipalPermission instance implies the
+     *         specified permission
+     */
+    public boolean implies(Permission p) {
+        if (!(p instanceof DmtPrincipalPermission))
+            return false;
+
+        DmtPrincipalPermission other = (DmtPrincipalPermission) p;
+
+        return impliesPrincipal(other);
+    }
+
+    /**
+     * Returns a new PermissionCollection object for storing
+     * DmtPrincipalPermission objects.
+     * 
+     * @return the new PermissionCollection
+     */
+    public PermissionCollection newPermissionCollection() {
+        return new DmtPrincipalPermissionCollection();
+    }
+
+    /*
+     * Returns true if the principal parameter of the given
+     * DmtPrincipalPermission is implied by the principal of this permission,
+     * i.e. this principal is a prefix of the other principal but ends with a *,
+     * or the two principal strings are equal.
+     */
+    boolean impliesPrincipal(DmtPrincipalPermission p) {
+        return isPrefix ? p.principal.startsWith(principal) : !p.isPrefix
+                && p.principal.equals(principal);
+    }
+}
+
+/**
+ * Represents a homogeneous collection of DmtPrincipalPermission objects.
+ */
+final class DmtPrincipalPermissionCollection extends PermissionCollection {
+    private static final long serialVersionUID = -6692103535775802684L;
+
+    private ArrayList perms;
+
+    /**
+     * Create an empty DmtPrincipalPermissionCollection object.
+     */
+    public DmtPrincipalPermissionCollection() {
+        perms = new ArrayList();
+    }
+
+    /**
+     * Adds a permission to the DmtPrincipalPermissionCollection.
+     * 
+     * @param permission the Permission object to add
+     * @exception IllegalArgumentException if the permission is not a
+     *            DmtPrincipalPermission
+     * @exception SecurityException if this DmtPrincipalPermissionCollection
+     *            object has been marked readonly
+     */
+    public void add(Permission permission) {
+        if (!(permission instanceof DmtPrincipalPermission))
+            throw new IllegalArgumentException(
+                    "Cannot add permission, invalid permission type: "
+                            + permission);
+        if (isReadOnly())
+            throw new SecurityException(
+                    "Cannot add permission, collection is marked read-only.");
+
+        // only add new permission if it is not already implied by the
+        // permissions in the collection
+        if (!implies(permission)) {
+            // remove all permissions that are implied by the new one
+            Iterator i = perms.iterator();
+            while (i.hasNext())
+                if (permission.implies((DmtPrincipalPermission) i.next()))
+                    i.remove();
+
+            // no need to synchronize because all adds are done sequentially
+            // before any implies() calls
+            perms.add(permission);
+
+        }
+    }
+
+    /**
+     * Check whether this set of permissions implies the permission specified in
+     * the parameter.
+     * 
+     * @param permission the Permission object to compare
+     * @return true if the parameter permission is a proper subset of the
+     *         permissions in the collection, false otherwise
+     */
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof DmtPrincipalPermission))
+            return false;
+
+        DmtPrincipalPermission other = (DmtPrincipalPermission) permission;
+
+        Iterator i = perms.iterator();
+        while (i.hasNext())
+            if (((DmtPrincipalPermission) i.next()).impliesPrincipal(other))
+                return true;
+
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of all the DmtPrincipalPermission objects in the
+     * container. The returned value cannot be <code>null</code>.
+     * 
+     * @return an enumeration of all the DmtPrincipalPermission objects
+     */
+    public Enumeration elements() {
+        // Convert Iterator into Enumeration
+        return Collections.enumeration(perms);
+    }
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,137 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/spi/DataPlugin.java,v 1.4 2006/06/16 16:31:59 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * 
+ * Licensed 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 info.dmtree.spi;
+
+import info.dmtree.DmtException;
+import info.dmtree.DmtSession;
+
+/**
+ * An implementation of this interface takes the responsibility of handling data
+ * requests in a subtree of the DMT.
+ * <p>
+ * In an OSGi environment such implementations should be registered at the OSGi
+ * service registry specifying the list of root node URIs in a
+ * <code>String</code> array in the <code>dataRootURIs</code> registration
+ * parameter.
+ * <p>
+ * When the first reference in a session is made to a node handled by this
+ * plugin, the DmtAdmin calls one of the <code>open...</code> methods to
+ * retrieve a plugin session object for processing the request. The called
+ * method depends on the lock type of the current session. In case of
+ * {@link #openReadWriteSession(String[], DmtSession)} and
+ * {@link #openAtomicSession(String[], DmtSession)}, the plugin may return
+ * <code>null</code> to indicate that the specified lock type is not
+ * supported. In this case the DmtAdmin may call
+ * {@link #openReadOnlySession(String[], DmtSession)} to start a read-only
+ * plugin session, which can be used as long as there are no write operations on
+ * the nodes handled by this plugin.
+ * <p>
+ * The <code>sessionRoot</code> parameter of each method is a String array
+ * containing the segments of the URI pointing to the root of the session. This
+ * is an absolute path, so the first segment is always &quot;.&quot;. Special
+ * characters appear escaped in the segments.
+ * <p>
+ */
+public interface DataPlugin {
+
+    /**
+     * This method is called to signal the start of a read-only session when the
+     * first reference is made within a <code>DmtSession</code> to a node
+     * which is handled by this plugin. Session information is given as it is
+     * needed for sending alerts back from the plugin.
+     * <p>
+     * The plugin can assume that there are no writing sessions open on any
+     * subtree that has any overlap with the subtree of this session.
+     * 
+     * @param sessionRoot the path to the subtree which is accessed in the
+     *        current session, must not be <code>null</code>
+     * @param session the session from which this plugin instance is accessed,
+     *        must not be <code>null</code>
+     * @return a plugin session capable of executing read operations
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>sessionRoot</code>
+     *         points to a non-existing node
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if some underlying operation failed because of
+     *         lack of permissions
+     */
+    ReadableDataSession openReadOnlySession(String[] sessionRoot,
+            DmtSession session) throws DmtException;
+
+    /**
+     * This method is called to signal the start of a non-atomic read-write
+     * session when the first reference is made within a <code>DmtSession</code>
+     * to a node which is handled by this plugin. Session information is given
+     * as it is needed for sending alerts back from the plugin.
+     * <p>
+     * The plugin can assume that there are no other sessions open on any
+     * subtree that has any overlap with the subtree of this session.
+     * 
+     * @param sessionRoot the path to the subtree which is locked in the current
+     *        session, must not be <code>null</code>
+     * @param session the session from which this plugin instance is accessed,
+     *        must not be <code>null</code>
+     * @return a plugin session capable of executing read-write operations, or
+     *         <code>null</code> if the plugin does not support non-atomic
+     *         read-write sessions
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>sessionRoot</code>
+     *         points to a non-existing node
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if some underlying operation failed because of
+     *         lack of permissions
+     */
+    ReadWriteDataSession openReadWriteSession(String[] sessionRoot,
+            DmtSession session) throws DmtException;
+
+    /**
+     * This method is called to signal the start of an atomic read-write session
+     * when the first reference is made within a <code>DmtSession</code> to a
+     * node which is handled by this plugin. Session information is given as it
+     * is needed for sending alerts back from the plugin.
+     * <p>
+     * The plugin can assume that there are no other sessions open on any
+     * subtree that has any overlap with the subtree of this session.
+     * 
+     * @param sessionRoot the path to the subtree which is locked in the current
+     *        session, must not be <code>null</code>
+     * @param session the session from which this plugin instance is accessed,
+     *        must not be <code>null</code>
+     * @return a plugin session capable of executing read-write operations in an
+     *         atomic block, or <code>null</code> if the plugin does not
+     *         support atomic read-write sessions
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>sessionRoot</code>
+     *         points to a non-existing node
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if some underlying operation failed because of
+     *         lack of permissions
+     */
+    TransactionalDataSession openAtomicSession(String[] sessionRoot,
+            DmtSession session) throws DmtException;
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,74 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/spi/ExecPlugin.java,v 1.3 2006/06/16 16:31:59 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * 
+ * Licensed 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 info.dmtree.spi;
+
+import info.dmtree.DmtException;
+import info.dmtree.DmtSession;
+
+/**
+ * An implementation of this interface takes the responsibility of handling node
+ * execute requests requests in a subtree of the DMT.
+ * <p>
+ * In an OSGi environment such implementations should be registered at the OSGi
+ * service registry specifying the list of root node URIs in a
+ * <code>String</code> array in the <code>execRootURIs</code> registration
+ * parameter.
+ */
+public interface ExecPlugin {
+
+    /**
+     * Execute the given node with the given data. This operation corresponds to
+     * the EXEC command in OMA DM.
+     * <p>
+     * The semantics of an execute operation and the data parameter it takes
+     * depends on the definition of the managed object on which the command is
+     * issued. Session information is given as it is needed for sending alerts
+     * back from the plugin. If a correlation ID is specified, it should be used
+     * as the <code>correlator</code> parameter for alerts sent in response to
+     * this execute operation.
+     * <p>
+     * The <code>nodePath</code> parameter contains an array of path segments
+     * identifying the node to be executed in the subtree of this plugin. This
+     * is an absolute path, so the first segment is always &quot;.&quot;.
+     * Special characters appear escaped in the segments.
+     * 
+     * @param session a reference to the session in which the operation was
+     *        issued, must not be <code>null</code>
+     * @param nodePath the absolute path of the node to be executed, must not be
+     *        <code>null</code>
+     * @param correlator an identifier to associate this operation with any
+     *        alerts sent in response to it, can be <code>null</code>
+     * @param data the parameter of the execute operation, can be
+     *        <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if the node does not exist and
+     *         the plugin does not allow executing unexisting nodes
+     *         <li><code>METADATA_MISMATCH</code> if the command failed
+     *         because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @see DmtSession#execute(String, String)
+     * @see DmtSession#execute(String, String, String)
+     */
+    void execute(DmtSession session, String[] nodePath, String correlator,
+            String data) throws DmtException;
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,297 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/spi/ReadWriteDataSession.java,v 1.4 2006/07/12 21:21:52 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * 
+ * Licensed 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 info.dmtree.spi;
+
+import info.dmtree.*;
+
+/**
+ * Provides non-atomic read-write access to the part of the tree handled by the
+ * plugin that created this session.
+ * <p>
+ * The <code>nodePath</code> parameters appearing in this interface always
+ * contain an array of path segments identifying a node in the subtree of this
+ * plugin. This parameter contains an absolute path, so the first segment is
+ * always &quot;.&quot;. Special characters appear escaped in the segments.
+ * <p>
+ * <strong>Error handling</strong>
+ * <p>
+ * When a tree manipulation command is called on the DmtAdmin service, it must
+ * perform an extensive set of checks on the parameters and the authority of the
+ * caller before delegating the call to a plugin. Therefore plugins can take
+ * certain circumstances for granted: that the path is valid and is within the
+ * subtree of the plugin and the session, the command can be applied to the
+ * given node (e.g. the target of <code>setNodeValue</code> is a leaf node),
+ * etc. All errors described by the error codes {@link DmtException#INVALID_URI},
+ * {@link DmtException#URI_TOO_LONG}, {@link DmtException#PERMISSION_DENIED},
+ * {@link DmtException#COMMAND_NOT_ALLOWED} and
+ * {@link DmtException#TRANSACTION_ERROR} are fully filtered out before control
+ * reaches the plugin.
+ * <p>
+ * If the plugin provides meta-data for a node, the DmtAdmin service must also
+ * check the constraints specified by it, as described in {@link MetaNode}. If
+ * the plugin does not provide meta-data, it must perform the necessary checks
+ * for itself and use the {@link DmtException#METADATA_MISMATCH} error code to
+ * indicate such discrepancies.
+ * <p>
+ * The DmtAdmin also ensures that the targeted nodes exist before calling the
+ * plugin (or that they do not exist, in case of node creation). However, some
+ * small amount of time elapses between the check and the call, so in case of
+ * plugins where the node structure can change independantly from the DMT, the
+ * target node might appear/disappear in that time. For example, a whole subtree
+ * can disappear when a Monitorable application is unregistered, which might
+ * happen in the middle of a DMT session accessing it. Plugins managing such
+ * nodes always need to check the existance or non-existance of nodes and throw
+ * {@link DmtException#NODE_NOT_FOUND} or
+ * {@link DmtException#NODE_ALREADY_EXISTS} as necessary, but for more static
+ * subtrees there is no need for the plugin to use these error codes.
+ * <p>
+ * The plugin can use the remaining error codes as needed. If an error does not
+ * fit into any other category, the {@link DmtException#COMMAND_FAILED} code
+ * should be used.
+ */
+public interface ReadWriteDataSession extends ReadableDataSession {
+
+    /**
+     * Create a copy of a node or a whole subtree. Beside the structure and
+     * values of the nodes, most properties managed by the plugin must also be
+     * copied, with the exception of the Timestamp and Version properties.
+     * 
+     * @param nodePath an absolute path specifying the node or the root of a
+     *        subtree to be copied
+     * @param newNodePath the absolute path of the new node or root of a subtree
+     * @param recursive <code>false</code> if only a single node is copied,
+     *        <code>true</code> if the whole subtree is copied
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node, or if <code>newNodePath</code>
+     *         points to a node that cannot exist in the tree
+     *         <li><code>NODE_ALREADY_EXISTS</code> if
+     *         <code>newNodePath</code> points to a node that already exists
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         copied because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the copy operation
+     *         is not supported by the plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#copy(String, String, boolean)
+     */
+    void copy(String[] nodePath, String[] newNodePath, boolean recursive)
+            throws DmtException;
+
+    /**
+     * Create an interior node with a given type. The type of interior node, if
+     * specified, is a URI identifying a DDF document.
+     * 
+     * @param nodePath the absolute path of the node to create
+     * @param type the type URI of the interior node, can be <code>null</code>
+     *        if no node type is defined
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a node that cannot exist in the tree
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodeUri</code>
+     *         points to a node that already exists
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#createInteriorNode(String)
+     * @see DmtSession#createInteriorNode(String, String)
+     */
+    void createInteriorNode(String[] nodePath, String type) throws DmtException;
+
+    /**
+     * Create a leaf node with a given value and MIME type. If the specified
+     * value or MIME type is <code>null</code>, their default values must be
+     * taken.
+     * 
+     * @param nodePath the absolute path of the node to create
+     * @param value the value to be given to the new node, can be
+     *        <code>null</code>
+     * @param mimeType the MIME type to be given to the new node, can be
+     *        <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a node that cannot exist in the tree
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodePath</code>
+     *         points to a node that already exists
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#createLeafNode(String)
+     * @see DmtSession#createLeafNode(String, DmtData)
+     * @see DmtSession#createLeafNode(String, DmtData, String)
+     */
+    void createLeafNode(String[] nodePath, DmtData value, String mimeType)
+            throws DmtException;
+
+    /**
+     * Delete the given node. Deleting interior nodes is recursive, the whole
+     * subtree under the given node is deleted.
+     * 
+     * @param nodePath the absolute path of the node to delete
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         deleted because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#deleteNode(String)
+     */
+    void deleteNode(String[] nodePath) throws DmtException;
+
+    /**
+     * Rename a node. This operation only changes the name of the node (updating
+     * the timestamp and version properties if they are supported), the value
+     * and the other properties are not changed. The new name of the node must
+     * be provided, the new path is constructed from the base of the old path
+     * and the given name.
+     * 
+     * @param nodePath the absolute path of the node to rename
+     * @param newName the new name property of the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node, or if the new node is not defined
+     *         in the tree
+     *         <li><code>NODE_ALREADY_EXISTS</code> if there already exists a
+     *         sibling of <code>nodePath</code> with the name
+     *         <code>newName</code>
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         renamed because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#renameNode(String, String)
+     */
+    void renameNode(String[] nodePath, String newName) throws DmtException;
+
+    /**
+     * Set the title property of a node. The length of the title is guaranteed
+     * not to exceed the limit of 255 bytes in UTF-8 encoding.
+     * 
+     * @param nodePath the absolute path of the node
+     * @param title the title text of the node, can be <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the title could not be
+     *         set because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Title property
+     *         is not supported by the plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#setNodeTitle(String, String)
+     */
+    void setNodeTitle(String[] nodePath, String title) throws DmtException;
+
+    /**
+     * Set the type of a node. The type of leaf node is the MIME type of the
+     * data it contains. The type of an interior node is a URI identifying a DDF
+     * document.
+     * <p>
+     * For interior nodes, the <code>null</code> type should remove the
+     * reference (if any) to a DDF document overriding the tree structure
+     * defined by the ancestors. For leaf nodes, it requests that the default
+     * MIME type is used for the given node.
+     * 
+     * @param nodePath the absolute path of the node
+     * @param type the type of the node, can be <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the type could not be
+     *         set because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#setNodeType(String, String)
+     */
+    void setNodeType(String[] nodePath, String type) throws DmtException;
+
+    /**
+     * Set the value of a leaf or interior node. The format of the node is
+     * contained in the <code>DmtData</code> object. For interior nodes, the
+     * format is <code>FORMAT_NODE</code>, while for leaf nodes this format is
+     * never used.
+     * <p>
+     * If the specified value is <code>null</code>, the default value must be
+     * taken; if there is no default value, a <code>DmtException</code> with
+     * error code <code>METADATA_MISMATCH</code> must be thrown.
+     * 
+     * @param nodePath the absolute path of the node
+     * @param data the data to be set, can be <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the value could not be
+     *         set because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the specified node is
+     *         an interior node and does not support Java object values
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtSession#setNodeValue(String, DmtData)
+     */
+    void setNodeValue(String[] nodePath, DmtData data) throws DmtException;
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,360 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/spi/ReadableDataSession.java,v 1.4 2006/07/12 21:21:52 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * 
+ * Licensed 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 info.dmtree.spi;
+
+import info.dmtree.*;
+
+import java.util.Date;
+
+/**
+ * Provides read-only access to the part of the tree handled by the plugin that
+ * created this session.
+ * <p>
+ * Since the {@link ReadWriteDataSession} and {@link TransactionalDataSession}
+ * interfaces inherit from this interface, some of the method descriptions do
+ * not apply for an instance that is only a <code>ReadableDataSession</code>.
+ * For example, the {@link #close} method description also contains information
+ * about its behaviour when invoked as part of a transactional session.
+ * <p>
+ * The <code>nodePath</code> parameters appearing in this interface always
+ * contain an array of path segments identifying a node in the subtree of this
+ * plugin. This parameter contains an absolute path, so the first segment is
+ * always &quot;.&quot;. Special characters appear escaped in the segments.
+ * <p>
+ * <strong>Error handling</strong>
+ * <p>
+ * When a tree access command is called on the DmtAdmin service, it must
+ * perform an extensive set of checks on the parameters and the authority of the
+ * caller before delegating the call to a plugin. Therefore plugins can take
+ * certain circumstances for granted: that the path is valid and is within the
+ * subtree of the plugin and the session, the command can be applied to the
+ * given node (e.g. the target of <code>getChildNodeNames</code> is an
+ * interior node), etc. All errors described by the error codes
+ * {@link DmtException#INVALID_URI}, {@link DmtException#URI_TOO_LONG},
+ * {@link DmtException#PERMISSION_DENIED},
+ * {@link DmtException#COMMAND_NOT_ALLOWED} and
+ * {@link DmtException#TRANSACTION_ERROR} are fully filtered out before control
+ * reaches the plugin.
+ * <p>
+ * If the plugin provides meta-data for a node, the DmtAdmin service must also
+ * check the constraints specified by it, as described in {@link MetaNode}. If
+ * the plugin does not provide meta-data, it must perform the necessary checks
+ * for itself and use the {@link DmtException#METADATA_MISMATCH} error code to
+ * indicate such discrepancies.
+ * <p>
+ * The DmtAdmin also ensures that the targeted nodes exist before calling the
+ * plugin (except, of course, before the <code>isNodeUri</code> call).
+ * However, some small amount of time elapses between the check and the call, so
+ * in case of plugins where the node structure can change independantly from the
+ * DMT, the target node might disappear in that time. For example, a whole
+ * subtree can disappear when a Monitorable application is unregistered, which
+ * might happen in the middle of a DMT session accessing it. Plugins managing
+ * such nodes always need to check whether they still exist and throw
+ * {@link DmtException#NODE_NOT_FOUND} as necessary, but for more static
+ * subtrees there is no need for the plugin to use this error code.
+ * <p>
+ * The plugin can use the remaining error codes as needed. If an error does not
+ * fit into any other category, the {@link DmtException#COMMAND_FAILED} code
+ * should be used.
+ */
+public interface ReadableDataSession {
+    /**
+     * Notifies the plugin that the given node has changed outside the scope of
+     * the plugin, therefore the Version and Timestamp properties must be
+     * updated (if supported). This method is needed because the ACL property of
+     * a node is managed by the DmtAdmin instead of the plugin. The DmtAdmin
+     * must call this method whenever the ACL property of a node changes.
+     * 
+     * @param nodePath the absolute path of the node that has changed
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     */
+    void nodeChanged(String[] nodePath) throws DmtException;
+
+    /**
+     * Closes a session. This method is always called when the session ends for
+     * any reason: if the session is closed, if a fatal error occurs in any
+     * method, or if any error occurs during commit or rollback. In case the
+     * session was invalidated due to an exception during commit or rollback, it
+     * is guaranteed that no methods are called on the plugin until it is
+     * closed. In case the session was invalidated due to a fatal exception in
+     * one of the tree manipulation methods, only the rollback method is called
+     * before this (and only in atomic sessions).
+     * <p>
+     * This method should not perform any data manipulation, only cleanup
+     * operations. In non-atomic read-write sessions the data manipulation
+     * should be done instantly during each tree operation, while in atomic
+     * sessions the <code>DmtAdmin</code> always calls
+     * {@link TransactionalDataSession#commit} automatically before the session
+     * is actually closed.
+     * 
+     * @throws DmtException with the error code <code>COMMAND_FAILED</code> if
+     *         the plugin failed to close for any reason
+     */
+    void close() throws DmtException;
+
+    /**
+     * Get the list of children names of a node. The returned array contains the
+     * names - not the URIs - of the immediate children nodes of the given node.
+     * The returned child names must be mangled ({@link info.dmtree.Uri#mangle}).
+     * The returned array may contain <code>null</code> entries, but these are
+     * removed by the DmtAdmin before returning it to the client.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return the list of child node names as a string array or an empty string
+     *         array if the node has no children
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    String[] getChildNodeNames(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the meta data which describes a given node. Meta data can be only
+     * inspected, it can not be changed.
+     * <p>
+     * Meta data support by plugins is an optional feature. It can be used, for
+     * example, when a data plugin is implemented on top of a data store or
+     * another API that has their own metadata, such as a relational database,
+     * in order to avoid metadata duplication and inconsistency. The meta data
+     * specific to the plugin returned by this method is complemented by meta
+     * data from the DmtAdmin before returning it to the client. If there are
+     * differences in the meta data elements known by the plugin and the
+     * <code>DmtAdmin</code> then the plugin specific elements take
+     * precedence.
+     * <p>
+     * Note, that a node does not have to exist for having meta-data associated
+     * with it. This method may provide meta-data for any node that can possibly
+     * exist in the tree (any node defined by the Management Object provided by
+     * the plugin). For nodes that are not defined, a <code>DmtException</code>
+     * may be thrown with the <code>NODE_NOT_FOUND</code> error code. To allow
+     * easier implementation of plugins that do not provide meta-data, it is
+     * allowed to return <code>null</code> for any node, regardless of whether
+     * it is defined or not.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return a MetaNode which describes meta data information, can be
+     *         <code>null</code> if there is no meta data available for the
+     *         given node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a node that is not defined in the tree (see above)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    MetaNode getMetaNode(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the size of the data in a leaf node. The value to return depends on
+     * the format of the data in the node, see the description of the
+     * {@link DmtData#getSize()} method for the definition of node size for each
+     * format.
+     * 
+     * @param nodePath the absolute path of the leaf node
+     * @return the size of the data in the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Size property is
+     *         not supported by the plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     * @see DmtData#getSize
+     */
+    int getNodeSize(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the timestamp when the node was last modified.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return the timestamp of the last modification
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Timestamp
+     *         property is not supported by the plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    Date getNodeTimestamp(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the title of a node. There might be no title property set for a node.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return the title of the node, or <code>null</code> if the node has no
+     *         title
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Title property
+     *         is not supported by the plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    String getNodeTitle(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the type of a node. The type of leaf node is the MIME type of the
+     * data it contains. The type of an interior node is a URI identifying a DDF
+     * document; a <code>null</code> type means that there is no DDF document
+     * overriding the tree structure defined by the ancestors.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return the type of the node, can be <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    String getNodeType(String[] nodePath) throws DmtException;
+
+    /**
+     * Check whether the specified path corresponds to a valid node in the DMT.
+     * 
+     * @param nodePath the absolute path to check
+     * @return true if the given node exists in the DMT
+     */
+    boolean isNodeUri(String[] nodePath);
+
+    /**
+     * Tells whether a node is a leaf or an interior node of the DMT.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return true if the given node is a leaf node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    boolean isLeafNode(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the data contained in a leaf or interior node.
+     * 
+     * @param nodePath the absolute path of the node to retrieve
+     * @return the data of the leaf node, must not be <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the specified node is
+     *         an interior node and does not support Java object values
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    DmtData getNodeValue(String[] nodePath) throws DmtException;
+
+    /**
+     * Get the version of a node. The version can not be set, it is calculated
+     * automatically by the device. It is incremented modulo 0x10000 at every
+     * modification of the value or any other property of the node, for both
+     * leaf and interior nodes. When a node is created the initial value is 0.
+     * 
+     * @param nodePath the absolute path of the node
+     * @return the version of the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodePath</code>
+     *         points to a non-existing node
+     *         <li><code>METADATA_MISMATCH</code> if the information could
+     *         not be retrieved because of meta-data restrictions
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Version property
+     *         is not supported by the plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    int getNodeVersion(String[] nodePath) throws DmtException;
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java Sat Aug  2 02:56:01 2008
@@ -0,0 +1,77 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/spi/TransactionalDataSession.java,v 1.2 2006/06/16 16:31:59 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * 
+ * Licensed 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 info.dmtree.spi;
+
+import info.dmtree.DmtException;
+
+/**
+ * Provides atomic read-write access to the part of the tree handled by the
+ * plugin that created this session.
+ */
+public interface TransactionalDataSession extends ReadWriteDataSession {
+
+    /**
+     * Commits a series of DMT operations issued in the current atomic session
+     * since the last transaction boundary. Transaction boundaries are the
+     * creation of this object that starts the session, and all subsequent
+     * {@link #commit} and {@link #rollback} calls.
+     * <p>
+     * This method can fail even if all operations were successful. This can
+     * happen due to some multi-node semantic constraints defined by a specific
+     * implementation. For example, node A can be required to always have
+     * children A/B, A/C and A/D. If this condition is broken when
+     * <code>commit()</code> is executed, the method will fail, and throw a
+     * <code>METADATA_MISMATCH</code> exception.
+     * <p>
+     * In many cases the tree is not the only way to manage a given part of the
+     * system. It may happen that while modifying some nodes in an atomic
+     * session, the underlying settings are modified parallelly outside the
+     * scope of the DMT. If this is detected during commit, an exception with
+     * the code <code>CONCURRENT_ACCESS</code> is thrown.
+     * 
+     * @throws DmtException with the following possible error codes
+     *         <ul>
+     *         <li><code>METADATA_MISMATCH</code> if the operation failed
+     *         because of meta-data restrictions
+     *         <li><code>CONCURRENT_ACCESS</code> if it is detected that some
+     *         modification has been made outside the scope of the DMT to the
+     *         nodes affected in the session's operations
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    void commit() throws DmtException;
+
+    /**
+     * Rolls back a series of DMT operations issued in the current atomic
+     * session since the last transaction boundary. Transaction boundaries are
+     * the creation of this object that starts the session, and all subsequent
+     * {@link #commit} and {@link #rollback} calls.
+     * 
+     * @throws DmtException with the error code <code>ROLLBACK_FAILED</code>
+     *         in case the rollback did not succeed
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    void rollback() throws DmtException;
+}