FELIX-514 Updated compendium bundle to R4.1 OSGi API

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@681945 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/Acl.java b/org.osgi.compendium/src/main/java/info/dmtree/Acl.java
new file mode 100644
index 0000000..b550d8d
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/Acl.java
@@ -0,0 +1,576 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/Acl.java,v 1.6 2006/07/12 21:21:37 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;
+
+import java.util.*;
+
+/**
+ * <code>Acl</code> is an immutable class representing structured access to
+ * DMT ACLs. Under OMA DM the ACLs are defined as strings with an internal
+ * syntax.
+ * <p>
+ * The methods of this class taking a principal as parameter accept remote
+ * server IDs (as passed to
+ * {@link DmtAdmin#getSession(String, String, int) DmtAdmin.getSession}), as
+ * well as &quot; <code>*</code> &quot; indicating any principal.
+ * <p>
+ * The syntax for valid remote server IDs:<br>
+ * &lt;<i>server-identifier</i>&gt; ::= All printable characters except 
+ *   <code>'='</code>, <code>'&amp;'</code>, <code>'*'</code>, <code>'+'</code>
+ *    or white-space characters.
+ */
+public final class Acl {
+
+    // ----- Public constants -----//
+
+    /**
+     * Principals holding this permission can issue GET command on the node
+     * having this ACL.
+     */
+    public static final int GET = 1;
+
+    /**
+     * Principals holding this permission can issue ADD commands on the node
+     * having this ACL.
+     */
+    public static final int ADD = 2;
+
+    /**
+     * Principals holding this permission can issue REPLACE commands on the node
+     * having this ACL.
+     */
+    public static final int REPLACE = 4;
+
+    /**
+     * Principals holding this permission can issue DELETE commands on the node
+     * having this ACL.
+     */
+    public static final int DELETE = 8;
+
+    /**
+     * Principals holding this permission can issue EXEC commands on the node
+     * having this ACL.
+     */
+    public static final int EXEC = 16;
+
+    /**
+     * Principals holding this permission can issue any command on the node
+     * having this ACL. This permission is the logical OR of {@link #ADD},
+     * {@link #DELETE}, {@link #EXEC}, {@link #GET} and {@link #REPLACE}
+     * permissions.
+     */
+    public static final int ALL_PERMISSION = ADD | DELETE | EXEC | GET
+            | REPLACE;
+
+    // ----- Private constants -----//
+
+    private static final int[] PERMISSION_CODES = new int[] { ADD, DELETE,
+            EXEC, GET, REPLACE };
+
+    private static final String[] PERMISSION_NAMES = new String[] { "Add",
+            "Delete", "Exec", "Get", "Replace" };
+
+    private static final String ALL_PRINCIPALS = "*";
+
+    // ----- Private fields -----//
+
+    // the implementation takes advantage of this being a sorted map
+    private final TreeMap principalPermissions;
+
+    private final int globalPermissions;
+
+    // ----- Public constructors -----//
+
+    /**
+     * Create an instance of the ACL from its canonic string representation.
+     * 
+     * @param acl The string representation of the ACL as defined in OMA DM. If
+     *        <code>null</code> or empty then it represents an empty list of
+     *        principals with no permissions.
+     * @throws IllegalArgumentException if acl is not a valid OMA DM ACL string
+     */
+    public Acl(String acl) {
+        if (acl == null || acl.equals("")) { // empty permission set
+            principalPermissions = new TreeMap();
+            globalPermissions = 0;
+            return;
+        }
+
+        TreeMap tempPrincipalPermissions = new TreeMap();
+        int tempGlobalPermissions = 0;
+
+        String[] aclEntries = split(acl, '&', -1);
+        for (int i = 0; i < aclEntries.length; i++) {
+            if (aclEntries[i].length() == 0)
+                throw new IllegalArgumentException(
+                        "Invalid ACL string: empty ACL entry.");
+
+            String[] entryParts = split(aclEntries[i], '=', 2);
+            if (entryParts.length == 1)
+                throw new IllegalArgumentException(
+                        "Invalid ACL string: no '=' in ACL entry.");
+            if (entryParts[1].length() == 0)
+                throw new IllegalArgumentException(
+                        "Invalid ACL string: no server identifiers in ACL entry.");
+
+            int command = parseCommand(entryParts[0]);
+            String[] serverIds = split(entryParts[1], '+', -1);
+            for (int j = 0; j < serverIds.length; j++) {
+                if (serverIds[j].length() == 0)
+                    throw new IllegalArgumentException(
+                            "Invalid ACL string: empty server identifier.");
+
+                if (serverIds[j].equals(ALL_PRINCIPALS))
+                    tempGlobalPermissions |= command;
+                else {
+                    checkServerId(serverIds[j], "Invalid ACL string: "
+                            + "server ID contains illegal character");
+                    Integer n = (Integer) tempPrincipalPermissions
+                            .get(serverIds[j]);
+                    int oldPermission = (n != null) ? n.intValue() : 0;
+                    tempPrincipalPermissions.put(serverIds[j], new Integer(
+                            oldPermission | command));
+                }
+            }
+        }
+
+        principalPermissions = tempPrincipalPermissions;
+        globalPermissions = tempGlobalPermissions;
+    }
+
+    /**
+     * Creates an instance with a specified list of principals and the
+     * permissions they hold. The two arrays run in parallel, that is
+     * <code>principals[i]</code> will hold <code>permissions[i]</code> in
+     * the ACL.
+     * <p>
+     * A principal name may not appear multiple times in the 'principals'
+     * argument. If the &quot;*&quot; principal appears in the array, the
+     * corresponding permissions will be granted to all principals (regardless
+     * of whether they appear in the array or not).
+     * 
+     * @param principals The array of principals
+     * @param permissions The array of permissions
+     * @throws IllegalArgumentException if the length of the two arrays are not
+     *         the same, if any array element is invalid, or if a principal
+     *         appears multiple times in the <code>principals</code> array
+     */
+    public Acl(String[] principals, int[] permissions) {
+        if (principals.length != permissions.length)
+            throw new IllegalArgumentException(
+                    "The lengths of the principal and permission arrays are not the same.");
+
+        TreeMap tempPrincipalPermissions = new TreeMap();
+        int tempGlobalPermissions = 0;
+
+        for (int i = 0; i < principals.length; i++) {
+            // allow one * in 'principals' array, remove after loop
+            if (!ALL_PRINCIPALS.equals(principals[i]))
+                checkPrincipal(principals[i]);
+            checkPermissions(permissions[i]);
+
+            Integer permInt = new Integer(permissions[i]);
+            Object old = tempPrincipalPermissions.put(principals[i], permInt);
+            if (old != null)
+                throw new IllegalArgumentException("Principal '"
+                        + principals[i]
+                        + "' appears multiple times in the principal array.");
+        }
+
+        // set the global permissions if there was a * in the array
+        Object globalPermObj = tempPrincipalPermissions.remove(ALL_PRINCIPALS);
+        if (globalPermObj != null)
+            tempGlobalPermissions = ((Integer) globalPermObj).intValue();
+
+        principalPermissions = tempPrincipalPermissions;
+        globalPermissions = tempGlobalPermissions;
+    }
+
+    // ----- Private constructors -----//
+
+    /**
+     * Creates an instance identical to the <code>base</code> ACL except for
+     * the permissions of the given <code>principal</code>, which are
+     * overwritten with the given <code>permissions</code>.
+     * <p>
+     * Assumes that the permissions parameter has been checked. All
+     * modifications of an <code>Acl</code> (add, delete, set) are done
+     * through this method.
+     * 
+     * @param base The ACL that provides all permissions except for permissions
+     *        of the given principal.
+     * @param principal The entity to which permission should be granted.
+     * @param permissions The set of permissions to be given. The parameter can
+     *        be a logical <code>or</code> of the permission constants defined
+     *        in this class.
+     */
+    private Acl(Acl base, String principal, int permissions) {
+        // make a shallow copy of the permission table, the keys (String) and
+        // values (Integer) are immutable anyway
+        TreeMap tempPrincipalPermissions = (TreeMap) base.principalPermissions
+                .clone();
+        int tempGlobalPermissions = base.globalPermissions;
+
+        int deletedGlobalPerm = tempGlobalPermissions & ~permissions;
+        if (ALL_PRINCIPALS.equals(principal)) {
+            deleteFromAll(tempPrincipalPermissions, deletedGlobalPerm);
+            tempGlobalPermissions = permissions;
+        } else {
+            checkPrincipal(principal);
+
+            if (deletedGlobalPerm != 0)
+                throw new IllegalArgumentException(
+                        "Cannot revoke globally set permissions ("
+                                + writeCommands(deletedGlobalPerm)
+                                + ") from a specific principal (" + principal
+                                + ").");
+
+            setPrincipalPermission(tempPrincipalPermissions, principal,
+                    permissions);
+        }
+
+        principalPermissions = tempPrincipalPermissions;
+        globalPermissions = tempGlobalPermissions;
+    }
+
+    // ----- Public methods -----//
+
+    /**
+     * Checks whether the given object is equal to this <code>Acl</code>
+     * instance. Two <code>Acl</code> instances are equal if they allow the
+     * same set of permissions for the same set of principals.
+     * 
+     * @param obj the object to compare with this <code>Acl</code> instance
+     * @return <code>true</code> if the parameter represents the same ACL as
+     *         this instance
+     */
+    public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
+
+        if (!(obj instanceof Acl))
+            return false;
+
+        Acl other = (Acl) obj;
+
+        if (globalPermissions != other.globalPermissions
+                || principalPermissions.size() != other.principalPermissions
+                        .size())
+            return false;
+
+        // principalPermissions sets cannot be easily compared, because they are
+        // not canonical: the global permissions may or may not be present for
+        // each principal, without changing the meaning of the Acl object.
+
+        // Compare canonical string representations, inefficient but simple.
+        return toString().equals(other.toString());
+    }
+
+    /**
+     * Returns the hash code for this ACL instance. If two <code>Acl</code>
+     * instances are equal according to the {@link #equals} method, then calling
+     * this method on each of them must produce the same integer result.
+     * 
+     * @return hash code for this ACL
+     */
+    public int hashcode() {
+        // Using the hash code of the canonical string representation, because
+        // the principalPermissions set is not canonical (see above).
+        return toString().hashCode();
+    }
+
+    /**
+     * Create a new <code>Acl</code> instance from this <code>Acl</code> with 
+     * the given permission added for the given principal. The already existing
+     * permissions of the principal are not affected.
+     * 
+     * @param principal The entity to which permissions should be granted, or
+     *        &quot;*&quot; to grant permissions to all principals.
+     * @param permissions The permissions to be given. The parameter can be a
+     *        logical <code>or</code> of more permission constants defined in
+     *        this class.
+     * @return a new <code>Acl</code> instance
+     * @throws IllegalArgumentException if <code>principal</code> is not a
+     *         valid principal name or if <code>permissions</code> is not a
+     *         valid combination of the permission constants defined in this
+     *         class
+     */
+    public synchronized Acl addPermission(String principal, int permissions) {
+        checkPermissions(permissions);
+
+        int oldPermissions = getPermissions(principal);
+        return setPermission(principal, oldPermissions | permissions);
+    }
+
+    /**
+     * Create a new <code>Acl</code> instance from this <code>Acl</code> with 
+     * the given permission revoked from the given principal. Other permissions 
+     * of the principal are not affected.
+     * <p>
+     * Note, that it is not valid to revoke a permission from a specific
+     * principal if that permission is granted globally to all principals.
+     * 
+     * @param principal The entity from which permissions should be revoked, or
+     *        &quot;*&quot; to revoke permissions from all principals.
+     * @param permissions The permissions to be revoked. The parameter can be a
+     *        logical <code>or</code> of more permission constants defined in
+     *        this class.
+     * @return a new <code>Acl</code> instance
+     * @throws IllegalArgumentException if <code>principal</code> is not a
+     *         valid principal name, if <code>permissions</code> is not a
+     *         valid combination of the permission constants defined in this
+     *         class, or if a globally granted permission would have been
+     *         revoked from a specific principal
+     */
+    public synchronized Acl deletePermission(String principal, int permissions) {
+        checkPermissions(permissions);
+
+        int oldPermissions = getPermissions(principal);
+        return setPermission(principal, oldPermissions & ~permissions);
+    }
+
+    /**
+     * Get the permissions associated to a given principal.
+     * 
+     * @param principal The entity whose permissions to query, or &quot;*&quot;
+     *        to query the permissions that are granted globally, to all
+     *        principals
+     * @return The permissions of the given principal. The returned
+     *         <code>int</code> is a bitmask of the permission constants defined
+     *         in this class
+     * @throws IllegalArgumentException if <code>principal</code> is not a
+     *         valid principal name
+     */
+    public synchronized int getPermissions(String principal) {
+        int permissions = 0;
+
+        if (!(ALL_PRINCIPALS.equals(principal))) {
+            checkPrincipal(principal);
+            Object po = principalPermissions.get(principal);
+            if (po != null)
+                permissions = ((Integer) po).intValue();
+        }
+
+        return permissions | globalPermissions;
+    }
+
+    /**
+     * Check whether the given permissions are granted to a certain principal.
+     * The requested permissions are specified as a bitfield, for example
+     * <code>(Acl.ADD | Acl.DELETE | Acl.GET)</code>.
+     * 
+     * @param principal The entity to check, or &quot;*&quot; to check whether
+     *        the given permissions are granted to all principals globally
+     * @param permissions The permissions to check
+     * @return <code>true</code> if the principal holds all the given permissions
+     * @throws IllegalArgumentException if <code>principal</code> is not a
+     *         valid principal name or if <code>permissions</code> is not a
+     *         valid combination of the permission constants defined in this
+     *         class
+     */
+    public synchronized boolean isPermitted(String principal, int permissions) {
+        checkPermissions(permissions);
+
+        int hasPermissions = getPermissions(principal);
+        return (permissions & hasPermissions) == permissions;
+    }
+
+    /**
+     * Create a new <code>Acl</code> instance from this <code>Acl</code> where 
+     * all permissions for the given principal are overwritten with the given 
+     * permissions.
+     * <p>
+     * Note, that when changing the permissions of a specific principal, it is
+     * not allowed to specify a set of permissions stricter than the global set
+     * of permissions (that apply to all principals).
+     * 
+     * @param principal The entity to which permissions should be granted, or
+     *        &quot;*&quot; to globally grant permissions to all principals.
+     * @param permissions The set of permissions to be given. The parameter is
+     *        a bitmask of the permission constants defined in this class.
+     * @return a new <code>Acl</code> instance
+     * @throws IllegalArgumentException if <code>principal</code> is not a
+     *         valid principal name, if <code>permissions</code> is not a
+     *         valid combination of the permission constants defined in this
+     *         class, or if a globally granted permission would have been
+     *         revoked from a specific principal
+     */
+    public synchronized Acl setPermission(String principal, int permissions) {
+        checkPermissions(permissions);
+
+        Acl newPermission = new Acl(this, principal, permissions);
+        return newPermission;
+    }
+
+    /**
+     * Get the list of principals who have any kind of permissions on this node.
+     * The list only includes those principals that have been explicitly
+     * assigned permissions (so &quot;*&quot; is never returned), globally set
+     * permissions naturally apply to all other principals as well.
+     * 
+     * @return The array of principals having permissions on this node.
+     */
+    public String[] getPrincipals() {
+        return (String[]) (principalPermissions.keySet().toArray(new String[0]));
+    }
+
+    /**
+     * Give the canonic string representation of this ACL. The operations are in
+     * the following order: {Add, Delete, Exec, Get, Replace}, principal names
+     * are sorted alphabetically.
+     * 
+     * @return The string representation as defined in OMA DM.
+     */
+    public synchronized String toString() {
+        String acl = null;
+        for (int i = 0; i < PERMISSION_CODES.length; i++)
+            acl = writeEntry(PERMISSION_CODES[i], acl);
+
+        return (acl != null) ? acl : "";
+    }
+
+    // ----- Private utility methods -----//
+
+    private String writeEntry(int command, String acl) {
+        String aclEntry = null;
+
+        if ((command & globalPermissions) > 0)
+            aclEntry = ALL_PRINCIPALS;
+        else {
+            // TreeMap guarantees alphabetical ordering of keys during traversal
+            Iterator i = principalPermissions.entrySet().iterator();
+            while (i.hasNext()) {
+                Map.Entry entry = (Map.Entry) i.next();
+                if ((command & ((Integer) entry.getValue()).intValue()) > 0)
+                    aclEntry = appendEntry(aclEntry, '+', (String) entry
+                            .getKey());
+            }
+        }
+
+        if (aclEntry == null)
+            return acl;
+
+        return appendEntry(acl, '&', writeCommands(command) + '=' + aclEntry);
+    }
+
+    private static void deleteFromAll(TreeMap principalPermissions, int perm) {
+        Iterator i = principalPermissions.entrySet().iterator();
+        while (i.hasNext()) {
+            Map.Entry entry = (Map.Entry) i.next();
+            setPrincipalPermission(principalPermissions, (String) entry
+                    .getKey(), ((Integer) entry.getValue()).intValue() & ~perm);
+        }
+    }
+
+    private static void setPrincipalPermission(TreeMap principalPermissions,
+            String principal, int perm) {
+        if (perm == 0)
+            principalPermissions.remove(principal);
+        else
+            principalPermissions.put(principal, new Integer(perm));
+    }
+
+    private static String writeCommands(int command) {
+        String commandStr = null;
+        for (int i = 0; i < PERMISSION_CODES.length; i++)
+            if ((command & PERMISSION_CODES[i]) != 0)
+                commandStr = appendEntry(commandStr, ',', PERMISSION_NAMES[i]);
+
+        return (commandStr != null) ? commandStr : "";
+    }
+
+    private static String appendEntry(String base, char separator, String entry) {
+        return (base != null) ? base + separator + entry : entry;
+    }
+
+    private static int parseCommand(String command) {
+        int i = Arrays.asList(PERMISSION_NAMES).indexOf(command);
+        if (i == -1)
+            throw new IllegalArgumentException(
+                    "Invalid ACL string: unknown command '" + command + "'.");
+
+        return PERMISSION_CODES[i];
+    }
+
+    private static void checkPermissions(int perm) {
+        if ((perm & ~ALL_PERMISSION) != 0)
+            throw new IllegalArgumentException("Invalid ACL permission value: "
+                    + perm);
+    }
+
+    private static void checkPrincipal(String principal) {
+        if (principal == null)
+            throw new IllegalArgumentException("Principal is null.");
+
+        checkServerId(principal, "Principal name contains illegal character");
+    }
+
+    private static void checkServerId(String serverId, String errorText) {
+        char[] chars = serverId.toCharArray();
+        for (int i = 0; i < chars.length; i++)
+            if ("*=+&".indexOf(chars[i]) != -1
+                    || Character.isWhitespace(chars[i]))
+                throw new IllegalArgumentException(errorText + " '" + chars[i]
+                        + "'.");
+    }
+
+    private static String[] split(String input, char sep, int limit) {
+        Vector v = new Vector();
+        boolean limited = (limit > 0);
+        int applied = 0;
+        int index = 0;
+        StringBuffer part = new StringBuffer();
+
+        while (index < input.length()) {
+            char ch = input.charAt(index);
+            if (ch != sep)
+                part.append(ch);
+            else {
+                ++applied;
+                v.add(part.toString());
+                part = new StringBuffer();
+            }
+            ++index;
+            if (limited && applied == limit - 1)
+                break;
+        }
+        while (index < input.length()) {
+            char ch = input.charAt(index);
+            part.append(ch);
+            ++index;
+        }
+        v.add(part.toString());
+
+        int last = v.size();
+        if (0 == limit) {
+            for (int j = v.size() - 1; j >= 0; --j) {
+                String s = (String) v.elementAt(j);
+                if ("".equals(s))
+                    --last;
+                else
+                    break;
+            }
+        }
+
+        String[] ret = new String[last];
+        for (int i = 0; i < last; ++i)
+            ret[i] = (String) v.elementAt(i);
+
+        return ret;
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java
new file mode 100644
index 0000000..abfcc6f
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java
@@ -0,0 +1,281 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtAdmin.java,v 1.9 2006/07/11 16:59:41 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;
+
+/**
+ * An interface providing methods to open sessions and register listeners. The
+ * implementation of <code>DmtAdmin</code> should register itself in the OSGi
+ * service registry as a service. <code>DmtAdmin</code> is the entry point for
+ * applications to use the DMT API.
+ * <p>
+ * The <code>getSession</code> methods are used to open a session on a
+ * specified subtree of the DMT. A typical way of usage:
+ * <pre>
+ * serviceRef = context.getServiceReference(DmtAdmin.class.getName());
+ * DmtAdmin admin = (DmtAdmin) context.getService(serviceRef);
+ * DmtSession session = admin.getSession(&quot;./OSGi/Configuration&quot;);
+ * session.createInteriorNode(&quot;./OSGi/Configuration/my.table&quot;);
+ * </pre>
+ * <p>
+ * The methods for opening a session take a node URI (the session root) as a
+ * parameter. All segments of the given URI must be within the segment length
+ * limit of the implementation, and the special characters '/' and '\' must be
+ * escaped (preceded by a '\'). Any string can be converted to a valid URI
+ * segment using the {@link Uri#mangle(String)} method.
+ * <p>
+ * It is possible to specify a lock mode when opening the session (see lock type
+ * constants in {@link DmtSession}).  This determines whether the session can
+ * run in parallel with other sessions, and the kinds of operations that can be 
+ * performed in the session.  All Management Objects constituting the device 
+ * management tree must support read operations on their nodes, while support 
+ * for write operations depends on the Management Object. Management Objects 
+ * supporting write access may support transactional write, non-transactional 
+ * write or both. Users of <code>DmtAdmin</code> should consult the Management 
+ * Object specification and implementation for the supported update modes. If 
+ * Management Object definition permits, implementations are encouraged to 
+ * support both update modes. 
+ * <p>
+ * This interface also contains methods for manipulating the set of
+ * <code>DmtEventListener</code> objects that are called when the structure or
+ * content of the tree is changed. These methods are not needed in an OSGi
+ * environment, clients should register listeners through the Event Admin 
+ * service.
+ */
+public interface DmtAdmin {
+    /**
+     * Opens a <code>DmtSession</code> for local usage on a given subtree of
+     * the DMT with non transactional write lock. This call is equivalent to the
+     * following:
+     * <code>getSession(null, subtreeUri, DmtSession.LOCK_TYPE_EXCLUSIVE)</code>
+     * <p>
+     * The <code>subtreeUri</code> parameter must contain an absolute URI.  It
+     * can also be <code>null</code>, in this case the session is opened with 
+     * the default session root, &quot;.&quot;, that gives access to the whole 
+     * tree.
+     * <p>
+     * To perform this operation the caller must have <code>DmtPermission</code>
+     * for the <code>subtreeUri</code> node with the Get action present.
+     * 
+     * @param subtreeUri the subtree on which DMT manipulations can be performed
+     *        within the returned session
+     * @return a <code>DmtSession</code> object for the requested subtree
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>subtreeUri</code> or
+     *         a segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>subtreeUri</code> is
+     *         syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>subtreeUri</code>
+     *         specifies a non-existing node
+     *         <li><code>SESSION_CREATION_TIMEOUT</code> if the operation
+     *         timed out because of another ongoing session
+     *         <li><code>COMMAND_FAILED</code> if <code>subtreeUri</code>
+     *         specifies a relative URI, or some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have 
+     *         <code>DmtPermission</code> for the given root node with the Get 
+     *         action present 
+     */
+    DmtSession getSession(String subtreeUri) throws DmtException;
+
+    /**
+     * Opens a <code>DmtSession</code> for local usage on a specific DMT
+     * subtree with a given lock mode. This call is equivalent to the
+     * following: <code>getSession(null, subtreeUri, lockMode)</code>
+     * <p>
+     * The <code>subtreeUri</code> parameter must contain an absolute URI.  It
+     * can also be <code>null</code>, in this case the session is opened with 
+     * the default session root, &quot;.&quot;, that gives access to the whole 
+     * tree.
+     * <p>
+     * To perform this operation the caller must have <code>DmtPermission</code>
+     * for the <code>subtreeUri</code> node with the Get action present.
+     * 
+     * @param subtreeUri the subtree on which DMT manipulations can be performed
+     *        within the returned session
+     * @param lockMode one of the lock modes specified in
+     *        <code>DmtSession</code>
+     * @return a <code>DmtSession</code> object for the requested subtree
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>subtreeUri</code> or
+     *         a segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>subtreeUri</code> is
+     *         syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>subtreeUri</code>
+     *         specifies a non-existing node
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if atomic sessions are
+     *         not supported by the implementation and <code>lockMode</code> 
+     *         requests an atomic session
+     *         <li><code>SESSION_CREATION_TIMEOUT</code> if the operation 
+     *         timed out because of  another ongoing session
+     *         <li><code>COMMAND_FAILED</code> if <code>subtreeUri</code>
+     *         specifies a relative URI, if <code>lockMode</code> is unknown,
+     *         or some unspecified error is encountered while attempting to 
+     *         complete the command
+     *         </ul>
+     * @throws SecurityException if the caller does not have 
+     *         <code>DmtPermission</code> for the given root node with the Get 
+     *         action present 
+     */
+    DmtSession getSession(String subtreeUri, int lockMode) throws DmtException;
+
+    /**
+     * Opens a <code>DmtSession</code> on a specific DMT subtree using a
+     * specific lock mode on behalf of a remote principal. If local management
+     * applications are using this method then they should provide
+     * <code>null</code> as the first parameter. Alternatively they can use
+     * other forms of this method without providing a principal string. 
+     * <p>
+     * The <code>subtreeUri</code> parameter must contain an absolute URI.  It
+     * can also be <code>null</code>, in this case the session is opened with 
+     * the default session root, &quot;.&quot;, that gives access to the whole 
+     * tree.  
+     * <p>
+     * This method is guarded by <code>DmtPrincipalPermission</code> in case of
+     * remote sessions.  In addition, the caller must have Get access rights 
+     * (ACL in case of remote sessions, <code>DmtPermission</code> in case of
+     * local sessions) on the <code>subtreeUri</code> node to perform this
+     * operation. 
+     * 
+     * @param principal the identifier of the remote server on whose behalf the
+     *        data manipulation is performed, or <code>null</code> for local
+     *        sessions
+     * @param subtreeUri the subtree on which DMT manipulations can be performed
+     *        within the returned session
+     * @param lockMode one of the lock modes specified in
+     *        <code>DmtSession</code>
+     * @return a <code>DmtSession</code> object for the requested subtree
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>subtreeUri</code> or
+     *         a segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>subtreeUri</code> is
+     *         syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>subtreeUri</code>
+     *         specifies a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if <code>principal</code> is
+     *         not <code>null</code> and the ACL of the node does not allow the
+     *         <code>Get</code> operation for the principal on the given root 
+     *         node 
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if atomic sessions are
+     *         not supported by the implementation and <code>lockMode</code> 
+     *         requests an atomic session
+     *         <li><code>SESSION_CREATION_TIMEOUT</code> if the operation
+     *         timed out because of  another ongoing session
+     *         <li><code>COMMAND_FAILED</code> if <code>subtreeUri</code>
+     *         specifies a relative URI, if <code>lockMode</code> is unknown,
+     *         or some unspecified error is encountered while attempting to 
+     *         complete the command
+     *         </ul>
+     * @throws SecurityException in case of remote sessions, if the caller does 
+     *         not have the required <code>DmtPrincipalPermission</code> with a 
+     *         target matching the <code>principal</code> parameter, or in case
+     *         of local sessions, if the caller does not have 
+     *         <code>DmtPermission</code> for the given root node with the Get 
+     *         action present 
+     */
+    DmtSession getSession(String principal, String subtreeUri, int lockMode)
+            throws DmtException;
+
+    /**
+     * Registers an event listener on behalf of a local application. The given
+     * listener will receive notification on all changes affecting the specified
+     * subtree. The subtree is specified by its root node URI. An event is
+     * delivered to the registered listener if at least one affected node is
+     * within this subtree. The events can also be filtered by specifying a
+     * bitmask of relevant event types (e.g.
+     * <code>DmtEvent.ADDED | DmtEvent.REPLACED | DmtEvent.SESSION_CLOSED</code>).
+     * Only event types included in the bitmask will be delivered to the
+     * listener.
+     * <p>
+     * The listener will only receive the change notifications of nodes for
+     * which the registering application has the appropriate GET
+     * {@link info.dmtree.security.DmtPermission}.
+     * <p>
+     * If the specified <code>listener</code> was already registered, calling
+     * this method will update the registration.
+     * 
+     * @param type a bitmask of event types the caller is interested in
+     * @param uri the URI of the root node of a subtree, must not be
+     *        <code>null</code>
+     * @param listener the listener to be registered, must not be
+     *        <code>null</code>
+     * @throws SecurityException if the caller doesn't have the necessary GET
+     *         <code>DmtPermission</code> for the given URI
+     * @throws NullPointerException if the <code>uri</code> or
+     *         <code>listener</code> parameter is <code>null</code>
+     * @throws IllegalArgumentException if the <code>type</code> parameter
+     *         contains invalid bits (not corresponding to any event type
+     *         defined in <code>DmtEvent</code>), or if the <code>uri</code>
+     *         parameter is invalid (is not an absolute URI or is syntactically
+     *         incorrect)
+     */
+    void addEventListener(int type, String uri, DmtEventListener listener);
+
+    /**
+     * Registers an event listener on behalf of a remote principal. The given
+     * listener will receive notification on all changes affecting the specified
+     * subtree. The subtree is specified by its root node URI. An event is
+     * delivered to the registered listener if at least one affected node is
+     * within this subtree. The events can also be filtered by specifying a
+     * bitmask of relevant event types (e.g.
+     * <code>DmtEvent.ADDED | DmtEvent.REPLACED | DmtEvent.SESSION_CLOSED</code>).
+     * Only event types included in the bitmask will be delivered to the
+     * listener.
+     * <p>
+     * The listener will only receive the change notifications of nodes for
+     * which the node ACL grants GET access to the specified principal.
+     * <p>
+     * If the specified <code>listener</code> was already registered, calling
+     * this method will update the registration.
+     * 
+     * @param principal the management server identity the caller is acting on
+     *        behalf of, must not be <code>null</code>
+     * @param type a bitmask of event types the caller is interested in
+     * @param uri the URI of the root node of a subtree, must not be
+     *        <code>null</code>
+     * @param listener the listener to be registered, must not be
+     *        <code>null</code>
+     * @throws SecurityException if the caller doesn't have the necessary
+     *         <code>DmtPrincipalPermission</code> to use the specified
+     *         principal
+     * @throws NullPointerException if the <code>principal</code>,
+     *         <code>uri</code> or <code>listener</code> parameter is 
+     *         <code>null</code>
+     * @throws IllegalArgumentException if the <code>type</code> parameter
+     *         contains invalid bits (not corresponding to any event type
+     *         defined in <code>DmtEvent</code>), or if the <code>uri</code>
+     *         parameter is invalid (is not an absolute URI or is syntactically
+     *         incorrect)
+     */
+    void addEventListener(String principal, int type, String uri,
+            DmtEventListener listener);
+
+    /**
+     * Remove a previously registered listener. After this call, the listener
+     * will not receive change notifications.
+     * 
+     * @param listener the listener to be unregistered, must not be
+     *        <code>null</code>
+     * @throws NullPointerException if the <code>listener</code> parameter is
+     *         <code>null</code>
+     */
+    void removeEventListener(DmtEventListener listener);
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java
new file mode 100644
index 0000000..51c5cae
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java
@@ -0,0 +1,907 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtData.java,v 1.8 2006/07/10 21:37:07 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;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+
+// Possible enhancements to this class:
+// * new constructors and get/set methods for b64, to access the encoded value
+// * new constructors and get/set methods for date/time, for more convenient 
+//   Java access
+/**
+ * An immutable data structure representing the contents of a leaf or interior
+ * node. This structure represents only the value and the format property of the
+ * node, all other properties (like MIME type) can be set and read using the
+ * <code>DmtSession</code> interface.
+ * <p>
+ * Different constructors are available to create nodes with different formats.
+ * Nodes of <code>null</code> format can be created using the static
+ * {@link #NULL_VALUE} constant instance of this class.
+ * <p>
+ * {@link #FORMAT_RAW_BINARY} and {@link #FORMAT_RAW_STRING} enable the support
+ * of future data formats. When using these formats, the actual format name is
+ * specified as a <code>String</code>. The application is responsible for the
+ * proper encoding of the data according to the specified format.
+ */
+public final class DmtData {
+
+    /**
+     * The node holds an OMA DM <code>int</code> value.
+     */
+    public static final int FORMAT_INTEGER = 0x0001;
+
+    /**
+     * The node holds an OMA DM <code>float</code> value.
+     */
+    public static final int FORMAT_FLOAT = 0x0002;
+
+    /**
+     * The node holds an OMA DM <code>chr</code> value.
+     */
+    public static final int FORMAT_STRING = 0x0004;
+
+    /**
+     * The node holds an OMA DM <code>bool</code> value.
+     */
+    public static final int FORMAT_BOOLEAN = 0x0008;
+
+    /**
+     * The node holds an OMA DM <code>date</code> value.
+     */
+    public static final int FORMAT_DATE = 0x0010;
+
+    /**
+     * The node holds an OMA DM <code>time</code> value.
+     */
+    public static final int FORMAT_TIME = 0x0020;
+
+    /**
+     * The node holds an OMA DM <code>bin</code> value. The value of the node
+     * corresponds to the Java <code>byte[]</code> type.
+     */
+    public static final int FORMAT_BINARY = 0x0040;
+
+    /**
+     * The node holds an OMA DM <code>b64</code> value. Like
+     * {@link #FORMAT_BINARY}, this format is also represented by the Java
+     * <code>byte[]</code> type, the difference is only in the corresponding
+     * OMA DM format.
+     */
+    public static final int FORMAT_BASE64 = 0x0080;
+
+    /**
+     * The node holds an OMA DM <code>xml</code> value.
+     */
+    public static final int FORMAT_XML = 0x0100;
+
+    /**
+     * The node holds an OMA DM <code>null</code> value. This corresponds to
+     * the Java <code>null</code> type.
+     */
+    public static final int FORMAT_NULL = 0x0200;
+
+    /**
+     * Format specifier of an internal node. An interior node can hold a Java
+     * object as value (see {@link DmtData#DmtData(Object)} and
+     * {@link DmtData#getNode()}). This value can be used by Java programs that
+     * know a specific URI understands the associated Java type. This type is
+     * further used as a return value of the {@link MetaNode#getFormat} method
+     * for interior nodes.
+     */
+    public static final int FORMAT_NODE = 0x0400;
+    
+    /**
+     * The node holds raw protocol data encoded as <code>String</code>. The
+     * {@link #getFormatName()} method can be used to get the actual format
+     * name.
+     */
+    public static final int FORMAT_RAW_STRING = 0x0800;
+
+    /**
+     * The node holds raw protocol data encoded in binary format. The
+     * {@link #getFormatName()} method can be used to get the actual format
+     * name.
+     */
+    public static final int FORMAT_RAW_BINARY = 0x1000;
+
+    
+    private static final Hashtable FORMAT_NAMES = new Hashtable();
+    
+    static {
+        FORMAT_NAMES.put(new Integer(FORMAT_BASE64),    "b64");
+        FORMAT_NAMES.put(new Integer(FORMAT_BINARY),    "bin");
+        FORMAT_NAMES.put(new Integer(FORMAT_BOOLEAN),   "bool");
+        FORMAT_NAMES.put(new Integer(FORMAT_DATE),      "date");
+        FORMAT_NAMES.put(new Integer(FORMAT_FLOAT),     "float");
+        FORMAT_NAMES.put(new Integer(FORMAT_INTEGER),   "int");
+        FORMAT_NAMES.put(new Integer(FORMAT_NODE),      "node");
+        FORMAT_NAMES.put(new Integer(FORMAT_NULL),      "null");
+        FORMAT_NAMES.put(new Integer(FORMAT_STRING),    "chr");
+        FORMAT_NAMES.put(new Integer(FORMAT_TIME),      "time");
+        FORMAT_NAMES.put(new Integer(FORMAT_XML),       "xml");
+    }
+
+    /**
+     * Constant instance representing a leaf node of <code>null</code> format.
+     */
+    public static final DmtData NULL_VALUE = new DmtData();
+        // FORMAT_NAMES must be initialized by the time the constr. is called 
+    
+    private final String str;
+
+    private final int integer;
+
+    private final float flt;
+
+    private final boolean bool;
+
+    private final byte[] bytes;
+
+    private final int format;
+    
+    private final String formatName;
+
+    private final Object complex;
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>null</code> format.
+     * This constructor is private and used only to create the public
+     * {@link #NULL_VALUE} constant.
+     */
+    private DmtData() {
+        format = FORMAT_NULL;
+        formatName = getFormatName(format);
+
+        this.str = null;
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.bytes = null;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>chr</code> format
+     * with the given string value. The <code>null</code> string argument is
+     * valid.
+     * 
+     * @param str the string value to set
+     */
+    public DmtData(String str) {
+        format = FORMAT_STRING;
+        formatName = getFormatName(format);
+        this.str = str;
+
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.bytes = null;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>node</code> format
+     * with the given object value. The value represents complex data associated
+     * with an interior node.
+     * <p>
+     * Certain interior nodes can support access to their subtrees through such
+     * complex values, making it simpler to retrieve or update all leaf nodes in
+     * a subtree.
+     * <p>
+     * The given value must be a non-<code>null</code> immutable object.
+     * 
+     * @param complex the complex data object to set
+     */
+    public DmtData(Object complex) {
+        if(complex == null)
+            throw new NullPointerException("Complex data argument is null.");
+
+        format = FORMAT_NODE;
+        formatName = getFormatName(format);
+        this.complex = complex;
+
+        this.str     = null;
+        this.integer = 0;
+        this.flt     = 0;
+        this.bool    = false;
+        this.bytes   = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of the specified format and set
+     * its value based on the given string. Only the following string-based
+     * formats can be created using this constructor:
+     * <ul>
+     * <li>{@link #FORMAT_STRING} - value can be any string
+     * <li>{@link #FORMAT_XML} - value must contain an XML fragment (the
+     *     validity is not checked by this constructor)
+     * <li>{@link #FORMAT_DATE} - value must be parseable to an ISO 8601
+     *     calendar date in complete representation, basic format (pattern
+     * <tt>CCYYMMDD</tt>)
+     * <li>{@link #FORMAT_TIME} - value must be parseable to an ISO 8601 time
+     *     of day in either local time, complete representation, basic format
+     *     (pattern <tt>hhmmss</tt>) or Coordinated Universal Time, basic format
+     *     (pattern <tt>hhmmssZ</tt>)
+     * </ul>
+     * The <code>null</code> string argument is only valid if the format is
+     * string or XML.
+     * 
+     * @param value the string, XML, date or time value to set
+     * @param format the format of the <code>DmtData</code> instance to be
+     *        created, must be one of the formats specified above
+     * @throws IllegalArgumentException if <code>format</code> is not one of
+     *         the allowed formats, or <code>value</code> is not a valid
+     *         string for the given format
+     * @throws NullPointerException if a date or time is constructed and
+     *         <code>value</code> is <code>null</code>
+     */
+    public DmtData(String value, int format) {
+        switch (format) {
+        case FORMAT_DATE:
+            checkDateFormat(value);
+            break;
+        case FORMAT_TIME:
+            checkTimeFormat(value);
+            break;
+        case FORMAT_STRING:
+        case FORMAT_XML:
+            break; // nothing to do, all string values are accepted
+        default:
+            throw new IllegalArgumentException(
+                    "Invalid format in string constructor: " + format);
+        }
+        this.format = format;
+        this.formatName = getFormatName(format);
+        this.str = value;
+
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.bytes = null;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>int</code> format and
+     * set its value.
+     * 
+     * @param integer the integer value to set
+     */
+    public DmtData(int integer) {
+        format = FORMAT_INTEGER;
+        formatName = getFormatName(format);
+        this.integer = integer;
+
+        this.str = null;
+        this.flt = 0;
+        this.bool = false;
+        this.bytes = null;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>float</code> format
+     * and set its value.
+     * 
+     * @param flt the float value to set
+     */
+    public DmtData(float flt) {
+        format = FORMAT_FLOAT;
+        formatName = getFormatName(format);
+        this.flt = flt;
+
+        this.str = null;
+        this.integer = 0;
+        this.bool = false;
+        this.bytes = null;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>bool</code> format
+     * and set its value.
+     * 
+     * @param bool the boolean value to set
+     */
+    public DmtData(boolean bool) {
+        format = FORMAT_BOOLEAN;
+        formatName = getFormatName(format);
+        this.bool = bool;
+
+        this.str = null;
+        this.integer = 0;
+        this.flt = 0;
+        this.bytes = null;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>bin</code> format and
+     * set its value.
+     * 
+     * @param bytes the byte array to set, must not be <code>null</code>
+     * @throws NullPointerException if <code>bytes</code> is <code>null</code>
+     */
+    public DmtData(byte[] bytes) {
+        if (bytes == null)
+            throw new NullPointerException("Binary data argument is null.");
+
+        format = FORMAT_BINARY;
+        formatName = getFormatName(format);
+        this.bytes = bytes;
+
+        this.str = null;
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance of <code>bin</code> or
+     * <code>b64</code> format and set its value. The chosen format is
+     * specified by the <code>base64</code> parameter.
+     * 
+     * @param bytes the byte array to set, must not be <code>null</code>
+     * @param base64 if <code>true</code>, the new instance will have
+     *        <code>b64</code> format, if <code>false</code>, it will have
+     *        <code>bin</code> format
+     * @throws NullPointerException if <code>bytes</code> is <code>null</code>
+     */
+    public DmtData(byte[] bytes, boolean base64) {
+        if (bytes == null)
+            throw new NullPointerException("Binary data argument is null.");
+
+        format = base64 ? FORMAT_BASE64 : FORMAT_BINARY;
+        formatName = getFormatName(format);
+        this.bytes = bytes;
+
+        this.str = null;
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.complex = null;
+    }
+
+    /**
+     * Create a <code>DmtData</code> instance in {@link #FORMAT_RAW_STRING}
+     * format. The data is provided encoded as a <code>String</code>. The
+     * actual data format is specified in <code>formatName</code>. The
+     * encoding used in <code>data</code> must conform to this format.
+     * 
+     * @param formatName the name of the format, must not be <code>null</code>
+     * @param data the data encoded according to the specified format, must not
+     *        be <code>null</code>
+     * @throws NullPointerException if <code>formatName</code> or
+     *         <code>data</code> is <code>null</code>
+     */
+    public DmtData(String formatName, String data) {
+        if(formatName == null)
+            throw new NullPointerException("Format name argument is null.");
+        if(data == null)
+            throw new NullPointerException("Data argument is null.");
+        
+        format = FORMAT_RAW_STRING;
+        this.formatName = formatName;
+        this.str = data;
+
+        this.bytes = null;
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.complex = null;
+    }
+    
+    /**
+     * Create a <code>DmtData</code> instance in {@link #FORMAT_RAW_BINARY}
+     * format. The data is provided encoded as binary. The actual data format is
+     * specified in <code>formatName</code>. The encoding used in
+     * <code>data</code> must conform to this format.
+     * 
+     * @param formatName the name of the format, must not be <code>null</code>
+     * @param data the data encoded according to the specified format, must not
+     *        be <code>null</code>
+     * @throws NullPointerException if <code>formatName</code> or
+     *         <code>data</code> is <code>null</code>
+     */
+    public DmtData(String formatName, byte[] data) {
+        if(formatName == null)
+            throw new NullPointerException("Format name argument is null.");
+        if(data == null)
+            throw new NullPointerException("Data argument is null.");
+
+        format = FORMAT_RAW_BINARY;
+        this.formatName = formatName;
+        this.bytes = (byte[]) data.clone();
+
+        this.str = null;
+        this.integer = 0;
+        this.flt = 0;
+        this.bool = false;
+        this.complex = null;
+    }
+    
+    /**
+     * Gets the value of a node with string (<code>chr</code>) format.
+     * 
+     * @return the string value
+     * @throws DmtIllegalStateException if the format of the node is not string
+     */
+    public String getString() {
+        if (format == FORMAT_STRING)
+            return str;
+
+        throw new DmtIllegalStateException("DmtData value is not string.");
+    }
+
+    /**
+     * Gets the value of a node with date format. The returned date string is
+     * formatted according to the ISO 8601 definition of a calendar date in
+     * complete representation, basic format (pattern <tt>CCYYMMDD</tt>).
+     * 
+     * @return the date value
+     * @throws DmtIllegalStateException if the format of the node is not date
+     */
+    public String getDate() {
+        if (format == FORMAT_DATE)
+            return str;
+
+        throw new DmtIllegalStateException("DmtData value is not date.");
+    }
+
+    /**
+     * Gets the value of a node with time format. The returned time string is
+     * formatted according to the ISO 8601 definition of the time of day. The
+     * exact format depends on the value the object was initialized with: either
+     * local time, complete representation, basic format (pattern
+     * <tt>hhmmss</tt>) or Coordinated Universal Time, basic format (pattern
+     * <tt>hhmmssZ</tt>).
+     * 
+     * @return the time value
+     * @throws DmtIllegalStateException if the format of the node is not time
+     */
+    public String getTime() {
+        if (format == FORMAT_TIME)
+            return str;
+
+        throw new DmtIllegalStateException("DmtData value is not time.");
+    }
+
+    /**
+     * Gets the value of a node with <code>xml</code> format.
+     * 
+     * @return the XML value
+     * @throws DmtIllegalStateException if the format of the node is not
+     *         <code>xml</code>
+     */
+    public String getXml() {
+        if (format == FORMAT_XML)
+            return str;
+
+        throw new DmtIllegalStateException("DmtData value is not XML.");
+    }
+
+    /**
+     * Gets the value of a node with integer (<code>int</code>) format.
+     * 
+     * @return the integer value
+     * @throws DmtIllegalStateException if the format of the node is not integer
+     */
+    public int getInt() {
+        if (format == FORMAT_INTEGER)
+            return integer;
+
+        throw new DmtIllegalStateException("DmtData value is not integer.");
+    }
+
+    /**
+     * Gets the value of a node with <code>float</code> format.
+     * 
+     * @return the float value
+     * @throws DmtIllegalStateException if the format of the node is not
+     *         <code>float</code>
+     */
+    public float getFloat() {
+        if (format == FORMAT_FLOAT)
+            return flt;
+
+        throw new DmtIllegalStateException("DmtData value is not float.");
+    }
+
+    /**
+     * Gets the value of a node with boolean (<code>bool</code>) format.
+     * 
+     * @return the boolean value
+     * @throws DmtIllegalStateException if the format of the node is not boolean
+     */
+    public boolean getBoolean() {
+        if (format == FORMAT_BOOLEAN)
+            return bool;
+
+        throw new DmtIllegalStateException("DmtData value is not boolean.");
+    }
+
+    /**
+     * Gets the value of a node with binary (<code>bin</code>) format.
+     * 
+     * @return the binary value
+     * @throws DmtIllegalStateException if the format of the node is not binary
+     */
+    public byte[] getBinary() {
+        if (format == FORMAT_BINARY) {
+            byte[] bytesCopy = new byte[bytes.length];
+            for (int i = 0; i < bytes.length; i++)
+                bytesCopy[i] = bytes[i];
+
+            return bytesCopy;
+        }
+
+        throw new DmtIllegalStateException("DmtData value is not a byte array.");
+    }
+    
+    /**
+     * Gets the value of a node in raw binary ({@link #FORMAT_RAW_BINARY})
+     * format.
+     * 
+     * @return the data value in raw binary format
+     * @throws DmtIllegalStateException if the format of the node is not raw binary
+     */
+    public byte[] getRawBinary() {
+        if (format == FORMAT_RAW_BINARY)
+            return (byte[]) bytes.clone();
+        
+        throw new DmtIllegalStateException(
+                "DmtData value is not in raw binary format.");
+    }
+
+    /**
+     * Gets the value of a node in raw <code>String</code>
+     * ({@link #FORMAT_RAW_STRING}) format.
+     * 
+     * @return the data value in raw <code>String</code> format
+     * @throws DmtIllegalStateException if the format of the node is not raw
+     *     <code>String</code>
+    */
+    public String getRawString() {
+        if (format == FORMAT_RAW_STRING)
+            return str;
+        
+        throw new DmtIllegalStateException(
+                "DmtData value is not in raw string format.");
+    }
+
+    /**
+     * Gets the value of a node with base 64 (<code>b64</code>) format.
+     * 
+     * @return the binary value
+     * @throws DmtIllegalStateException if the format of the node is not base 64.
+     */
+    public byte[] getBase64() {
+        if (format == FORMAT_BASE64) {
+            byte[] bytesCopy = new byte[bytes.length];
+            for (int i = 0; i < bytes.length; i++)
+                bytesCopy[i] = bytes[i];
+
+            return bytesCopy;
+        }
+
+        throw new DmtIllegalStateException(
+                "DmtData value is not in base 64 format.");
+    }
+
+    /**
+     * Gets the complex data associated with an interior node (<code>node</code>
+     * format).
+     * <p>
+     * Certain interior nodes can support access to their subtrees through
+     * complex values, making it simpler to retrieve or update all leaf nodes in
+     * the subtree.
+     * 
+     * @return the data object associated with an interior node
+     * @throws DmtIllegalStateException if the format of the data is not 
+     *         <code>node</code>
+     */
+    public Object getNode() {
+        if(format == FORMAT_NODE)
+            return complex;
+
+        throw new DmtIllegalStateException(
+                "DmtData does not contain interior node data.");
+    }
+
+    /**
+     * Get the node's format, expressed in terms of type constants defined in
+     * this class. Note that the 'format' term is a legacy from OMA DM, it is
+     * more customary to think of this as 'type'.
+     * 
+     * @return the format of the node
+     */
+    public int getFormat() {
+        return format;
+    }
+
+    /**
+     * Returns the format of this <code>DmtData</code> as <code>String</code>.
+     * For the predefined data formats this is the OMA DM defined name of the
+     * format. For {@link #FORMAT_RAW_STRING} and {@link #FORMAT_RAW_BINARY}
+     * this is the format specified when the object was created.
+     * 
+     * @return the format name as <code>String</code>
+     */
+    public String getFormatName() {
+        return formatName;
+    }
+
+    /**
+     * Get the size of the data. The returned value depends on the format of
+     * data in the node:
+     * <ul>
+     * <li>{@link #FORMAT_STRING}, {@link #FORMAT_XML}, {@link #FORMAT_BINARY},
+     *     {@link #FORMAT_BASE64}, {@link #FORMAT_RAW_STRING}, and
+     *     {@link #FORMAT_RAW_BINARY}: the length of the stored data, or 0 if
+     *     the data is <code>null</code>
+     * <li>{@link #FORMAT_INTEGER} and {@link #FORMAT_FLOAT}: 4
+     * <li>{@link #FORMAT_DATE} and {@link #FORMAT_TIME}: the length of the
+     *     date or time in its string representation
+     * <li>{@link #FORMAT_BOOLEAN}: 1
+     * <li>{@link #FORMAT_NODE}: -1 (unknown)
+     * <li>{@link #FORMAT_NULL}: 0
+     * </ul>
+     * 
+     * @return the size of the data stored by this object
+     */
+    public int getSize() {
+        switch (format) {
+        case FORMAT_STRING:
+        case FORMAT_XML:
+        case FORMAT_DATE:
+        case FORMAT_TIME:
+        case FORMAT_RAW_STRING:
+            return str == null ? 0 : str.length();
+        case FORMAT_BINARY:
+        case FORMAT_BASE64:
+        case FORMAT_RAW_BINARY:
+            return bytes.length;
+        case FORMAT_INTEGER:
+        case FORMAT_FLOAT:
+            return 4;
+        case FORMAT_BOOLEAN:
+            return 1;
+        case FORMAT_NODE:
+            return -1;
+        case FORMAT_NULL:
+            return 0;
+        }
+
+        return 0; // never reached
+    }
+
+    /**
+     * Gets the string representation of the <code>DmtData</code>. This
+     * method works for all formats.
+     * <p>
+     * For string format data - including {@link #FORMAT_RAW_STRING} - the
+     * string value itself is returned, while for XML, date, time, integer,
+     * float, boolean and node formats the string form of the value is returned.
+     * Binary - including {@link #FORMAT_RAW_BINARY} - and base64 data is
+     * represented by two-digit hexadecimal numbers for each byte separated by
+     * spaces. The {@link #NULL_VALUE} data has the string form of
+     * "<code>null</code>". Data of string or XML format containing the Java
+     * <code>null</code> value is represented by an empty string.
+     * 
+     * @return the string representation of this <code>DmtData</code> instance
+     */
+    public String toString() {
+        switch (format) {
+        case FORMAT_STRING:
+        case FORMAT_XML:
+        case FORMAT_DATE:
+        case FORMAT_TIME:
+        case FORMAT_RAW_STRING:
+            return str == null ? "" : str;
+        case FORMAT_INTEGER:
+            return String.valueOf(integer);
+        case FORMAT_FLOAT:
+            return String.valueOf(flt);
+        case FORMAT_BOOLEAN:
+            return String.valueOf(bool);
+        case FORMAT_BINARY:
+        case FORMAT_BASE64:
+        case FORMAT_RAW_BINARY:
+            return getHexDump(bytes);
+        case FORMAT_NODE:
+            return complex.toString();
+        case FORMAT_NULL:
+            return "null";
+        }
+
+        return null; // never reached
+    }
+
+    /**
+     * Compares the specified object with this <code>DmtData</code> instance.
+     * Two <code>DmtData</code> objects are considered equal if their format
+     * is the same, and their data (selected by the format) is equal.
+     * <p>
+     * In case of {@link #FORMAT_RAW_BINARY} and {@link #FORMAT_RAW_STRING}
+     * the textual name of the data format - as returned by
+     * {@link #getFormatName()} - must be equal as well.
+     * 
+     * @param obj the object to compare with this <code>DmtData</code>
+     * @return true if the argument represents the same <code>DmtData</code>
+     *         as this object
+     */
+    public boolean equals(Object obj) {
+        if (!(obj instanceof DmtData))
+            return false;
+
+        DmtData other = (DmtData) obj;
+
+        if (format != other.format)
+            return false;
+
+        switch (format) {
+        case FORMAT_STRING:
+        case FORMAT_XML:
+        case FORMAT_DATE:
+        case FORMAT_TIME:
+            return str == null ? other.str == null : str.equals(other.str);
+        case FORMAT_INTEGER:
+            return integer == other.integer;
+        case FORMAT_FLOAT:
+            return flt == other.flt;
+        case FORMAT_BOOLEAN:
+            return bool == other.bool;
+        case FORMAT_BINARY:
+        case FORMAT_BASE64:
+            return Arrays.equals(bytes, other.bytes);
+        case FORMAT_NODE:
+            return complex.equals(other.complex);
+        case FORMAT_NULL:
+            return true;
+        case FORMAT_RAW_BINARY:
+            return formatName.equals(other.formatName)
+                    && Arrays.equals(bytes, other.bytes);
+        case FORMAT_RAW_STRING:
+            // in this case str cannot be null
+            return formatName.equals(other.formatName) && str.equals(other.str);
+        }
+
+        return false; // never reached
+    }
+
+    /**
+     * Returns the hash code value for this <code>DmtData</code> instance. The
+     * hash code is calculated based on the data (selected by the format) of
+     * this object.
+     * 
+     * @return the hash code value for this object
+     */
+    public int hashCode() {
+        switch (format) {
+        case FORMAT_STRING:
+        case FORMAT_XML:
+        case FORMAT_DATE:
+        case FORMAT_TIME:
+        case FORMAT_RAW_STRING:
+            return str == null ? 0 : str.hashCode();
+        case FORMAT_INTEGER:
+            return new Integer(integer).hashCode();
+        case FORMAT_FLOAT:
+            return new Float(flt).hashCode();
+        case FORMAT_BOOLEAN:
+            return new Boolean(bool).hashCode();
+        case FORMAT_BINARY:
+        case FORMAT_BASE64:
+        case FORMAT_RAW_BINARY:
+            return new String(bytes).hashCode();
+        case FORMAT_NODE:
+            return complex.hashCode();
+        case FORMAT_NULL:
+            return 0;
+        }
+
+        return 0; // never reached
+    }
+    
+    private static void checkDateFormat(String value) {
+        if(value.length() != 8)
+            throw new IllegalArgumentException("Date string '" + value +
+                    "' does not follow the format 'CCYYMMDD'.");
+        
+        int year = checkNumber(value, "Date", 0, 4, 0, 9999);
+        int month = checkNumber(value, "Date", 4, 2, 1, 12);
+        int day = checkNumber(value, "Date", 6, 2, 1, 31);
+        
+        // Date checking is not prepared for all special rules (for example
+        // historical leap years), production code could contain a full check.
+        
+        // Day 31 is invalid for April, June, September and November
+        if((month == 4 || month == 6 || month == 9 || month == 11) && day == 31)
+        	throw new IllegalArgumentException("Date string '" + value +
+        			"' contains an invalid date.");
+        
+        // February 29 is invalid except for leap years, Feb. 30-31 are invalid
+        if(month == 2 && day > 28 &&
+        	!(day == 29 && year%4 == 0 && (year%100 != 0 || year%400 == 0)))
+        	throw new IllegalArgumentException("Date string '" + value +
+        			"' contains an invalid date.");
+    }
+    
+    private static void checkTimeFormat(String value) {
+        if(value.length() > 0 && value.charAt(value.length()-1) == 'Z')
+            value = value.substring(0, value.length()-1);
+        
+        if(value.length() != 6)
+            throw new IllegalArgumentException("Time string '" + value +
+                    "' does not follow the format 'hhmmss' or 'hhmmssZ'.");
+            
+        // Time checking is not prepared for all special rules (for example
+        // leap seconds), production code could contain a full check.
+        
+        // if hour is 24, only 240000 should be allowed
+        checkNumber(value, "Time", 0, 2, 0, 24);
+        checkNumber(value, "Time", 2, 2, 0, 59);
+        checkNumber(value, "Time", 4, 2, 0, 59);
+        
+        if(value.startsWith("24") && !value.startsWith("240000"))
+        	throw new IllegalArgumentException("Time string is out of range.");
+    }
+    
+    private static int checkNumber(String value, String name, int from,
+            int length, int min, int max) {
+        String part = value.substring(from, from+length);
+        int number;
+        try {
+            number = Integer.parseInt(part);
+        } catch(NumberFormatException e) {
+            throw new IllegalArgumentException(name + " string '" + value +
+                    "' contains a non-numeric part.");
+        }
+        if(number < min || number > max)
+            throw new IllegalArgumentException("A segment of the " + name +
+                    " string '" + value + "' is out of range.");
+        
+        return number;
+    }
+
+    // character array of hexadecimal digits, used for printing binary data
+    private static char[] hex = "0123456789ABCDEF".toCharArray();
+
+    // generates a hexadecimal dump of the given binary data
+    private static String getHexDump(byte[] bytes) {
+        if (bytes.length == 0)
+            return "";
+
+        StringBuffer buf = new StringBuffer();
+        appendHexByte(buf, bytes[0]);
+        for (int i = 1; i < bytes.length; i++)
+            appendHexByte(buf.append(' '), bytes[i]);
+
+        return buf.toString();
+    }
+
+    private static void appendHexByte(StringBuffer buf, byte b) {
+        buf.append(hex[(b & 0xF0) >> 4]).append(hex[b & 0x0F]);
+    }
+    
+    private static String getFormatName(int format) {
+        return (String) FORMAT_NAMES.get(new Integer(format));
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtEvent.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtEvent.java
new file mode 100644
index 0000000..dac4f1d
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtEvent.java
@@ -0,0 +1,140 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtEvent.java,v 1.8 2006/07/04 12:12:16 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;
+
+/**
+ * Event class storing the details of a change in the tree.
+ * <code>DmtEvent</code> is used by <code>DmtAdmin</code> to notify registered
+ * {@link DmtEventListener EventListeners} about important changes. Events are
+ * generated after every successful DMT change, and also when sessions are
+ * opened or closed. If a {@link DmtSession} is opened in atomic mode, DMT
+ * events are only sent when the session is committed, when the changes are
+ * actually performed.
+ * <p>
+ * An event is generated for each group of nodes added, deleted, replaced,
+ * renamed or copied, in this order.  Events are also generated when sessions
+ * are opened and closed.
+ * <p>
+ * The <code>type</code> of the event describes the change that triggered the
+ * event delivery. Each event carries the unique identifier of the session in
+ * which the described change happened. The events describing changes in the DMT
+ * carry the list of affected nodes. In case of {@link #COPIED} or
+ * {@link #RENAMED} events, the event carries the list of new nodes as well.
+ * <p>
+ * When a <code>DmtEvent</code> is delivered to a listener, the event contains
+ * only those node URIs that the listener has access to. This access control
+ * decision is based on the principal specified when the listener was
+ * registered:
+ * <ul>
+ * <li> If the listener was registered specifying an explicit principal, using
+ * the {@link DmtAdmin#addEventListener(String, int, String, DmtEventListener)}
+ * method, then the target node ACLs should be checked for providing GET access
+ * to the specified principal;
+ * <li> When the listener was registered without an explicit principal then the
+ * listener needs GET {@link info.dmtree.security.DmtPermission} for
+ * the corresponding node.
+ * </ul>
+ */
+public interface DmtEvent {
+
+    /**
+     * Event type indicating nodes that were added.
+     */
+    int ADDED = 0x01;
+
+    /**
+     * Event type indicating nodes that were copied.
+     */
+    int COPIED = 0x02;
+
+    /**
+     * Event type indicating nodes that were deleted.
+     */
+    int DELETED = 0x04;
+
+    /**
+     * Event type indicating nodes that were renamed.
+     */
+    int RENAMED = 0x08;
+
+    /**
+     * Event type indicating nodes that were replaced.
+     */
+    int REPLACED = 0x10;
+
+    /**
+     * Event type indicating that a new session was opened.
+     */
+    int SESSION_OPENED = 0x20;
+
+    /**
+     * Event type indicating that a session was closed.  This type of event is
+     * sent when the session is closed by the client or becomes inactive for any
+     * other reason (session timeout, fatal errors in business methods, etc.).
+     */
+    int SESSION_CLOSED = 0x40;
+
+    /**
+     * This method returns the type of this event.
+     * 
+     * @return the type of this event.
+     */
+    int getType();
+
+    /**
+     * This method returns the identifier of the session in which this event
+     * took place. The ID is guaranteed to be unique on a machine.
+     * 
+     * @return the unique indetifier of the session that triggered the event
+     */
+    int getSessionId();
+
+    /**
+     * This method can be used to query the subject nodes of this event. The
+     * method returns <code>null</code> for {@link #SESSION_OPENED} and
+     * {@link #SESSION_CLOSED}.
+     * <p>
+     * The method returns only those affected nodes that the caller has the GET
+     * permission for (or in case of {@link #COPIED} or {@link #RENAMED} events,
+     * where the caller has GET permissions for either the source or the
+     * destination nodes). Therefore, it is possible that the method returns an
+     * empty array. All returned URIs are absolute.
+     * 
+     * @return the array of affected nodes
+     * @see #getNewNodes
+     */
+    String[] getNodes();
+
+    /**
+     * This method can be used to query the new nodes, when the type of the
+     * event is {@link #COPIED} or {@link #RENAMED}. For all other event types
+     * this method returns <code>null</code>.
+     * <p>
+     * The array returned by this method runs parallel to the array returned by
+     * {@link #getNodes}, the elements in the two arrays contain the source and
+     * destination URIs for the renamed or copied nodes in the same order. All
+     * returned URIs are absolute.
+     * <p>
+     * This method returns only those nodes where the caller has the GET
+     * permission for the source or destination node of the operation.
+     * Therefore, it is possible that the method returns an empty array.
+     * 
+     * @return the array of newly created nodes
+     */
+    String[] getNewNodes();
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtEventListener.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtEventListener.java
new file mode 100644
index 0000000..c5425b7
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtEventListener.java
@@ -0,0 +1,37 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtEventListener.java,v 1.6 2006/07/04 12:12:16 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;
+
+/**
+ * Registered implementations of this class are notified via {@link DmtEvent}
+ * objects about important changes in the tree. Events are generated after every
+ * successful DMT change, and also when sessions are opened or closed. If a
+ * {@link DmtSession} is opened in atomic mode, DMT events are only sent when
+ * the session is committed, when the changes are actually performed.
+ */
+public interface DmtEventListener {
+
+    /**
+     * <code>DmtAdmin</code> uses this method to notify the registered
+     * listeners about the change. This method is called asynchronously from the
+     * actual event occurrence.
+     * 
+     * @param event the <code>DmtEvent</code> describing the change in detail
+     */
+    void changeOccurred(DmtEvent event);
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtException.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtException.java
new file mode 100644
index 0000000..c723bc0
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtException.java
@@ -0,0 +1,639 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtException.java,v 1.9 2006/07/12 21:21:37 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;
+
+import java.io.PrintStream;
+import java.util.Vector;
+
+/**
+ * Checked exception received when a DMT operation fails. Beside the exception
+ * message, a <code>DmtException</code> always contains an error code (one of
+ * the constants specified in this class), and may optionally contain the URI of
+ * the related node, and information about the cause of the exception.
+ * <p>
+ * Some of the error codes defined in this class have a corresponding error code
+ * defined in OMA DM, in these cases the name and numerical value from OMA DM is
+ * used. Error codes without counterparts in OMA DM were given numbers from a
+ * different range, starting from 1.
+ * <p>
+ * The cause of the exception (if specified) can either be a single
+ * <code>Throwable</code> instance, or a list of such instances if several
+ * problems occurred during the execution of a method. An example for the latter
+ * is the <code>close</code> method of <code>DmtSession</code> that tries to
+ * close multiple plugins, and has to report the exceptions of all failures.
+ * <p>
+ * Each constructor has two variants, one accepts a <code>String</code> node
+ * URI, the other accepts a <code>String[]</code> node path. The former is
+ * used by the DmtAdmin implementation, the latter by the plugins, who receive
+ * the node URI as an array of segment names. The constructors are otherwise
+ * identical.
+ * <p>
+ * Getter methods are provided to retrieve the values of the additional
+ * parameters, and the <code>printStackTrace(PrintWriter)</code> method is
+ * extended to print the stack trace of all causing throwables as well.
+ */
+public class DmtException extends Exception {
+    private static final long serialVersionUID = -63006267148118655L;
+
+    // ----- Public constants -----//
+
+    /**
+     * The originator's authentication credentials specify a principal with
+     * insufficient rights to complete the command.
+     * <p>
+     * This status code is used as response to device originated sessions if the
+     * remote management server cannot authorize the device to perform the
+     * requested operation.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 401
+     * &quot;Unauthorized&quot;.
+     */
+    public static final int UNAUTHORIZED = 401;
+
+    /**
+     * The requested target node was not found. No indication is given as to
+     * whether this is a temporary or permanent condition, unless otherwise
+     * noted.
+     * <p>
+     * This is only used when the requested node name is valid, otherwise the
+     * more specific error codes {@link #URI_TOO_LONG} or {@link #INVALID_URI}
+     * are used. This error code corresponds to the OMA DM response status code
+     * 404 &quot;Not Found&quot;.
+     */
+    public static final int NODE_NOT_FOUND = 404;
+
+    /**
+     * The requested command is not allowed on the target node. This includes
+     * the following situations:
+     * <ul>
+     * <li>an interior node operation is requested for a leaf node, or vice
+     * versa (e.g. trying to retrieve the children of a leaf node)
+     * <li>an attempt is made to create a node where the parent is a leaf node
+     * <li>an attempt is made to rename or delete the root node of the tree
+     * <li>an attempt is made to rename or delete the root node of the session
+     * <li>a write operation (other than setting the ACL) is performed in a
+     * non-atomic write session on a node provided by a plugin that is read-only
+     * or does not support non-atomic writing
+     * <li>a node is copied to its descendant
+     * <li>the ACL of the root node is changed not to include Add rights for
+     * all principals
+     * </ul>
+     * <p>
+     * This error code corresponds to the OMA DM response status code 405
+     * &quot;Command not allowed&quot;.
+     */
+    public static final int COMMAND_NOT_ALLOWED = 405;
+
+    /**
+     * The requested command failed because an optional feature required by the
+     * command is not supported. For example, opening an atomic session might
+     * return this error code if the DmtAdmin implementation does not support
+     * transactions. Similarly, accessing the optional node properties (Title,
+     * Timestamp, Version, Size) might not succeed if either the DmtAdmin
+     * implementation or the underlying plugin does not support the property.
+     * <p>
+     * When getting or setting values for interior nodes (an optional
+     * optimization feature), a plugin can use this error code to indicate that
+     * the given interior node does not support values.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 406
+     * &quot;Optional feature not supported&quot;.
+     */
+    public static final int FEATURE_NOT_SUPPORTED = 406;
+    
+    /**
+     * The requested command failed because the target URI or one of its
+     * segments is too long for what the recipient is able or willing to
+     * process, or the target URI contains too many segments. The length and
+     * segment number limits are implementation dependent, their minimum values
+     * can be found in the Non Functional Requirements section of the OSGi
+     * specification.
+     * <p>
+     * The {@link Uri#mangle(String)} method provides support for ensuring that
+     * a URI segment conforms to the length limits set by the implementation.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 414
+     * &quot;URI too long&quot;.
+     * 
+     * @see "OSGi Service Platform, Mobile Specification Release 4"
+     */
+    public static final int URI_TOO_LONG = 414;
+
+    /**
+     * The requested node creation operation failed because the target already
+     * exists. This can occur if the node is created directly (with one of the
+     * <code>create...</code> methods), or indirectly (during a
+     * <code>copy</code> operation).
+     * <p>
+     * This error code corresponds to the OMA DM response status code 418
+     * &quot;Already exists&quot;.
+     */
+    public static final int NODE_ALREADY_EXISTS = 418;
+
+    /**
+     * The requested command failed because the principal associated with the
+     * session does not have adequate access control permissions (ACL) on the
+     * target. This can only appear in case of remote sessions, i.e. if the
+     * session is associated with an authenticated principal.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 425
+     * &quot;Permission denied&quot;.
+     */
+    public static final int PERMISSION_DENIED = 425;
+
+    /**
+     * The recipient encountered an error which prevented it from fulfilling the
+     * request.
+     * <p>
+     * This error code is only used in situations not covered by any of the
+     * other error codes that a method may use. Some methods specify more
+     * specific error situations for this code, but it can generally be used for
+     * any unexpected condition that causes the command to fail.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 500
+     * &quot;Command Failed&quot;.
+     */
+    public static final int COMMAND_FAILED = 500;
+
+    /**
+     * An error related to the recipient data store occurred while processing
+     * the request. This error code may be thrown by any of the methods
+     * accessing the tree, but whether it is really used depends on the
+     * implementation, and the data store it uses.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 510
+     * &quot;Data store failure&quot;.
+     */
+    public static final int DATA_STORE_FAILURE = 510;
+
+    /**
+     * The rollback command was not completed successfully. The tree might be in
+     * an inconsistent state after this error.
+     * <p>
+     * This error code corresponds to the OMA DM response status code 516
+     * &quot;Atomic roll back failed&quot;.
+     */
+    public static final int ROLLBACK_FAILED = 516;
+    
+
+    /**
+     * A device initiated remote operation failed. This is used when the
+     * protocol adapter fails to send an alert for any reason.
+     * <p>
+     * Alert routing errors (that occur while looking for the proper protocol
+     * adapter to use) are indicated by {@link #ALERT_NOT_ROUTED}, this code is
+     * only for errors encountered while sending the routed alert. This error
+     * code does not correspond to any OMA DM response status code. It should be
+     * translated to the code 500 &quot;Command Failed&quot; when transferring
+     * over OMA DM.
+     */
+    public static final int REMOTE_ERROR = 1;
+
+    /**
+     * Operation failed because of meta data restrictions. This covers any
+     * attempted deviation from the parameters defined by the
+     * <code>MetaNode</code> objects of the affected nodes, for example in the
+     * following situations:
+     * <ul>
+     * <li>creating, deleting or renaming a permanent node, or modifying its
+     * type or value
+     * <li>creating an interior node where the meta-node defines it as a leaf,
+     * or vice versa
+     * <li>any operation on a node which does not have the required access type
+     * (e.g. executing a node that lacks the <code>MetaNode.CMD_EXECUTE</code>
+     * access type)
+     * <li>any node creation or deletion that would violate the cardinality
+     * constraints
+     * <li>any leaf node value setting that would violate the allowed formats,
+     * values, mime types, etc.
+     * <li>any node creation that would violate the allowed node names
+     * </ul>
+     * <p>
+     * This error code can also be used to indicate any other meta data
+     * violation, even if it cannot be described by the <code>MetaNode</code>
+     * class. For example, detecting a multi-node constraint violation while
+     * committing an atomic session should result in this error.
+     * <p>
+     * This error code does not correspond to any OMA DM response status code.
+     * It should be translated to the code 405 &quot;Command not allowed&quot;
+     * when transferring over OMA DM.
+     */
+    public static final int METADATA_MISMATCH = 2;
+
+    /**
+     * The requested command failed because the target URI or node name is
+     * <code>null</code> or syntactically invalid. This covers the following
+     * cases:
+     * <ul>
+     * <li>the URI or node name ends with the '\' or '/' character
+     * <li>the URI is an empty string (only invalid if the method does not 
+     * accept relative URIs)
+     * <li>the URI contains the segment &quot;<code>.</code>&quot; at a position
+     * other than the beginning of the URI
+     * <li>the node name is &quot;<code>..</code>&quot; or the URI contains such
+     * a segment
+     * <li>the node name is an empty string or the URI contains an empty segment
+     * <li>the node name contains an unescaped '/' character
+     * </ul>
+     * <p>
+     * See the {@link Uri#mangle(String)} method for support on escaping invalid
+     * characters in a URI.
+     * <p>
+     * This code is only used if the URI or node name does not match any of the
+     * criteria for {@link #URI_TOO_LONG}. This error code does not correspond
+     * to any OMA DM response status code. It should be translated to the code
+     * 404 &quot;Not Found&quot; when transferring over OMA DM.
+     */
+    public static final int INVALID_URI = 3;
+
+    /**
+     * An error occurred related to concurrent access of nodes. This can happen
+     * for example if a configuration node was deleted directly through the
+     * Configuration Admin service, while the node was manipulated via the tree.
+     * <p>
+     * This error code does not correspond to any OMA DM response status code.
+     * It should be translated to the code 500 &quot;Command Failed&quot; when
+     * transferring over OMA DM.
+     */
+    public static final int CONCURRENT_ACCESS = 4;
+
+    /**
+     * An alert can not be sent from the device to the given principal. This can
+     * happen if there is no Remote Alert Sender willing to forward the alert to
+     * the given principal, or if no principal was given and the DmtAdmin did
+     * not find an appropriate default destination.
+     * <p>
+     * This error code does not correspond to any OMA DM response status code.
+     * It should be translated to the code 500 &quot;Command Failed&quot; when
+     * transferring over OMA DM.
+     */
+    public static final int ALERT_NOT_ROUTED = 5;
+
+    /**
+     * A transaction-related error occurred in an atomic session. This error is
+     * caused by one of the following situations:
+     * <ul>
+     * <li>an updating method within an atomic session can not be executed
+     * because the underlying plugin is read-only or does not support atomic
+     * writing</li>
+     * <li>a commit operation at the end of an atomic session failed because
+     * one of the underlying plugins failed to close</li>
+     * </ul>
+     * The latter case may leave the tree in an inconsistent state due to the
+     * lack of a two-phase commit system, see {@link DmtSession#commit} for
+     * details.
+     * <p>
+     * This error code does not correspond to any OMA DM response status code.
+     * It should be translated to the code 500 &quot;Command Failed&quot; when
+     * transferring over OMA DM.
+     */
+    public static final int TRANSACTION_ERROR = 6;
+
+    /**
+     * Creation of a session timed out because of another ongoing session. The
+     * length of time while the DmtAdmin waits for the blocking session(s) to
+     * finish is implementation dependant.
+     * <p>
+     * This error code does not correspond to any OMA DM response status code.
+     * OMA has several status codes related to timeout, but these are meant to
+     * be used when a request times out, not if a session can not be
+     * established. This error code should be translated to the code 500
+     * &quot;Command Failed&quot; when transferring over OMA DM.
+     */
+    public static final int SESSION_CREATION_TIMEOUT = 7;
+
+    // ----- Content fields -----//
+
+    /**
+     * The URI of the node on which the failed DMT operation was issued, or
+     * <code>null</code> if the operation was not associated with a node.
+     */
+    private final String uri;
+
+    /**
+     * The error code of the failure, one of the constants defined in this
+     * class.
+     */
+    private final int code;
+
+    /**
+     * The message associated with the exception, or <code>null</code> if
+     * there is no error message.
+     */
+    private final String message;
+
+    /**
+     * The list of originating exceptions, or empty list or <code>null</code>
+     * if there are no originating exceptions.
+     */
+    private final Throwable[] causes;
+
+    /**
+     * Determines whether the exception is fatal or not. This is basically a
+     * two-state severity indicator, with the 'fatal' severity being the more
+     * serious one.
+     */
+    private final boolean fatal;
+
+    // ----- Constructors -----//
+
+    /**
+     * Create an instance of the exception. The <code>uri</code> and
+     * <code>message</code> parameters are optional. No originating exception
+     * is specified.
+     * 
+     * @param uri the node on which the failed DMT operation was issued, or
+     *        <code>null</code> if the operation is not associated with a node
+     * @param code the error code of the failure
+     * @param message the message associated with the exception, or
+     *        <code>null</code> if there is no error message
+     */
+    public DmtException(String uri, int code, String message) {
+        this(uri, code, message, new Throwable[0], false);
+    }
+
+    /**
+     * Create an instance of the exception, specifying the cause exception. The
+     * <code>uri</code>, <code>message</code> and <code>cause</code>
+     * parameters are optional.
+     * 
+     * @param uri the node on which the failed DMT operation was issued, or
+     *        <code>null</code> if the operation is not associated with a node
+     * @param code the error code of the failure
+     * @param message the message associated with the exception, or
+     *        <code>null</code> if there is no error message
+     * @param cause the originating exception, or <code>null</code> if there
+     *        is no originating exception
+     */
+    public DmtException(String uri, int code, String message, Throwable cause) {
+        this(uri, code, message, (cause == null) ? new Throwable[0]
+                : new Throwable[] { cause }, false);
+    }
+
+    /**
+     * Create an instance of the exception, specifying the list of cause
+     * exceptions and whether the exception is a fatal one. This constructor is
+     * meant to be used by plugins wishing to indicate that a serious error
+     * occurred which should invalidate the ongoing atomic session. The
+     * <code>uri</code>, <code>message</code> and <code>causes</code>
+     * parameters are optional.
+     * <p>
+     * If a fatal exception is thrown, no further business methods will be
+     * called on the originator plugin. In case of atomic sessions, all other
+     * open plugins will be rolled back automatically, except if the fatal
+     * exception was thrown during commit.
+     * 
+     * @param uri the node on which the failed DMT operation was issued, or
+     *        <code>null</code> if the operation is not associated with a node
+     * @param code the error code of the failure
+     * @param message the message associated with the exception, or
+     *        <code>null</code> if there is no error message
+     * @param causes the list of originating exceptions, or empty list or
+     *        <code>null</code> if there are no originating exceptions
+     * @param fatal whether the exception is fatal
+     */
+    public DmtException(String uri, int code, String message, Vector causes,
+            boolean fatal) {
+        this(uri, code, message, (causes == null) ? new Throwable[0]
+                : (Throwable[]) causes.toArray(new Throwable[causes.size()]),
+                fatal);
+    }
+    
+    private DmtException(String uri, int code, String message, 
+            Throwable[] causes, boolean fatal) {
+        this.uri = uri;
+        this.code = code;
+        this.message = message;
+        this.causes = causes;
+        this.fatal = fatal;
+    }
+
+    /**
+     * Create an instance of the exception, specifying the target node as an
+     * array of path segments. This method behaves in exactly the same way as if
+     * the path was given as a URI string.
+     * 
+     * @param path the path of the node on which the failed DMT operation was
+     *        issued, or <code>null</code> if the operation is not associated
+     *        with a node
+     * @param code the error code of the failure
+     * @param message the message associated with the exception, or
+     *        <code>null</code> if there is no error message
+     * @see #DmtException(String, int, String)
+     */
+    public DmtException(String[] path, int code, String message) {
+        this(pathToUri(path), code, message);
+    }
+
+    /**
+     * Create an instance of the exception, specifying the target node as an
+     * array of path segments, and specifying the cause exception. This method
+     * behaves in exactly the same way as if the path was given as a URI string.
+     * 
+     * @param path the path of the node on which the failed DMT operation was
+     *        issued, or <code>null</code> if the operation is not associated
+     *        with a node
+     * @param code the error code of the failure
+     * @param message the message associated with the exception, or
+     *        <code>null</code> if there is no error message
+     * @param cause the originating exception, or <code>null</code> if there
+     *        is no originating exception
+     * @see #DmtException(String, int, String, Throwable)
+     */
+    public DmtException(String[] path, int code, String message, Throwable cause) {
+        this(pathToUri(path), code, message, cause);
+    }
+
+    /**
+     * Create an instance of the exception, specifying the target node as an
+     * array of path segments, the list of cause exceptions, and whether the
+     * exception is a fatal one. This method behaves in exactly the same way as
+     * if the path was given as a URI string.
+     * 
+     * @param path the path of the node on which the failed DMT operation was
+     *        issued, or <code>null</code> if the operation is not associated
+     *        with a node
+     * @param code the error code of the failure
+     * @param message the message associated with the exception, or
+     *        <code>null</code> if there is no error message
+     * @param causes the list of originating exceptions, or empty list or
+     *        <code>null</code> if there are no originating exceptions
+     * @param fatal whether the exception is fatal
+     * @see #DmtException(String, int, String, Vector, boolean)
+     */
+    public DmtException(String[] path, int code, String message, Vector causes,
+            boolean fatal) {
+        this(pathToUri(path), code, message, causes, fatal);
+    }
+
+    // ----- Public methods -----//
+
+    /**
+     * Get the node on which the failed DMT operation was issued. Some
+     * operations like <code>DmtSession.close()</code> don't require an URI,
+     * in this case this method returns <code>null</code>.
+     * 
+     * @return the URI of the node, or <code>null</code>
+     */
+    public String getURI() {
+        return uri;
+    }
+
+    /**
+     * Get the error code associated with this exception. Most of the error
+     * codes within this exception correspond to OMA DM error codes.
+     * 
+     * @return the error code
+     */
+    public int getCode() {
+        return code;
+    }
+
+    /**
+     * Get the message associated with this exception. The returned string also
+     * contains the associated URI (if any) and the exception code. The
+     * resulting message has the following format (parts in square brackets are
+     * only included if the field inside them is not <code>null</code>):
+     * 
+     * <pre>
+     *  &lt;exception_code&gt;[: '&lt;uri&gt;'][: &lt;error_message&gt;]
+     * </pre>
+     * 
+     * @return the error message in the format described above
+     */
+    public String getMessage() {
+        StringBuffer sb = new StringBuffer(getCodeText(code));
+        if (uri != null)
+            sb.append(": '").append(uri).append('\'');
+        if (message != null)
+            sb.append(": ").append(message);
+
+        return sb.toString();
+    }
+
+    /**
+     * Get the cause of this exception. Returns non-<code>null</code>, if
+     * this exception is caused by one or more other exceptions (like a
+     * <code>NullPointerException</code> in a DmtPlugin). If there are more
+     * than one cause exceptions, the first one is returned.
+     * 
+     * @return the cause of this exception, or <code>null</code> if no cause
+     *         was given
+     */
+    public Throwable getCause() {
+        return causes.length == 0 ? null : causes[0];
+    }
+
+    /**
+     * Get all causes of this exception. Returns the causing exceptions in an
+     * array. If no cause was specified, an empty array is returned.
+     * 
+     * @return the list of causes of this exception
+     */
+    public Throwable[] getCauses() {
+        return (Throwable[]) causes.clone();
+    }
+
+    /**
+     * Check whether this exception is marked as fatal in the session. Fatal
+     * exceptions trigger an automatic rollback of atomic sessions.
+     * 
+     * @return whether the exception is marked as fatal
+     */
+    public boolean isFatal() {
+        return fatal;
+    }
+
+    /**
+     * Prints the exception and its backtrace to the specified print stream. Any
+     * causes that were specified for this exception are also printed, together
+     * with their backtraces.
+     * 
+     * @param s <code>PrintStream</code> to use for output
+     */
+    public void printStackTrace(PrintStream s) {
+        super.printStackTrace(s);
+        for (int i = 0; i<causes.length; i++) {
+            s.print("Caused by" + (i > 0 ? " (" + (i+1) + ")" : "") + ": ");
+            causes[i].printStackTrace(s);
+        }
+    }
+
+    // ----- Utility methods -----//
+
+    /**
+     * Converts the given path, given as an array of path segments, to a single
+     * URI string.
+     * 
+     * @param path the path to convert
+     * @return the URI string representing the same node as the given path
+     */
+    static String pathToUri(String[] path) {
+        if (path == null)
+            return null;
+
+        return Uri.toUri(path);
+    }
+
+    /**
+     * Returns the name of the given error code.
+     * 
+     * @param code the error code
+     * @return a string containing the error code name
+     */
+    private static String getCodeText(int code) {
+        // todo sync codes
+        switch (code) {
+        case NODE_NOT_FOUND:
+            return "NODE_NOT_FOUND";
+        case COMMAND_NOT_ALLOWED:
+            return "COMMAND_NOT_ALLOWED";
+        case FEATURE_NOT_SUPPORTED:
+            return "FEATURE_NOT_SUPPORTED";
+        case URI_TOO_LONG:
+            return "URI_TOO_LONG";
+        case NODE_ALREADY_EXISTS:
+            return "NODE_ALREADY_EXISTS";
+        case PERMISSION_DENIED:
+            return "PERMISSION_DENIED";
+        case COMMAND_FAILED:
+            return "COMMAND_FAILED";
+        case DATA_STORE_FAILURE:
+            return "DATA_STORE_FAILURE";
+        case ROLLBACK_FAILED:
+            return "ROLLBACK_FAILED";
+
+        case REMOTE_ERROR:
+            return "REMOTE_ERROR";
+        case METADATA_MISMATCH:
+            return "METADATA_MISMATCH";
+        case INVALID_URI:
+            return "INVALID_URI";
+        case CONCURRENT_ACCESS:
+            return "CONCURRENT_ACCESS";
+        case ALERT_NOT_ROUTED:
+            return "ALERT_NOT_ROUTED";
+        case TRANSACTION_ERROR:
+            return "TRANSACTION_ERROR";
+        case SESSION_CREATION_TIMEOUT:
+            return "SESSION_CREATION_TIMEOUT";
+        default:
+            return "<unknown code>";
+        }
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtIllegalStateException.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtIllegalStateException.java
new file mode 100644
index 0000000..e5c06b2
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtIllegalStateException.java
@@ -0,0 +1,84 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtIllegalStateException.java,v 1.4 2006/07/13 13:42:12 tszeredi Exp $
+ *
+ * Copyright (c) OSGi Alliance (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;
+
+/**
+ * Unchecked illegal state exception. This class is used in DMT because 
+ * java.lang.IllegalStateException does not exist in CLDC.
+ */
+public class DmtIllegalStateException extends RuntimeException {
+    private static final long serialVersionUID = 2015244852018469700L;
+
+    /**
+     * Nested exception.
+     */
+    private final Throwable cause;
+
+    /**
+     * Create an instance of the exception with no message.
+     */
+    public DmtIllegalStateException() {
+        super();
+        cause = null;
+    }
+
+    /**
+     * Create an instance of the exception with the specified message.
+     * 
+     * @param message the reason for the exception
+     */
+    public DmtIllegalStateException(String message) {
+        super(message);
+        cause = null;
+    }
+
+    /**
+     * Create an instance of the exception with the specified cause exception
+     * and no message.
+     * 
+     * @param cause the cause of the exception
+     */
+    public DmtIllegalStateException(Throwable cause) {
+        super();
+        this.cause = cause;
+    }
+
+    /**
+     * Create an instance of the exception with the specified message and cause
+     * exception.
+     * 
+     * @param message the reason for the exception
+     * @param cause the cause of the exception
+     */
+    public DmtIllegalStateException(String message, Throwable cause) {
+        super(message);
+        this.cause = cause;
+    }
+
+    /**
+     * Returns the cause of this exception or <code>null</code> if no cause
+     * was specified when this exception was created.
+     * 
+     * @return the cause of this exception or <code>null</code> if no cause
+     *         was specified
+     */
+    public Throwable getCause() {
+        return cause;
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtSession.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtSession.java
new file mode 100644
index 0000000..dd1913a
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtSession.java
@@ -0,0 +1,1599 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtSession.java,v 1.7 2006/07/11 16:58:20 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;
+
+import java.util.Date;
+
+/**
+ * DmtSession provides concurrent access to the DMT. All DMT manipulation
+ * commands for management applications are available on the
+ * <code>DmtSession</code> interface. The session is associated with a root
+ * node which limits the subtree in which the operations can be executed within
+ * this session.
+ * <p>
+ * Most of the operations take a node URI as parameter, which can be either an
+ * absolute URI (starting with &quot;./&quot;) or a URI relative to the root
+ * node of the session. The empty string as relative URI means the root URI the
+ * session was opened with. All segments of a URI must be within the segment
+ * length limit of the implementation, and the special characters '/' and '\'
+ * must be escaped (preceded by a '\'). Any string can be converted to a valid
+ * URI segment using the {@link Uri#mangle(String)} method.
+ * <p>
+ * If the URI specified does not correspond to a legitimate node in the tree an
+ * exception is thrown. The only exception is the {@link #isNodeUri(String)}
+ * method which returns <code>false</code> in case of an invalid URI.
+ * <p>
+ * Each method of <code>DmtSession</code> that accesses the tree in any way
+ * can throw <code>DmtIllegalStateException</code> if the session has been
+ * closed or invalidated (due to timeout, fatal exceptions, or unexpectedly
+ * unregistered plugins).
+ */
+public interface DmtSession {
+    /**
+     * Sessions created with <code>LOCK_TYPE_SHARED</code> lock allows
+     * read-only access to the tree, but can be shared between multiple readers.
+     */
+    int LOCK_TYPE_SHARED = 0;
+
+    /**
+     * <code>LOCK_TYPE_EXCLUSIVE</code> lock guarantees full access to the
+     * tree, but can not be shared with any other locks.
+     */
+    int LOCK_TYPE_EXCLUSIVE = 1;
+
+    /**
+     * <code>LOCK_TYPE_ATOMIC</code> is an exclusive lock with transactional
+     * functionality. Commands of an atomic session will either fail or succeed
+     * together, if a single command fails then the whole session will be rolled
+     * back.
+     */
+    int LOCK_TYPE_ATOMIC = 2;
+
+    /**
+     * The session is open, all session operations are available.
+     */
+    int STATE_OPEN = 0;
+
+    /**
+     * The session is closed, DMT manipulation operations are not available,
+     * they throw <code>DmtIllegalStateException</code> if tried.
+     */
+    int STATE_CLOSED = 1;
+
+    /**
+     * The session is invalid because a fatal error happened. Fatal errors
+     * include the timeout of the session, any DmtException with the 'fatal'
+     * flag set, or the case when a plugin service is unregistered while in use
+     * by the session. DMT manipulation operations are not available, they throw
+     * <code>DmtIllegalStateException</code> if tried.
+     */
+    int STATE_INVALID = 2;
+
+    /**
+     * Get the current state of this session.
+     * 
+     * @return the state of the session, one of {@link #STATE_OPEN},
+     *         {@link #STATE_CLOSED} and {@link #STATE_INVALID}
+     */
+    int getState();
+
+    /**
+     * Gives the type of lock the session has.
+     * 
+     * @return the lock type of the session, one of {@link #LOCK_TYPE_SHARED},
+     *         {@link #LOCK_TYPE_EXCLUSIVE} and {@link #LOCK_TYPE_ATOMIC}
+     */
+    int getLockType();
+
+    /**
+     * Gives the name of the principal on whose behalf the session was created.
+     * Local sessions do not have an associated principal, in this case
+     * <code>null</code> is returned.
+     * 
+     * @return the identifier of the remote server that initiated the session,
+     *         or <code>null</code> for local sessions
+     */
+    String getPrincipal();
+
+    /**
+     * The unique identifier of the session. The ID is generated automatically,
+     * and it is guaranteed to be unique on a machine.
+     * 
+     * @return the session identification number
+     */
+    int getSessionId();
+
+    /**
+     * Get the root URI associated with this session. Gives "<code>.</code>"
+     * if the session was created without specifying a root, which means that
+     * the target of this session is the whole DMT.
+     * 
+     * @return the root URI
+     */
+    String getRootUri();
+
+    /**
+     * 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>
+     * An error situation can arise due to the lack of a two phase commit
+     * mechanism in the underlying plugins. As an example, if plugin A has
+     * committed successfully but plugin B failed, the whole session must fail,
+     * but there is no way to undo the commit performed by A. To provide
+     * predictable behaviour, the commit operation should continue with the
+     * remaining plugins even after detecting a failure. All exceptions received
+     * from failed commits are aggregated into one
+     * <code>TRANSACTION_ERROR</code> exception thrown by this method.
+     * <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 in parallel 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>TRANSACTION_ERROR</code> if an error occurred during
+     *         the commit of any of the underlying plugins
+     *         <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 DmtIllegalStateException if the session was not opened using the
+     *         <code>LOCK_TYPE_ATOMIC</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @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 DmtIllegalStateException if the session was not opened using the
+     *         <code>LOCK_TYPE_ATOMIC</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    void rollback() throws DmtException;
+
+    /**
+     * Closes a session. If the session was opened with atomic lock mode, the
+     * <code>DmtSession</code> must first persist the changes made to the DMT
+     * by calling <code>commit()</code> on all (transactional) plugins
+     * participating in the session. See the documentation of the
+     * {@link #commit} method for details and possible errors during this
+     * operation.
+     * <p>
+     * The state of the session changes to <code>DmtSession.STATE_CLOSED</code>
+     * if the close operation completed successfully, otherwise it becomes
+     * <code>DmtSession.STATE_INVALID</code>.
+     * 
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>METADATA_MISMATCH</code> in case of atomic sessions,
+     *         if the commit operation failed because of meta-data restrictions
+     *         <li><code>CONCURRENT_ACCESS</code> in case of atomic sessions,
+     *         if the commit operation failed because of some modification
+     *         outside the scope of the DMT to the nodes affected in the session
+     *         <li><code>TRANSACTION_ERROR</code> in case of atomic sessions,
+     *         if an underlying plugin failed to commit
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if an underlying plugin failed
+     *         to close, or if some unspecified error is encountered while
+     *         attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation
+     */
+    void close() throws DmtException;
+
+    /**
+     * Executes a node. This corresponds to the EXEC operation in OMA DM.  This
+     * method cannot be called in a read-only session.
+     * <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.
+     * 
+     * @param nodeUri the node on which the execute operation is issued
+     * @param data the parameter of the execute operation, can be
+     *        <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if the node does not exist and
+     *         the plugin does not allow executing unexisting nodes
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Execute</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if the node cannot be
+     *         executed according to the meta-data (does not have
+     *         <code>MetaNode.CMD_EXECUTE</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, if no DmtExecPlugin is associated with
+     *         the node and the DmtAdmin can not execute the node, or if some
+     *         unspecified error is encountered while attempting to complete the
+     *         command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Exec action
+     *         present
+     * 
+     * @see #execute(String, String, String)
+     */
+    void execute(String nodeUri, String data) throws DmtException;
+
+    /**
+     * Executes a node, also specifying a correlation ID for use in response
+     * notifications. This operation corresponds to the EXEC command in OMA DM.
+     * This method cannot be called in a read-only session.
+     * <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. If a correlation ID is specified, it should be used as the
+     * <code>correlator</code> parameter for notifications sent in response to this
+     * execute operation.
+     * 
+     * @param nodeUri the node on which the execute operation is issued
+     * @param correlator an identifier to associate this operation with any
+     *        notifications sent in response to it, can be <code>null</code> if not
+     *        needed
+     * @param data the parameter of the execute operation, can be
+     *        <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if the node does not exist and
+     *         the plugin does not allow executing unexisting nodes
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Execute</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if the node cannot be
+     *         executed according to the meta-data (does not have
+     *         <code>MetaNode.CMD_EXECUTE</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, if no DmtExecPlugin is associated with
+     *         the node, or if some unspecified error is encountered while
+     *         attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Exec action
+     *         present
+     * @see #execute(String, String)
+     */
+    void execute(String nodeUri, String correlator, String data)
+            throws DmtException;
+
+    /**
+     * Get the Access Control List associated with a given node. The returned
+     * <code>Acl</code> object does not take inheritance into account, it
+     * gives the ACL specifically given to the node.
+     * 
+     * @param nodeUri the URI of the node
+     * @return the Access Control List belonging to the node or
+     *         <code>null</code> if none defined
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (the node does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException in case of local sessions, if the caller does
+     *         not have <code>DmtPermission</code> for the node with the Get
+     *         action present
+     * @see #getEffectiveNodeAcl
+     */
+    Acl getNodeAcl(String nodeUri) throws DmtException;
+
+    /**
+     * Gives the Access Control List in effect for a given node. The returned
+     * <code>Acl</code> takes inheritance into account, that is if there is no
+     * ACL defined for the node, it will be derived from the closest ancestor
+     * having an ACL defined.
+     * 
+     * @param nodeUri the URI of the node
+     * @return the Access Control List belonging to the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (the node does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException in case of local sessions, if the caller does
+     *         not have <code>DmtPermission</code> for the node with the Get
+     *         action present
+     * @see #getNodeAcl
+     */
+    Acl getEffectiveNodeAcl(String nodeUri) throws DmtException;
+
+    /**
+     * Set the Access Control List associated with a given node. To perform this
+     * operation, the caller needs to have replace rights (<code>Acl.REPLACE</code>
+     * or the corresponding Java permission depending on the session type) as
+     * described below:
+     * <ul>
+     * <li>if <code>nodeUri</code> specifies a leaf node, replace rights are
+     * needed on the parent of the node
+     * <li>if <code>nodeUri</code> specifies an interior node, replace rights
+     * on either the node or its parent are sufficient
+     * </ul>
+     * <p>
+     * If the given <code>acl</code> is <code>null</code> or an empty ACL
+     * (not specifying any permissions for any principals), then the ACL of the 
+     * node is deleted, and the node will inherit the ACL from its parent node.
+     * 
+     * @param nodeUri the URI of the node
+     * @param acl the Access Control List to be set on the node, can be
+     *        <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node or its parent
+     *         (see above) does not allow the <code>Replace</code> operation
+     *         for the associated principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the command attempts
+     *         to set the ACL of the root node not to include Add rights for all
+     *         principals
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException in case of local sessions, if the caller does
+     *         not have <code>DmtPermission</code> for the node or its parent
+     *         (see above) with the Replace action present
+     */
+    void setNodeAcl(String nodeUri, Acl acl) throws DmtException;
+
+    /**
+     * Create a copy of a node or a whole subtree. Beside the structure and
+     * values of the nodes, most properties are also copied, with the exception
+     * of the ACL (Access Control List), Timestamp and Version properties.
+     * <p>
+     * The copy method is essentially a convenience method that could be
+     * substituted with a sequence of retrieval and update operations. This
+     * determines the permissions required for copying. However, some
+     * optimization can be possible if the source and target nodes are all
+     * handled by DmtAdmin or by the same plugin. In this case, the handler
+     * might be able to perform the underlying management operation more
+     * efficiently: for example, a configuration table can be copied at once
+     * instead of reading each node for each entry and creating it in the new
+     * tree.
+     * <p>
+     * This method may result in any of the errors possible for the contributing
+     * operations. Most of these are collected in the exception descriptions
+     * below, but for the full list also consult the documentation of
+     * {@link #getChildNodeNames(String)}, {@link #isLeafNode(String)},
+     * {@link #getNodeValue(String)}, {@link #getNodeType(String)},
+     * {@link #getNodeTitle(String)}, {@link #setNodeTitle(String, String)},
+     * {@link #createLeafNode(String, DmtData, String)} and
+     * {@link #createInteriorNode(String, String)}.
+     * 
+     * @param nodeUri the node or root of a subtree to be copied
+     * @param newNodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or
+     *         <code>newNodeUri</code> or any segment of them is too long, or
+     *         if they have too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> or
+     *         <code>newNodeUri</code> is <code>null</code> or syntactically
+     *         invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node, or if <code>newNodeUri</code>
+     *         points to a node that cannot exist in the tree according to the
+     *         meta-data (see {@link #getMetaNode(String)})
+     *         <li><code>NODE_ALREADY_EXISTS</code> if
+     *         <code>newNodeUri</code> points to a node that already exists
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the copied node(s)
+     *         does not allow the <code>Get</code> operation, or the ACL of
+     *         the parent of the target node does not allow the <code>Add</code>
+     *         operation for the associated principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if <code>nodeUri</code>
+     *         is an ancestor of <code>newNodeUri</code>, or if any of the
+     *         implied retrieval or update operations are not allowed
+     *         <li><code>METADATA_MISMATCH</code> if any of the meta-data
+     *         constraints of the implied retrieval or update operations are
+     *         violated
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if either URI is not within
+     *         the current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the copied node(s) with the Get
+     *         action present, or for the parent of the target node with the Add
+     *         action
+     */
+    void copy(String nodeUri, String newNodeUri, boolean recursive)
+            throws DmtException;
+
+    /**
+     * Create an interior node. If the parent node does not exist, it is created
+     * automatically, as if this method were called for the parent URI. This way
+     * all missing ancestor nodes leading to the specified node are created. Any
+     * exceptions encountered while creating the ancestors are propagated to the
+     * caller of this method, these are not explicitly listed in the error
+     * descriptions below.
+     * <p>
+     * If meta-data is available for the node, several checks are made before
+     * creating it. The node must have <code>MetaNode.CMD_ADD</code> access
+     * type, it must be defined as a non-permanent interior node, the node name
+     * must conform to the valid names, and the creation of the new node must
+     * not cause the maximum occurrence number to be exceeded.
+     * <p>
+     * If the meta-data cannot be retrieved because the given node cannot
+     * possibly exist in the tree (it is not defined in the specification), the
+     * <code>NODE_NOT_FOUND</code> error code is returned (see
+     * {@link #getMetaNode(String)}).
+     * 
+     * @param nodeUri the URI of the node to create
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a node that cannot exist in the tree (see above)
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodeUri</code>
+     *         points to a node that already exists
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the parent node does
+     *         not allow the <code>Add</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the parent node is not
+     *         an interior node, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the parent node with the Add
+     *         action present
+     */
+    void createInteriorNode(String nodeUri) throws DmtException;
+
+    /**
+     * Create an interior node with a given type. The type of interior node, if
+     * specified, is a URI identifying a DDF document. If the parent node does
+     * not exist, it is created automatically, as if
+     * {@link #createInteriorNode(String)} were called for the parent URI. This
+     * way all missing ancestor nodes leading to the specified node are created.
+     * Any exceptions encountered while creating the ancestors are propagated to
+     * the caller of this method, these are not explicitly listed in the error
+     * descriptions below.
+     * <p>
+     * If meta-data is available for the node, several checks are made before
+     * creating it. The node must have <code>MetaNode.CMD_ADD</code> access
+     * type, it must be defined as a non-permanent interior node, the node name
+     * must conform to the valid names, and the creation of the new node must
+     * not cause the maximum occurrence number to be exceeded.
+     * <p>
+     * If the meta-data cannot be retrieved because the given node cannot
+     * possibly exist in the tree (it is not defined in the specification), the
+     * <code>NODE_NOT_FOUND</code> error code is returned (see
+     * {@link #getMetaNode(String)}).
+     * <p>
+     * Interior node type identifiers must follow the format defined in section
+     * 7.7.7.2 of the OMA Device Management Tree and Description document.
+     * Checking the validity of the type string does not have to be done by the
+     * DmtAdmin, this can be left to the plugin handling the node (if any), to
+     * avoid unnecessary double-checks.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a node that cannot exist in the tree (see above)
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodeUri</code>
+     *         points to a node that already exists
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the parent node does
+     *         not allow the <code>Add</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the parent node is not
+     *         an interior node, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, if the type string is invalid (see
+     *         above), or if some unspecified error is encountered while
+     *         attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the parent node with the Add
+     *         action present
+     * @see #createInteriorNode(String)
+     * @see <a
+     *      href="http://member.openmobilealliance.org/ftp/public_documents/dm/Permanent_documents/OMA-TS-DM-TND-V1_2-20050615-C.zip">
+     *      OMA Device Management Tree and Description v1.2 draft</a>
+     */
+    void createInteriorNode(String nodeUri, String type) throws DmtException;
+
+    /**
+     * Create a leaf node with default value and MIME type. If a node does not
+     * have a default value or MIME type, this method will throw a
+     * <code>DmtException</code> with error code
+     * <code>METADATA_MISMATCH</code>. Note that a node might have a default
+     * value or MIME type even if there is no meta-data for the node or its
+     * meta-data does not specify the default.
+     * <p>
+     * If the parent node does not exist, it is created automatically, as if
+     * {@link #createInteriorNode(String)} were called for the parent URI. This
+     * way all missing ancestor nodes leading to the specified node are created.
+     * Any exceptions encountered while creating the ancestors are propagated to
+     * the caller of this method, these are not explicitly listed in the error
+     * descriptions below.
+     * <p>
+     * If meta-data is available for a node, several checks are made before
+     * creating it. The node must have <code>MetaNode.CMD_ADD</code> access
+     * type, it must be defined as a non-permanent leaf node, the node name must
+     * conform to the valid names, and the creation of the new node must not
+     * cause the maximum occurrence number to be exceeded.
+     * <p>
+     * If the meta-data cannot be retrieved because the given node cannot
+     * possibly exist in the tree (it is not defined in the specification), the
+     * <code>NODE_NOT_FOUND</code> error code is returned (see
+     * {@link #getMetaNode(String)}).
+     * 
+     * @param nodeUri the URI of the node to create
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a node that cannot exist in the tree (see above)
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodeUri</code>
+     *         points to a node that already exists
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the parent node does
+     *         not allow the <code>Add</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the parent node is not
+     *         an interior node, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the parent node with the Add
+     *         action present
+     * @see #createLeafNode(String, DmtData)
+     */
+    void createLeafNode(String nodeUri) throws DmtException;
+
+    /**
+     * Create a leaf node with a given value and the default MIME type. If the
+     * specified value is <code>null</code>, the default value is taken. If
+     * the node does not have a default MIME type or value (if needed), this
+     * method will throw a <code>DmtException</code> with error code
+     * <code>METADATA_MISMATCH</code>. Note that a node might have a default
+     * value or MIME type even if there is no meta-data for the node or its
+     * meta-data does not specify the default.
+     * <p>
+     * If the parent node does not exist, it is created automatically, as if
+     * {@link #createInteriorNode(String)} were called for the parent URI. This
+     * way all missing ancestor nodes leading to the specified node are created.
+     * Any exceptions encountered while creating the ancestors are propagated to
+     * the caller of this method, these are not explicitly listed in the error
+     * descriptions below.
+     * <p>
+     * If meta-data is available for a node, several checks are made before
+     * creating it. The node must have <code>MetaNode.CMD_ADD</code> access
+     * type, it must be defined as a non-permanent leaf node, the node name must
+     * conform to the valid names, the node value must conform to the value
+     * constraints, and the creation of the new node must not cause the maximum
+     * occurrence number to be exceeded.
+     * <p>
+     * If the meta-data cannot be retrieved because the given node cannot
+     * possibly exist in the tree (it is not defined in the specification), the
+     * <code>NODE_NOT_FOUND</code> error code is returned (see
+     * {@link #getMetaNode(String)}).
+     * <p>
+     * Nodes of <code>null</code> format can be created by using
+     * {@link DmtData#NULL_VALUE} as second argument.
+     * 
+     * @param nodeUri the URI of the node to create
+     * @param value the value to be given to the new node, can be
+     *        <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a node that cannot exist in the tree (see above)
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodeUri</code>
+     *         points to a node that already exists
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the parent node does
+     *         not allow the <code>Add</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the parent node is not
+     *         an interior node, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the parent node with the Add
+     *         action present
+     */
+    void createLeafNode(String nodeUri, DmtData value) 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 are
+     * taken. If the node does not have the necessary defaults, this method will
+     * throw a <code>DmtException</code> with error code
+     * <code>METADATA_MISMATCH</code>. Note that a node might have a default
+     * value or MIME type even if there is no meta-data for the node or its
+     * meta-data does not specify the default.
+     * <p>
+     * If the parent node does not exist, it is created automatically, as if
+     * {@link #createInteriorNode(String)} were called for the parent URI. This
+     * way all missing ancestor nodes leading to the specified node are created.
+     * Any exceptions encountered while creating the ancestors are propagated to
+     * the caller of this method, these are not explicitly listed in the error
+     * descriptions below.
+     * <p>
+     * If meta-data is available for a node, several checks are made before
+     * creating it. The node must have <code>MetaNode.CMD_ADD</code> access
+     * type, it must be defined as a non-permanent leaf node, the node name must
+     * conform to the valid names, the node value must conform to the value
+     * constraints, the MIME type must be among the listed types, and the
+     * creation of the new node must not cause the maximum occurrence number to
+     * be exceeded.
+     * <p>
+     * If the meta-data cannot be retrieved because the given node cannot
+     * possibly exist in the tree (it is not defined in the specification), the
+     * <code>NODE_NOT_FOUND</code> error code is returned (see
+     * {@link #getMetaNode(String)}).
+     * <p>
+     * Nodes of <code>null</code> format can be created by using
+     * {@link DmtData#NULL_VALUE} as second argument.
+     * <p>
+     * The MIME type string must conform to the definition in RFC 2045. Checking
+     * its validity does not have to be done by the DmtAdmin, this can be left
+     * to the plugin handling the node (if any), to avoid unnecessary
+     * double-checks.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a node that cannot exist in the tree (see above)
+     *         <li><code>NODE_ALREADY_EXISTS</code> if <code>nodeUri</code>
+     *         points to a node that already exists
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the parent node does
+     *         not allow the <code>Add</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the parent node is not
+     *         an interior node, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         created because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, if <code>mimeType</code> is not a
+     *         proper MIME type string (see above), or if some unspecified error
+     *         is encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the parent node with the Add
+     *         action present
+     * @see #createLeafNode(String, DmtData)
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
+     */
+    void createLeafNode(String nodeUri, DmtData value, String mimeType)
+            throws DmtException;
+
+    /**
+     * Delete the given node. Deleting interior nodes is recursive, the whole
+     * subtree under the given node is deleted.  It is not allowed to delete 
+     * the root node of the session.
+     * <p>
+     * If meta-data is available for a node, several checks are made before
+     * deleting it. The node must be non-permanent, it must have the
+     * <code>MetaNode.CMD_DELETE</code> access type, and if zero occurrences
+     * of the node are not allowed, it must not be the last one.
+     * 
+     * @param nodeUri the URI of the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Delete</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the target node is the
+     *         root of the session, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         deleted because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Delete action
+     *         present
+     */
+    void deleteNode(String nodeUri) 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 URI is constructed from the base of the old URI and
+     * the given name. It is not allowed to rename the root node of the session.
+     * <p>
+     * If available, the meta-data of the original and the new nodes are checked
+     * before performing the rename operation. Neither node can be permanent,
+     * their leaf/interior property must match, and the name change must not
+     * violate any of the cardinality constraints. The original node must have
+     * the <code>MetaNode.CMD_REPLACE</code> access type, and the name of the
+     * new node must conform to the valid names.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, if <code>nodeUri</code> has too many
+     *         segments, or if <code>newName</code> is too long
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> or
+     *         <code>newName</code> is <code>null</code> or syntactically
+     *         invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node, or if the new node is not defined
+     *         in the tree according to the meta-data (see
+     *         {@link #getMetaNode(String)})
+     *         <li><code>NODE_ALREADY_EXISTS</code> if there already exists a
+     *         sibling of <code>nodeUri</code> with the name
+     *         <code>newName</code>
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Replace</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the target node is the
+     *         root of the session, or in non-atomic sessions if the underlying
+     *         plugin is read-only or does not support non-atomic writing
+     *         <li><code>METADATA_MISMATCH</code> if the node could not be
+     *         renamed because of meta-data restrictions (see above)
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Replace action
+     *         present
+     */
+    void renameNode(String nodeUri, String newName) throws DmtException;
+
+    /**
+     * Set the value of a leaf or interior node to its default.  The default
+     * can be defined by the node's <code>MetaNode</code>. The method throws a 
+     * <code>METADATA_MISMATCH</code> exception if the node does not have a 
+     * default value.
+     * 
+     * @param nodeUri the URI of the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Replace</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> in non-atomic sessions if 
+     *         the underlying plugin is read-only or does not support non-atomic
+     *         writing 
+     *         <li><code>METADATA_MISMATCH</code> if the node is permanent or
+     *         cannot be modified according to the meta-data (does not have the
+     *         <code>MetaNode.CMD_REPLACE</code> access type), or if there is
+     *         no default value defined for this node
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the specified node is
+     *         an interior node and does not support Java object values
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Replace action
+     *         present
+     * @see #setNodeValue
+     */
+    void setDefaultNodeValue(String nodeUri) 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 must be <code>FORMAT_NODE</code>, while for leaf nodes this
+     * format must not be used. 
+     * <p>
+     * If the specified value is <code>null</code>, the default value is taken. 
+     * In this case, if the node does not have a default value, this method will
+     * throw a <code>DmtException</code> with error code 
+     * <code>METADATA_MISMATCH</code>. Nodes of <code>null</code> format can be 
+     * set by using {@link DmtData#NULL_VALUE} as second argument.
+     * <p>
+     * An Event of type REPLACE is sent out for a leaf node. A replaced interior
+     * node sends out events for each of its children in depth first order
+     * and node names sorted with Arrays.sort(String[]).  When setting a value
+     * on an interior node, the values of the leaf nodes under it can change,
+     * but the structure of the subtree is not modified by the operation. 
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Replace</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the given data has
+     *         <code>FORMAT_NODE</code> format but the node is a leaf node (or
+     *         vice versa), or in non-atomic sessions if the underlying plugin
+     *         is read-only or does not support non-atomic writing 
+     *         <li><code>METADATA_MISMATCH</code> if the node is permanent or
+     *         cannot be modified according to the meta-data (does not have the
+     *         <code>MetaNode.CMD_REPLACE</code> access type), or if the given
+     *         value does not conform to the meta-data value constraints
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the specified node is
+     *         an interior node and does not support Java object values
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Replace action
+     *         present
+     */
+    void setNodeValue(String nodeUri, DmtData data) throws DmtException;
+
+    /**
+     * Set the title property of a node. The length of the title string in UTF-8
+     * encoding must not exceed 255 bytes.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Replace</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> in non-atomic sessions if
+     *         the underlying plugin is read-only or does not support non-atomic
+     *         writing
+     *         <li><code>METADATA_MISMATCH</code> if the node cannot be
+     *         modified according to the meta-data (does not have the
+     *         <code>MetaNode.CMD_REPLACE</code> access type)
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Title property
+     *         is not supported by the DmtAdmin implementation or the
+     *         underlying plugin
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the title string is too
+     *         long, if the URI is not within the current session's subtree, or
+     *         if some unspecified error is encountered while attempting to
+     *         complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Replace action
+     *         present
+     */
+    void setNodeTitle(String nodeUri, 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, a <code>null</code> type string means that there is
+     * no 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. If the node does not have a default MIME type this method
+     * will throw a <code>DmtException</code> with error code
+     * <code>METADATA_MISMATCH</code>. Note that a node might have a default
+     * MIME type even if there is no meta-data for the node or its meta-data
+     * does not specify the default.
+     * <p>
+     * MIME types must conform to the definition in RFC 2045. Interior node type
+     * identifiers must follow the format defined in section 7.7.7.2 of the OMA
+     * Device Management Tree and Description document. Checking the validity of
+     * the type string does not have to be done by the DmtAdmin, this can be
+     * left to the plugin handling the node (if any), to avoid unnecessary
+     * double-checks.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Replace</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> in non-atomic sessions if
+     *         the underlying plugin is read-only or does not support non-atomic
+     *         writing
+     *         <li><code>METADATA_MISMATCH</code> if the node is permanent or
+     *         cannot be modified according to the meta-data (does not have the
+     *         <code>MetaNode.CMD_REPLACE</code> access type), and in case of
+     *         leaf nodes, if <code>null</code> is given and there is no
+     *         default MIME type, or the given MIME type is not allowed
+     *         <li><code>TRANSACTION_ERROR</code> in an atomic session if the
+     *         underlying plugin is read-only or does not support atomic writing
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, if the type string is invalid (see
+     *         above), or if some unspecified error is encountered while
+     *         attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session was opened using the
+     *         <code>LOCK_TYPE_SHARED</code> lock type, or if the session is
+     *         already closed or invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Replace action
+     *         present
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
+     * @see <a
+     *      href="http://member.openmobilealliance.org/ftp/public_documents/dm/Permanent_documents/OMA-TS-DM-TND-V1_2-20050615-C.zip">
+     *      OMA Device Management Tree and Description v1.2 draft</a>
+     */
+    void setNodeType(String nodeUri, String type) 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 are mangled ({@link Uri#mangle}). The elements
+     * are in no particular order. The returned array must not contain
+     * <code>null</code> entries.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the specified node is
+     *         not an interior node
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    String[] getChildNodeNames(String nodeUri) throws DmtException;
+
+    /**
+     * Get the meta data which describes a given node. Meta data can only be
+     * inspected, it can not be changed.
+     * <p>
+     * The <code>MetaNode</code> object returned to the client is the
+     * combination of the meta data returned by the data plugin (if any) plus
+     * the meta data returned by the DmtAdmin. If there are differences in the
+     * meta data elements known by the plugin and the DmtAdmin 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 in the specification). For nodes that
+     * are not defined, it may throw <code>DmtException</code> with the error
+     * code <code>NODE_NOT_FOUND</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 nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <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>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    MetaNode getMetaNode(String nodeUri) throws DmtException;
+
+    /**
+     * Get the size of the data in a leaf node. The returned value 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 nodeUri the URI of the leaf node
+     * @return the size of the data in the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>COMMAND_NOT_ALLOWED</code> if the specified node is
+     *         not a leaf node
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Size property is
+     *         not supported by the DmtAdmin implementation or the underlying
+     *         plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     * @see DmtData#getSize
+     */
+    int getNodeSize(String nodeUri) throws DmtException;
+
+    /**
+     * Get the timestamp when the node was created or last modified.
+     * 
+     * @param nodeUri the URI of the node
+     * @return the timestamp of the last modification
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Timestamp
+     *         property is not supported by the DmtAdmin implementation or the
+     *         underlying plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    Date getNodeTimestamp(String nodeUri) throws DmtException;
+
+    /**
+     * Get the title of a node. There might be no title property set for a node.
+     * 
+     * @param nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Title property
+     *         is not supported by the DmtAdmin implementation or the
+     *         underlying plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    String getNodeTitle(String nodeUri) 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 nodeUri the URI 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>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    String getNodeType(String nodeUri) throws DmtException;
+
+    /**
+     * Get the data contained in a leaf or interior node.  When retrieving the
+     * value associated with an interior node, the caller must have rights to
+     * read all nodes in the subtree under the given node.
+     * 
+     * @param nodeUri the URI of the node to retrieve
+     * @return the data of the node, can not be <code>null</code>
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node (and the ACLs
+     *         of all its descendants in case of interior nodes) do not allow
+     *         the <code>Get</code> operation for the associated principal
+     *         <li><code>METADATA_MISMATCH</code> if the node value cannot be
+     *         retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <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 the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node (and all its descendants
+     *         in case of interior nodes) with the Get action present
+     */
+    DmtData getNodeValue(String nodeUri) 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 nodeUri the URI of the node
+     * @return the version of the node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the Version property
+     *         is not supported by the DmtAdmin implementation or the
+     *         underlying plugin
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    int getNodeVersion(String nodeUri) throws DmtException;
+
+    /**
+     * Tells whether a node is a leaf or an interior node of the DMT.
+     * 
+     * @param nodeUri the URI of the node
+     * @return true if the given node is a leaf node
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>URI_TOO_LONG</code> if <code>nodeUri</code> or a
+     *         segment of it is too long, or if it has too many segments
+     *         <li><code>INVALID_URI</code> if <code>nodeUri</code> is
+     *         <code>null</code> or syntactically invalid
+     *         <li><code>NODE_NOT_FOUND</code> if <code>nodeUri</code>
+     *         points to a non-existing node
+     *         <li><code>PERMISSION_DENIED</code> if the session is
+     *         associated with a principal and the ACL of the node does not
+     *         allow the <code>Get</code> operation for the associated
+     *         principal
+     *         <li><code>METADATA_MISMATCH</code> if node information cannot
+     *         be retrieved according to the meta-data (it does not have
+     *         <code>MetaNode.CMD_GET</code> access type)
+     *         <li><code>DATA_STORE_FAILURE</code> if an error occurred while
+     *         accessing the data store
+     *         <li><code>COMMAND_FAILED</code> if the URI is not within the
+     *         current session's subtree, or if some unspecified error is
+     *         encountered while attempting to complete the command
+     *         </ul>
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    boolean isLeafNode(String nodeUri) throws DmtException;
+
+    /**
+     * Check whether the specified URI corresponds to a valid node in the DMT.
+     * 
+     * @param nodeUri the URI to check
+     * @return true if the given node exists in the DMT
+     * @throws DmtIllegalStateException if the session is already closed or
+     *         invalidated
+     * @throws SecurityException if the caller does not have the necessary
+     *         permissions to execute the underlying management operation, or,
+     *         in case of local sessions, if the caller does not have
+     *         <code>DmtPermission</code> for the node with the Get action
+     *         present
+     */
+    boolean isNodeUri(String nodeUri);
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/MetaNode.java b/org.osgi.compendium/src/main/java/info/dmtree/MetaNode.java
new file mode 100644
index 0000000..18afcfc
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/MetaNode.java
@@ -0,0 +1,383 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/MetaNode.java,v 1.4 2006/07/04 12:26:16 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;
+
+/**
+ * The MetaNode contains meta data as standardized by OMA DM but extends it 
+ * (without breaking the compatibility) to provide for better DMT data quality 
+ * in an environment where many software components manipulate this data.
+ * <p>
+ * The interface has several types of functions to describe the nodes in the
+ * DMT. Some methods can be used to retrieve standard OMA DM metadata such as
+ * access type, cardinality, default, etc., others are for data extensions such
+ * as valid names and values. In some cases the standard behaviour has been
+ * extended, for example it is possible to provide several valid MIME types, or
+ * to differentiate between normal and automatic dynamic nodes.
+ * <p>
+ * Most methods in this interface receive no input, just return information
+ * about some aspect of the node. However, there are two methods that behave
+ * differently, {@link #isValidName} and {@link #isValidValue}. These
+ * validation methods are given a potential node name or value (respectively),
+ * and can decide whether it is valid for the given node. Passing the validation
+ * methods is a necessary condition for a name or value to be used, but it is
+ * not necessarily sufficient: the plugin may carry out more thorough (more
+ * expensive) checks when the node is actually created or set.
+ * <p>
+ * If a <code>MetaNode</code> is available for a node, the DmtAdmin must use
+ * the information provided by it to filter out invalid requests on that node.
+ * However, not all methods on this interface are actually used for this
+ * purpose, as many of them (e.g. {@link #getFormat} or {@link #getValidNames})
+ * can be substituted with the validating methods. For example,
+ * {@link #isValidValue} can be expected to check the format, minimum, maximum,
+ * etc. of a given value, making it unnecessary for the DmtAdmin to call
+ * {@link #getFormat()}, {@link #getMin()}, {@link #getMax()} etc. separately.
+ * It is indicated in the description of each method if the DmtAdmin does not
+ * enforce the constraints defined by it - such methods are only for external
+ * use, for example in user interfaces.
+ * <p>
+ * Most of the methods of this class return <code>null</code> if a certain
+ * piece of meta information is not defined for the node or providing this
+ * information is not supported. Methods of this class do not throw exceptions.
+ */
+public interface MetaNode {
+
+    /**
+     * Constant for the ADD access type. If {@link #can(int)} returns
+     * <code>true</code> for this operation, this node can potentially be
+     * added to its parent. Nodes with {@link #PERMANENT} or {@link #AUTOMATIC}
+     * scope typically do not have this access type.
+     */
+    int CMD_ADD = 0;
+
+    /**
+     * Constant for the DELETE access type. If {@link #can(int)} returns
+     * <code>true</code> for this operation, the node can potentially be
+     * deleted.
+     */
+    int CMD_DELETE = 1;
+
+    /**
+     * Constant for the EXECUTE access type. If {@link #can(int)} returns
+     * <code>true</code> for this operation, the node can potentially be
+     * executed.
+     */
+    int CMD_EXECUTE = 2;
+
+    /**
+     * Constant for the REPLACE access type. If {@link #can(int)} returns
+     * <code>true</code> for this operation, the value and other properties of
+     * the node can potentially be modified.
+     */
+    int CMD_REPLACE = 3;
+
+    /**
+     * Constant for the GET access type. If {@link #can(int)} returns
+     * <code>true</code> for this operation, the value, the list of child nodes
+     * (in case of interior nodes) and the properties of the node can
+     * potentially be retrieved.
+     */
+    int CMD_GET = 4;
+
+    /**
+     * Constant for representing a permanent node in the tree. This must be
+     * returned by {@link #getScope} if the node cannot be added, deleted or
+     * modified in any way through tree operations. Permanent nodes cannot have
+     * non-permanent nodes as parents.
+     */
+    int PERMANENT = 0;
+
+    /**
+     * Constant for representing a dynamic node in the tree. This must be
+     * returned by {@link #getScope} for all nodes that are not permanent and
+     * are not created automatically by the management object.
+     */
+    int DYNAMIC = 1;
+
+    /**
+     * Constant for representing an automatic node in the tree. This must be
+     * returned by {@link #getScope()} for all nodes that are created
+     * automatically by the management object. Automatic nodes represent a
+     * special case of dynamic nodes, so this scope should be mapped to
+     * {@link #DYNAMIC} when used in an OMA DM context.
+     * <p>
+     * An automatic node is usually created instantly when its parent is
+     * created, but it is also valid if it only appears later, triggered by some
+     * other condition. The exact behaviour must be defined by the Management
+     * Object.
+     */
+    int AUTOMATIC = 2;
+
+    /**
+     * Check whether the given operation is valid for this node. If no meta-data
+     * is provided for a node, all operations are valid.
+     * 
+     * @param operation One of the <code>MetaNode.CMD_...</code> constants.
+     * @return <code>false</code> if the operation is not valid for this node
+     *         or the operation code is not one of the allowed constants
+     */
+    boolean can(int operation);
+
+    /**
+     * Check whether the node is a leaf node or an internal one.
+     * 
+     * @return <code>true</code> if the node is a leaf node
+     */
+    boolean isLeaf();
+
+    /**
+     * Return the scope of the node. Valid values are
+     * {@link #PERMANENT MetaNode.PERMANENT}, {@link #DYNAMIC MetaNode.DYNAMIC}
+     * and {@link #AUTOMATIC MetaNode.AUTOMATIC}. Note that a permanent node is
+     * not the same as a node where the DELETE operation is not allowed.
+     * Permanent nodes never can be deleted, whereas a non-deletable node can
+     * disappear in a recursive DELETE operation issued on one of its parents.
+     * If no meta-data is provided for a node, it can be assumed to be a dynamic
+     * node.
+     * 
+     * @return {@link #PERMANENT} for permanent nodes, {@link #AUTOMATIC} for
+     *         nodes that are automatically created, and {@link #DYNAMIC}
+     *         otherwise
+     */
+    int getScope();
+
+    /**
+     * Get the explanation string associated with this node. Can be
+     * <code>null</code> if no description is provided for this node.
+     * 
+     * @return node description string or <code>null</code> for no description
+     */
+    String getDescription();
+
+    /**
+     * Get the number of maximum occurrences of this type of nodes on the same
+     * level in the DMT. Returns <code>Integer.MAX_VALUE</code> if there is no
+     * upper limit. Note that if the occurrence is greater than 1 then this node
+     * can not have siblings with different metadata. In other words, if
+     * different types of nodes coexist on the same level, their occurrence can
+     * not be greater than 1. If no meta-data is provided for a node, there is
+     * no upper limit on the number of occurrences.
+     * 
+     * @return The maximum allowed occurrence of this node type
+     */
+    int getMaxOccurrence();
+
+    /**
+     * Check whether zero occurrence of this node is valid. If no meta-data is
+     * returned for a node, zero occurrences are allowed.
+     * 
+     * @return <code>true</code> if zero occurrence of this node is valid
+     */
+    boolean isZeroOccurrenceAllowed();
+
+    /**
+     * Get the default value of this node if any.
+     * 
+     * @return The default value or <code>null</code> if not defined
+     */
+    DmtData getDefault();
+
+    /**
+     * Get the list of MIME types this node can hold. The first element of the
+     * returned list must be the default MIME type.
+     * <p>
+     * All MIME types are considered valid if no meta-data is provided for a
+     * node or if <code>null</code> is returned by this method. In this case
+     * the default MIME type cannot be retrieved from the meta-data, but the
+     * node may still have a default. This hidden default (if it exists) can be
+     * utilized by passing <code>null</code> as the type parameter of
+     * {@link DmtSession#setNodeType(String, String)} or
+     * {@link DmtSession#createLeafNode(String, DmtData, String)}.
+     * 
+     * @return the list of allowed MIME types for this node, starting with the
+     *         default MIME type, or <code>null</code> if all types are
+     *         allowed
+     */
+    String[] getMimeTypes();
+
+    /**
+     * Get the maximum allowed value associated with a node of numeric format.
+     * If no meta-data is provided for a node, there is no upper limit to its
+     * value. This method is only meaningful if the node has integer or float
+     * format. The returned limit has <code>double</code> type, as this can be
+     * used to denote both integer and float limits with full precision. The
+     * actual maximum should be the largest integer or float number that does
+     * not exceed the returned value.
+     * <p>
+     * The information returned by this method is not checked by DmtAdmin, it
+     * is only for external use, for example in user interfaces. DmtAdmin only
+     * calls {@link #isValidValue} for checking the value, its behaviour should
+     * be consistent with this method.
+     * 
+     * @return the allowed maximum, or <code>Double.MAX_VALUE</code> if there
+     *         is no upper limit defined or the node's format is not integer or
+     *         float
+     */
+    double getMax();
+
+    /**
+     * Get the minimum allowed value associated with a node of numeric format.
+     * If no meta-data is provided for a node, there is no lower limit to its
+     * value. This method is only meaningful if the node has integer or float
+     * format. The returned limit has <code>double</code> type, as this can be
+     * used to denote both integer and float limits with full precision. The
+     * actual minimum should be the smallest integer or float number that is
+     * larger than the returned value.
+     * <p>
+     * The information returned by this method is not checked by DmtAdmin, it
+     * is only for external use, for example in user interfaces. DmtAdmin only
+     * calls {@link #isValidValue} for checking the value, its behaviour should
+     * be consistent with this method.
+     * 
+     * @return the allowed minimum, or <code>Double.MIN_VALUE</code> if there
+     *         is no lower limit defined or the node's format is not integer or
+     *         float
+     */
+    double getMin();
+
+    /**
+     * Return an array of DmtData objects if valid values are defined for the
+     * node, or <code>null</code> otherwise. If no meta-data is provided for a
+     * node, all values are considered valid.
+     * <p>
+     * The information returned by this method is not checked by DmtAdmin, it
+     * is only for external use, for example in user interfaces. DmtAdmin only
+     * calls {@link #isValidValue} for checking the value, its behaviour should
+     * be consistent with this method.
+     * 
+     * @return the valid values for this node, or <code>null</code> if not
+     *         defined
+     */
+    DmtData[] getValidValues();
+
+    /**
+     * Get the node's format, expressed in terms of type constants defined in
+     * {@link DmtData}. If there are multiple formats allowed for the node then
+     * the format constants are OR-ed. Interior nodes must have
+     * {@link DmtData#FORMAT_NODE} format, and this code must not be returned
+     * for leaf nodes. If no meta-data is provided for a node, all applicable
+     * formats are considered valid (with the above constraints regarding
+     * interior and leaf nodes).
+     * <p>
+     * Note that the 'format' term is a legacy from OMA DM, it is more customary
+     * to think of this as 'type'.
+     * <p>
+     * The formats returned by this method are not checked by DmtAdmin, they
+     * are only for external use, for example in user interfaces. DmtAdmin only
+     * calls {@link #isValidValue} for checking the value, its behaviour should
+     * be consistent with this method.
+     * 
+     * @return the allowed format(s) of the node
+     */
+    int getFormat();
+    
+    /**
+     * Get the format names for any raw formats supported by the node.  This
+     * method is only meaningful if the list of supported formats returned by
+     * {@link #getFormat()} contains {@link DmtData#FORMAT_RAW_STRING} or
+     * {@link DmtData#FORMAT_RAW_BINARY}: it specifies precisely which raw
+     * format(s) are actually supported.  If the node cannot contain data in one
+     * of the raw types, this method must return <code>null</code>.
+     * <p>
+     * The format names returned by this method are not checked by DmtAdmin,
+     * they are only for external use, for example in user interfaces. DmtAdmin
+     * only calls {@link #isValidValue} for checking the value, its behaviour
+     * should be consistent with this method.
+     * 
+     * @return the allowed format name(s) of raw data stored by the node, or
+     *         <code>null</code> if raw formats are not supported
+     */
+    String[] getRawFormatNames();
+
+    /**
+     * Checks whether the given value is valid for this node. This method can be
+     * used to ensure that the value has the correct format and range, that it
+     * is well formed, etc. This method should be consistent with the
+     * constraints defined by the {@link #getFormat}, {@link #getValidValues},
+     * {@link #getMin} and {@link #getMax} methods (if applicable), as the Dmt
+     * Admin only calls this method for value validation.
+     * <p>
+     * This method may return <code>true</code> even if not all aspects of the
+     * value have been checked, expensive operations (for example those that
+     * require external resources) need not be performed here. The actual value
+     * setting method may still indicate that the value is invalid.
+     * 
+     * @param value the value to check for validity
+     * @return <code>false</code> if the specified value is found to be
+     *         invalid for the node described by this meta-node,
+     *         <code>true</code> otherwise
+     */
+    boolean isValidValue(DmtData value);
+
+    /**
+     * Return an array of Strings if valid names are defined for the node, or
+     * <code>null</code> if no valid name list is defined or if this piece of
+     * meta info is not supported. If no meta-data is provided for a node, all
+     * names are considered valid.
+     * <p>
+     * The information returned by this method is not checked by DmtAdmin, it
+     * is only for external use, for example in user interfaces. DmtAdmin only
+     * calls {@link #isValidName} for checking the name, its behaviour should be
+     * consistent with this method.
+     * 
+     * @return the valid values for this node name, or <code>null</code> if
+     *         not defined
+     */
+    String[] getValidNames();
+
+    /**
+     * Checks whether the given name is a valid name for this node. This method
+     * can be used for example to ensure that the node name is always one of a
+     * predefined set of valid names, or that it matches a specific pattern.
+     * This method should be consistent with the values returned by
+     * {@link #getValidNames} (if any), the DmtAdmin only calls this method for
+     * name validation.
+     * <p>
+     * This method may return <code>true</code> even if not all aspects of the
+     * name have been checked, expensive operations (for example those that
+     * require external resources) need not be performed here. The actual node
+     * creation may still indicate that the node name is invalid.
+     * 
+     * @param name the node name to check for validity
+     * @return <code>false</code> if the specified name is found to be invalid
+     *         for the node described by this meta-node, <code>true</code>
+     *         otherwise
+     */
+    boolean isValidName(String name);
+
+    /**
+     * Returns the list of extension property keys, if the provider of this
+     * <code>MetaNode</code> provides proprietary extensions to node meta
+     * data. The method returns <code>null</code> if the node doesn't provide
+     * such extensions.
+     * 
+     * @return the array of supported extension property keys
+     */
+    String[] getExtensionPropertyKeys();
+
+    /**
+     * Returns the value for the specified extension property key. This method
+     * only works if the provider of this <code>MetaNode</code> provides
+     * proprietary extensions to node meta data.
+     * 
+     * @param key the key for the extension property
+     * @return the value of the requested property, cannot be <code>null</code>
+     * @throws IllegalArgumentException if the specified key is not supported by
+     *         this <code>MetaNode</code>
+     */
+    Object getExtensionProperty(String key);
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/Uri.java b/org.osgi.compendium/src/main/java/info/dmtree/Uri.java
new file mode 100644
index 0000000..c9ad62c
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/Uri.java
@@ -0,0 +1,563 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/Uri.java,v 1.12 2006/10/24 17:54:28 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;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class contains static utility methods to manipulate DMT URIs.
+ * <p>
+ * Syntax of valid DMT URIs:
+ * <ul>
+ * <li>A slash (<code>'/'</code> &#92;u002F) is the separator of the node names.
+ * Slashes used in node name must therefore be escaped using a backslash slash 
+ * (<code>"\/"</code>). The backslash must be escaped with a double backslash 
+ * sequence. A backslash found must be ignored when it is not followed by a 
+ * slash or backslash.
+ * <li>The node name can be constructed using full Unicode character set
+ * (except the Supplementary code, not being supported by CLDC/CDC). However,
+ * using the full Unicode character set for node names is discouraged because
+ * the encoding in the underlying storage as well as the encoding needed in
+ * communications can create significant performance and memory usage overhead.
+ * Names that are restricted to the URI set <code>[-a-zA-Z0-9_.!~*'()]</code>
+ * are most efficient.
+ * <li>URIs used in the DMT must be treated and interpreted as case sensitive.
+ * <li>No End Slash: URI must not end with the delimiter slash (<code>'/'</code>
+ * &#92;u002F). This implies that the root node must be denoted as 
+ * <code>"."</code> and not <code>"./"</code>.
+ * <li>No parent denotation: URI must not be constructed using the character
+ * sequence <code>"../"</code> to traverse the tree upwards.
+ * <li>Single Root: The character sequence <code>"./"</code> must not be used
+ * anywhere else but in the beginning of a URI.
+ * </ul>
+ */
+public final class Uri {
+	/*
+	 * NOTE: An implementor may also choose to replace this class in
+	 * their distribution with a class that directly interfaces with the
+	 * info.dmtree implementation. This replacement class MUST NOT alter the
+	 * public/protected signature of this class.
+	 */
+
+	/*
+	 * This class will load the class named
+	 * by the org.osgi.vendor.dmtree.DigestDelegate property. This class will call
+	 * the public static byte[] digest(byte[]) method on that class.
+	 */
+	
+	private static class ImplHolder implements PrivilegedAction {
+		// the name of the system property containing the digest delegate class name
+		private static final String DIGEST_DELEGATE_PROPERTY = 
+			"org.osgi.vendor.dmtree.DigestDelegate";
+		
+		// the Method where message digest requests can be delegated
+		static final Method digestMethod;
+		
+		static {
+			digestMethod = (Method) AccessController.doPrivileged(new ImplHolder());
+		}
+		
+		private ImplHolder() {
+		}
+
+		public Object run() {
+			String className = System
+			.getProperty(DIGEST_DELEGATE_PROPERTY);
+			if (className == null) {
+				throw new NoClassDefFoundError("Digest " +
+						"delegate class property '" + 
+						DIGEST_DELEGATE_PROPERTY +
+						"' must be set to a " +
+						"class which implements a " +
+				"public static byte[] digest(byte[]) method."); 
+			}
+			
+			Class delegateClass;
+			try {
+				delegateClass = Class.forName(className);
+			}
+			catch (ClassNotFoundException e) {
+				throw new NoClassDefFoundError(e.toString());
+			}
+			
+			Method result;
+			try {
+				result = delegateClass.getMethod("digest",
+						new Class[] {byte[].class});
+			}
+			catch (NoSuchMethodException e) {
+				throw new NoSuchMethodError(e.toString());
+			}
+			
+			if (!Modifier.isStatic(result.getModifiers())) {
+				throw new NoSuchMethodError(
+				"digest method must be static");
+			}
+			
+			return result;
+		}
+	}
+
+
+    // the name of the system property containing the URI segment length limit 
+    private static final String SEGMENT_LENGTH_LIMIT_PROPERTY = 
+        "org.osgi.impl.service.dmt.uri.limits.segmentlength";
+    
+    // the smallest valid value for the URI segment length limit
+    private static final int MINIMAL_SEGMENT_LENGTH_LIMIT = 32;
+
+    // contains the maximum length of node names
+    private static final int segmentLengthLimit;
+
+    static {
+    	segmentLengthLimit = ((Integer) AccessController
+    	.doPrivileged(new PrivilegedAction() {
+    		public Object run() {
+    			String limitString = System.getProperty(SEGMENT_LENGTH_LIMIT_PROPERTY);
+    			int limit = MINIMAL_SEGMENT_LENGTH_LIMIT; // min. used as default
+    			
+    			try {
+    				int limitInt = Integer.parseInt(limitString);
+    				if(limitInt >= MINIMAL_SEGMENT_LENGTH_LIMIT)
+    					limit = limitInt;
+    			} catch(NumberFormatException e) {}
+    			
+    			return new Integer(limit);
+    		}
+    	})).intValue();
+    }
+    
+    // base64 encoding table, modified for use in node name mangling 
+    private static final char BASE_64_TABLE[] = {
+        'A','B','C','D','E','F','G','H',
+        'I','J','K','L','M','N','O','P',
+        'Q','R','S','T','U','V','W','X',
+        'Y','Z','a','b','c','d','e','f',
+        'g','h','i','j','k','l','m','n',
+        'o','p','q','r','s','t','u','v',
+        'w','x','y','z','0','1','2','3',
+        '4','5','6','7','8','9','+','_', // !!! this differs from base64
+    };
+
+    
+    /**
+     * A private constructor to suppress the default public constructor.
+     */
+    private Uri() {}
+    
+   /**
+     * Returns a node name that is valid for the tree operation methods, based
+     * on the given node name. This transformation is not idempotent, so it must
+     * not be called with a parameter that is the result of a previous
+     * <code>mangle</code> method call.
+     * <p>
+     * Node name mangling is needed in the following cases:
+     * <ul>
+     * <li>if the name contains '/' or '\' characters
+     * <li>if the length of the name exceeds the limit defined by the
+     * implementation
+     * </ul>
+     * <p>
+     * A node name that does not suffer from either of these problems is
+     * guaranteed to remain unchanged by this method. Therefore the client may
+     * skip the mangling if the node name is known to be valid (though it is
+     * always safe to call this method).
+     * <p>
+     * The method returns the normalized <code>nodeName</code> as described
+     * below. Invalid node names are normalized in different ways, depending on
+     * the cause. If the length of the name does not exceed the limit, but the
+     * name contains '/' or '\' characters, then these are simply escaped by
+     * inserting an additional '\' before each occurrence. If the length of the
+     * name does exceed the limit, the following mechanism is used to normalize
+     * it:
+     * <ul>
+     * <li>the SHA 1 digest of the name is calculated
+     * <li>the digest is encoded with the base 64 algorithm
+     * <li>all '/' characters in the encoded digest are replaced with '_'
+     * <li>trailing '=' signs are removed
+     * </ul>
+     * 
+     * @param nodeName the node name to be mangled (if necessary), must not be
+     *        <code>null</code> or empty
+     * @return the normalized node name that is valid for tree operations
+     * @throws NullPointerException if <code>nodeName</code> is
+     *         <code>null</code>
+     * @throws IllegalArgumentException if <code>nodeName</code> is empty
+     */
+    public static String mangle(String nodeName) {
+        return mangle(nodeName, getMaxSegmentNameLength());
+    }
+
+    /**
+     * Construct a URI from the specified URI segments. The segments must
+     * already be mangled.
+     * <p>
+     * If the specified path is an empty array then an empty URI 
+     * (<code>""</code>) is returned.
+     * 
+     * @param path a possibly empty array of URI segments, must not be 
+     *        <code>null</code>
+     * @return the URI created from the specified segments
+     * @throws NullPointerException if the specified path or any of its
+     *         segments are <code>null</code>
+     * @throws IllegalArgumentException if the specified path contains too many
+     *         or malformed segments or the resulting URI is too long
+     */
+    public static String toUri(String[] path) {
+        if (0 == path.length) {
+            return "";
+        }
+
+        if (path.length > getMaxUriSegments()) {
+            throw new IllegalArgumentException(
+                    "Path contains too many segments.");
+        }
+
+        StringBuffer uri = new StringBuffer();
+        int uriLength = 0;
+        for (int i = 0; i < path.length; ++i) {
+            // getSegmentLength throws exceptions on malformed segments.
+            int segmentLength = getSegmentLength(path[i]);
+            if (segmentLength > getMaxSegmentNameLength()) {
+                throw new IllegalArgumentException("URI segment too long.");
+            }
+            if (i > 0) {
+                uri.append('/');
+                uriLength++;
+            }
+            uriLength += segmentLength;
+            uri.append(path[i]);
+        }
+        if (uriLength > getMaxUriLength()) {
+            throw new IllegalArgumentException("URI too long.");
+        }
+        return uri.toString();
+    }
+
+    /**
+     * This method returns the length of a URI segment. The length of the URI
+     * segment is defined as the number of bytes in the unescaped, UTF-8 encoded
+     * represenation of the segment.
+     * <p>
+     * The method verifies that the URI segment is well-formed.
+     * 
+     * @param segment the URI segment
+     * @return URI segment length
+     * @throws NullPointerException if the specified segment is 
+     *         <code>null</code>
+     * @throws IllegalArgumentException if the specified URI segment is
+     *         malformed
+     */
+    private static int getSegmentLength(String segment) {
+        if (segment.length() == 0)
+            throw new IllegalArgumentException("URI segment is empty.");
+
+        StringBuffer newsegment = new StringBuffer(segment);
+        int i = 0;
+        while (i < newsegment.length()) { // length can decrease during the loop!
+            if (newsegment.charAt(i) == '\\') {
+                if (i == newsegment.length() - 1) // last character cannot be a '\'
+                    throw new IllegalArgumentException(
+                            "URI segment ends with the escape character.");
+
+                newsegment.deleteCharAt(i); // remove the extra '\'
+            } else if (newsegment.charAt(i) == '/')
+                throw new IllegalArgumentException(
+                        "URI segment contains an unescaped '/' character.");
+
+            i++;
+        }
+
+        if (newsegment.toString().equals(".."))
+            throw new IllegalArgumentException(
+                    "URI segment must not be \"..\".");
+
+        try {
+            return newsegment.toString().getBytes("UTF-8").length;
+        } catch (UnsupportedEncodingException e) {
+            // This should never happen. All implementations must support
+            // UTF-8 encoding;
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Split the specified URI along the path separator '/' charaters and return
+     * an array of URI segments. Special characters in the returned segments are
+     * escaped. The returned array may be empty if the specifed URI was empty.
+     * 
+     * @param uri the URI to be split, must not be <code>null</code>
+     * @return an array of URI segments created by splitting the specified URI
+     * @throws NullPointerException if the specified URI is <code>null</code>
+     * @throws IllegalArgumentException if the specified URI is malformed
+     */
+    public static String[] toPath(String uri) {
+        if (uri == null)
+            throw new NullPointerException("'uri' parameter is null.");
+
+        if (!isValidUri(uri))
+            throw new IllegalArgumentException("Malformed URI: " + uri);
+
+        if (uri.length() == 0)
+            return new String[] {};
+
+        List segments = new ArrayList();
+        StringBuffer segment = new StringBuffer();
+
+        boolean escape = false;
+        for (int i = 0; i < uri.length(); i++) {
+            char ch = uri.charAt(i);
+
+            if (escape) {
+                if(ch == '/' || ch == '\\')
+                    segment.append('\\');
+                segment.append(ch);
+                escape = false;
+            } else if (ch == '/') {
+                segments.add(segment.toString());
+                segment = new StringBuffer();
+            } else if (ch == '\\') {
+                escape = true;
+            } else
+                segment.append(ch);
+        }
+        if (segment.length() > 0) {
+            segments.add(segment.toString());
+        }
+
+        return (String[]) segments.toArray(new String[segments.size()]);
+    }
+
+    /**
+     * Returns the maximum allowed number of URI segments. The returned value is
+     * implementation specific.
+     * <p>
+     * The return value of <code>Integer.MAX_VALUE</code> indicates that there
+     * is no upper limit on the number of URI segments.
+     * 
+     * @return maximum number of URI segments supported by the implementation
+     */
+    public static int getMaxUriSegments() {
+        return Integer.MAX_VALUE;
+    }
+
+    /**
+     * Returns the maximum allowed length of a URI. The value is implementation
+     * specific. The length of the URI is defined as the number of bytes in the
+     * unescaped, UTF-8 encoded represenation of the URI.
+     * <p>
+     * The return value of <code>Integer.MAX_VALUE</code> indicates that there
+     * is no upper limit on the length of URIs.
+     * 
+     * @return maximum URI length supported by the implementation
+     */
+    public static int getMaxUriLength() {
+        return Integer.MAX_VALUE;
+    }
+
+    /**
+     * Returns the maximum allowed length of a URI segment. The value is
+     * implementation specific. The length of the URI segment is defined as the
+     * number of bytes in the unescaped, UTF-8 encoded represenation of the
+     * segment.
+     * <p>
+     * The return value of <code>Integer.MAX_VALUE</code> indicates that there
+     * is no upper limit on the length of segment names.
+     * 
+     * @return maximum URI segment length supported by the implementation
+     */
+    public static int getMaxSegmentNameLength() {
+        return segmentLengthLimit;
+    }
+
+    /**
+     * Checks whether the specified URI is an absolute URI. An absolute URI
+     * contains the complete path to a node in the DMT starting from the DMT
+     * root (".").
+     * 
+     * @param uri the URI to be checked, must not be <code>null</code> and must 
+     *        contain a valid URI
+     * @return whether the specified URI is absolute
+     * @throws NullPointerException if the specified URI is <code>null</code>
+     * @throws IllegalArgumentException if the specified URI is malformed
+     */
+    public static boolean isAbsoluteUri(String uri) {
+        if( null == uri ) {
+            throw new NullPointerException("'uri' parameter is null.");
+        }
+        if( !isValidUri(uri) ) 
+            throw new IllegalArgumentException("Malformed URI: " + uri);
+        return uri.equals(".") || uri.equals("\\.") || uri.startsWith("./") 
+                 || uri.startsWith("\\./");
+    }
+
+    /**
+     * Checks whether the specified URI is valid. A URI is considered valid if
+     * it meets the following constraints:
+     * <ul>
+     * <li>the URI is not <code>null</code>;
+     * <li>the URI follows the syntax defined for valid DMT URIs;
+     * <li>the length of the URI is not more than {@link #getMaxUriLength()};
+     * <li>the URI doesn't contain more than {@link #getMaxUriSegments()}
+     * segments;
+     * <li>the length of each segment of the URI is less than or equal to
+     * {@link #getMaxSegmentNameLength()}.
+     * </ul>
+     * The exact definition of the length of a URI and its segments is
+     * given in the descriptions of the <code>getMaxUriLength()</code> and
+     * <code>getMaxSegmentNameLength()</code> methods.  
+     * 
+     * @param uri the URI to be validated
+     * @return whether the specified URI is valid
+     */
+    public static boolean isValidUri(String uri) {
+        if (null == uri)
+            return false;
+        
+        int paramLen = uri.length();
+        if( paramLen == 0 ) 
+            return true;
+        if( uri.charAt(0) == '/' || uri.charAt(paramLen-1) == '\\' )
+            return false;
+        
+        int processedUriLength = 0;
+        int segmentNumber = 0;
+        
+        // append a '/' to indicate the end of the last segment (the URI in the
+        // parameter must not end with a '/')
+        uri += '/';
+        paramLen++;
+        
+        int start = 0;
+        for(int i = 1; i < paramLen; i++) { // first character is not a '/'
+            if(uri.charAt(i) == '/' && uri.charAt(i-1) != '\\') {
+                segmentNumber++;
+
+                String segment = uri.substring(start, i);
+                if(segmentNumber > 1 && segment.equals("."))
+                    return false; // the URI contains the "." node name at a 
+                                 // position other than the beginning of the URI
+                
+                int segmentLength;
+                try {
+                    // also checks that the segment is valid
+                    segmentLength = getSegmentLength(segment);
+                } catch(IllegalArgumentException e) {
+                    return false;
+                }
+                
+                if(segmentLength > getMaxSegmentNameLength())
+                    return false;
+                
+                // the extra byte is for the separator '/' (will be deducted
+                // again for the last segment of the URI)
+                processedUriLength += segmentLength + 1; 
+                start = i+1;
+            }
+        }
+        
+        processedUriLength--; // remove the '/' added to the end of the URI
+        
+        return segmentNumber <= getMaxUriSegments() &&  
+                processedUriLength <= getMaxUriLength();
+    }
+
+    // Non-public fields and methods
+
+    // package private method for testing purposes 
+    static String mangle(String nodeName, int limit) {
+        if(nodeName == null)
+            throw new NullPointerException(
+                    "The 'nodeName' parameter must not be null.");
+            
+        if(nodeName.equals(""))
+            throw new IllegalArgumentException(
+                    "The 'nodeName' parameter must not be empty.");        
+
+        if(nodeName.length() > limit)
+            // create node name hash
+            return getHash(nodeName);
+
+        // escape any '/' and '\' characters in the node name
+        StringBuffer nameBuffer = new StringBuffer(nodeName);
+        for(int i = 0; i < nameBuffer.length(); i++) // 'i' can increase in loop
+            if(nameBuffer.charAt(i) == '\\' || nameBuffer.charAt(i) == '/')
+                nameBuffer.insert(i++, '\\');
+        
+        return nameBuffer.toString();
+    }
+
+    private static String getHash(String from) {
+        byte[] byteStream;
+        try {
+            byteStream = from.getBytes("UTF-8");
+        }
+        catch (UnsupportedEncodingException e) {
+            // There's no way UTF-8 encoding is not implemented...
+            throw new IllegalStateException("there's no UTF-8 encoder here!");
+        }
+        byte[] digest = digestMessage(byteStream);
+        
+        // very dumb base64 encoder code. There is no need for multiple lines
+        // or trailing '='-s....
+        // also, we hardcoded the fact that sha-1 digests are 20 bytes long
+        StringBuffer sb = new StringBuffer(digest.length*2);
+        for(int i=0;i<6;i++) {
+            int d0 = digest[i*3]&0xff;
+            int d1 = digest[i*3+1]&0xff;
+            int d2 = digest[i*3+2]&0xff;
+            sb.append(BASE_64_TABLE[d0>>2]);
+            sb.append(BASE_64_TABLE[(d0<<4|d1>>4)&63]);
+            sb.append(BASE_64_TABLE[(d1<<2|d2>>6)&63]);
+            sb.append(BASE_64_TABLE[d2&63]);
+        }
+        int d0 = digest[18]&0xff;
+        int d1 = digest[19]&0xff;
+        sb.append(BASE_64_TABLE[d0>>2]);
+        sb.append(BASE_64_TABLE[(d0<<4|d1>>4)&63]);
+        sb.append(BASE_64_TABLE[(d1<<2)&63]);
+        
+        return sb.toString();
+    }
+    
+    private static byte[] digestMessage(byte[] byteStream) {
+		try {
+			try {
+				return (byte[]) ImplHolder.digestMethod.invoke(null, new Object[] {
+						byteStream});
+			}
+			catch (InvocationTargetException e) {
+				throw e.getTargetException();
+			}
+		}
+		catch (Error e) {
+			throw e;
+		}
+		catch (RuntimeException e) {
+			throw e;
+		}
+		catch (Throwable e) {
+			throw new RuntimeException(e.toString());
+		}
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/notification/AlertItem.java b/org.osgi.compendium/src/main/java/info/dmtree/notification/AlertItem.java
new file mode 100644
index 0000000..2088743
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/notification/AlertItem.java
@@ -0,0 +1,170 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/notification/AlertItem.java,v 1.3 2006/07/04 12:26:50 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.notification;
+
+import info.dmtree.DmtData;
+import info.dmtree.Uri;
+
+/**
+ * Immutable data structure carried in an alert (client initiated notification).
+ * The <code>AlertItem</code> describes details of various notifications that
+ * can be sent by the client, for example as alerts in the OMA DM protocol. The
+ * use cases include the client sending a session request to the server (alert
+ * 1201), the client notifying the server of completion of a software update
+ * operation (alert 1226) or sending back results in response to an asynchronous
+ * EXEC command.
+ * <p>
+ * The data syntax and semantics varies widely between various alerts, so does
+ * the optionality of particular parameters of an alert item. If an item, such
+ * as source or type, is not defined, the corresponding getter method returns
+ * <code>null</code>. For example, for alert 1201 (client-initiated session)
+ * all elements will be <code>null</code>.
+ * <P>
+ * The syntax used in <code>AlertItem</code> class corresponds to the OMA DM
+ * alert format. {@link NotificationService} implementations on other management
+ * protocols should map these constructs to the underlying protocol.
+ */
+public class AlertItem {
+
+    private final String source;
+
+    private final String type;
+
+    private final String mark;
+
+    private final DmtData data;
+
+    /**
+     * Create an instance of the alert item. The constructor takes all possible
+     * data entries as parameters. Any of these parameters can be
+     * <code>null</code>. The semantics of the parameters may be refined by
+     * the definition of a specific alert, identified by its alert code (see
+     * {@link NotificationService#sendNotification}). In case of Generic Alerts
+     * for example (code 1226), the <code>mark</code> parameter contains a
+     * severity string.
+     * 
+     * @param source the URI of the node which is the source of the alert item
+     * @param type a MIME type or a URN that identifies the type of the data in
+     *        the alert item
+     * @param data a <code>DmtData</code> object that contains the format and
+     *        value of the data in the alert item
+     * @param mark the mark parameter of the alert item
+     */
+    public AlertItem(String source, String type, String mark, DmtData data) {
+        this.source = source;
+        this.type = type;
+        this.mark = mark;
+        this.data = data;
+    }
+
+    /**
+     * Create an instance of the alert item, specifying the source node URI as
+     * an array of path segments. The constructor takes all possible data
+     * entries as parameters. Any of these parameters can be <code>null</code>.
+     * The semantics of the parameters may be refined by the definition of a
+     * specific alert, identified by its alert code (see
+     * {@link NotificationService#sendNotification}). In case of Generic Alerts
+     * for example (code 1226), the <code>mark</code> parameter contains a
+     * severity string.
+     * 
+     * @param source the path of the node which is the source of the alert item
+     * @param type a MIME type or a URN that identifies the type of the data in
+     *        the alert item
+     * @param data a <code>DmtData</code> object that contains the format and
+     *        value of the data in the alert item
+     * @param mark the mark parameter of the alert item
+     */
+    public AlertItem(String[] source, String type, String mark, DmtData data) {
+        if ((null == source)) {
+            this.source = null;
+        } else {
+            this.source = Uri.toUri(source);
+        }
+        this.type = type;
+        this.mark = mark;
+        this.data = data;
+    }
+
+    /**
+     * Get the node which is the source of the alert. There might be no source
+     * associated with the alert item.
+     * 
+     * @return the URI of the node which is the source of this alert, or
+     *         <code>null</code> if there is no source
+     */
+    public String getSource() {
+        return source;
+    }
+
+    /**
+     * Get the type associated with the alert item. The type string is a MIME
+     * type or a URN that identifies the type of the data in the alert item
+     * (returned by {@link #getData}). There might be no type associated with
+     * the alert item.
+     * 
+     * @return the type type associated with the alert item, or
+     *         <code>null</code> if there is no type
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Get the mark parameter associated with the alert item. The interpretation
+     * of the mark parameter depends on the alert being sent, as identified by
+     * the alert code in {@link NotificationService#sendNotification}. There
+     * might be no mark associated with the alert item.
+     * 
+     * @return the mark associated with the alert item, or <code>null</code>
+     *         if there is no mark
+     */
+    public String getMark() {
+        return mark;
+    }
+
+    /**
+     * Get the data associated with the alert item. The returned
+     * <code>DmtData</code> object contains the format and the value of the
+     * data in the alert item. There might be no data associated with the alert
+     * item.
+     * 
+     * @return the data associated with the alert item, or <code>null</code>
+     *         if there is no data
+     */
+    public DmtData getData() {
+        return data;
+    }
+
+    /**
+     * Returns the string representation of this alert item. The returned string
+     * includes all parameters of the alert item, and has the following format:
+     * 
+     * <pre>
+     *   AlertItem(&lt;source&gt;, &lt;type&gt;, &lt;mark&gt;, &lt;data&gt;)
+     * </pre>
+     * 
+     * The last parameter is the string representation of the data value. The
+     * format of the data is not explicitly included.
+     * 
+     * @return the string representation of this alert item
+     */
+    public String toString() {
+        return "AlertItem(" + source + ", " + type + ", " + mark + ", " + data
+                + ")";
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/notification/NotificationService.java b/org.osgi.compendium/src/main/java/info/dmtree/notification/NotificationService.java
new file mode 100644
index 0000000..6ee9cd3
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/notification/NotificationService.java
@@ -0,0 +1,99 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/notification/NotificationService.java,v 1.5 2006/07/04 12:26:50 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.notification;
+
+import info.dmtree.DmtException;
+import info.dmtree.DmtSession;
+
+/**
+ * NotificationService enables sending aynchronous notifications to a management
+ * server. The implementation of <code>NotificationService</code> should
+ * register itself in the OSGi service registry as a service.
+ */
+public interface NotificationService {
+
+    /**
+     * Sends a notification to a named principal. It is the responsibility of
+     * the <code>NotificationService</code> to route the notification to the 
+     * given principal using the registered
+     * {@link info.dmtree.notification.spi.RemoteAlertSender} services.
+     * <p>
+     * In remotely initiated sessions the principal name identifies the remote
+     * server that created the session, this can be obtained using the session's
+     * {@link DmtSession#getPrincipal getPrincipal} call.
+     * <p>
+     * The principal name may be omitted if the client does not know the
+     * principal name. Even in this case the routing might be possible if the
+     * Notification Service finds an appropriate default destination (for
+     * example if it is only connected to one protocol adapter, which is only
+     * connected to one management server).
+     * <p>
+     * Since sending the notification and receiving acknowledgment for it is
+     * potentially a very time-consuming operation, notifications are sent
+     * asynchronously. This method should attempt to ensure that the
+     * notification can be sent successfully, and should throw an exception if
+     * it detects any problems. If the method returns without error, the
+     * notification is accepted for sending and the implementation must make a
+     * best-effort attempt to deliver it.
+     * <p>
+     * In case the notification is an asynchronous response to a previous
+     * {@link DmtSession#execute(String, String, String) execute} command, a
+     * correlation identifier can be specified to provide the association
+     * between the execute and the notification.
+     * <p>
+     * In order to send a notification using this method, the caller must have
+     * an <code>AlertPermission</code> with a target string matching the
+     * specified principal name. If the <code>principal</code> parameter is
+     * <code>null</code> (the principal name is not known), the target of the
+     * <code>AlertPermission</code> must be &quot;*&quot;.
+     * <p>
+     * When this method is called with all its parameters <code>null</code> or 0
+     * (except <code>principal</code>), it should send a protocol 
+     * specific default notification to initiate a management session. For 
+     * example, in case of OMA DM this is alert 1201 "Client Initiated Session".
+     * The <code>principal</code> parameter can be used to determine the
+     * recipient of the session initiation request.
+     * 
+     * @param principal the principal name which is the recipient of this
+     *        notification, can be <code>null</code>
+     * @param code the alert code, can be 0 if not needed
+     * @param correlator optional field that contains the correlation identifier
+     *        of an associated exec command, can be <code>null</code> if not
+     *        needed
+     * @param items the data of the alert items carried in this alert, can be
+     *        <code>null</code> or empty if not needed
+     * @throws DmtException with the following possible error codes:
+     *         <ul>
+     *         <li><code>UNAUTHORIZED</code> when the remote server rejected
+     *         the request due to insufficient authorization
+     *         <li><code>ALERT_NOT_ROUTED</code> when the alert can not be
+     *         routed to the given principal
+     *         <li><code>REMOTE_ERROR</code> in case of communication
+     *         problems between the device and the destination
+     *         <li><code>COMMAND_FAILED</code> for unspecified errors
+     *         encountered while attempting to complete the command
+     *         <li><code>FEATURE_NOT_SUPPORTED</code> if the underlying
+     *         management protocol doesn't support asynchronous notifications
+     *         </ul>
+     * @throws SecurityException if the caller does not have the required
+     *         <code>AlertPermission</code> with a target matching the
+     *         <code>principal</code> parameter, as described above
+     */
+    void sendNotification(String principal, int code, String correlator,
+            AlertItem[] items) throws DmtException;
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/notification/spi/RemoteAlertSender.java b/org.osgi.compendium/src/main/java/info/dmtree/notification/spi/RemoteAlertSender.java
new file mode 100644
index 0000000..3e28892
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/notification/spi/RemoteAlertSender.java
@@ -0,0 +1,84 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/notification/spi/RemoteAlertSender.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.notification.spi;
+
+import info.dmtree.notification.AlertItem;
+
+/**
+ * The RemoteAlertSender can be used to send notifications to (remote) entities
+ * identified by principal names. This service is provided by Protocol Adapters,
+ * and is used by the
+ * {@link info.dmtree.notification.NotificationService} when sending
+ * alerts. Implementations of this interface have to be able to connect and send
+ * alerts to one or more management servers in a protocol specific way.
+ * <p>
+ * The properties of the service registration should specify a list of
+ * destinations (principals) where the service is capable of sending alerts.
+ * This can be done by providing a <code>String</code> array of principal
+ * names in the <code>principals</code> registration property. If this property
+ * is not registered, the service will be treated as the default sender. The
+ * default alert sender is only used when a more specific alert sender cannot be
+ * found.
+ * <p>
+ * The <code>principals</code> registration property is used when the
+ * {@link info.dmtree.notification.NotificationService#sendNotification}
+ * method is called, to find the proper <code>RemoteAlertSender</code> for the
+ * given destination. If the caller does not specify a principal, the alert is
+ * only sent if the Notification Sender finds a default alert sender, or if the
+ * choice is unambiguous for some other reason (for example if only one alert
+ * sender is registered).
+ */
+public interface RemoteAlertSender {
+    /**
+     * Sends an alert to a server identified by its principal name. In case the
+     * alert is sent in response to a previous
+     * {@link info.dmtree.DmtSession#execute(String, String, String) execute}
+     * command, a correlation identifier can be specified to provide the
+     * association between the execute and the alert.
+     * <p>
+     * The <code>principal</code> parameter specifies which server the alert
+     * should be sent to. This parameter can be <code>null</code> if the
+     * client does not know the name of the destination. The alert should still
+     * be delivered if possible; for example if the alert sender is only
+     * connected to one destination.
+     * <p>
+     * Any exception thrown on this method will be propagated to the original
+     * sender of the event, wrapped in a <code>DmtException</code> with the
+     * code <code>REMOTE_ERROR</code>.
+     * <p>
+     * Since sending the alert and receiving acknowledgment for it is
+     * potentially a very time-consuming operation, alerts are sent
+     * asynchronously. This method should attempt to ensure that the alert can
+     * be sent successfully, and should throw an exception if it detects any
+     * problems. If the method returns without error, the alert is accepted for
+     * sending and the implementation must make a best-effort attempt to deliver
+     * it.
+     * 
+     * @param principal the name identifying the server where the alert should
+     *        be sent, can be <code>null</code>
+     * @param code the alert code, can be 0 if not needed
+     * @param correlator the correlation identifier of an associated EXEC
+     *        command, or <code>null</code> if there is no associated EXEC
+     * @param items the data of the alert items carried in this alert, can be
+     *        empty or <code>null</code> if no alert items are needed
+     * @throws Exception if the alert can not be sent to the server
+     */
+    void sendAlert(String principal, int code, String correlator,
+            AlertItem[] items) throws Exception;
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/registry/DmtServiceFactory.java b/org.osgi.compendium/src/main/java/info/dmtree/registry/DmtServiceFactory.java
new file mode 100644
index 0000000..9820277
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/registry/DmtServiceFactory.java
@@ -0,0 +1,96 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/registry/DmtServiceFactory.java,v 1.5 2006/07/11 09:38:25 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.registry;
+
+import info.dmtree.DmtAdmin;
+import info.dmtree.notification.NotificationService;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+
+/**
+ * This class is the central access point for Device Management services.
+ * Applications can use the static factory methods provided in this class to
+ * obtain access to the different Device Management related services, such as
+ * the DmtAdmin for manipulating the tree, or the Notification Service for
+ * sending notifications to management servers.
+ * <p>
+ * These methods are not needed in an OSGi environment, clients should retrieve
+ * the required service objects from the OSGi Service Registry. 
+ */
+public final class DmtServiceFactory {
+    private static BundleContext context = null;
+    
+    /**
+     * A private constructor to suppress the default public constructor.
+     */
+    private DmtServiceFactory() {}
+    
+    /**
+     * This method is used to obtain access to <code>DmtAdmin</code>, which
+     * enables applications to manipulate the Device Management Tree.
+     * 
+     * @return a DmtAdmin service object
+     */
+    public static DmtAdmin getDmtAdmin() {
+        if(context == null)
+            throw new IllegalStateException("Cannot retrieve Dmt Admin " +
+                    "service, implementation bundle not started yet.");
+        
+        ServiceReference dmtAdminRef = 
+            context.getServiceReference(DmtAdmin.class.getName());
+        if(dmtAdminRef == null)
+            throw new IllegalStateException("Dmt Admin service not found in " +
+                    "service registry.");
+        
+        DmtAdmin dmtAdmin = (DmtAdmin) context.getService(dmtAdminRef);
+        if(dmtAdmin == null)
+            throw new IllegalStateException("Dmt Admin service not found in " +
+                    "service registry.");
+        
+        return dmtAdmin;
+    }
+
+    /**
+     * This method is used to obtain access to <code>NotificationService</code>,
+     * which enables applications to send asynchronous notifications to
+     * management servers.
+     * 
+     * @return a NotificationService service object
+     */
+    public static NotificationService getNotificationService() {
+        if(context == null)
+            throw new IllegalStateException("Cannot retrieve Notification " +
+                    "service, implementation bundle not started yet.");
+        
+        ServiceReference notificationServiceRef = 
+            context.getServiceReference(NotificationService.class.getName());
+        if(notificationServiceRef == null)
+            throw new IllegalStateException("Notification service not found " +
+                    "in service registry.");
+        
+        NotificationService notificationService = 
+            (NotificationService) context.getService(notificationServiceRef);
+        if(notificationService == null)
+            throw new IllegalStateException("Notification service not found " +
+                    "in service registry.");
+        
+        return notificationService;
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/security/AlertPermission.java b/org.osgi.compendium/src/main/java/info/dmtree/security/AlertPermission.java
new file mode 100644
index 0000000..6fc0209
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/security/AlertPermission.java
@@ -0,0 +1,261 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/security/AlertPermission.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 send alerts to management servers,
+ * identified by their principal names.
+ * <p>
+ * <code>AlertPermission</code> has a target string which controls the
+ * principal names where alerts can be sent. A wildcard is allowed at the end of
+ * the target string, to allow sending alerts to any principal with a name
+ * matching the given prefix. The &quot;*&quot; target means that alerts can be
+ * sent to any destination.
+ */
+public class AlertPermission extends Permission {
+    private static final long serialVersionUID = -3206463101788245739L;
+
+    // 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 serverId;
+
+    /**
+     * Creates a new <code>AlertPermission</code> object with its name set to
+     * the target string. Name must be non-null and non-empty.
+     * 
+     * @param target the name of a principal, can end with <code>*</code> to
+     *        match any principal identifier with the given prefix
+     * @throws NullPointerException if <code>name</code> is <code>null</code>
+     * @throws IllegalArgumentException if <code>name</code> is empty
+     */
+    public AlertPermission(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)
+            serverId = target.substring(0, target.length() - 1);
+        else
+            serverId = target;
+    }
+
+    /**
+     * Creates a new <code>AlertPermission</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 server, can end with <code>*</code> to
+     *        match any server identifier 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 AlertPermission(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 AlertPermission
+     * instance. Two AlertPermission instances are equal if they have the same
+     * target string.
+     * 
+     * @param obj the object to compare to this AlertPermission 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 AlertPermission))
+            return false;
+
+        AlertPermission other = (AlertPermission) obj;
+
+        return isPrefix == other.isPrefix && serverId.equals(other.serverId);
+    }
+
+    /**
+     * 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 AlertPermission
+     * objects are equal according to the {@link #equals} method, then calling
+     * this method on each of the two AlertPermission objects must produce the
+     * same integer result.
+     * 
+     * @return hash code for this permission object
+     */
+    public int hashCode() {
+        return new Boolean(isPrefix).hashCode() ^ serverId.hashCode();
+    }
+
+    /**
+     * Checks if this AlertPermission object implies the specified permission.
+     * Another AlertPermission 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 AlertPermission instance implies the specified
+     *         permission
+     */
+    public boolean implies(Permission p) {
+        if (!(p instanceof AlertPermission))
+            return false;
+
+        AlertPermission other = (AlertPermission) p;
+
+        return impliesServer(other);
+    }
+
+    /**
+     * Returns a new PermissionCollection object for storing AlertPermission
+     * objects.
+     * 
+     * @return the new PermissionCollection
+     */
+    public PermissionCollection newPermissionCollection() {
+        return new DmtAlertPermissionCollection();
+    }
+
+    /*
+     * Returns true if the server name parameter of the given AlertPermission is
+     * implied by the server name of this permission, i.e. this server name is a
+     * prefix of the other one but ends with a *, or the two server names are
+     * equal.
+     */
+    boolean impliesServer(AlertPermission p) {
+        return isPrefix ? p.serverId.startsWith(serverId) : !p.isPrefix
+                && p.serverId.equals(serverId);
+    }
+}
+
+/**
+ * Represents a homogeneous collection of AlertPermission objects.
+ */
+final class DmtAlertPermissionCollection extends PermissionCollection {
+    private static final long serialVersionUID = 2288462124510043429L;
+
+    private ArrayList perms;
+
+    /**
+     * Create an empty DmtAlertPermissionCollection object.
+     */
+    public DmtAlertPermissionCollection() {
+        perms = new ArrayList();
+    }
+
+    /**
+     * Adds a permission to the DmtAlertPermissionCollection.
+     * 
+     * @param permission the Permission object to add
+     * @exception IllegalArgumentException if the permission is not a
+     *            AlertPermission
+     * @exception SecurityException if this DmtAlertPermissionCollection object
+     *            has been marked readonly
+     */
+    public void add(Permission permission) {
+        if (!(permission instanceof AlertPermission))
+            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((AlertPermission) 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 AlertPermission))
+            return false;
+
+        AlertPermission other = (AlertPermission) permission;
+
+        Iterator i = perms.iterator();
+        while (i.hasNext())
+            if (((AlertPermission) i.next()).impliesServer(other))
+                return true;
+
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of all the AlertPermission objects in the
+     * container. The returned value cannot be <code>null</code>.
+     * 
+     * @return an enumeration of all the AlertPermission objects
+     */
+    public Enumeration elements() {
+        // Convert Iterator into Enumeration
+        return Collections.enumeration(perms);
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java b/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java
new file mode 100644
index 0000000..aa9702b
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java
@@ -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);
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java b/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java
new file mode 100644
index 0000000..e875055
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java
@@ -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);
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java b/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java
new file mode 100644
index 0000000..ab3eea4
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java
@@ -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;
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java b/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java
new file mode 100644
index 0000000..b2fe0f3
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java
@@ -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;
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java b/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java
new file mode 100644
index 0000000..54e444b
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java
@@ -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;
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java b/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java
new file mode 100644
index 0000000..7897bad
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java
@@ -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;
+}
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java b/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java
new file mode 100644
index 0000000..4ddb694
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java
@@ -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;
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationContext.java b/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationContext.java
new file mode 100644
index 0000000..c04fcf8
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationContext.java
@@ -0,0 +1,355 @@
+/*
+ * $Header: /cvshome/build/org.osgi.application/src/org/osgi/application/ApplicationContext.java,v 1.15 2006/07/11 13:19:02 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.application;
+
+import java.util.Dictionary;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+
+/**
+ * <code>ApplicationContext</code> is the access point for an OSGi-aware
+ * application to the features of the OSGi Service Platform. Each application
+ * instance will have its own <code>ApplicationContext</code> instance, which
+ * will not be reused after destorying the corresponding application instace.
+ * <p>
+ * Application instances can obtain their <code>ApplicationContext</code>
+ * using the {@link Framework#getApplicationContext} method.
+ * <p>
+ * The lifecycle of an <code>ApplicationContext</code> instance is bound to
+ * the lifecycle of the corresponding application instance. The 
+ * <code>ApplicationContext</code> becomes available when the application is
+ * started and it is invalidated when the application instance is stopped (i.e.
+ * the "stop" method of the application activator object returned).
+ * All method calls (except {@link #getApplicationId()} and 
+ * {@link #getInstanceId()}) to an invalidated context object result an 
+ * <code>IllegalStateException</code>.
+ * 
+ * @see org.osgi.application.Framework
+ */
+public interface ApplicationContext {
+
+    /**
+     * Adds the specified {@link ApplicationServiceListener} object to this context
+     * application instance's list of listeners. The specified <code>referenceName</code> is a 
+     * reference name specified in the descriptor of the corresponding application. The registered
+     * <code>listener> will only receive the {@link ApplicationServiceEvent}s realted to the referred service.
+     * <p>
+     * If the <code>listener</code> was already added, calling this method will overwrite the previous
+     * registration.
+     * <p>
+     * 
+     * @param listener
+     *            The {@link org.osgi.application.ApplicationServiceListener} to be added. It must
+     *            not be <code>null</code>
+     * @param referenceName the reference name of a service from the descriptor of the corresponding
+     *            application. It must not be <code>null</code>.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     * @throws java.lang.NullPointerException If <code>listener</code> or <code>referenceName</code>
+     *             is <code>null</code>      
+     * @throws java.lang.IllegalArgumentException If there is no service in the
+     *             application descriptor with the specified <code>referenceName</code>.
+     */
+    public void addServiceListener(ApplicationServiceListener listener, String referenceName) throws java.lang.IllegalArgumentException;
+
+    /**
+     * Adds the specified {@link ApplicationServiceListener} object to this context
+     * application instance's list of listeners. The <code>referenceNames</code> parameter is an 
+     * array of reference name specified in the descriptor of the corresponding application. The registered
+     * <code>listener> will only receive the {@link ApplicationServiceEvent}s realted to the referred 
+     * services.
+     * <p>
+     * If the <code>listener</code> was already added, calling this method will overwrite the previous
+     * registration.
+     * <p>
+     * 
+     * @param listener
+     *            The {@link org.osgi.application.ApplicationServiceListener} to be added. It must not
+     *            be <code>null</code>
+     * @param referenceNames and array of service reference names from the descriptor of the corresponding
+     *            application. It must not be <code>null</code> and it must not be empty.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     * @throws java.lang.NullPointerException If <code>listener</code> or <code>referenceNames</code>
+     *             is <code>null</code>      
+     * @throws java.lang.IllegalArgumentException If <code>referenceNames</code> array is empty or it 
+     *    contains unknown references
+     */
+    public void addServiceListener(ApplicationServiceListener listener, String[] referenceNames) throws java.lang.IllegalArgumentException;
+
+    /**
+     * Removes the specified {@link org.osgi.application.ApplicationServiceListener} object from this
+     * context application instances's list of listeners.
+     * <p>
+     * If <code>listener</code> is not contained in this context application
+     * instance's list of listeners, this method does nothing.
+     * 
+     * @param listener
+     *            The {@link org.osgi.application.ApplicationServiceListener} object to be removed.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     */
+    public void removeServiceListener(ApplicationServiceListener listener);
+    
+    /**
+     * This method returns the identifier of the corresponding application instace.
+     * This identifier is guarateed to be unique within the scope of the device.
+     * 
+      * Note: this method can safely be called on an invalid 
+     * <code>ApplicationContext</code> as well.
+     * 
+     * @see org.osgi.service.application.ApplicationHandle#getInstanceId()
+     * 
+     * @return the unique identifier of the corresponding application instance
+     */
+    public String getInstanceId();
+    
+    /**
+     * This method return the identifier of the correspondig application type. This identifier
+     * is the same for the different instances of the same application but it is different for
+     * different application type.
+     * <p>
+     * Note: this method can safely be called on an invalid 
+     * <code>ApplicationContext</code> as well.
+     * 
+     * @see org.osgi.service.application.ApplicationDescriptor#getApplicationId()
+     * 
+     * @return the identifier of the application type.
+     */
+    public String getApplicationId();
+
+    /**
+     * This method returns the service object for the specified
+     * <code>referenceName</code>. If the cardinality of the reference is
+     * 0..n or 1..n and multiple services are bound to the reference, the
+     * service with the highest ranking (as specified in its
+     * {@link org.osgi.framework.Constants#SERVICE_RANKING} property) is returned. If there
+     * is a tie in ranking, the service with the lowest service ID (as specified
+     * in its {@link org.osgi.framework.Constants#SERVICE_ID} property); that is, the
+     * service that was registered first is returned.
+     * 
+     * @param referenceName
+     *            The name of a reference as specified in a reference element in
+     *            this context applications's description. It must not be <code>null</code>
+     * @return A service object for the referenced service or <code>null</code>
+     *         if the reference cardinality is 0..1 or 0..n and no bound service
+     *         is available.
+     * @throws java.lang.NullPointerException If <code>referenceName</code> is <code>null</code>.
+     * @throws java.lang.IllegalArgumentException If there is no service in the
+     *             application descriptor with the specified <code>referenceName</code>.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     */
+    public Object locateService(String referenceName);
+
+    /**
+     * This method returns the service objects for the specified
+     * <code>referenceName</code>.
+     * 
+     * @param referenceName
+     *            The name of a reference as specified in a reference element in
+     *            this context applications's description. It must not be 
+     *            <code>null</code>.
+     * @return An array of service object for the referenced service or
+     *         <code>null</code> if the reference cardinality is 0..1 or 0..n
+     *         and no bound service is available.
+     * @throws java.lang.NullPointerException If <code>referenceName</code> is <code>null</code>.
+     * @throws java.lang.IllegalArgumentException If there is no service in the
+     *             application descriptor with the specified <code>referenceName</code>.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     */
+    public Object[] locateServices(String referenceName);
+    
+    /**
+     * Returns the startup parameters specified when calling the 
+     * {@link org.osgi.service.application.ApplicationDescriptor#launch}
+     * method.
+     * <p>
+     * Startup arguments can be specified as name, value pairs. The name
+     * must be of type {@link java.lang.String}, which must not be
+     * <code>null</code> or empty {@link java.lang.String} (<code>""</code>), 
+     * the value can be any object including <code>null</code>.
+     * 
+     * @return a {@link java.util.Map} containing the startup arguments. 
+     *     It can be <code>null</code>.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     */
+    public Map getStartupParameters();
+    
+    /**
+     * Application can query the service properties of a service object
+     * it is bound to. Application gets bound to a service object when
+     * it fisrt obtains a reference to the service by calling 
+     * <code>locateService</code> or <code>locateServices</code> methods.
+     * 
+     * @param serviceObject A service object the application is bound to.
+     *    It must not be null.
+     * @return The service properties associated with the specified service
+     *    object.
+     * @throws NullPointerException if the specified <code>serviceObject</code>
+     *    is <code>null</code>
+     * @throws IllegalArgumentException if the application is not
+     *    bound to the specified service object or it is not a service
+     *    object at all.
+     * @throws java.lang.IllegalStateException
+     *             If this context application instance has stopped.
+     */
+    public Map getServiceProperties(Object serviceObject);
+
+    
+    /**
+	 * Registers the specified service object with the specified properties
+	 * under the specified class names into the Framework. A
+	 * {@link org.osgi.framework.ServiceRegistration} object is returned. The
+	 * {@link org.osgi.framework.ServiceRegistration} object is for the private use of the
+	 * application registering the service and should not be shared with other
+	 * applications. The registering application is defined to be the context application.
+	 * Bundles can locate the service by using either the
+	 * {@link org.osgi.framework.BundleContext#getServiceReferences} or 
+	 * {@link org.osgi.framework.BundleContext#getServiceReference} method. Other applications
+	 * can locate this service by using {@link #locateService(String)} or {@link #locateServices(String)}
+	 * method, if they declared their dependece on the registered service.
+	 * 
+	 * <p>
+	 * An application can register a service object that implements the
+	 * {@link org.osgi.framework.ServiceFactory} interface to have more flexibility in providing
+	 * service objects to other applications or bundles.
+	 * 
+	 * <p>
+	 * The following steps are required to register a service:
+	 * <ol>
+	 * <li>If <code>service</code> is not a <code>ServiceFactory</code>,
+	 * an <code>IllegalArgumentException</code> is thrown if
+	 * <code>service</code> is not an <code>instanceof</code> all the
+	 * classes named.
+	 * <li>The Framework adds these service properties to the specified
+	 * <code>Dictionary</code> (which may be <code>null</code>): a property
+	 * named {@link org.osgi.framework.Constants#SERVICE_ID} identifying the registration number of
+	 * the service and a property named {@link org.osgi.framework.Constants#OBJECTCLASS} containing
+	 * all the specified classes. If any of these properties have already been
+	 * specified by the registering bundle, their values will be overwritten by
+	 * the Framework.
+	 * <li>The service is added to the Framework service registry and may now
+	 * be used by others.
+	 * <li>A service event of type {@link org.osgi.framework.ServiceEvent#REGISTERED} is
+	 * fired. This event triggers the corresponding {@link ApplicationServiceEvent} to be 
+	 * delivered to the applications that registered the appropriate listener.
+	 * <li>A <code>ServiceRegistration</code> object for this registration is
+	 * returned.
+	 * </ol>
+	 * 
+	 * @param clazzes The class names under which the service can be located.
+	 *        The class names in this array will be stored in the service's
+	 *        properties under the key {@link org.osgi.framework.Constants#OBJECTCLASS}.
+     *        This parameter must not be <code>null</code>.
+	 * @param service The service object or a <code>ServiceFactory</code>
+	 *        object.
+	 * @param properties The properties for this service. The keys in the
+	 *        properties object must all be <code>String</code> objects. See
+	 *        {@link org.osgi.framework.Constants} for a list of standard service property keys.
+	 *        Changes should not be made to this object after calling this
+	 *        method. To update the service's properties the
+	 *        {@link org.osgi.framework.ServiceRegistration#setProperties} method must be called.
+	 *        The set of properties may be <code>null</code> if the service
+	 *        has no properties.
+	 * 
+	 * @return A {@link org.osgi.framework.ServiceRegistration} object for use by the application
+	 *         registering the service to update the service's properties or to
+	 *         unregister the service.
+	 * 
+	 * @throws java.lang.IllegalArgumentException If one of the following is
+	 *         true:
+	 *         <ul>
+	 *         <li><code>service</code> is <code>null</code>.
+	 *         <li><code>service</code> is not a <code>ServiceFactory</code>
+	 *         object and is not an instance of all the named classes in
+	 *         <code>clazzes</code>.
+	 *         <li><code>properties</code> contains case variants of the same
+	 *         key name.
+	 *         </ul>
+	 * @throws NullPointerException if <code>clazzes</code> is <code>null</code>
+     * 
+	 * @throws java.lang.SecurityException If the caller does not have the
+	 *         <code>ServicePermission</code> to register the service for all
+	 *         the named classes and the Java Runtime Environment supports
+	 *         permissions.
+	 * 
+	 * @throws java.lang.IllegalStateException If this ApplicationContext is no
+	 *         longer valid.
+	 * 
+	 * @see org.osgi.framework.BundleContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+	 * @see org.osgi.framework.ServiceRegistration
+	 * @see org.osgi.framework.ServiceFactory
+	 */
+    public ServiceRegistration registerService(String[] clazzes,
+            Object service, Dictionary properties);
+
+    /**
+	 * Registers the specified service object with the specified properties
+	 * under the specified class name with the Framework.
+	 * 
+	 * <p>
+	 * This method is otherwise identical to
+	 * {@link #registerService(java.lang.String[], java.lang.Object,
+	 * java.util.Dictionary)} and is provided as a convenience when
+	 * <code>service</code> will only be registered under a single class name.
+	 * Note that even in this case the value of the service's
+	 * {@link Constants#OBJECTCLASS} property will be an array of strings,
+	 * rather than just a single string.
+	 * 
+	 * @param clazz The class name under which the service can be located. It
+     *        must not be <code>null</code>
+	 * @param service The service object or a <code>ServiceFactory</code>
+	 *        object.
+	 * @param properties The properties for this service. 
+	 * 
+	 * @return A <code>ServiceRegistration</code> object for use by the application
+	 *         registering the service to update the service's properties or to
+	 *         unregister the service.
+	 *         
+     * @throws java.lang.IllegalArgumentException If one of the following is
+     *         true:
+     *         <ul>
+     *         <li><code>service</code> is <code>null</code>.
+     *         <li><code>service</code> is not a <code>ServiceFactory</code>
+     *         object and is not an instance of the named class in
+     *         <code>clazz</code>.
+     *         <li><code>properties</code> contains case variants of the same
+     *         key name.
+     *         </ul>
+     * @throws NullPointerException if <code>clazz</code> is <code>null</code>
+     * 
+     * @throws java.lang.SecurityException If the caller does not have the
+     *         <code>ServicePermission</code> to register the service 
+     *         the named class and the Java Runtime Environment supports
+     *         permissions.
+     *
+	 * @throws java.lang.IllegalStateException If this ApplicationContext is no
+	 *         longer valid.
+	 * @see #registerService(java.lang.String[], java.lang.Object,
+	 *      java.util.Dictionary)
+	 */
+    public ServiceRegistration registerService(String clazz, Object service,
+            Dictionary properties);
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceEvent.java b/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceEvent.java
new file mode 100644
index 0000000..cfb4cec
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceEvent.java
@@ -0,0 +1,83 @@
+/*
+ * $Header: /cvshome/build/org.osgi.application/src/org/osgi/application/ApplicationServiceEvent.java,v 1.6 2006/07/11 13:19:02 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.application;
+
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An event from the Framework describing a service lifecycle change.
+ * <p>
+ * <code>ApplicationServiceEvent</code> objects are delivered to a
+ * <code>ApplicationServiceListener</code> objects when a change occurs in this service's
+ * lifecycle. The delivery of an <code>ApplicationServiceEvent</code> is 
+ * always triggered by a {@link org.osgi.framework.ServiceEvent}.
+ * <code>ApplicationServiceEvent</code> extends the content of <code>ServiceEvent</code>
+ * with the service object the event is referring to as applications has no means to
+ * find the corresponding service object for a {@link org.osgi.framework.ServiceReference}.
+ * A type code is used to identify the event type for future
+ * extendability. The available type codes are defined in {@link org.osgi.framework.ServiceEvent}.
+ * 
+ * <p>
+ * OSGi Alliance reserves the right to extend the set of types.
+ * 
+ * @see org.osgi.framework.ServiceEvent
+ * @see ApplicationServiceListener
+ * 
+ * @version $Revision: 1.6 $
+ */
+public class ApplicationServiceEvent extends ServiceEvent {
+
+	private static final long serialVersionUID = -4762149286971897323L;
+	final Object serviceObject;
+
+	/**
+	 * Creates a new application service event object.
+	 * 
+	 * @param type The event type. Available type codes are defines in 
+	 *        {@link org.osgi.framework.ServiceEvent}
+	 * @param reference A <code>ServiceReference</code> object to the service
+	 *        that had a lifecycle change. This reference will be used as the <code>source</code>
+	 *        in the {@link java.util.EventObject} baseclass, therefore, it must not be
+	 *        null.
+	 * @param serviceObject The service object bound to this application instance. It can
+	 *    be <code>null</code> if this application is not bound to this service yet.
+	 * @throws IllegalArgumentException if the specified <code>reference</code> is null. 
+	 */
+	public ApplicationServiceEvent(int type, ServiceReference reference, Object serviceObject) {
+		super(type, reference);
+		this.serviceObject = serviceObject;
+	}
+	
+	/**
+	 * This method returns the service object of this service bound to the listener
+	 * application instace. A service object becomes bound to the application when it
+	 * first obtains a service object reference to that service by calling the
+	 * <code>ApplicationContext.locateService</code> or <code>locateServices</code>
+	 * methods. If the application is not bound to the service yet, this method returns
+	 * <code>null</code>.
+	 * 
+	 * @return the service object bound to the listener application or <code>null</code>
+	 *   if it isn't bound to this service yet.
+	 */
+	public Object getServiceObject() {
+		return this.serviceObject;
+	}
+
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceListener.java b/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceListener.java
new file mode 100644
index 0000000..1a7d36f
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceListener.java
@@ -0,0 +1,68 @@
+/*
+ * $Header: /cvshome/build/org.osgi.application/src/org/osgi/application/ApplicationServiceListener.java,v 1.6 2006/07/12 21:21:34 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.application;
+
+import java.util.EventListener;
+
+import org.osgi.framework.*;
+
+/**
+ * An <code>ApplicationServiceEvent</code> listener. When a 
+ * <code>ServiceEvent</code> is
+ * fired, it is converted to an <code>ApplictionServiceEvent</code>
+ * and it is synchronously delivered to an <code>ApplicationServiceListener</code>.
+ * 
+ * <p>
+ * <code>ApplicationServiceListener</code> is a listener interface that may be
+ * implemented by an application developer.
+ * <p>
+ * An <code>ApplicationServiceListener</code> object is registered with the Framework
+ * using the <code>ApplicationContext.addServiceListener</code> method.
+ * <code>ApplicationServiceListener</code> objects are called with an
+ * <code>ApplicationServiceEvent</code> object when a service is registered, modified, or
+ * is in the process of unregistering.
+ * 
+ * <p>
+ * <code>ApplicationServiceEvent</code> object delivery to 
+ * <code>ApplicationServiceListener</code>
+ * objects is filtered by the filter specified when the listener was registered.
+ * If the Java Runtime Environment supports permissions, then additional
+ * filtering is done. <code>ApplicationServiceEvent</code> objects are only delivered to
+ * the listener if the application which defines the listener object's class has the
+ * appropriate <code>ServicePermission</code> to get the service using at
+ * least one of the named classes the service was registered under, and the application
+ * specified its dependece on the corresponding service in the application metadata.
+ * 
+ * <p>
+ * <code>ApplicationServiceEvent</code> object delivery to <code>ApplicationServiceListener</code>
+ * objects is further filtered according to package sources as defined in
+ * {@link ServiceReference#isAssignableTo(Bundle, String)}.
+ * 
+ * @version $Revision: 1.6 $
+ * @see ApplicationServiceEvent
+ * @see ServicePermission
+ */
+public interface ApplicationServiceListener extends EventListener {
+	/**
+	 * Receives notification that a service has had a lifecycle change.
+	 * 
+	 * @param event The <code>ApplicationServiceEvent</code> object.
+	 */
+	public void serviceChanged(ApplicationServiceEvent event);
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/application/Framework.java b/org.osgi.compendium/src/main/java/org/osgi/application/Framework.java
new file mode 100644
index 0000000..4696115
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/application/Framework.java
@@ -0,0 +1,59 @@
+/*
+ * $Header: /cvshome/build/org.osgi.application/src/org/osgi/application/Framework.java,v 1.9 2006/07/11 13:19:02 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.application;
+
+import java.util.Hashtable;
+
+/**
+ * Using this class, OSGi-aware applications can obtain their {@link ApplicationContext}. 
+ *
+ */
+public final class Framework {
+
+    private Framework() { }
+    
+    private static Hashtable appContextHash;
+    
+    /**
+     * This method needs an argument, an object that represents the application instance. 
+     * An application consists of a set of object, however there is a single object, which 
+     * is used by the corresponding application container to manage the lifecycle on the 
+     * application instance. The lifetime of this object equals the lifetime of 
+     * the application instance; therefore, it is suitable to represent the instance. 
+     * <P>
+     * The returned {@link ApplicationContext} object is singleton for the 
+     * specified application instance. Subsequent calls to this method with the same 
+     * application instance must return the same context object
+     * 
+     * @param applicationInstance is the activator object of an application instance
+     * @throws java.lang.NullPointerException If <code>applicationInstance</code>
+     *     is <code>null</code>      
+     * @throws java.lang.IllegalArgumentException if  called with an object that is not 
+     *     the activator object of an application.
+     * @return the {@link ApplicationContext} of the specified application instance.
+     */
+    public static ApplicationContext getApplicationContext(Object applicationInstance) {
+    	  if( applicationInstance == null )
+    		  throw new NullPointerException( "Instance cannot be null!" );
+    	  ApplicationContext appContext = (ApplicationContext)appContextHash.get( applicationInstance );
+    	  if( appContext == null )
+    		  throw new IllegalArgumentException( "ApplicationContext not found!" );
+        return appContext;        
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationAdminPermission.java b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationAdminPermission.java
new file mode 100644
index 0000000..5615148
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationAdminPermission.java
@@ -0,0 +1,407 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationAdminPermission.java,v 1.34 2006/07/12 21:22:11 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 org.osgi.service.application;
+
+import java.security.Permission;
+import java.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * This class implements permissions for manipulating applications and
+ * their instances.
+ * <P>
+ * ApplicationAdminPermission can be targeted to applications that matches the
+ * specified filter.
+ * <P>
+ * ApplicationAdminPermission may be granted for different actions:
+ * <code>lifecycle</code>, <code>schedule</code> and <code>lock</code>.
+ * The permission <code>schedule</code> implies the permission
+ * <code>lifecycle</code>.
+ */
+public class ApplicationAdminPermission extends Permission {
+	private static final long serialVersionUID = 1L;
+  
+	/**
+	 * Allows the lifecycle management of the target applications.
+	 */
+	public static final String LIFECYCLE_ACTION = "lifecycle";
+
+	/**
+	 * Allows scheduling of the target applications. The permission to
+	 * schedule an application implies that the scheduler can also 
+	 * manage the lifecycle of that application i.e. <code>schedule</code>
+	 * implies <code>lifecycle</code>
+	 */
+	public static final String SCHEDULE_ACTION = "schedule";
+
+	/**
+	 * Allows setting/unsetting the locking state of the target applications.
+	 */
+	public static final String LOCK_ACTION = "lock";
+
+	private ApplicationDescriptor	applicationDescriptor;
+
+	/**
+	 * Constructs an ApplicationAdminPermission. The <code>filter</code>
+	 * specifies the target application. The <code>filter</code> is an
+	 * LDAP-style filter, the recognized properties are <code>signer</code>
+	 * and <code>pid</code>. The pattern specified in the <code>signer</code>
+	 * is matched with the Distinguished Name chain used to sign the application. 
+	 * Wildcards in a DN are not matched according to the filter string rules, 
+	 * but according to the rules defined for a DN chain. The attribute 
+	 * <code>pid</code> is matched with the PID of the application according to
+	 * the filter string rules. 
+	 * <p>
+	 * If the <code>filter</code> is <code>null</code> then it matches 
+	 * <code>"*"</code>. If
+	 * <code>actions</code> is <code>"*"</code> then it identifies all the
+	 * possible actions.
+	 * 
+	 * @param filter
+	 *            filter to identify application. The value <code>null</code>
+	 *            is equivalent to <code>"*"</code> and it indicates "all application".
+	 * @param actions
+	 *            comma-separated list of the desired actions granted on the
+	 *            applications or "*" means all the actions. It must not be
+	 *            <code>null</code>. The order of the actions in the list is
+	 *            not significant.
+	 * @throws InvalidSyntaxException 
+	 *            is thrown if the specified <code>filter</code> is not syntactically
+	 *            correct.
+	 * 
+	 * @exception NullPointerException
+	 *                is thrown if the actions parameter is <code>null</code>
+	 * 
+	 * @see ApplicationDescriptor
+	 * @see org.osgi.framework.AdminPermission
+	 */
+	public ApplicationAdminPermission(String filter, String actions) throws InvalidSyntaxException {
+		super(filter == null ? "*" : filter);
+		
+		if( filter == null )
+			filter = "*";
+		
+		if( actions == null )
+			throw new NullPointerException( "Action string cannot be null!" );
+		
+		this.applicationDescriptor = null;
+		this.filter = (filter == null ? "*" : filter);
+		this.actions = actions;
+
+		if( !filter.equals( "*" ) && !filter.equals( "<<SELF>>" ) )
+			FrameworkUtil.createFilter( this.filter ); // check if the filter is valid
+		init();
+	}
+	
+	/**
+	 * This contructor should be used when creating <code>ApplicationAdminPermission</code>
+	 * instance for <code>checkPermission</code> call. 
+	 * @param application the tareget of the operation, it must not be <code>null</code>
+	 * @param actions the required operation. it must not be <code>null</code>
+	 * @throws NullPointerException if any of the arguments is null. 
+	 */
+	public ApplicationAdminPermission(ApplicationDescriptor application, String actions) {
+		super(application.getApplicationId());
+				
+		if( application == null || actions == null )
+			throw new NullPointerException( "ApplicationDescriptor and action string cannot be null!" );
+		
+		this.filter = application.getApplicationId();
+		this.applicationDescriptor = application;
+		this.actions = actions;
+		
+		init();
+	}
+	
+	/**
+	 * This method can be used in the {@link java.security.ProtectionDomain}
+	 * implementation in the <code>implies</code> method to insert the
+	 * application ID of the current application into the permission being
+	 * checked. This enables the evaluation of the 
+	 * <code>&lt;&lt;SELF&gt;&gt;</code> pseudo targets.
+	 * @param applicationId the ID of the current application.
+	 * @return the permission updated with the ID of the current application
+	 */
+	public ApplicationAdminPermission setCurrentApplicationId(String applicationId) {
+		ApplicationAdminPermission newPerm = null;
+		
+		if( this.applicationDescriptor == null ) {
+			try {
+				newPerm = new ApplicationAdminPermission( this.filter, this.actions );
+			}catch( InvalidSyntaxException e ) {
+				throw new RuntimeException( "Internal error" ); /* this can never happen */
+			}
+		}
+		else	
+		    newPerm = new ApplicationAdminPermission( this.applicationDescriptor, this.actions );
+		
+		newPerm.applicationID = applicationId;
+		
+		return newPerm;
+	}
+
+	/**
+	 * Checks if the specified <code>permission</code> is implied by this permission.
+	 * The method returns true under the following conditions:
+	 * <UL>
+	 * <LI> This permission was created by specifying a filter (see {@link #ApplicationAdminPermission(String, String)})
+	 * <LI> The implied <code>otherPermission</code> was created for a particular {@link ApplicationDescriptor}
+	 *      (see {@link #ApplicationAdminPermission(ApplicationDescriptor, String)})
+	 * <LI> The <code>filter</code> of this permission mathes the <code>ApplicationDescriptor</code> specified
+	 *      in the <code>otherPermission</code>. If the filter in this permission is the 
+	 *      <code>&lt;&lt;SELF&gt;&gt;</code> pseudo target, then the currentApplicationId set in the 
+	 *      <code>otherPermission</code> is compared to the application Id of the target 
+	 *      <code>ApplicationDescriptor</code>.
+	 * <LI> The list of permitted actions in this permission contains all actions required in the 
+	 *      <code>otherPermission</code>  
+	 * </UL> 
+	 * Otherwise the method returns false.
+	 * @param otherPermission the implied permission
+	 * @return true if this permission implies the <code>otherPermission</code>, false otherwise.
+	 */
+  public boolean implies(Permission otherPermission) {
+  	  if( otherPermission == null )
+  	  	return false;
+  	  	
+      if(!(otherPermission instanceof ApplicationAdminPermission))
+          return false;
+
+      ApplicationAdminPermission other = (ApplicationAdminPermission) otherPermission;
+
+      if( !filter.equals("*") ) {
+       	if( other.applicationDescriptor == null )
+       		return false;
+       	
+      	if( filter.equals( "<<SELF>>") ) {
+            if( other.applicationID == null )
+          		return false; /* it cannot be, this might be a bug */
+            
+      		if( !other.applicationID.equals( other.applicationDescriptor.getApplicationId() ) )
+      			return false;
+      	}
+      	else {
+      		Hashtable props = new Hashtable();
+      		props.put( "pid", other.applicationDescriptor.getApplicationId() );
+      		props.put( "signer", new SignerWrapper( other.applicationDescriptor ) );
+      		      		
+      		Filter flt = getFilter();
+      		if( flt == null )
+      			return false;
+      		
+      		if( !flt.match( props ) )
+      			return false;
+      	}
+      }
+      
+      if( !actionsVector.containsAll( other.actionsVector ) )
+      	return false;
+      
+      return true;
+  }
+
+  public boolean equals(Object with) {
+  	if( with == null || !(with instanceof ApplicationAdminPermission) )
+  		return false;
+  	
+  	ApplicationAdminPermission other = (ApplicationAdminPermission)with;  	
+  	
+  	// Compare actions:
+  	if( other.actionsVector.size() != actionsVector.size() )
+  		return false;
+  	
+  	for( int i=0; i != actionsVector.size(); i++ )
+  		if( !other.actionsVector.contains( actionsVector.get( i ) ) )
+  			return false;
+  	
+  	
+  	return equal(this.filter, other.filter ) && equal(this.applicationDescriptor, other.applicationDescriptor)
+  			&& equal(this.applicationID, other.applicationID);
+  }
+  
+  /**
+   * Compares parameters for equality. If both object are null, they are considered
+   * equal.
+   * @param a object to compare
+   * @param b other object to compare
+   * @return true if both objects are equal or both are null
+   */
+  private static boolean equal(Object a, Object b) {
+	  // This equation is true if both references are null or both point
+	  // to the same object. In both cases they are considered as equal.
+	  if( a == b ) {
+		  return true;
+	  }
+	  
+	  return a.equals(b);
+  }
+
+  public int hashCode() {
+	  int hc = 0;
+	  for( int i=0; i != actionsVector.size(); i++ )
+		  hc ^= ((String)actionsVector.get( i )).hashCode();
+	  hc ^= (null == this.filter )? 0 : this.filter.hashCode();
+	  hc ^= (null == this.applicationDescriptor) ? 0 : this.applicationDescriptor.hashCode();
+	  hc ^= (null == this.applicationID) ? 0 : this.applicationID.hashCode();
+	  return hc;
+  }
+
+  /**
+   * Returns the actions of this permission.
+   * @return the actions specified when this permission was created
+   */
+  public String getActions() {
+  	return actions;
+  }
+
+  private String applicationID;
+
+  private static final Vector ACTIONS = new Vector();
+  private              Vector actionsVector;
+  private final        String filter;
+  private final        String actions;
+  private              Filter appliedFilter = null; 
+  
+  static {
+      ACTIONS.add(LIFECYCLE_ACTION);
+      ACTIONS.add(SCHEDULE_ACTION);
+      ACTIONS.add(LOCK_ACTION);
+  }
+
+  private static Vector actionsVector(String actions) {
+      Vector v = new Vector();
+      StringTokenizer t = new StringTokenizer(actions.toUpperCase(), ",");
+      while (t.hasMoreTokens()) {
+          String action = t.nextToken().trim();
+          v.add(action.toLowerCase());
+      }
+      
+      if( v.contains( SCHEDULE_ACTION ) && !v.contains( LIFECYCLE_ACTION ) )
+    	  v.add( LIFECYCLE_ACTION );
+      
+      return v;
+  }
+  
+
+  private static class SignerWrapper extends Object {
+  	private String pattern;
+  	private ApplicationDescriptor appDesc;
+  	
+  	public SignerWrapper(String pattern) {
+  		this.pattern = pattern;    			
+  	}
+  	
+  	SignerWrapper(ApplicationDescriptor appDesc) {
+  		this.appDesc = appDesc;
+  	}
+  	
+  	public boolean equals(Object o) {
+  		if (!(o instanceof SignerWrapper))
+  			return false;
+  		SignerWrapper other = (SignerWrapper) o;
+  		ApplicationDescriptor matchAppDesc = (ApplicationDescriptor) (appDesc != null ? appDesc : other.appDesc);
+  		String matchPattern = appDesc != null ? other.pattern : pattern;
+  		return matchAppDesc.matchDNChain(matchPattern);
+  	}
+  }
+  
+  private void init() {
+		actionsVector = actionsVector( actions );
+
+		if ( actions.equals("*") )
+			actionsVector = actionsVector( LIFECYCLE_ACTION + "," + SCHEDULE_ACTION + "," + LOCK_ACTION );
+		else if (!ACTIONS.containsAll(actionsVector))
+      throw new IllegalArgumentException("Illegal action!");
+		
+		applicationID = null;
+  }
+  
+  private Filter getFilter() {
+  	String transformedFilter = filter;
+  	
+  	if (appliedFilter == null) {
+  		try {
+  			int pos = filter.indexOf("signer"); //$NON-NLS-1$
+  			if (pos != -1){ 
+  			
+  				//there may be a signer attribute 
+    			StringBuffer filterBuf = new StringBuffer(filter);
+    			int numAsteriskFound = 0; //use as offset to replace in buffer
+    			
+    			int walkbackPos; //temp pos
+
+    			//find occurences of (signer= and escape out *'s
+    			while (pos != -1) {
+
+    				//walk back and look for '(' to see if this is an attr
+    				walkbackPos = pos-1; 
+    				
+    				//consume whitespace
+    				while(walkbackPos >= 0 && Character.isWhitespace(filter.charAt(walkbackPos))) {
+    					walkbackPos--;
+    				}
+    				if (walkbackPos <0) {
+    					//filter is invalid - FilterImpl will throw error
+    					break;
+    				}
+    				
+    				//check to see if we have unescaped '('
+    				if (filter.charAt(walkbackPos) != '(' || (walkbackPos > 0 && filter.charAt(walkbackPos-1) == '\\')) {
+    					//'(' was escaped or not there
+    					pos = filter.indexOf("signer",pos+6); //$NON-NLS-1$
+    					continue;
+    				}     				
+    				pos+=6; //skip over 'signer'
+
+    				//found signer - consume whitespace before '='
+    				while (Character.isWhitespace(filter.charAt(pos))) {
+    					pos++;
+    				}
+
+    				//look for '='
+    				if (filter.charAt(pos) != '=') {
+    					//attr was signerx - keep looking
+    					pos = filter.indexOf("signer",pos); //$NON-NLS-1$
+    					continue;
+    				}
+    				pos++; //skip over '='
+    				
+    				//found signer value - escape '*'s
+    				while (!(filter.charAt(pos) == ')' && filter.charAt(pos-1) != '\\')) {
+    					if (filter.charAt(pos) == '*') {
+    						filterBuf.insert(pos+numAsteriskFound,'\\');
+    						numAsteriskFound++;
+    					}
+    					pos++;
+    				}
+
+    				//end of signer value - look for more?
+    				pos = filter.indexOf("signer",pos); //$NON-NLS-1$
+    			} //end while (pos != -1)
+    			transformedFilter = filterBuf.toString();
+  			} //end if (pos != -1)
+
+  			appliedFilter = FrameworkUtil.createFilter( transformedFilter );
+		} catch (InvalidSyntaxException e) {
+			//we will return null
+		}
+  	}     		
+  	return appliedFilter;
+  }
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationDescriptor.java b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationDescriptor.java
new file mode 100644
index 0000000..4764bd7
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationDescriptor.java
@@ -0,0 +1,714 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationDescriptor.java,v 1.61 2006/07/10 12:02:31 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 org.osgi.service.application;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.*;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * An OSGi service that represents an installed application and stores
+ * information about it. The application descriptor can be used for instance
+ * creation.
+ */
+
+public abstract class ApplicationDescriptor {
+	/*
+	 * NOTE: An implementor may also choose to replace this class in
+	 * their distribution with a class that directly interfaces with the
+	 * org.osgi.service.application implementation. This replacement class MUST NOT alter the
+	 * public/protected signature of this class.
+	 */
+
+	/**
+	 * The property key for the localized name of the application.
+	 */
+	public static final String APPLICATION_NAME = "application.name";
+
+	/**
+	 * The property key for the localized icon of the application.
+	 */
+	public static final String APPLICATION_ICON = "application.icon";
+
+	/**
+	 * The property key for the unique identifier (PID) of the application.
+	 */
+	public static final String APPLICATION_PID = Constants.SERVICE_PID;
+
+	/**
+	 * The property key for the version of the application.
+	 */
+	public static final String APPLICATION_VERSION = "application.version";
+
+	/**
+	 * The property key for the name of the application vendor.
+	 */
+	public static final String APPLICATION_VENDOR = Constants.SERVICE_VENDOR;
+
+
+	/**
+	 * The property key for the visibility property of the application.
+	 */
+	public static final String APPLICATION_VISIBLE = "application.visible";
+
+	/**
+	 * The property key for the launchable property of the application.
+	 */
+	public static final String APPLICATION_LAUNCHABLE = "application.launchable";
+
+	/**
+	 * The property key for the locked property of the application.
+	 */
+	public static final String APPLICATION_LOCKED = "application.locked";
+
+	/**
+	 * The property key for the localized description of the application.
+	 */
+	public static final String APPLICATION_DESCRIPTION = "application.description";
+
+	/**
+	 * The property key for the localized documentation of the application.
+	 */
+	public static final String APPLICATION_DOCUMENTATION = "application.documentation";
+
+	/**
+	 * The property key for the localized copyright notice of the application.
+	 */
+	public static final String APPLICATION_COPYRIGHT = "application.copyright";
+
+	/**
+	 * The property key for the localized license of the application.
+	 */
+	public static final String APPLICATION_LICENSE = "application.license";
+
+	/**
+	 * The property key for the application container of the application.
+	 */
+	public static final String APPLICATION_CONTAINER = "application.container";
+
+	/**
+	 * The property key for the location of the application.
+	 */
+	public static final String APPLICATION_LOCATION = "application.location";
+
+	
+	private final String	pid;
+
+
+	/**
+	 * Constructs the <code>ApplicationDescriptor</code>.
+	 *
+	 * @param applicationId
+	 *            The identifier of the application. Its value is also available
+	 *            as the <code>service.pid</code> service property of this 
+	 *            <code>ApplicationDescriptor</code> service. This parameter must not
+	 *            be <code>null</code>.
+	 * @throws NullPointerException if the specified <code>applicationId</code> is null.
+	 */
+	protected  ApplicationDescriptor(String applicationId) {
+		if(null == applicationId ) {
+			throw new NullPointerException("Application ID must not be null!");
+		}
+		
+		this.pid = applicationId;
+		try {
+			delegate = new Delegate();
+			delegate.setApplicationDescriptor( this, applicationId );
+		}
+		catch (Exception e) {
+			// Too bad ...
+			e.printStackTrace();
+			System.err
+					.println("No implementation available for ApplicationDescriptor, property is: "
+							+ Delegate.cName);
+		}
+	}
+
+	/**
+	 * Returns the identifier of the represented application.
+	 * 
+	 * @return the identifier of the represented application
+	 */
+	public final String getApplicationId() {
+		return pid;
+	}
+
+	/**
+	 * This method verifies whether the specified <code>pattern</code>
+	 * matches the Distinguished Names of any of the certificate chains
+	 * used to authenticate this application.
+	 * <P>
+	 * The <code>pattern</code> must adhere to the 
+	 * syntax defined in {@link org.osgi.service.application.ApplicationAdminPermission}
+	 * for signer attributes. 
+	 * <p>
+	 * This method is used by {@link ApplicationAdminPermission#implies(java.security.Permission)} method
+	 * to match target <code>ApplicationDescriptor</code> and filter. 
+	 * 
+	 * @param pattern a pattern for a chain of Distinguished Names. It must not be null.
+	 * @return <code>true</code> if the specified pattern matches at least
+	 *   one of the certificate chains used to authenticate this application 
+	 * @throws NullPointerException if the specified <code>pattern</code> is null.
+     * @throws IllegalStateException if the application descriptor was
+     *   unregistered
+	 */	
+	public abstract boolean matchDNChain( String pattern );
+	
+	/**
+	 * Returns the properties of the application descriptor as key-value pairs.
+	 * The return value contains the locale aware and unaware properties as
+	 * well. The returned <code>Map</code> will include the service
+	 * properties of this <code>ApplicationDescriptor</code> as well.
+	 * <p>
+	 * This method will call the <code>getPropertiesSpecific</code> method
+	 * to enable the container implementation to insert application model and/or
+	 * container implementation specific properties.
+	 * <P>
+	 * The returned {@link java.util.Map} will contain the standard OSGi service 
+	 * properties as well
+	 * (e.g. service.id, service.vendor etc.) and specialized application
+	 * descriptors may offer further service properties. The returned Map contains
+	 * a snapshot of the properties. It will not reflect further changes in the
+	 * property values nor will the update of the Map change the corresponding
+	 * service property.
+	 *   
+	 * @param locale
+	 *            the locale string, it may be null, the value null means the
+	 *            default locale. If the provided locale is the empty String 
+	 *            (<code>""</code>)then raw (non-localized) values are returned.
+	 * 
+	 * @return copy of the service properties of this application descriptor service,
+	 *         according to the specified locale. If locale is null then the
+	 *         default locale's properties will be returned. (Since service
+	 *         properties are always exist it cannot return null.)
+	 * 
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 */
+	public final Map getProperties(String locale) {
+		Map props = getPropertiesSpecific( locale );
+		
+		/* currently the ApplicationDescriptor manages the load/save of locking */
+		boolean isLocked = delegate.isLocked(); // the real locking state
+		Boolean containerLocked = (Boolean)props.remove( APPLICATION_LOCKED );
+		if( containerLocked != null && containerLocked.booleanValue() != isLocked ) {
+			try {
+			  if( isLocked )      /* if the container's information is not correct */
+				  lockSpecific();   /* about the locking state (after loading the lock states) */
+			  else
+				  unlockSpecific();
+			}catch( Exception e ) {}
+		}
+		/* replace the container's lock with the application model's lock, that's the correct */
+		props.put( APPLICATION_LOCKED, new Boolean( isLocked ) ); 		
+		return props;
+	}
+	
+	/**
+	 * Container implementations can provide application model specific
+	 * and/or container implementation specific properties via this 
+	 * method. 
+	 * 
+	 * Localizable properties must be returned localized if the provided
+	 * <code>locale</code> argument is not the empty String. The value
+	 * <code>null</code> indicates to use the default locale, for other
+	 * values the specified locale should be used.
+	 *  
+	 * The returned {@link java.util.Map} must contain the standard OSGi service 
+	 * properties as well
+	 * (e.g. service.id, service.vendor etc.) and specialized application
+	 * descriptors may offer further service properties. 
+	 * The returned <code>Map</code>
+	 * contains a snapshot of the properties. It will not reflect further changes in the
+	 * property values nor will the update of the Map change the corresponding
+	 * service property.
+
+	 * @param locale the locale to be used for localizing the properties.
+	 * If <code>null</code> the default locale should be used. If it is
+	 * the empty String (<code>""</code>) then raw (non-localized) values
+	 * should be returned.
+	 * 
+	 * @return the application model specific and/or container implementation
+	 * specific properties of this application descriptor.
+	 *  
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 */
+	protected abstract Map getPropertiesSpecific(String locale);
+
+	/**
+	 * Launches a new instance of an application. The <code>args</code> parameter specifies
+	 * the startup parameters for the instance to be launched, it may be null.
+	 * <p>
+	 * The following steps are made:
+	 * <UL>
+	 * <LI>Check for the appropriate permission.
+	 * <LI>Check the locking state of the application. If locked then return
+	 * null otherwise continue.
+	 * <LI>Calls the <code>launchSpecific()</code> method to create and start an application
+	 * instance.
+	 * <LI>Returns the <code>ApplicationHandle</code> returned by the 
+	 * launchSpecific()
+	 * </UL>
+	 * The caller has to have ApplicationAdminPermission(applicationPID,
+	 * "launch") in order to be able to perform this operation.
+	 * <P>
+	 * The <code>Map</code> argument of the launch method contains startup 
+	 * arguments for the
+	 * application. The keys used in the Map must be non-null, non-empty <code>String<code>
+	 * objects. They can be standard or application
+	 * specific. OSGi defines the <code>org.osgi.triggeringevent</code>
+	 * key to be used to
+	 * pass the triggering event to a scheduled application, however
+	 * in the future it is possible that other well-known keys will be defined.
+	 * To avoid unwanted clashes of keys, the following rules should be applied:
+	 * <ul>
+	 *   <li>The keys starting with the dash (-) character are application
+	 *       specific, no well-known meaning should be associated with them.</li>
+	 *   <li>Well-known keys should follow the reverse domain name based naming.
+	 *       In particular, the keys standardized in OSGi should start with
+	 *       <code>org.osgi.</code>.</li>
+	 * </ul>
+	 * <P>
+	 * The method is synchonous, it return only when the application instance was
+	 * successfully started or the attempt to start it failed.
+	 * <P>
+	 * This method never returns <code>null</code>. If launching an application fails,
+	 * the appropriate exception is thrown.
+	 * 
+	 * @param arguments
+	 *            Arguments for the newly launched application, may be null
+	 * 
+	 * @return the registered ApplicationHandle, which represents the newly 
+	 *         launched application instance. Never returns <code>null</code>.
+	 * 
+	 * @throws SecurityException
+	 *             if the caller doesn't have "lifecycle"
+	 *             ApplicationAdminPermission for the application.
+	 * @throws ApplicationException
+	 *             if starting the application failed
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 * @throws IllegalArgumentException 
+	 *             if the specified <code>Map</code> contains invalid keys
+	 *             (null objects, empty <code>String</code> or a key that is not
+	 *              <code>String</code>)
+	 */
+	public final ApplicationHandle launch(Map arguments)
+			throws ApplicationException {
+		try {
+			delegate.launch(arguments);
+		}catch( SecurityException se ) {
+			isLaunchableSpecific(); /* check whether the bundle was uninstalled */
+			                        /* if yes, throws IllegalStateException */
+			throw se;               /* otherwise throw the catched SecurityException */
+		}
+		if( !isLaunchableSpecific() )
+			throw new ApplicationException(ApplicationException.APPLICATION_NOT_LAUNCHABLE,
+					 "Cannot launch the application!");
+		try {
+			return launchSpecific(arguments);
+		} catch(IllegalStateException ise) {
+			throw ise;
+		} catch(SecurityException se) {
+			throw se;
+		} catch( ApplicationException ae) {
+			throw ae;
+		} catch(Exception t) {
+			throw new ApplicationException(ApplicationException.APPLICATION_INTERNAL_ERROR, t);
+		}
+	}
+
+	/**
+	 * Called by launch() to create and start a new instance in an application
+	 * model specific way. It also creates and registeres the application handle
+	 * to represent the newly created and started instance and registeres it.
+	 * The method is synchonous, it return only when the application instance was
+	 * successfully started or the attempt to start it failed.
+	 * <P>
+	 * This method must not return <code>null</code>. If launching the application
+	 * failed, and exception must be thrown.
+	 * 
+	 * @param arguments
+	 *            the startup parameters of the new application instance, may be
+	 *            null
+	 * 
+	 * @return the registered application model
+	 *         specific application handle for the newly created and started
+	 *         instance.
+	 * 
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 * @throws Exception
+	 *             if any problem occures.
+	 */
+	protected abstract ApplicationHandle launchSpecific(Map arguments)
+			throws Exception;
+	
+	/**
+	 * This method is called by launch() to verify that according to the
+	 * container, the application is launchable.
+	 * 
+	 * @return true, if the application is launchable according to the 
+	 *  container, false otherwise.
+	 *  
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 */
+	protected abstract boolean isLaunchableSpecific();
+
+	/**
+	 * Schedules the application at a specified event. Schedule information
+	 * should not get lost even if the framework or the device restarts so it
+	 * should be stored in a persistent storage. The method registers a
+	 * {@link ScheduledApplication} service in Service Registry, representing
+	 * the created schedule.
+	 * <p>
+	 * The <code>Map</code> argument of the  method contains startup 
+	 * arguments for the application. The keys used in the Map must be non-null, 
+	 * non-empty <code>String<code> objects.
+     * <p>
+     * The created schedules have a unique identifier within the scope of this
+     * <code>ApplicationDescriptor</code>. This identifier can be specified
+     * in the <code>scheduleId</code> argument. If this argument is <code>null</code>,
+     * the identifier is automatically generated.
+     * 
+	 * @param scheduleId 
+	 *             the identifier of the created schedule. It can be <code>null</code>,
+     *             in this case the identifier is automatically generated.
+	 * @param arguments
+	 *            the startup arguments for the scheduled application, may be
+	 *            null
+	 * @param topic
+	 *            specifies the topic of the triggering event, it may contain a
+	 *            trailing asterisk as wildcard, the empty string is treated as
+	 *            "*", must not be null
+	 * @param eventFilter
+	 *            specifies and LDAP filter to filter on the properties of the
+	 *            triggering event, may be null
+	 * @param recurring
+	 *            if the recurring parameter is false then the application will
+	 *            be launched only once, when the event firstly occurs. If the
+	 *            parameter is true then scheduling will take place for every
+	 *            event occurrence; i.e. it is a recurring schedule
+	 * 
+	 * @return the registered scheduled application service
+	 * 
+	 * @throws NullPointerException
+	 *             if the topic is <code>null</code>
+	 * @throws InvalidSyntaxException 
+	 * 			   if the specified <code>eventFilter</code> is not syntactically correct
+	 * @throws ApplicationException
+     *              if the schedule couldn't be created. The possible error
+     *              codes are 
+     *              <ul>
+     *               <li> {@link ApplicationException#APPLICATION_DUPLICATE_SCHEDULE_ID}
+     *                 if the specified <code>scheduleId</code> is already used
+     *                 for this <code>ApplicationDescriptor</code>
+     *               <li> {@link ApplicationException#APPLICATION_SCHEDULING_FAILED}
+     *                 if the scheduling failed due to some internal reason
+     *                 (e.g. persistent storage error).
+     *              </ul>
+	 * @throws SecurityException
+	 *             if the caller doesn't have "schedule"
+	 *             ApplicationAdminPermission for the application.
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 * @throws IllegalArgumentException
+	 *             if the specified <code>Map</code> contains invalid keys
+	 *             (null objects, empty <code>String</code> or a key that is not
+	 *              <code>String</code>)
+	 */
+	public final ScheduledApplication schedule(String scheduleId, Map arguments, String topic,
+			String eventFilter, boolean recurring) throws InvalidSyntaxException, 
+            ApplicationException {
+		isLaunchableSpecific(); // checks if the ApplicationDescriptor was already unregistered
+		try {
+			return delegate.schedule(scheduleId, arguments, topic, eventFilter, recurring);
+		}catch( SecurityException se ) {
+			isLaunchableSpecific(); /* check whether the bundle was uninstalled */
+			                        /* if yes, throws IllegalStateException */
+			throw se;               /* otherwise throw the catched SecurityException */
+		}
+	}
+
+	/**
+	 * Sets the lock state of the application. If an application is locked then
+	 * launching a new instance is not possible. It does not affect the already
+	 * launched instances.
+	 * 
+	 * @throws SecurityException
+	 *             if the caller doesn't have "lock" ApplicationAdminPermission
+	 *             for the application.
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 */
+	public final void lock() {
+		try {
+			delegate.lock();
+		}catch( SecurityException se ) {
+			isLaunchableSpecific(); /* check whether the bundle was uninstalled */
+			                        /* if yes, throws IllegalStateException */
+			throw se;               /* otherwise throw the catched SecurityException */
+		}
+		lockSpecific();
+	}
+	
+	/**
+	 * This method is used to notify the container implementation that the
+	 * corresponding application has been locked and it should update the
+	 * <code>application.locked</code> service property accordingly.
+     * @throws IllegalStateException
+     *             if the application descriptor is unregistered
+	 */
+	protected abstract void lockSpecific();
+
+	/**
+	 * Unsets the lock state of the application.
+	 * 
+	 * @throws SecurityException
+	 *             if the caller doesn't have "lock" ApplicationAdminPermission
+	 *             for the application.
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 */
+	public final void unlock() {
+		try {
+			delegate.unlock();
+		}catch( SecurityException se ) {
+			isLaunchableSpecific(); /* check whether the bundle was uninstalled */
+			                        /* if yes, throws IllegalStateException */
+			throw se;               /* otherwise throw the catched SecurityException */
+		}
+		unlockSpecific();
+	}
+	
+	/**
+	 * This method is used to notify the container implementation that the
+	 * corresponding application has been unlocked and it should update the
+	 * <code>application.locked</code> service property accordingly.
+
+	 * @throws IllegalStateException
+	 *             if the application descriptor is unregistered
+	 */
+	protected abstract void unlockSpecific();
+
+	Delegate	delegate;
+	/**
+	 * This class will load the class named
+	 * by the org.osgi.vendor.application.ApplicationDescriptor and delegate
+	 * method calls to an instance of the class.
+	 */
+	static class Delegate {
+		static String cName;
+		static Class implementation;
+		static Method setApplicationDescriptor;
+		static Method isLocked;
+		static Method lock;
+		static Method unlock;
+		static Method schedule;
+		static Method launch;
+		
+		static {
+			AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {			
+					cName = System.getProperty("org.osgi.vendor.application.ApplicationDescriptor");
+					if (cName == null) {
+						throw new NoClassDefFoundError("org.osgi.vendor.application.ApplicationDescriptor property must be set"); 
+					}
+					
+					try {
+						implementation = Class.forName(cName);
+					}
+					catch (ClassNotFoundException e) {
+						throw new NoClassDefFoundError(e.toString());
+					}
+					
+					try {
+						setApplicationDescriptor = implementation.getMethod("setApplicationDescriptor",
+								new Class[] {ApplicationDescriptor.class, String.class});
+						isLocked = implementation.getMethod("isLocked",
+								new Class[] {});
+						lock = implementation.getMethod("lock",
+								new Class[] {});
+						unlock = implementation.getMethod("unlock",
+								new Class[] {});
+						schedule = implementation.getMethod("schedule",
+								new Class[] {String.class, Map.class, String.class, String.class,
+								boolean.class});
+						launch = implementation.getMethod("launch",
+								new Class[] {Map.class});
+					}
+					catch (NoSuchMethodException e) {
+						throw new NoSuchMethodError(e.toString());
+					}
+					
+					return null;
+				}
+			});
+		}
+
+		Object target; 
+		
+		Delegate() throws Exception {
+			target = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+				public Object run() throws Exception {			
+					return implementation.newInstance();
+				}
+			});
+		}
+
+		void setApplicationDescriptor(ApplicationDescriptor d, String pid ) {
+			try {
+				try {
+					setApplicationDescriptor.invoke(target, new Object[] {d, pid});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+
+		boolean isLocked() {
+			try {
+				try {
+					return ((Boolean)isLocked.invoke(target, new Object[] {})).booleanValue();
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+
+		void lock() {
+			try {
+				try {
+					lock.invoke(target, new Object[] {});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+
+		void unlock() {
+			try {
+				try {
+					unlock.invoke(target, new Object[] {});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+
+		ScheduledApplication schedule(String scheduleId, Map args, String topic, String filter,
+				boolean recurs) throws InvalidSyntaxException, ApplicationException {
+			try {
+				try {
+					return (ScheduledApplication)schedule.invoke(target, new Object[] {scheduleId, args, topic, filter,
+							new Boolean(recurs)});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (InvalidSyntaxException e) {
+				throw e;
+			}
+			catch (ApplicationException e) {
+				throw e;
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+
+		void launch(Map arguments) throws ApplicationException {
+			try {
+				try {
+					launch.invoke(target, new Object[] {arguments});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (ApplicationException e) {
+				throw e;
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+	}
+
+
+}
\ No newline at end of file
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationException.java b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationException.java
new file mode 100644
index 0000000..c30e112
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationException.java
@@ -0,0 +1,136 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationException.java,v 1.10 2006/07/10 11:49:12 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.application;
+
+/**
+ * This exception is used to indicate problems related to application 
+ * lifecycle management.
+ * 
+ * <code>ApplicationException</code> object is created by the Application Admin to denote
+ * an exception condition in the lifecycle of an application.
+ * <code>ApplicationException</code>s should not be created by developers.
+ * <br/>
+ * <code>ApplicationException</code>s are associated with an error code. This code
+ * describes the type of problem reported in this exception. The possible codes are:
+ * <ul>
+ * <li> {@link #APPLICATION_LOCKED} - The application couldn't be launched because it is locked.</li>
+ * <li> {@link #APPLICATION_NOT_LAUNCHABLE} - The application is not in launchable state.</li>
+ * <li> {@link #APPLICATION_INTERNAL_ERROR} - An exception was thrown by the application or its
+ *       container during launch.</li>
+ * <li> {@link #APPLICATION_SCHEDULING_FAILED} - The scheduling of an application
+ *       failed.
+ * </ul>
+ * 
+ */
+public class ApplicationException extends Exception {
+	private static final long serialVersionUID = -7173190453622508207L;
+	private final Throwable cause;
+	private final int errorCode;
+	
+	/**
+	 * The application couldn't be launched because it is locked.
+	 */
+	public static final int APPLICATION_LOCKED	= 0x01;
+	
+	/**
+	 * The application is not in launchable state, it's 
+	 * {@link ApplicationDescriptor#APPLICATION_LAUNCHABLE}
+	 * attribute is false.
+	 */
+	public static final int APPLICATION_NOT_LAUNCHABLE = 0x02;
+	
+	/**
+	 * An exception was thrown by the application or the corresponding
+	 * container during launch. The exception is available in {@link #getCause()}.
+	 */
+	public static final int APPLICATION_INTERNAL_ERROR = 0x03;
+    
+    /**
+     * The application schedule could not be created due to some internal error
+     * (for example, the schedule information couldn't be saved).
+     */
+    public static final int APPLICATION_SCHEDULING_FAILED = 0x04;
+    
+    /**
+     * The application scheduling failed because the specified identifier
+     * is already in use.
+     */
+    public static final int APPLICATION_DUPLICATE_SCHEDULE_ID = 0x05;
+
+	/**
+	 * Creates an <code>ApplicationException</code> with the specified error code.
+	 * @param errorCode The code of the error 
+	 */
+	public ApplicationException(int errorCode) {
+		this(errorCode,(Throwable) null);
+	}
+	
+	/**
+	 * Creates a <code>ApplicationException</code> that wraps another exception.
+	 * 
+	 * @param errorCode The code of the error 
+	 * @param cause The cause of this exception.
+	 */
+	public ApplicationException(int errorCode, Throwable cause) {
+		super();
+		this.cause = cause;
+		this.errorCode = errorCode;
+	}
+
+	/**
+	 * Creates an <code>ApplicationException</code> with the specified error code.
+	 * @param errorCode The code of the error 
+	 * @param message The associated message
+	 */
+	public ApplicationException(int errorCode, String message) {
+		this(errorCode, message,null);
+	}
+
+	/**
+	 * Creates a <code>ApplicationException</code> that wraps another exception.
+	 * 
+	 * @param errorCode The code of the error 
+	 * @param message The associated message.
+	 * @param cause The cause of this exception.
+	 */
+	public ApplicationException(int errorCode, String message, Throwable cause) {
+		super(message);
+		this.cause = cause;
+		this.errorCode = errorCode;
+	}
+
+	/**
+	 * Returns the cause of this exception or <code>null</code> if no cause
+	 * was specified when this exception was created.
+	 * 
+	 * @return The cause of this exception or <code>null</code> if no cause
+	 *         was specified.
+	 */
+	public Throwable getCause() {
+		return cause;
+	}
+
+	/**
+	 * Returns the error code associcated with this exception.
+	 * @return The error code of this exception.
+	 */
+	public int getErrorCode() {
+		return errorCode;
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationHandle.java b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationHandle.java
new file mode 100644
index 0000000..a0bdc89
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationHandle.java
@@ -0,0 +1,291 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationHandle.java,v 1.41 2006/07/10 12:02:31 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 org.osgi.service.application;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.*;
+
+import org.osgi.framework.Constants;
+
+/**
+ * ApplicationHandle is an OSGi service interface which represents an instance
+ * of an application. It provides the functionality to query and manipulate the
+ * lifecycle state of the represented application instance. It defines constants
+ * for the lifecycle states.
+ */
+public abstract class ApplicationHandle {
+	/*
+	 * NOTE: An implementor may also choose to replace this class in
+	 * their distribution with a class that directly interfaces with the
+	 * org.osgi.service.application implementation. This replacement class MUST NOT alter the
+	 * public/protected signature of this class.
+	 */
+
+	/**
+	 * The property key for the unique identifier (PID) of the application
+	 * instance.
+	 */
+	public static final String APPLICATION_PID = Constants.SERVICE_PID;
+	
+	/**
+	 * The property key for the pid of the corresponding application descriptor.
+	 */
+	public final static String APPLICATION_DESCRIPTOR	= "application.descriptor";
+	
+	/**
+	 * The property key for the state of this appliction instance.
+	 */
+	public final static String APPLICATION_STATE		= "application.state";
+
+	/**
+	 * The application instance is running. This is the initial state of a newly
+	 * created application instance.
+	 */
+	public final static String RUNNING = "RUNNING";
+	
+  /**
+   * The application instance is being stopped. This is the state of the
+   * application instance during the execution of the <code>destroy()</code>
+   * method.
+   */
+	public final static String STOPPING = "STOPPING";
+
+	private final String instanceId;
+	
+	private final ApplicationDescriptor	descriptor;
+
+	/**
+	 * Application instance identifier is specified by the container when the
+	 * instance is created. The instance identifier must remain static for the 
+	 * lifetime of the instance, it must remain the same even across framework
+	 * restarts for the same application instance. This value must be the same
+	 * as the <code>service.pid</code> service property of this application
+	 * handle.
+	 * <p>
+	 * The instance identifier should follow the following scheme: 
+	 * &lt;<i>application descriptor PID</i>&gt;.&lt;<i>index</i>&gt;
+	 * where &lt;<i>application descriptor PID</i>&gt; is the PID of the 
+	 * corresponding <code>ApplicationDescriptor</code> and &lt;<i>index</i>&gt;
+	 * is a unique integer index assigned by the application container. 
+	 * Even after destroying the application index the same index value should not
+	 * be reused in a reasonably long timeframe.
+	 * 
+	 * @param instanceId the instance identifier of the represented application
+	 * instance. It must not be null.
+	 * 
+	 * @param descriptor the <code>ApplicationDescriptor</code> of the represented
+	 * application instance. It must not be null.
+	 * 
+	 * @throws NullPointerException if any of the arguments is null.
+	 */
+	protected ApplicationHandle(String instanceId, ApplicationDescriptor descriptor ) {
+		if( (null == instanceId) || (null == descriptor) ) {
+			throw new NullPointerException("Parameters must not be null!");
+		}
+		
+		this.instanceId	= instanceId;
+		this.descriptor = descriptor;
+
+		try {
+			delegate = new Delegate();
+			delegate.setApplicationHandle( this, descriptor.delegate );
+		}
+		catch (Exception e) {
+			// Too bad ...
+			e.printStackTrace();
+			System.err
+					.println("No implementation available for ApplicationDescriptor, property is: "
+							+ Delegate.cName);
+		}
+	}
+
+	/**
+	 * Retrieves the <code>ApplicationDescriptor</code> to which this 
+	 * <code>ApplicationHandle</code> belongs. 
+	 * 
+	 * @return The corresponding <code>ApplicationDescriptor</code>
+	 */
+	public final ApplicationDescriptor getApplicationDescriptor() {
+		return descriptor;
+	}
+
+	/**
+	 * Get the state of the application instance.
+	 * 
+	 * @return the state of the application.
+	 * 
+	 * @throws IllegalStateException
+	 *             if the application handle is unregistered
+	 */
+	public abstract String getState();
+
+	/**
+	 * Returns the unique identifier of this instance. This value is also
+	 * available as a service property of this application handle's service.pid.
+	 * 
+	 * @return the unique identifier of the instance
+	 */
+	public final String getInstanceId() {
+		return instanceId;
+	}
+
+	/**
+	 * The application instance's lifecycle state can be influenced by this
+	 * method. It lets the application instance perform operations to stop
+	 * the application safely, e.g. saving its state to a permanent storage.
+	 * <p>
+	 * The method must check if the lifecycle transition is valid; a STOPPING
+	 * application cannot be stopped. If it is invalid then the method must
+	 * exit. Otherwise the lifecycle state of the application instance must be
+	 * set to STOPPING. Then the destroySpecific() method must be called to
+	 * perform any application model specific steps for safe stopping of the
+	 * represented application instance.
+	 * <p>
+	 * At the end the <code>ApplicationHandle</code> must be unregistered. 
+	 * This method should  free all the resources related to this 
+	 * <code>ApplicationHandle</code>.
+	 * <p>
+	 * When this method is completed the application instance has already made
+	 * its operations for safe stopping, the ApplicationHandle has been
+	 * unregistered and its related resources has been freed. Further calls on
+	 * this application should not be made because they may have unexpected
+	 * results.
+	 * 
+	 * @throws SecurityException
+	 *             if the caller doesn't have "lifecycle"
+	 *             <code>ApplicationAdminPermission</code> for the corresponding application.
+	 * 
+	 * @throws IllegalStateException
+	 *             if the application handle is unregistered
+	 */
+	public final void destroy() {
+		try {
+			delegate.destroy();
+		}catch( SecurityException se ) {
+			descriptor.isLaunchableSpecific(); /* check whether the bundle was uninstalled */
+			                                   /* if yes, throws IllegalStateException */
+			throw se;                          /* otherwise throw the catched SecurityException */
+		}
+		destroySpecific();
+	}
+
+	/**
+	 * Called by the destroy() method to perform application model specific
+	 * steps to stop and destroy an application instance safely.
+	 * 
+	 * @throws IllegalStateException
+	 *             if the application handle is unregistered
+	 */
+	protected abstract void destroySpecific();
+	
+	Delegate	delegate;
+	
+
+	/**
+	 * This class will load the class named
+	 * by the org.osgi.vendor.application.ApplicationHandle and delegate
+	 * method calls to an instance of the class.
+	 */
+	static class Delegate {
+		static String cName;
+		static Class implementation;
+		static Method setApplicationHandle;
+		static Method destroy;
+
+		static {
+			AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run(){			
+					cName = System.getProperty("org.osgi.vendor.application.ApplicationHandle");
+					if (cName == null) {
+						throw new NoClassDefFoundError("org.osgi.vendor.application.ApplicationHandle property must be set"); 
+					}
+					
+					try {
+						implementation = Class.forName(cName);
+					}
+					catch (ClassNotFoundException e) {
+						throw new NoClassDefFoundError(e.toString());
+					}
+					
+					try {
+						setApplicationHandle = implementation.getMethod("setApplicationHandle",
+								new Class[] {ApplicationHandle.class, Object.class});
+						destroy = implementation.getMethod("destroy",
+								new Class[] {});
+					}
+					catch (NoSuchMethodException e) {
+						throw new NoSuchMethodError(e.toString());
+					}
+					
+					return null;
+				}
+			});
+		}
+		
+		Object target; 
+		
+		Delegate() throws Exception {
+			target = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+				public Object run() throws Exception {			
+					return implementation.newInstance();
+				}
+			});
+		}
+
+		void setApplicationHandle(ApplicationHandle d, ApplicationDescriptor.Delegate descriptor ) {
+			try {
+				try {
+					setApplicationHandle.invoke(target, new Object[] {d, descriptor.target});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+		void destroy() {
+			try {
+				try {
+					destroy.invoke(target, new Object[] {});
+				}
+				catch (InvocationTargetException e) {
+					throw e.getTargetException();
+				}
+			}
+			catch (Error e) {
+				throw e;
+			}
+			catch (RuntimeException e) {
+				throw e;
+			}
+			catch (Throwable e) {
+				throw new RuntimeException(e.toString());
+			}
+		}
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/application/ScheduledApplication.java b/org.osgi.compendium/src/main/java/org/osgi/service/application/ScheduledApplication.java
new file mode 100644
index 0000000..46cfe5e
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/application/ScheduledApplication.java
@@ -0,0 +1,176 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ScheduledApplication.java,v 1.20 2006/07/06 14:59:29 sboshev 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 org.osgi.service.application;
+
+import java.util.Map;
+
+/**
+ * It is allowed to schedule an application based on a specific event.
+ * ScheduledApplication service keeps the schedule information. When the
+ * specified event is fired a new instance must be launched. Note that launching
+ * operation may fail because e.g. the application is locked.
+ * <p>
+ * Each <code>ScheduledApplication</code> instance has an identifier which is
+ * unique within the scope of the application being scheduled.
+ * <p>
+ * <code>ScheduledApplication</code> instances are registered as services.
+ * The {@link #APPLICATION_PID} service property contains the PID of the
+ * application being scheduled, the {@link #SCHEDULE_ID} service property
+ * contains the schedule identifier.
+ */
+public interface ScheduledApplication {
+    
+    /**
+     * The property key for the identifier of the application being scheduled.
+     */
+    public static final String APPLICATION_PID = ApplicationDescriptor.APPLICATION_PID;
+    
+    /**
+     * The property key for the schedule identifier. The identifier is unique
+     * within the scope of the application being scheduled.
+     */
+    public static final String SCHEDULE_ID = "schedule.id";
+    
+    /**
+     * The key for the startup argument used to pass the event object that 
+     * triggered the schedule to launch the application instance.
+     * The event is passed in a {@link java.security.GuardedObject}
+     * protected by the corresponding 
+     * {@link org.osgi.service.event.TopicPermission}.
+     */
+    public static final String TRIGGERING_EVENT = "org.osgi.triggeringevent";
+    
+    /**
+     * The topic name for the virtual timer topic. Time based schedules
+     * should be created using this topic.
+     */
+    public static final String TIMER_TOPIC = "org/osgi/application/timer";
+    
+    /**
+     * The name of the <i>year</i> attribute of a virtual timer event. The value is
+     * defined by {@link java.util.Calendar#YEAR}.
+     */
+    public static final String YEAR = "year";
+    
+    /**
+     * The name of the <i>month</i> attribute of a virtual timer event. The value is
+     * defined by {@link java.util.Calendar#MONTH}.
+     */
+    public static final String MONTH = "month";
+    
+    /**
+     * The name of the <i>day of month</i> attribute of a virtual timer event. The value is
+     * defined by {@link java.util.Calendar#DAY_OF_MONTH}.
+     */
+    public static final String DAY_OF_MONTH = "day_of_month";
+    
+    /**
+     * The name of the <i>day of week</i> attribute of a virtual timer event. The value is
+     * defined by {@link java.util.Calendar#DAY_OF_WEEK}.
+     */
+    public static final String DAY_OF_WEEK = "day_of_week";
+    
+    /**
+     * The name of the <i>hour of day</i> attribute of a virtual timer event. The value is
+     * defined by {@link java.util.Calendar#HOUR_OF_DAY}.
+     */
+    public static final String HOUR_OF_DAY = "hour_of_day";
+    
+    /**
+     * The name of the <i>minute</i> attribute of a virtual timer event. The value is
+     * defined by {@link java.util.Calendar#MINUTE}.
+     */
+    public static final String MINUTE = "minute";
+    
+    
+    /**
+     * Returns the identifier of this schedule. The identifier is unique within
+     * the scope of the application that the schedule is related to. 
+     * @return the identifier of this schedule
+     * 
+     */
+    public String getScheduleId();
+
+	/**
+	 * Queries the topic of the triggering event. The topic may contain a
+	 * trailing asterisk as wildcard.
+	 * 
+	 * @return the topic of the triggering event
+	 * 
+	 * @throws IllegalStateException
+	 *             if the scheduled application service is unregistered
+	 */
+	public String getTopic();
+
+	/**
+	 * Queries the event filter for the triggering event.
+	 * 
+	 * @return the event filter for triggering event
+	 * 
+	 * @throws IllegalStateException
+	 *             if the scheduled application service is unregistered
+	 */
+	public String getEventFilter();
+
+	/**
+	 * Queries if the schedule is recurring.
+	 * 
+	 * @return true if the schedule is recurring, otherwise returns false
+	 * 
+	 * @throws IllegalStateException
+	 *             if the scheduled application service is unregistered
+	 */
+	public boolean isRecurring();
+
+	/**
+	 * Retrieves the ApplicationDescriptor which represents the application and
+	 * necessary for launching.
+	 * 
+	 * @return the application descriptor that
+	 *         represents the scheduled application
+	 * 
+	 * @throws IllegalStateException
+	 *             if the scheduled application service is unregistered
+	 */
+	public ApplicationDescriptor getApplicationDescriptor();
+
+	/**
+	 * Queries the startup arguments specified when the application was
+	 * scheduled. The method returns a copy of the arguments, it is not possible
+	 * to modify the arguments after scheduling.
+	 * 
+	 * @return the startup arguments of the scheduled application. It may be
+	 *         null if null argument was specified.
+	 * 
+	 * @throws IllegalStateException
+	 *             if the scheduled application service is unregistered
+	 */
+	public Map getArguments();
+
+	/**
+	 * Cancels this schedule of the application.
+	 * 
+	 * @throws SecurityException
+	 *             if the caller doesn't have "schedule"
+	 *             ApplicationAdminPermission for the scheduled application.
+	 * @throws IllegalStateException
+	 *             if the scheduled application service is unregistered
+	 */
+	public void remove();
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/cm/ConfigurationAdmin.java b/org.osgi.compendium/src/main/java/org/osgi/service/cm/ConfigurationAdmin.java
index 0d8c6a8..131f863 100644
--- a/org.osgi.compendium/src/main/java/org/osgi/service/cm/ConfigurationAdmin.java
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/cm/ConfigurationAdmin.java
@@ -1,5 +1,5 @@
 /*
- * $Header: /cvshome/build/org.osgi.service.cm/src/org/osgi/service/cm/ConfigurationAdmin.java,v 1.16 2006/07/11 00:54:03 hargrave Exp $
+ * $Header: /cvshome/build/org.osgi.service.cm/src/org/osgi/service/cm/ConfigurationAdmin.java,v 1.17 2006/09/26 13:33:09 hargrave Exp $
  *
  * Copyright (c) OSGi Alliance (2001, 2006). All Rights Reserved.
  *
@@ -100,7 +100,7 @@
  * <code>ConfigurationAdmin</code> must use a
  * {@link org.osgi.framework.ServiceFactory} to support this concept.
  * 
- * @version $Revision: 1.16 $
+ * @version $Revision: 1.17 $
  */
 public interface ConfigurationAdmin {
 	/**
@@ -230,7 +230,7 @@
 	 * <code>ConfigurationPermission[*,CONFIGURE]</code>.
 	 * 
 	 * <p>
-	 * The syntax of the filter string is as defined in the <code>Filter</code>
+	 * The syntax of the filter string is as defined in the {@link org.osgi.framework.Filter}
 	 * class. The filter can test any configuration parameters including the
 	 * following system properties:
 	 * <ul>
@@ -244,10 +244,10 @@
 	 * The filter can also be <code>null</code>, meaning that all
 	 * <code>Configuration</code> objects should be returned.
 	 * 
-	 * @param filter a <code>Filter</code> object, or <code>null</code> to
+	 * @param filter A filter string, or <code>null</code> to
 	 *        retrieve all <code>Configuration</code> objects.
-	 * @return all matching <code>Configuration</code> objects, or
-	 *         <code>null</code> if there aren't any
+	 * @return All matching <code>Configuration</code> objects, or
+	 *         <code>null</code> if there aren't any.
 	 * @throws IOException if access to persistent storage fails
 	 * @throws InvalidSyntaxException if the filter string is invalid
 	 */
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/BundleInfo.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/BundleInfo.java
new file mode 100644
index 0000000..9bd31fb
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/BundleInfo.java
@@ -0,0 +1,43 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/BundleInfo.java,v 1.3 2006/06/16 16:31:39 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin;
+
+import org.osgi.framework.Version;
+
+/**
+ * Represents a bundle in the array given back by the {@link DeploymentPackage#getBundleInfos()}  
+ * method.
+ */
+public interface BundleInfo {
+	
+	/**
+	 * Returns the Bundle Symbolic Name of the represented bundle.
+	 * 
+	 * @return the Bundle Symbolic Name 
+	 */
+	String getSymbolicName();
+	
+	/**
+	 * Returns the version of the represented bundle.
+	 * 
+	 * @return the version of the represented bundle
+	 */
+	Version getVersion();
+
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdmin.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdmin.java
new file mode 100644
index 0000000..9376d14
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdmin.java
@@ -0,0 +1,139 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/DeploymentAdmin.java,v 1.28 2007/02/07 18:53:07 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 2007). 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 org.osgi.service.deploymentadmin;
+
+import java.io.InputStream;
+
+import org.osgi.framework.Bundle;
+
+/**
+  * This is the interface of the Deployment Admin service.<p>
+  * 
+  * The OSGi Service Platform provides mechanisms to manage the life cycle of
+  * bundles, configuration objects, permission objects, etc. but the overall consistency
+  * of the runtime configuration is the responsibility of the management
+  * agent. In other words, the management agent decides to install, update,
+  * or uninstall bundles, create or delete configuration or permission objects, as
+  * well as manage other resource types, etc.<p>
+  * 
+  * The Deployment Admin service standardizes the access to some of the responsibilities
+  * of the management agent. The service provides functionality to manage Deployment Packages 
+  * (see {@link DeploymentPackage}). A Deployment Package groups resources as a unit 
+  * of management. A Deployment Package is something that can be installed, updated, 
+  * and uninstalled as a unit.<p> 
+  * 
+  * The Deployment Admin functionality is exposed as a standard OSGi service with no 
+  * mandatory service parameters.
+  */
+public interface DeploymentAdmin {
+
+	/**
+	 * Installs a Deployment Package from an input stream. If a version of that Deployment Package
+	 * is already installed and the versions are different, the installed version is updated
+	 * with this new version even if it is older (downgrade). If the two versions are the same, then this 
+	 * method simply returns with the old (target) Deployment Package without any action.
+	 *  
+	 * @param  in the input stream the Deployment Package can be read from. It mustn't be <code>null</code>.
+	 * @return A DeploymentPackage object representing the newly installed/updated Deployment Package. 
+	 *         It is never <code>null</code>. 
+	 * @throws IllegalArgumentException if the got InputStream parameter is <code>null</code>         
+	 * @throws DeploymentException if the installation was not successful. For detailed error code description 
+	 *         see {@link DeploymentException}.
+	 * @throws SecurityException if the caller doesn't have the appropriate
+	 *         {@link DeploymentAdminPermission}("&lt;filter&gt;", "install") permission.
+	 * @see DeploymentAdminPermission
+	 * @see DeploymentPackage
+	 * @see DeploymentPackage
+	 */
+    DeploymentPackage installDeploymentPackage(InputStream in) throws DeploymentException;
+
+    /**
+      * Lists the Deployment Packages currently installed on the platform.<p>
+      * 
+      * {@link DeploymentAdminPermission}("&lt;filter&gt;", "list") is 
+      * needed for this operation to the effect that only those packages are listed in  
+      * the array to which the caller has appropriate DeploymentAdminPermission. It has 
+      * the consequence that the method never throws SecurityException only doesn't 
+      * put certain Deployment Packages into the array.<p>
+      * 
+      * During an installation of an existing package (update) or during an uninstallation, 
+      * the target must remain in this list until the installation (uninstallation) process 
+      * is completed, after which the source (or <code>null</code> in case of uninstall) 
+      * replaces the target.
+      * 
+      * @return the array of <code>DeploymentPackage</code> objects representing all the 
+      *         installed Deployment Packages. The return value cannot be <code>null</code>. 
+      *         In case of missing permissions it may give back an empty array.
+      * @see DeploymentPackage
+      * @see DeploymentAdminPermission
+      */
+    DeploymentPackage[] listDeploymentPackages();
+
+    /**
+     * Gets the currenlty installed {@link DeploymentPackage} instance which has the given 
+     * symbolic name.<p>
+     * 
+     * During an installation of an existing package (update) or during an uninstallation, 
+     * the target Deployment Package must remain the return value until the installation 
+     * (uninstallation) process is completed, after which the source (or <code>null</code> 
+     * in case of uninstall) is the return value.
+     * 
+     * @param  symbName the symbolic name of the Deployment Package to be retrieved. It mustn't be 
+     *         <code>null</code>.
+     * @return The <code>DeploymentPackage</code> for the given symbolic name. 
+     *         If there is no Deployment Package with that symbolic name currently installed, 
+     *         <code>null</code> is returned.
+     * @throws IllegalArgumentException if the given <code>symbName</code> is <code>null</code>
+     * @throws SecurityException if the caller doesn't have the appropriate 
+     *         {@link DeploymentAdminPermission}("&lt;filter&gt;", "list") permission.
+     * @see DeploymentPackage
+     * @see DeploymentAdminPermission
+     */
+    DeploymentPackage getDeploymentPackage(String symbName);  
+
+    /**
+     * Gives back the installed {@link DeploymentPackage} that owns the bundle. Deployment Packages own their 
+     * bundles by their Bundle Symbolic Name. It means that if a bundle belongs to an installed 
+     * Deployment Packages (and at most to one) the Deployment Admin assigns the bundle to its owner  
+     * Deployment Package by the Symbolic Name of the bundle.<p>
+     * 
+     * @param bundle the bundle whose owner is queried 
+     * @return the Deployment Package Object that owns the bundle or <code>null</code> if the bundle doesn't 
+     *         belong to any Deployment Packages (standalone bundles)
+     * @throws IllegalArgumentException if the given <code>bundle</code> is <code>null</code>
+     * @throws SecurityException if the caller doesn't have the appropriate 
+     *         {@link DeploymentAdminPermission}("&lt;filter&gt;", "list") permission.
+     * @see DeploymentPackage
+     * @see DeploymentAdminPermission
+     */
+    DeploymentPackage getDeploymentPackage(Bundle bundle);  
+  
+    /**
+     * This method cancels the currently active deployment session. This method addresses the need
+     * to cancel the processing of excessively long running, or resource consuming install, update
+     * or uninstall operations.<p>
+     * 
+     * @return true if there was an active session and it was successfully cancelled.
+     * @throws SecurityException if the caller doesn't have the appropriate 
+     *         {@link DeploymentAdminPermission}("&lt;filter&gt;", "cancel") permission.
+     * @see DeploymentAdminPermission
+     */
+    boolean cancel();     
+    
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdminPermission.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdminPermission.java
new file mode 100644
index 0000000..34509cf
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdminPermission.java
@@ -0,0 +1,331 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/DeploymentAdminPermission.java,v 1.40 2006/07/12 21:22:10 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin;
+
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.*;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * DeploymentAdminPermission controls access to the Deployment Admin service.<p>
+ * 
+ * The permission uses a filter string formatted similarly to the {@link org.osgi.framework.Filter}. 
+ * The filter determines the target of the permission. The <code>DeploymentAdminPermission</code> uses the 
+ * <code>name</code> and the <code>signer</code> filter attributes only. The value of the <code>signer</code> 
+ * attribute is matched against the signer chain (represented with its semicolon separated Distinguished Name chain) 
+ * of the Deployment Package, and the value of the <code>name</code> attribute is matched against the value of the 
+ * "DeploymentPackage-Name" manifest header of the Deployment Package. Example: 
+ * 
+ * <ul>
+ * 		<li>(signer=cn = Bugs Bunny, o = ACME, c = US)</li>
+ * 		<li>(name=org.osgi.ExampleApp)</li>
+ * </ul>
+ * 
+ * Wildcards also can be used:<p>
+ * 
+ * <pre>
+ * (signer=cn=*,o=ACME,c=*)  
+ * </pre>
+ * "cn" and "c" may have an arbitrary value
+ * 
+ * <pre>
+ * (signer=*, o=ACME, c=US)  
+ * </pre>
+ * Only the value of "o" and "c" are significant
+ * 
+ * <pre>
+ * (signer=* ; ou=S &amp; V, o=Tweety Inc., c=US)
+ * </pre>
+ * The first element of the certificate chain is 
+ * not important, only the second (the 
+ * Distingushed Name of the root certificate)
+ * 
+ * <pre>
+ * (signer=- ; *, o=Tweety Inc., c=US)
+ * </pre>
+ * The same as the previous but '-' represents 
+ * zero or more certificates, whereas the asterisk 
+ * only represents a single certificate
+ * 
+ * <pre>
+ * (name=*)                  
+ * </pre>
+ * The name of the Deployment Package doesn't matter
+ * 
+ * <pre>
+ * (name=org.osgi.*)         
+ * </pre>
+ * The name has to begin with "org.osgi."
+ * 
+ * <p>The following actions are allowed:<p>
+ * 
+ * <b>list</b>
+ * <p>
+ * A holder of this permission can access the inventory information of the deployment
+ * packages selected by the &lt;filter&gt; string. The filter selects the deployment packages
+ * on which the holder of the permission can acquire detailed inventory information.
+ * See {@link DeploymentAdmin#getDeploymentPackage(Bundle)}, 
+ * {@link DeploymentAdmin#getDeploymentPackage(String)} and
+ * {@link DeploymentAdmin#listDeploymentPackages}.<p>
+ * 
+ * <b>install</b><p>
+ * 
+ * A holder of this permission can install/update deployment packages if the deployment
+ * package satisfies the &lt;filter&gt; string. See {@link DeploymentAdmin#installDeploymentPackage}.<p>
+ * 
+ * <b>uninstall</b><p>
+ * 
+ * A holder of this permission can uninstall deployment packages if the deployment
+ * package satisfies the &lt;filter&gt; string. See {@link DeploymentPackage#uninstall}.<p>
+ * 
+ * <b>uninstall_forced</b><p>
+ * 
+ * A holder of this permission can forcefully uninstall deployment packages if the deployment
+ * package satisfies the &lt;filter&gt; string. See {@link DeploymentPackage#uninstallForced}.<p>
+ * 
+ * <b>cancel</b><p>
+ * 
+ * A holder of this permission can cancel an active deployment action. This action being
+ * cancelled could correspond to the install, update or uninstall of a deployment package
+ * that satisfies the &lt;filter&gt; string. See {@link DeploymentAdmin#cancel}<p>
+ * 
+ * <b>metadata</b><p>
+ * 
+ * A holder of this permission is able to retrieve metadata information about a Deployment 
+ * Package (e.g. is able to ask its manifest hedares). 
+ * See {@link org.osgi.service.deploymentadmin.DeploymentPackage#getBundle(String)},
+ * {@link org.osgi.service.deploymentadmin.DeploymentPackage#getBundleInfos()},
+ * {@link org.osgi.service.deploymentadmin.DeploymentPackage#getHeader(String)}, 
+ * {@link org.osgi.service.deploymentadmin.DeploymentPackage#getResourceHeader(String, String)},
+ * {@link org.osgi.service.deploymentadmin.DeploymentPackage#getResourceProcessor(String)}, 
+ * {@link org.osgi.service.deploymentadmin.DeploymentPackage#getResources()}<p>
+ *
+ * The actions string is converted to lowercase before processing.
+ */
+public final class DeploymentAdminPermission extends Permission {
+    
+    /**
+	 * 
+	 */
+	private static final long	serialVersionUID	= 1L;
+
+	/**
+     * Constant String to the "install" action.<p>
+     * 
+     * @see DeploymentAdmin#installDeploymentPackage(InputStream)
+     */
+    public static final String INSTALL            = "install";
+
+    /**
+     * Constant String to the "list" action.<p>
+     * 
+     * @see DeploymentAdmin#listDeploymentPackages()
+     * @see DeploymentAdmin#getDeploymentPackage(String)
+     * @see DeploymentAdmin#getDeploymentPackage(Bundle) 
+     */
+    public static final String LIST               = "list";
+    
+    /**
+     * Constant String to the "uninstall" action.<p>
+     * 
+     * @see DeploymentPackage#uninstall()
+     */
+    public static final String UNINSTALL          = "uninstall";
+
+    /**
+     * Constant String to the "uninstall_forced" action.<p>
+     * 
+     * @see DeploymentPackage#uninstallForced()
+     */
+    public static final String UNINSTALL_FORCED   = "uninstall_forced";
+    
+    /**
+     * Constant String to the "cancel" action.<p>
+     * 
+     * @see DeploymentAdmin#cancel
+     */
+    public static final String CANCEL             = "cancel";
+    
+    /**
+     * Constant String to the "metadata" action.<p>
+     * 
+     * @see org.osgi.service.deploymentadmin.DeploymentPackage#getBundle(String)
+     * @see org.osgi.service.deploymentadmin.DeploymentPackage#getBundleInfos()
+     * @see org.osgi.service.deploymentadmin.DeploymentPackage#getHeader(String)
+     * @see org.osgi.service.deploymentadmin.DeploymentPackage#getResourceHeader(String, String)
+     * @see org.osgi.service.deploymentadmin.DeploymentPackage#getResourceProcessor(String)
+     * @see org.osgi.service.deploymentadmin.DeploymentPackage#getResources()
+     */
+    public static final String METADATA           = "metadata";
+    
+    private static final String      delegateProperty = "org.osgi.vendor.deploymentadmin";
+    private static final Constructor constructor;
+    private final        Permission  delegate;
+    static {
+        constructor = (Constructor) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                String pckg = System.getProperty(delegateProperty);
+                if (null == pckg)
+                    throw new RuntimeException("Property '" + delegateProperty + "' is not set");
+                try {
+                    Class c = Class.forName(pckg + ".DeploymentAdminPermission");
+                    return c.getConstructor(new Class[] {String.class, String.class});    
+                }
+                catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }});
+    }
+    
+    /**
+     * Creates a new <code>DeploymentAdminPermission</code> object for the given <code>name</code> and 
+     * <code>action</code>.<p>
+     * The <code>name</code> parameter identifies the target depolyment package the permission 
+     * relates to. The <code>actions</code> parameter contains the comma separated list of allowed actions. 
+     * 
+     * @param name filter string, must not be null.
+     * @param actions action string, must not be null. "*" means all the possible actions.
+     * @throws IllegalArgumentException if the filter is invalid, the list of actions 
+     *         contains unknown operations or one of the parameters is null
+     */
+    public DeploymentAdminPermission(String name, String actions) {
+        super(name);
+		try {
+			try {
+	            delegate = (Permission) constructor.newInstance(new Object[] {name, actions});
+			}
+			catch (InvocationTargetException e) {
+				throw e.getTargetException();
+			}
+		}
+		catch (Error e) {
+			throw e;
+		}
+		catch (RuntimeException e) {
+			throw e;
+		}
+		catch (Throwable e) {
+			throw new RuntimeException(e.toString());
+		}
+    }
+
+    /**
+     * Checks two DeploymentAdminPermission objects for equality. 
+     * Two permission objects are equal if: <p>
+     * 
+     * <ul>
+     * 		<li>their target filters are semantically equal and</li>
+     * 		<li>their actions are the same</li> 
+     * </ul>
+     * 
+     * @param obj The reference object with which to compare.
+     * @return true if the two objects are equal.
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj == this)
+        	return true;
+        if (!(obj instanceof DeploymentAdminPermission))
+            return false;
+        DeploymentAdminPermission dap = (DeploymentAdminPermission) obj;
+        return delegate.equals(dap.delegate);
+    }
+
+    /**
+     * Returns hash code for this permission object.
+     * 
+     * @return Hash code for this permission object.
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    /**
+     * Returns the String representation of the action list.<p>
+     * The method always gives back the actions in the following (alphabetical) order: 
+     * <code>cancel, install, list, metadata, uninstall, uninstall_forced</code>
+     * 
+     * @return Action list of this permission instance. This is a comma-separated 
+     *         list that reflects the action parameter of the constructor.
+     * @see java.security.Permission#getActions()
+     */
+    public String getActions() {
+        return delegate.getActions();
+    }
+
+    /**
+     * Checks if this DeploymentAdminPermission would imply the parameter permission.<p>
+     * Precondition of the implication is that the action set of this permission is the superset 
+     * of the action set of the other permission. Further rules of implication are determined 
+     * by the {@link org.osgi.framework.Filter} rules and the "OSGi Service Platform, Core 
+     * Specification Release 4, Chapter Certificate Matching".<p>
+     * 
+     * The allowed attributes are: <code>name</code> (the symbolic name of the deployment 
+     * package) and <code>signer</code> (the signer of the deployment package). In both cases 
+     * wildcards can be used.<p>
+     * 
+     * Examples:
+     * 
+     * <pre>
+     * 		1. DeploymentAdminPermission("(name=org.osgi.ExampleApp)", "list")
+     * 		2. DeploymentAdminPermission("(name=org.osgi.ExampleApp)", "list, install")
+     * 		3. DeploymentAdminPermission("(name=org.osgi.*)", "list")
+     * 		4. DeploymentAdminPermission("(signer=*, o=ACME, c=US)", "list")
+     * 		5. DeploymentAdminPermission("(signer=cn = Bugs Bunny, o = ACME, c = US)", "list")
+     * </pre><p>
+     * 
+     * <pre>  
+     * 		1. implies 1.
+     * 		2. implies 1.
+     * 		1. doesn't implies 2.
+     * 		3. implies 1.
+     * 		4. implies 5.
+     * </pre>
+     * 
+     * @param permission Permission to check.
+     * @return true if this DeploymentAdminPermission object implies the 
+     * specified permission.
+     * @see java.security.Permission#implies(java.security.Permission)
+     * @see org.osgi.framework.Filter
+     */
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof DeploymentAdminPermission))
+    		return false;
+    	        
+        DeploymentAdminPermission dap = (DeploymentAdminPermission) permission;
+        
+        return delegate.implies(dap.delegate);
+    }
+
+    /**
+     * Returns a new PermissionCollection object for storing DeploymentAdminPermission 
+     * objects. 
+     * 
+     * @return The new PermissionCollection.
+     * @see java.security.Permission#newPermissionCollection()
+     */
+    public PermissionCollection newPermissionCollection() {
+        return delegate.newPermissionCollection();
+    }
+
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentException.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentException.java
new file mode 100644
index 0000000..bf5c6e6
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentException.java
@@ -0,0 +1,266 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/DeploymentException.java,v 1.20 2006/07/12 21:22:10 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin;
+
+import java.io.InputStream;
+
+/**
+ * Checked exception received when something fails during any deployment
+ * processes. A <code>DeploymentException</code> always contains an error code 
+ * (one of the constants specified in this class), and may optionally contain 
+ * the textual description of the error condition and a nested cause exception.
+ */
+public class DeploymentException extends Exception {
+
+	/**
+	 * 
+	 */
+	private static final long	serialVersionUID	= 916011169146851101L;
+
+	/**
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}, 
+	 * {@link DeploymentPackage#uninstall()} and {@link DeploymentPackage#uninstallForced()} 
+	 * methods can throw {@link DeploymentException} with this error code if the 
+	 * {@link DeploymentAdmin#cancel()} method is called from another thread.
+	 */
+	public static final int	CODE_CANCELLED                  = 401;
+
+	/**
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)} 
+	 * methods can throw {@link DeploymentException} with this error code if 
+	 * the got InputStream is not a jar. 
+	 */
+	public static final int	CODE_NOT_A_JAR                  = 404;
+
+	/**
+	 * Order of files in the deployment package is bad. The right order is the 
+	 * following:<p>
+	 * 
+	 * <ol>
+	 *    <li>META-INF/MANIFEST.MF</li>
+	 *    <li>META-INF/*.SF, META-INF/*.DSA, META-INF/*.RS</li>
+	 *    <li>Localization files</li>
+	 *    <li>Bundles</li>
+	 *    <li>Resources</li>
+	 * </ol>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_ORDER_ERROR				= 450;
+
+	/**
+	 * Missing mandatory manifest header.<p>
+	 *  
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)} can throw 
+	 * exception with this error code.  
+	 */
+	public static final int	CODE_MISSING_HEADER				= 451;
+
+	/**
+	 * Syntax error in any manifest header.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_BAD_HEADER					= 452;
+
+	/**
+	 * Fix pack version range doesn't fit to the version of the target
+	 * deployment package or the target deployment package of the fix pack
+	 * doesn't exist.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_MISSING_FIXPACK_TARGET		= 453;
+
+	/**
+	 * A bundle in the deployment package is marked as DeploymentPackage-Missing
+	 * but there is no such bundle in the target deployment package.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_MISSING_BUNDLE				= 454;
+
+	/**
+	 * A resource in the source deployment package is marked as
+	 * DeploymentPackage-Missing but there is no such resource in the target
+	 * deployment package.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_MISSING_RESOURCE			= 455;
+
+	/**
+	 * Bad deployment package signing.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_SIGNING_ERROR				= 456;
+
+	/**
+	 * Bundle symbolic name is not the same as defined by the deployment package
+	 * manifest.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_BUNDLE_NAME_ERROR			= 457;
+
+	/**
+	 * Matched resource processor service is a customizer from another
+	 * deployment package.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_FOREIGN_CUSTOMIZER			= 458;
+
+	/**
+	 * Bundle with the same symbolic name alerady exists.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_BUNDLE_SHARING_VIOLATION	= 460;
+
+	/**
+	 * An artifact of any resource already exists.<p>
+	 * 
+	 * This exception is thrown when the called resource processor throws a 
+	 * <code>ResourceProcessorException</code> with the 
+	 * {@link org.osgi.service.deploymentadmin.spi.ResourceProcessorException#CODE_RESOURCE_SHARING_VIOLATION} 
+	 * error code.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_RESOURCE_SHARING_VIOLATION	= 461;
+
+	/**
+	 * Exception with this error code is thrown when one of the Resource Processors 
+	 * involved in the deployment session threw a <code>ResourceProcessorException</code> with the 
+	 * {@link org.osgi.service.deploymentadmin.spi.ResourceProcessorException#CODE_PREPARE} error 
+	 * code.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)} and 
+	 * {@link DeploymentPackage#uninstall()} methods throw exception with this error code.  
+	 */
+	public static final int	CODE_COMMIT_ERROR				= 462;
+
+	/**
+	 * Other error condition.<p>
+	 * 
+	 * All Deployment Admin methods which throw <code>DeploymentException</code> 
+	 * can throw an exception with this error code if the error condition cannot be 
+	 * categorized. 
+	 */
+	public static final int	CODE_OTHER_ERROR				= 463;
+
+	/**
+	 * The Resource Processor service with the given PID (see
+	 * <code>Resource-Processor</code> manifest header) is not found.<p>
+	 *  
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)},
+	 * {@link DeploymentPackage#uninstall()} and 
+	 * {@link DeploymentPackage#uninstallForced()}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_PROCESSOR_NOT_FOUND		= 464;
+
+	/**
+	 * When a client requests a new session with an install or uninstall
+	 * operation, it must block that call until the earlier session is
+	 * completed. The Deployment Admin service must throw a Deployment Exception
+	 * with this error code when the session can not be created after an appropriate
+	 * time out period.<p>
+	 * 
+	 * {@link DeploymentAdmin#installDeploymentPackage(InputStream)},
+	 * {@link DeploymentPackage#uninstall()} and 
+	 * {@link DeploymentPackage#uninstallForced()}
+	 * throws exception with this error code.  
+	 */
+	public static final int	CODE_TIMEOUT					= 465;
+
+	private final int				code;
+	private final String			message;
+	private final Throwable		cause;
+
+	/**
+	 * Create an instance of the exception.
+	 * 
+	 * @param code The error code of the failure. Code should be one of the
+	 *        predefined integer values (<code>CODE_X</code>).
+	 * @param message Message associated with the exception
+	 * @param cause the originating exception
+	 */
+	public DeploymentException(int code, String message, Throwable cause) {
+		this.code = code;
+		this.message = message;
+		this.cause = cause;
+	}
+
+	/**
+	 * Create an instance of the exception. Cause exception is implicitly set to
+	 * null.
+	 * 
+	 * @param code The error code of the failure. Code should be one of the
+	 *        predefined integer values (<code>CODE_X</code>).
+	 * @param message Message associated with the exception
+	 */
+	public DeploymentException(int code, String message) {
+		this(code, message, null);
+	}
+
+	/**
+	 * Create an instance of the exception. Cause exception and message are
+	 * implicitly set to null.
+	 * 
+	 * @param code The error code of the failure. Code should be one of the
+	 *        predefined integer values (<code>CODE_X</code>).
+	 */
+	public DeploymentException(int code) {
+		this(code, null, null);
+	}
+
+	/**
+	 * @return Returns the cause.
+	 */
+	public Throwable getCause() {
+		return cause;
+	}
+
+	/**
+	 * @return Returns the code.
+	 */
+	public int getCode() {
+		return code;
+	}
+
+	/**
+	 * @return Returns the message.
+	 */
+	public String getMessage() {
+	    return message;
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentPackage.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentPackage.java
new file mode 100644
index 0000000..0155771
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentPackage.java
@@ -0,0 +1,240 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/DeploymentPackage.java,v 1.26 2006/07/12 21:22:10 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin;
+
+import org.osgi.framework.*;
+
+/**
+  * The <code>DeploymentPackage</code> object represents a deployment package (already installed
+  * or being currently processed). A Deployment Package groups resources as a unit 
+  * of management. A deployment package is something that can be installed, updated, 
+  * and uninstalled as a unit. A deployment package is a reified concept, like a bundle, 
+  * in an OSGi Service Platform. It is not known by the OSGi Framework, but it is managed 
+  * by the Deployment Admin service. A deployment package is a stream of resources 
+  * (including bundles) which, once processed, will result in new artifacts (effects on 
+  * the system) being added to the OSGi platform.  These new artifacts can include 
+  * installed Bundles, new configuration objects added to the Configuration Admin service, 
+  * new Wire objects added to the Wire Admin service, or changed system properties, etc. All 
+  * the changes caused by the processing of a deployment package are persistently 
+  * associated with the deployment package, so that they can be appropriately cleaned 
+  * up when the deployment package is uninstalled. There is a strict no overlap rule 
+  * imposed on deployment packages. Two deployment packages are not allowed to create or 
+  * manipulate the same artifact. Obviously, this means that a bundle cannot be in two 
+  * different deployment packagess. Any violation of this no overlap rule is considered 
+  * an error and the install or update of the offending deployment package must be aborted.<p>
+  * 
+  * The Deployment Admin service should do as much as possible to ensure transactionality. 
+  * It means that if a deployment package installation, update or removal (uninstall) fails 
+  * all the side effects caused by the process should be disappeared  and the system 
+  * should be in the state in which it was before the process.<p>
+  * 
+  * If a deployment package is being updated the old version is visible through the 
+  * <code>DeploymentPackage</code> interface until the update process ends. After the 
+  * package is updated the updated version is visible and the old one is not accessible 
+  * any more.
+  */
+public interface DeploymentPackage {
+ 
+	/**
+	 * Gives back the state of the deployment package whether it is stale or not).
+     * After uninstall of a deployment package it becomes stale. Any active method calls to a 
+     * stale deployment package raise {@link IllegalStateException}.
+     * Active methods are the following:<p>
+     * 
+     * <ul>
+     * 		<li>{@link #getBundle(String)}</li>
+     * 		<li>{@link #getResourceProcessor(String)}</li>
+     * 		<li>{@link #uninstall()}</li>
+     * 		<li>{@link #uninstallForced()}</li>
+     * </ul>
+     * 
+	 * @return <code>true</code> if the deployment package is stale. <code>false</code>
+	 *         otherwise
+     * @see #uninstall
+     * @see #uninstallForced
+	 */
+    boolean isStale();
+	  
+	/**
+	 * Returns the Deployment Pacakage Symbolic Name of the package.
+	 * 
+	 * @return The name of the deployment package. It cannot be null.
+	 */
+	String getName();
+	  
+	/**
+	 * Returns the version of the deployment package.
+	 * @return version of the deployment package. It cannot be null.
+	 */
+	Version getVersion();
+	  
+	/**
+	 * Returns an array of {@link BundleInfo} objects representing the bundles specified in the manifest 
+	 * of this deployment package. Its size is equal to the number of the bundles in the deployment package. 
+	 * 
+ 	 * @return array of <code>BundleInfo</code> objects 
+     * @throws SecurityException if the caller doesn't have the appropriate {@link DeploymentAdminPermission} 
+     *         with "metadata" action
+	 */
+	BundleInfo[] getBundleInfos();  
+ 
+    /**
+     * Returns the bundle instance, which is part of this deployment package, that corresponds 
+     * to the bundle's symbolic name passed in the <code>symbolicName</code> parameter.
+     * This method will return null for request for bundles that are not part 
+     * of this deployment package.<p>
+     * 
+     * As this instance is transient (i.e. a bundle can be removed at any time because of the 
+     * dynamic nature of the OSGi platform), this method may also return null if the bundle
+     * is part of this deployment package, but is not currently defined to the framework.
+     * 
+     * @param symbolicName the symbolic name of the requested bundle
+     * @return The <code>Bundle</code> instance for a given bundle symbolic name.
+     * @throws SecurityException if the caller doesn't have the appropriate {@link DeploymentAdminPermission} 
+     *         with "metadata" action
+     * @throws IllegalStateException if the package is stale
+     */
+    Bundle getBundle(String symbolicName);
+    
+    /**
+     * Returns an array of strings representing the resources (including bundles) that 
+     * are specified in the  manifest of this deployment package. A string element of the 
+     * array is the same as the value of the "Name" attribute in the manifest. The array 
+     * contains the bundles as well.<p>
+     * 
+     * E.g. if the "Name" section of the resource (or individual-section as the 
+     * <a href="http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Manifest%20Specification">Manifest Specification</a> 
+     * calls it) in the manifest is the following
+
+     * <pre>
+     *     Name: foo/readme.txt
+     *     Resource-Processor: foo.rp
+     * </pre>
+     * 
+     * then the corresponding array element is the "foo/readme.txt" string.<p>
+     * 
+     * @return The string array corresponding to resources. It cannot be null but its 
+     *         length can be zero.
+     * @throws SecurityException if the caller doesn't have the appropriate {@link DeploymentAdminPermission} 
+     *         with "metadata" action
+     */
+    String[] getResources();   
+    
+    /**
+     * At the time of deployment, resource processor service instances are located to 
+     * resources contained in a deployment package.<p> 
+     * 
+     * This call returns a service reference to the corresponding service instance.
+     * If the resource is not part of the deployment package or this call is made during 
+     * deployment, prior to the locating of the service to process a given resource, null will 
+     * be returned. Services can be updated after a deployment package has been deployed. 
+     * In this event, this call will return a reference to the updated service, not to the 
+     * instance that was used at deployment time.
+     * 
+     * @param resource the name of the resource (it is the same as the value of the "Name" 
+     *        attribute in the deployment package's manifest) 
+     * @return resource processor for the resource or <code>null</cpde>.
+     * @throws SecurityException if the caller doesn't have the appropriate {@link DeploymentAdminPermission} 
+     *         with "metadata" action
+     * @throws IllegalStateException if the package is stale        
+     */
+    ServiceReference getResourceProcessor(String resource);    
+
+    /**
+     * Returns the requested deployment package manifest header from the main section. 
+     * Header names are case insensitive. If the header doesn't exist it returns null.<p>
+     * 
+     * If the header is localized then the localized value is returned (see OSGi Service Platform,
+     * Mobile Specification Release 4 - Localization related chapters).
+     * 
+     * @param header the requested header
+     * @return the value of the header or <code>null</code> if the header does not exist
+     * @throws SecurityException if the caller doesn't have the appropriate {@link DeploymentAdminPermission} 
+     *         with "metadata" action
+     */
+    String getHeader(String header);
+
+    /**
+     * Returns the requested deployment package manifest header from the name 
+     * section determined by the resource parameter. Header names are case insensitive. 
+     * If the resource or the header doesn't exist it returns null.<p>
+     * 
+     * If the header is localized then the localized value is returned (see OSGi Service Platform,
+     * Mobile Specification Release 4 - Localization related chapters).
+
+     * @param resource the name of the resource (it is the same as the value of the "Name" 
+     *        attribute in the deployment package's manifest)
+     * @param header the requested header
+     * @return the value of the header or <code>null</code> if the resource or the header doesn't exist
+     * @throws SecurityException if the caller doesn't have the appropriate {@link DeploymentAdminPermission} 
+     *         with "metadata" action
+     */
+    String getResourceHeader(String resource, String header);
+    
+	/**
+	  * Uninstalls the deployment package. After uninstallation, the deployment package 
+	  * object becomes stale. This can be checked by using {@link #isStale()}, 
+	  * which will return <code>true</code> when stale.<p>
+	  * 
+	  * @throws DeploymentException if the deployment package could not be successfully uninstalled. 
+	  *         For detailed error code description see {@link DeploymentException}.
+	  * @throws SecurityException if the caller doesn't have the appropriate 
+	  * 		{@link DeploymentAdminPermission}("&lt;filter&gt;", "uninstall") permission.
+	  * @throws IllegalStateException if the package is stale 
+	  */
+    void uninstall() throws DeploymentException;
+ 
+    /**
+     * This method is called to completely uninstall a deployment package, which couldn't be uninstalled
+     * using traditional means ({@link #uninstall()}) due to exceptions. After uninstallation, the deployment 
+     * package object becomes stale. This can be checked by using {@link #isStale()}, 
+     * which will return <code>true</code> when stale.<p>
+     *  
+     * The method forces removal of the Deployment Package from the repository maintained by the 
+     * Deployment Admin service. This method follows the same steps as {@link #uninstall}. However, 
+     * any errors or the absence of Resource Processor services are ignored, they must not cause a roll back.
+     * These errors should be logged.
+     * 
+     * @return true if the operation was successful
+     * @throws DeploymentException only {@link DeploymentException#CODE_TIMEOUT} and 
+     *         {@link DeploymentException#CODE_CANCELLED} can be thrown. For detailed error code description 
+	 *         see {@link DeploymentException}.
+     * @throws SecurityException if the caller doesn't have the appropriate 
+     *         {@link DeploymentAdminPermission}("&lt;filter&gt;", "uninstall_forced") permission.
+     * @throws IllegalStateException if the package is stale
+     */  
+    boolean uninstallForced() throws DeploymentException;  
+ 
+    /**
+     * Returns a hash code value for the object.
+     * 
+     * @return a hash code value for this object
+     */
+    int hashCode();
+  
+    /**
+     * Indicates whether some other object is "equal to" this one. Two deployment packages 
+     * are equal if they have the same deployment package symbolicname and version.
+     * 
+     * @param other the reference object with which to compare.
+     * @return true if this object is the same as the obj argument; false otherwise.
+     */
+    boolean equals(Object other);
+  
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentCustomizerPermission.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentCustomizerPermission.java
new file mode 100644
index 0000000..681329e
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentCustomizerPermission.java
@@ -0,0 +1,200 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/spi/DeploymentCustomizerPermission.java,v 1.6 2006/06/21 15:16:13 hargrave Exp $
+ * 
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin.spi;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.*;
+
+import org.osgi.service.deploymentadmin.DeploymentAdminPermission;
+
+/**
+ * The <code>DeploymentCustomizerPermission</code> permission gives the right to 
+ * Resource Processors to access a bundle's (residing in a Deployment Package) private area.
+ * The bundle and the Resource Processor (customizer) have to be in the same Deployment Package.<p>
+ * 
+ * The Resource Processor that has this permission is allowed to access the bundle's 
+ * private area by calling the {@link DeploymentSession#getDataFile} method during the session 
+ * (see {@link DeploymentSession}). After the session ends the FilePermissions are withdrawn.
+ * The Resource Processor will have <code>FilePermission</code> with "read", "write" and "delete" 
+ * actions for the returned {@link java.io.File} that represents the the base directory of the 
+ * persistent storage area and for its subdirectories.<p>
+ * 
+ * The actions string is converted to lowercase before processing.
+ */
+public class DeploymentCustomizerPermission extends Permission {
+    
+    /**
+	 * 
+	 */
+	private static final long	serialVersionUID	= 1L;
+
+	/**
+     * Constant String to the "privatearea" action.
+     */
+    public static final String PRIVATEAREA = "privatearea";
+
+    private static final String      delegateProperty = "org.osgi.vendor.deploymentadmin";
+    private static final Constructor constructor;
+    private final        Permission  delegate;
+    static {
+        constructor = (Constructor) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                String pckg = System.getProperty(delegateProperty);
+                if (null == pckg)
+                    throw new RuntimeException("Property '" + delegateProperty + "' is not set");
+                try {
+                    Class c = Class.forName(pckg + ".DeploymentCustomizerPermission");
+                    return c.getConstructor(new Class[] {String.class, String.class});    
+                }
+                catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }});
+    }
+
+    /**
+     * Creates a new <code>DeploymentCustomizerPermission</code> object for the given 
+     * <code>name</code> and <code>action</code>.<p>
+     * 
+     * The name parameter is a filter string. This filter has the same syntax as an OSGi filter 
+     * but only the "name" attribute is allowed. The value of the attribute  
+     * is a Bundle Symbolic Name that represents a bundle. The only allowed action is the 
+     * "privatearea" action. E.g.
+     * 
+     * <pre>
+     * 		Permission perm = new DeploymentCustomizerPermission("(name=com.acme.bundle)", "privatearea");
+     * </pre>
+     * 
+     * The Resource Processor that has this permission is allowed to access the bundle's 
+     * private area by calling the {@link DeploymentSession#getDataFile} method. The 
+     * Resource Processor will have <code>FilePermission</code> with "read", "write" and "delete" 
+     * actions for the returned {@link java.io.File} and its subdirectories during the deployment 
+     * session.
+     * 
+     * @param name Bundle Symbolic Name of the target bundle, must not be <code>null</code>.
+     * @param actions action string (only the "privatearea" or "*" action is valid; "*" means all 
+     *        the possible actions), must not be <code>null</code>.
+     * @throws IllegalArgumentException if the filter is invalid, the list of actions 
+     *         contains unknown operations or one of the parameters is <code>null</code>
+     */
+    public DeploymentCustomizerPermission(String name, String actions) {
+        super(name);
+		try {
+			try {
+	            delegate = (Permission) constructor.newInstance(new Object[] {name, actions});
+			}
+			catch (InvocationTargetException e) {
+				throw e.getTargetException();
+			}
+		}
+		catch (Error e) {
+			throw e;
+		}
+		catch (RuntimeException e) {
+			throw e;
+		}
+		catch (Throwable e) {
+			throw new RuntimeException(e.toString());
+		}
+    }
+
+    /**
+     * Checks two DeploymentCustomizerPermission objects for equality. 
+     * Two permission objects are equal if: <p>
+     * 
+     * <ul>
+     * 		<li>their target filters are equal (semantically and not character by 
+     *   	character) and</li>
+     * 		<li>their actions are the same</li> 
+     * </ul>
+     * 
+     * @param obj the reference object with which to compare.
+     * @return true if the two objects are equal.
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj == this)
+        	return true;
+        if (!(obj instanceof DeploymentCustomizerPermission))
+            return false;
+        DeploymentCustomizerPermission dcp = (DeploymentCustomizerPermission) obj;
+        return delegate.equals(dcp.delegate);
+    }
+
+    /**
+     * Returns hash code for this permission object.
+     * 
+     * @return Hash code for this permission object.
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    /**
+     * Returns the String representation of the action list.
+     * 
+     * @return Action list of this permission instance. It is always "privatearea".
+     * @see java.security.Permission#getActions()
+     */
+    public String getActions() {
+        return delegate.getActions();
+    }
+
+    /**
+     * Checks if this DeploymentCustomizerPermission would imply the parameter permission.
+     * This permission implies another DeploymentCustomizerPermission permission if:
+     * 
+     * <ul>
+     * 		<li>both of them has the "privatearea" action (other actions are not allowed) and</li>
+     * 		<li>their filters (only name attribute is allowed in the filters) match similarly to 
+     * 		{@link DeploymentAdminPermission}.</li>
+     * </ul>
+     * 
+     * The value of the name attribute means Bundle Symbolic Name and not Deployment Package 
+     * Symbolic Name here!<p>
+     * 
+     * @param permission Permission to check.
+     * @return true if this DeploymentCustomizerPermission object implies the 
+     * specified permission.
+     * @see java.security.Permission#implies(java.security.Permission)
+     */
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof DeploymentCustomizerPermission))
+    		return false;
+    	        
+        DeploymentCustomizerPermission dcp = (DeploymentCustomizerPermission) permission;
+        
+        return delegate.implies(dcp.delegate);
+    }
+
+    /**
+     * Returns a new PermissionCollection object for storing DeploymentCustomizerPermission 
+     * objects.
+     *  
+     * @return The new PermissionCollection.
+     * @see java.security.Permission#newPermissionCollection()
+     */
+    public PermissionCollection newPermissionCollection() {
+        return delegate.newPermissionCollection();
+    }
+
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentSession.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentSession.java
new file mode 100644
index 0000000..87a5b1f
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentSession.java
@@ -0,0 +1,96 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/spi/DeploymentSession.java,v 1.6 2006/06/16 16:31:39 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin.spi;
+
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+
+/**
+ * The session interface represents a currently running deployment session 
+ * (install/update/uninstall).<p>
+ * 
+ * When a deployment package is installed the target package, when uninstalled the 
+ * source package is an empty deployment package. The empty deployment package is a virtual 
+ * entity it doesn't appear for the outside world. It is only visible on the 
+ * DeploymentSession interface used by Resource Processors. Although  the empty package 
+ * is only visible for Resource Processors it has the following characteristics:<p>
+ *  
+ * <ul>
+ *     <li>has version 0.0.0</li>
+ *     <li>its name is an empty string</li>
+ *     <li>it is stale</li>
+ *     <li>it has no bundles
+ *     		(see {@link DeploymentPackage#getBundle(String)})</li>
+ *     <li>it has no resources
+ *     		(see {@link DeploymentPackage#getResources()})</li>
+ *     <li>it has no headers except <br/>
+ *     		<code>DeploymentPackage-SymbolicName</code> and <br/>
+ *     		<code>DeploymentPackage-Version</code> <br/>
+ *     		(see {@link DeploymentPackage#getHeader(String)})</li>
+ *     <li>it has no resource headers (see 
+ *     		{@link DeploymentPackage#getResourceHeader(String, String)})</li>
+ *     <li>{@link DeploymentPackage#uninstall()} throws
+ *     		{@link java.lang.IllegalStateException}</li>
+ *     <li>{@link DeploymentPackage#uninstallForced()} throws
+ *     		{@link java.lang.IllegalStateException}</li>
+ * </ul>
+ *  
+ */
+public interface DeploymentSession {
+    
+    /**
+     * If the deployment action is an update or an uninstall, this call returns
+     * the <code>DeploymentPackage</code> instance for the installed deployment package. If the 
+     * deployment action is an install, this call returns the empty deploymet package (see
+     * {@link DeploymentPackage}).
+     * 
+     * @return the target deployment package
+     * @see DeploymentPackage
+     */
+    DeploymentPackage getTargetDeploymentPackage();
+    
+    /**
+     * If the deployment action is an install or an update, this call returns
+     * the <code>DeploymentPackage</code> instance that corresponds to the deployment package
+     * being streamed in for this session. If the deployment action is an uninstall, this call 
+     * returns the empty deploymet package (see {@link DeploymentPackage}).
+     * 
+     * @return the source deployment package
+     * @see DeploymentPackage
+     */ 
+    DeploymentPackage getSourceDeploymentPackage();
+
+    /**
+     * Returns the private data area of the specified bundle. The bundle must be part of 
+     * either the source or the target deployment packages. The permission set the caller 
+     * resource processor needs to manipulate the private area of the bundle is set by the 
+     * Deployment Admin on the fly when this method is called. The permissions remain available 
+     * during the deployment action only.<p>
+     * 
+     * The bundle and the caller Resource Processor have to be in the same Deployment Package.
+     * 
+     * @param bundle the bundle the private area belongs to
+     * @return file representing the private area of the bundle. It cannot be null.
+     * @throws SecurityException if the caller doesn't have the appropriate 
+     *         {@link DeploymentCustomizerPermission}("&lt;filter&gt;", "privatearea") permission.
+     * @see DeploymentPackage
+     * @see DeploymentCustomizerPermission
+     */     
+    java.io.File getDataFile(org.osgi.framework.Bundle bundle);
+     
+}
+
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessor.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessor.java
new file mode 100644
index 0000000..ec5a5fd
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessor.java
@@ -0,0 +1,139 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/spi/ResourceProcessor.java,v 1.6 2006/07/11 13:19:02 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin.spi;
+
+import java.io.InputStream;
+
+/**
+  * ResourceProcessor interface is implemented by processors handling resource files
+  * in deployment packages. Resource Processors expose their services as standard OSGi services.
+  * Bundles exporting the service may arrive in the deployment package (customizers) or may be 
+  * preregistered (they are installed prevoiusly). Resource processors has to define the 
+  * <code>service.pid</code> standard OSGi service property which should be a unique string.<p>
+  * 
+  * The order of the method calls on a particular Resource Processor in case of install/update 
+  * session is the following:<p>
+  * 
+  * <ol>
+  * 	<li>{@link #begin(DeploymentSession)}</li>
+  * 	<li>{@link #process(String, InputStream)} calls till there are resources to process 
+  * 		or {@link #rollback()} and the further steps are ignored</li>
+  * 	<li>{@link #dropped(String)} calls till there are resources to drop
+  * 	<li>{@link #prepare()}</li>
+  * 	<li>{@link #commit()} or {@link #rollback()}</li>
+  * </ol>
+  * 
+  * The order of the method calls on a particular Resource Processor in case of uninstall 
+  * session is the following:<p>
+  * 
+  * <ol>
+  * 	<li>{@link #begin(DeploymentSession)}</li>
+  * 	<li>{@link #dropAllResources()}	or {@link #rollback()} and the further steps are ignored</li>
+  * 	<li>{@link #prepare()}</li>
+  * 	<li>{@link #commit()} or {@link #rollback()}</li>
+  * </ol>
+  */
+public interface ResourceProcessor {
+
+	/**
+	  * Called when the Deployment Admin starts a new operation on the given deployment package, 
+	  * and the resource processor is associated a resource within the package. Only one 
+	  * deployment package can be processed at a time.
+	  * 
+	  * @param session object that represents the current session to the resource processor
+	  * @see DeploymentSession
+	  */
+    void begin(DeploymentSession session);
+  
+    /**
+     * Called when a resource is encountered in the deployment package for which this resource 
+     * processor has been  selected to handle the processing of that resource.
+     * 
+     * @param name The name of the resource relative to the deployment package root directory. 
+     * @param stream The stream for the resource. 
+     * @throws ResourceProcessorException if the resource cannot be processed. Only 
+     *         {@link ResourceProcessorException#CODE_RESOURCE_SHARING_VIOLATION} and 
+     *         {@link ResourceProcessorException#CODE_OTHER_ERROR} error codes are allowed.
+     */
+    void process(String name, InputStream stream) throws ResourceProcessorException;
+
+	/**
+	  * Called when a resource, associated with a particular resource processor, had belonged to 
+	  * an earlier version of a deployment package but is not present in the current version of 
+	  * the deployment package.  This provides an opportunity for the processor to cleanup any 
+	  * memory and persistent data being maintained for the particular resource.  
+	  * This method will only be called during "update" deployment sessions.
+	  * 
+	  * @param resource the name of the resource to drop (it is the same as the value of the 
+	  *        "Name" attribute in the deployment package's manifest)
+	  * @throws ResourceProcessorException if the resource is not allowed to be dropped. Only the 
+	  *         {@link ResourceProcessorException#CODE_OTHER_ERROR} error code is allowed
+	  */
+    void dropped(String resource) throws ResourceProcessorException;
+    
+    /**
+     * This method is called during an "uninstall" deployment session.
+     * This method will be called on all resource processors that are associated with resources 
+     * in the deployment package being uninstalled. This provides an opportunity for the processor 
+     * to cleanup any memory and persistent data being maintained for the deployment package.
+     * 
+     * @throws ResourceProcessorException if all resources could not be dropped. Only the 
+     *         {@link ResourceProcessorException#CODE_OTHER_ERROR} is allowed.
+     */
+    void dropAllResources() throws ResourceProcessorException;
+  
+    /**
+     * This method is called on the Resource Processor immediately before calling the 
+     * <code>commit</code> method. The Resource Processor has to check whether it is able 
+     * to commit the operations since the last <code>begin</code> method call. If it determines 
+     * that it is not able to commit the changes, it has to raise a 
+     * <code>ResourceProcessorException</code> with the {@link ResourceProcessorException#CODE_PREPARE} 
+     * error code.
+     * 
+     * @throws ResourceProcessorException if the resource processor is able to determine it is 
+     *         not able to commit. Only the {@link ResourceProcessorException#CODE_PREPARE} error 
+     *         code is allowed.
+     */
+    void prepare() throws ResourceProcessorException;        
+   
+    /**
+     * Called when the processing of the current deployment package is finished. 
+     * This method is called if the processing of the current deployment package was successful, 
+     * and the changes must be made permanent.
+     */
+    void commit();
+   
+     
+    /**
+     * Called when the processing of the current deployment package is finished. 
+     * This method is called if the processing of the current deployment package was unsuccessful, 
+     * and the changes made during the processing of the deployment package should be removed.  
+     */
+    void rollback();
+    
+    /**
+     * Processing of a resource passed to the resource processor may take long. 
+     * The <code>cancel()</code> method notifies the resource processor that it should 
+     * interrupt the processing of the current resource. This method is called by the 
+     * <code>DeploymentAdmin</code> implementation after the
+     * <code>DeploymentAdmin.cancel()</code> method is called.
+     */
+    void cancel();
+
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessorException.java b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessorException.java
new file mode 100644
index 0000000..eb9c3dc
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessorException.java
@@ -0,0 +1,123 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.deploymentadmin/src/org/osgi/service/deploymentadmin/spi/ResourceProcessorException.java,v 1.7 2006/07/12 21:22:10 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.deploymentadmin.spi;
+
+import java.io.InputStream;
+
+/**
+ * Checked exception received when something fails during a call to a Resource 
+ * Processor. A <code>ResourceProcessorException</code> always contains an error 
+ * code (one of the constants specified in this class), and may optionally contain 
+ * the textual description of the error condition and a nested cause exception.
+ */
+public class ResourceProcessorException extends Exception {
+	
+	/**
+	 * 
+	 */
+	private static final long	serialVersionUID	= 9135007015668223386L;
+
+	/**
+	 * Resource Processors are allowed to raise an exception with this error code 
+	 * to indicate that the processor is not able to commit the operations it made 
+	 * since the last call of {@link ResourceProcessor#begin(DeploymentSession)} method.<p>
+	 * 
+	 * Only the {@link ResourceProcessor#prepare()} method is allowed to throw exception 
+	 * with this error code.  
+	 */
+	public static final int	CODE_PREPARE					= 1;
+
+	/**
+	 * An artifact of any resource already exists.<p>
+	 * 
+	 * Only the {@link ResourceProcessor#process(String, InputStream)} method 
+	 * is allowed to throw exception with this error code.  
+	 */
+	public static final int	CODE_RESOURCE_SHARING_VIOLATION	= 461;
+
+	/**
+	 * Other error condition.<p>
+	 * 
+	 * All Resource Processor methods which throw <code>ResourceProcessorException</code> 
+	 * is allowed throw an exception with this erro code if the error condition cannot be 
+	 * categorized. 
+	 */
+	public static final int	CODE_OTHER_ERROR				= 463;
+
+	private final int				code;
+	private final String			message;
+	private final Throwable		cause;
+
+	/**
+	 * Create an instance of the exception.
+	 * 
+	 * @param code The error code of the failure. Code should be one of the
+	 *        predefined integer values (<code>CODE_X</code>).
+	 * @param message Message associated with the exception
+	 * @param cause the originating exception
+	 */
+	public ResourceProcessorException(int code, String message, Throwable cause) {
+		this.code = code;
+		this.message = message;
+		this.cause = cause;
+	}
+
+	/**
+	 * Create an instance of the exception. Cause exception is implicitly set to
+	 * null.
+	 * 
+	 * @param code The error code of the failure. Code should be one of the
+	 *        predefined integer values (<code>CODE_X</code>).
+	 * @param message Message associated with the exception
+	 */
+	public ResourceProcessorException(int code, String message) {
+		this(code, message, null);
+	}
+
+	/**
+	 * Create an instance of the exception. Cause exception and message are
+	 * implicitly set to null.
+	 * 
+	 * @param code The error code of the failure. Code should be one of the
+	 *        predefined integer values (<code>CODE_X</code>).
+	 */
+	public ResourceProcessorException(int code) {
+		this(code, null, null);
+	}
+
+	/**
+	 * @return Returns the cause.
+	 */
+	public Throwable getCause() {
+		return cause;
+	}
+
+	/**
+	 * @return Returns the code.
+	 */
+	public int getCode() {
+		return code;
+	}
+
+	/**
+	 * @return Returns the message.
+	 */
+	public String getMessage() {
+	    return message;
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorAdmin.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorAdmin.java
new file mode 100644
index 0000000..c9377c8
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorAdmin.java
@@ -0,0 +1,359 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/MonitorAdmin.java,v 1.25 2006/06/16 16:31:25 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 org.osgi.service.monitor;
+
+/**
+ * The <code>MonitorAdmin</code> service is a singleton service that handles
+ * <code>StatusVariable</code> query requests and measurement job control
+ * requests.
+ * <p>
+ * Note that an alternative but not recommended way of obtaining
+ * <code>StatusVariable</code>s is that applications having the required
+ * <code>ServicePermissions</code> can query the list of
+ * <code>Monitorable</code> services from the service registry and then query
+ * the list of <code>StatusVariable</code> names from the
+ * <code>Monitorable</code> services. This way all services which publish
+ * <code>StatusVariable</code>s will be returned regardless of whether they
+ * do or do not hold the necessary <code>MonitorPermission</code> for
+ * publishing <code>StatusVariable</code>s. By using the
+ * <code>MonitorAdmin</code> to obtain the <code>StatusVariable</code>s it
+ * is guaranteed that only those <code>Monitorable</code> services will be
+ * accessed who are authorized to publish <code>StatusVariable</code>s. It is
+ * the responsibility of the <code>MonitorAdmin</code> implementation to check
+ * the required permissions and show only those variables which pass this check.
+ * <p>
+ * The events posted by <code>MonitorAdmin</code> contain the following
+ * properties:
+ * <ul>
+ * <li><code>mon.monitorable.pid</code>: The identifier of the
+ * <code>Monitorable</code>
+ * <li><code>mon.statusvariable.name</code>: The identifier of the
+ * <code>StatusVariable</code> within the given <code>Monitorable</code>
+ * <li><code>mon.statusvariable.value</code>: The value of the
+ * <code>StatusVariable</code>, represented as a <code>String</code>
+ * <li><code>mon.listener.id</code>: The identifier of the initiator of the
+ * monitoring job (only present if the event was generated due to a monitoring
+ * job)
+ * </ul>
+ * <p>
+ * Most of the methods require either a Monitorable ID or a Status Variable path
+ * parameter, the latter in [Monitorable_ID]/[StatusVariable_ID] format.  These
+ * parameters must not be <code>null</code>, and the IDs they contain must
+ * conform to their respective definitions in {@link Monitorable} and
+ * {@link StatusVariable}.  If any of the restrictions are violated, the method
+ * must throw an <code>IllegalArgumentException</code>.
+ */
+public interface MonitorAdmin {
+
+    /**
+     * Returns a <code>StatusVariable</code> addressed by its full path. 
+     * The entity which queries a <code>StatusVariable</code> needs to hold
+     * <code>MonitorPermission</code> for the given target with the
+     * <code>read</code> action present.
+     * 
+     * @param path the full path of the <code>StatusVariable</code> in
+     *        [Monitorable_ID]/[StatusVariable_ID] format
+     * @return the <code>StatusVariable</code> object
+     * @throws java.lang.IllegalArgumentException if <code>path</code> is
+     *         <code>null</code> or otherwise invalid, or points to a
+     *         non-existing <code>StatusVariable</code>
+     * @throws java.lang.SecurityException if the caller does not hold a
+     *         <code>MonitorPermission</code> for the
+     *         <code>StatusVariable</code> specified by <code>path</code>
+     *         with the <code>read</code> action present
+     */
+    public StatusVariable getStatusVariable(String path)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Returns the names of the <code>Monitorable</code> services that are
+     * currently registered. The <code>Monitorable</code> instances are not
+     * accessible through the <code>MonitorAdmin</code>, so that requests to
+     * individual status variables can be filtered with respect to the
+     * publishing rights of the <code>Monitorable</code> and the reading
+     * rights of the caller.
+     * <p>
+     * The returned array contains the names in alphabetical order. It cannot be
+     * <code>null</code>, an empty array is returned if no
+     * <code>Monitorable</code> services are registered.
+     * 
+     * @return the array of <code>Monitorable</code> names
+     */
+    public String[] getMonitorableNames();
+
+    /**
+     * Returns the <code>StatusVariable</code> objects published by a
+     * <code>Monitorable</code> instance. The <code>StatusVariables</code>
+     * will hold the values taken at the time of this method call. Only those
+     * status variables are returned where the following two conditions are met:
+     * <ul>
+     * <li>the specified <code>Monitorable</code> holds a
+     * <code>MonitorPermission</code> for the status variable with the
+     * <code>publish</code> action present
+     * <li>the caller holds a <code>MonitorPermission</code> for the status
+     * variable with the <code>read</code> action present
+     * </ul>
+     * All other status variables are silently ignored, they are omitted from
+     * the result.
+     * <p>
+     * The elements in the returned array are in no particular order. The return
+     * value cannot be <code>null</code>, an empty array is returned if no 
+     * (authorized and readable) Status Variables are provided by the given 
+     * <code>Monitorable</code>.
+     * 
+     * @param monitorableId the identifier of a <code>Monitorable</code>
+     *        instance
+     * @return a list of <code>StatusVariable</code> objects published
+     *         by the specified <code>Monitorable</code>
+     * @throws java.lang.IllegalArgumentException if <code>monitorableId</code>
+     *         is <code>null</code> or otherwise invalid, or points to a
+     *         non-existing <code>Monitorable</code>
+     */
+    public StatusVariable[] getStatusVariables(String monitorableId)
+            throws IllegalArgumentException;
+
+    /**
+     * Returns the list of <code>StatusVariable</code> names published by a
+     * <code>Monitorable</code> instance. Only those status variables are
+     * listed where the following two conditions are met:
+     * <ul>
+     * <li>the specified <code>Monitorable</code> holds a
+     * <code>MonitorPermission</code> for the status variable with the
+     * <code>publish</code> action present
+     * <li>the caller holds a <code>MonitorPermission</code> for
+     * the status variable with the <code>read</code> action present
+     * </ul>
+     * All other status variables are silently ignored, their names are omitted
+     * from the list.
+     * <p>
+     * The returned array does not contain duplicates, and the elements are in 
+     * alphabetical order. It cannot be <code>null</code>, an empty array is 
+     * returned if no (authorized and readable) Status Variables are provided 
+     * by the given <code>Monitorable</code>.
+     * 
+     * @param monitorableId the identifier of a <code>Monitorable</code>
+     *        instance
+     * @return a list of <code>StatusVariable</code> objects names
+     *         published by the specified <code>Monitorable</code>
+     * @throws java.lang.IllegalArgumentException if <code>monitorableId</code>
+     *         is <code>null</code> or otherwise invalid, or points to a
+     *         non-existing <code>Monitorable</code>
+     */
+    public String[] getStatusVariableNames(String monitorableId)
+            throws IllegalArgumentException;
+
+    /**
+     * Switches event sending on or off for the specified 
+     * <code>StatusVariable</code>s. When the <code>MonitorAdmin</code> is
+     * notified about a <code>StatusVariable</code> being updated it sends an
+     * event unless this feature is switched off. Note that events within a
+     * monitoring job can not be switched off. The event sending state of the 
+     * <code>StatusVariables</code> must not be persistently stored. When a 
+     * <code>StatusVariable</code> is registered for the first time in a 
+     * framework session, its event sending state is set to ON by default.
+     * <p>
+     * Usage of the "*" wildcard is allowed in the path argument of this method
+     * as a convenience feature. The wildcard can be used in either or both path
+     * fragments, but only at the end of the fragments.  The semantics of the 
+     * wildcard is that it stands for any matching <code>StatusVariable</code> 
+     * at the time of the method call, it does not affect the event sending 
+     * status of <code>StatusVariable</code>s which are not yet registered. As 
+     * an example, when the <code>switchEvents("MyMonitorable/*", false)</code>
+     * method is executed, event sending from all <code>StatusVariables</code>
+     * of the MyMonitorable service are switched off. However, if the
+     * MyMonitorable service starts to publish a new <code>StatusVariable</code>
+     * later, it's event sending status is on by default.
+     * 
+     * @param path the identifier of the <code>StatusVariable</code>(s) in
+     *        [Monitorable_id]/[StatusVariable_id] format, possibly with the 
+     *        "*" wildcard at the end of either path fragment
+     * @param on <code>false</code> if event sending should be switched off, 
+     *        <code>true</code> if it should be switched on for the given path
+     * @throws java.lang.SecurityException if the caller does not hold
+     *         <code>MonitorPermission</code> with the
+     *         <code>switchevents</code> action or if there is any
+     *         <code>StatusVariable</code> in the <code>path</code> field for
+     *         which it is not allowed to switch event sending on or off as per 
+     *         the target field of the permission
+     * @throws java.lang.IllegalArgumentException if <code>path</code> is 
+     *         <code>null</code> or otherwise invalid, or points to a 
+     *         non-existing <code>StatusVariable</code>
+     */
+    public void switchEvents(String path, boolean on)
+        throws IllegalArgumentException, SecurityException;
+    
+    /**
+     * Issues a request to reset a given <code>StatusVariable</code>.
+     * Depending on the semantics of the <code>StatusVariable</code> this call
+     * may or may not succeed: it makes sense to reset a counter to its starting
+     * value, but e.g. a <code>StatusVariable</code> of type String might not
+     * have a meaningful default value. Note that for numeric
+     * <code>StatusVariable</code>s the starting value may not necessarily be
+     * 0. Resetting a <code>StatusVariable</code> triggers a monitor event if
+     * the <code>StatusVariable</code> supports update notifications.
+     * <p>
+     * The entity that wants to reset the <code>StatusVariable</code> needs to
+     * hold <code>MonitorPermission</code> with the <code>reset</code>
+     * action present. The target field of the permission must match the
+     * <code>StatusVariable</code> name to be reset.
+     * 
+     * @param path the identifier of the <code>StatusVariable</code> in
+     *        [Monitorable_id]/[StatusVariable_id] format
+     * @return <code>true</code> if the <code>Monitorable</code> could
+     *         successfully reset the given <code>StatusVariable</code>,
+     *         <code>false</code> otherwise
+     * @throws java.lang.IllegalArgumentException if <code>path</code> is 
+     *         <code>null</code> or otherwise invalid, or points to a 
+     *         non-existing <code>StatusVariable</code>
+     * @throws java.lang.SecurityException if the caller does not hold
+     *         <code>MonitorPermission</code> with the <code>reset</code>
+     *         action or if the specified <code>StatusVariable</code> is not
+     *         allowed to be reset as per the target field of the permission
+     */
+    public boolean resetStatusVariable(String path)
+            throws IllegalArgumentException, SecurityException;
+    
+    /**
+     * Returns a human readable description of the given 
+     * <code>StatusVariable</code>. The <code>null</code> value may be returned
+     * if there is no description for the given <code>StatusVariable</code>.
+     * <p>
+     * The entity that queries a <code>StatusVariable</code> needs to hold
+     * <code>MonitorPermission</code> for the given target with the
+     * <code>read</code> action present.
+     * 
+     * @param path the full path of the <code>StatusVariable</code> in
+     *        [Monitorable_ID]/[StatusVariable_ID] format
+     * @return the human readable description of this
+     *         <code>StatusVariable</code> or <code>null</code> if it is not
+     *         set
+     * @throws java.lang.IllegalArgumentException if <code>path</code> is 
+     *         <code>null</code> or otherwise invalid, or points to a 
+     *         non-existing <code>StatusVariable</code>
+     * @throws java.lang.SecurityException if the caller does not hold a
+     *         <code>MonitorPermission</code> for the
+     *         <code>StatusVariable</code> specified by <code>path</code>
+     *         with the <code>read</code> action present
+     */
+    public String getDescription(String path) 
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Starts a time based <code>MonitoringJob</code> with the parameters
+     * provided. Monitoring events will be sent according to the specified
+     * schedule. All specified <code>StatusVariable</code>s must exist when the
+     * job is started. The initiator string is used in the
+     * <code>mon.listener.id</code> field of all events triggered by the job,
+     * to allow filtering the events based on the initiator.
+     * <p>
+     * The <code>schedule</code> parameter specifies the time in seconds 
+     * between two measurements, it must be greater than 0.  The first 
+     * measurement will be taken when the timer expires for the first time, not 
+     * when this method is called.
+     * <p>
+     * The <code>count</code> parameter defines the number of measurements to be
+     * taken, and must either be a positive integer, or 0 if the measurement is
+     * to run until explicitely stopped.
+     * <p>
+     * The entity which initiates a <code>MonitoringJob</code> needs to hold
+     * <code>MonitorPermission</code> for all the specified target
+     * <code>StatusVariable</code>s with the <code>startjob</code> action
+     * present. If the permission's action string specifies a minimal sampling
+     * interval then the <code>schedule</code> parameter should be at least as
+     * great as the value in the action string.
+     * 
+     * @param initiator the identifier of the entity that initiated the job
+     * @param statusVariables the list of <code>StatusVariable</code>s to be
+     *        monitored, with each <code>StatusVariable</code> name given in
+     *        [Monitorable_PID]/[StatusVariable_ID] format
+     * @param schedule the time in seconds between two measurements
+     * @param count the number of measurements to be taken, or 0 for the
+     *        measurement to run until explicitely stopped
+     * @return the successfully started job object, cannot be <code>null</code>
+     * @throws java.lang.IllegalArgumentException if the list of
+     *         <code>StatusVariable</code> names contains an invalid or 
+     *         non-existing <code>StatusVariable</code>; if 
+     *         <code>initiator</code> is <code>null</code> or empty; or if the 
+     *         <code>schedule</code> or <code>count</code> parameters are 
+     *         invalid
+     * @throws java.lang.SecurityException if the caller does not hold
+     *         <code>MonitorPermission</code> for all the specified
+     *         <code>StatusVariable</code>s, with the <code>startjob</code>
+     *         action present, or if the permission does not allow starting the
+     *         job with the given frequency
+     */
+    public MonitoringJob startScheduledJob(String initiator,
+            String[] statusVariables, int schedule, int count)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Starts a change based <code>MonitoringJob</code> with the parameters
+     * provided. Monitoring events will be sent when the
+     * <code>StatusVariable</code>s of this job are updated. All specified
+     * <code>StatusVariable</code>s must exist when the job is started, and
+     * all must support update notifications. The initiator string is used in
+     * the <code>mon.listener.id</code> field of all events triggered by the
+     * job, to allow filtering the events based on the initiator.
+     * <p>
+     * The <code>count</code> parameter specifies the number of changes that
+     * must happen to a <code>StatusVariable</code> before a new notification is
+     * sent, this must be a positive integer.
+     * <p>
+     * The entity which initiates a <code>MonitoringJob</code> needs to hold
+     * <code>MonitorPermission</code> for all the specified target
+     * <code>StatusVariable</code>s with the <code>startjob</code> action
+     * present.
+     * 
+     * @param initiator the identifier of the entity that initiated the job
+     * @param statusVariables the list of <code>StatusVariable</code>s to be
+     *        monitored, with each <code>StatusVariable</code> name given in
+     *        [Monitorable_PID]/[StatusVariable_ID] format
+     * @param count the number of changes that must happen to a 
+     *        <code>StatusVariable</code> before a new notification is sent
+     * @return the successfully started job object, cannot be <code>null</code>
+     * @throws java.lang.IllegalArgumentException if the list of
+     *         <code>StatusVariable</code> names contains an invalid or 
+     *         non-existing <code>StatusVariable</code>, or one that does not 
+     *         support notifications; if the <code>initiator</code> is 
+     *         <code>null</code> or empty; or if <code>count</code> is invalid
+     * @throws java.lang.SecurityException if the caller does not hold
+     *         <code>MonitorPermission</code> for all the specified
+     *         <code>StatusVariable</code>s, with the <code>startjob</code>
+     *         action present
+     */
+    public MonitoringJob startJob(String initiator, String[] statusVariables,
+            int count) throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Returns the list of currently running <code>MonitoringJob</code>s.
+     * Jobs are only visible to callers that have the necessary permissions: to 
+     * receive a Monitoring Job in the returned list, the caller must hold all 
+     * permissions required for starting the job.  This means that if the caller
+     * does not have <code>MonitorPermission</code> with the proper
+     * <code>startjob</code> action for all the Status Variables monitored by a 
+     * job, then that job will be silently omitted from the results.
+     * <p>
+     * The returned array cannot be <code>null</code>, an empty array is
+     * returned if there are no running jobs visible to the caller at the time 
+     * of the call.
+     * 
+     * @return the list of running jobs visible to the caller
+     */
+    public MonitoringJob[] getRunningJobs();
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorListener.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorListener.java
new file mode 100644
index 0000000..44fa845
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorListener.java
@@ -0,0 +1,42 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/MonitorListener.java,v 1.11 2006/06/16 16:31:25 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 org.osgi.service.monitor;
+
+/**
+ * The <code>MonitorListener</code> is used by <code>Monitorable</code>
+ * services to send notifications when a <code>StatusVariable</code> value is
+ * changed. The <code>MonitorListener</code> should register itself as a
+ * service at the OSGi Service Registry. This interface must (only) be 
+ * implemented by the Monitor Admin component.
+ */
+public interface MonitorListener {
+    /**
+     * Callback for notification of a <code>StatusVariable</code> change.
+     * 
+     * @param monitorableId the identifier of the <code>Monitorable</code>
+     *        instance reporting the change
+     * @param statusVariable the <code>StatusVariable</code> that has changed
+     * @throws java.lang.IllegalArgumentException if the specified monitorable
+     *         ID is invalid (<code>null</code>, empty, or contains illegal
+     *         characters) or points to a non-existing <code>Monitorable</code>, 
+     *         or if <code>statusVariable</code> is <code>null</code>
+     */
+    public void updated(String monitorableId, StatusVariable statusVariable)
+            throws IllegalArgumentException;
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java
new file mode 100644
index 0000000..b0ca12d
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java
@@ -0,0 +1,366 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/MonitorPermission.java,v 1.17 2006/06/21 15:17:16 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2005, 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 org.osgi.service.monitor;
+
+import java.io.UnsupportedEncodingException;
+import java.security.Permission;
+import java.util.StringTokenizer;
+
+/**
+ * Indicates the callers authority to publish, read or reset
+ * <code>StatusVariable</code>s, to switch event sending on or off or to
+ * start monitoring jobs. The target of the permission is the identifier of the
+ * <code>StatusVariable</code>, the action can be <code>read</code>,
+ * <code>publish</code>, <code>reset</code>, <code>startjob</code>,
+ * <code>switchevents</code>, or the combination of these separated by
+ * commas.  Action names are interpreted case-insensitively, but the canonical 
+ * action string returned by {@link #getActions} uses the forms defined by the 
+ * action constants.
+ * <p>
+ * If the wildcard <code>*</code> appears in the actions field, all legal 
+ * monitoring commands are allowed on the designated target(s) by the owner of 
+ * the permission.
+ */
+public class MonitorPermission extends Permission {
+
+    /**
+	 * 
+	 */
+	private static final long	serialVersionUID	= -9084425194463274314L;
+
+	/**
+     * Holders of <code>MonitorPermission</code> with the <code>read</code>
+     * action present are allowed to read the value of the
+     * <code>StatusVariable</code>s specified in the permission's target field.
+     */
+    public static final String READ = "read";
+
+    /**
+     * Holders of <code>MonitorPermission</code> with the <code>reset</code>
+     * action present are allowed to reset the value of the
+     * <code>StatusVariable</code>s specified in the permission's target field.
+     */
+    public static final String RESET = "reset";
+
+    /**
+     * Holders of <code>MonitorPermission</code> with the <code>publish</code>
+     * action present are <code>Monitorable</code> services that are allowed
+     * to publish the <code>StatusVariable</code>s specified in the
+     * permission's target field.  Note, that this permission cannot be enforced 
+     * when a <code>Monitorable</code> registers to the framework, because the
+     * Service Registry does not know about this permission.  Instead, any
+     * <code>StatusVariable</code>s published by a <code>Monitorable</code>
+     * without the corresponding <code>publish</code> permission are silently
+     * ignored by <code>MonitorAdmin</code>, and are therefore invisible to the
+     * users of the monitoring service.   
+     */
+    public static final String PUBLISH = "publish";
+
+    /**
+     * Holders of <code>MonitorPermission</code> with the <code>startjob</code>
+     * action present are allowed to initiate monitoring jobs involving the 
+     * <code>StatusVariable</code>s specified in the permission's target field.
+     * <p>
+     * A minimal sampling interval can be optionally defined in the following
+     * form: <code>startjob:n</code>.  This allows the holder of the permission
+     * to initiate time based jobs with a measurement interval of at least
+     * <code>n</code> seconds. If <code>n</code> is not specified or 0 then the 
+     * holder of this permission is allowed to start monitoring jobs specifying 
+     * any frequency.
+     */
+    public static final String STARTJOB = "startjob";
+
+    /**
+     * Holders of <code>MonitorPermission</code> with the
+     * <code>switchevents</code> action present are allowed to switch event
+     * sending on or off for the value of the <code>StatusVariable</code>s
+     * specified in the permission's target field.
+     */
+    public static final String SWITCHEVENTS = "switchevents";
+
+    private static final int READ_FLAG         = 0x1;
+    private static final int RESET_FLAG        = 0x2;
+    private static final int PUBLISH_FLAG      = 0x4;
+    private static final int STARTJOB_FLAG     = 0x8;
+    private static final int SWITCHEVENTS_FLAG = 0x10;
+    
+    private static final int ALL_FLAGS = READ_FLAG | RESET_FLAG | 
+        PUBLISH_FLAG | STARTJOB_FLAG | SWITCHEVENTS_FLAG;
+
+    private String monId;
+    private String varId;
+    private boolean prefixMonId;
+    private boolean prefixVarId;
+    private int mask;
+    private int minJobInterval;
+
+    /**
+     * Create a <code>MonitorPermission</code> object, specifying the target
+     * and actions.
+     * <p>
+     * The <code>statusVariable</code> parameter is the target of the 
+     * permission, defining one or more status variable names to which the
+     * specified actions apply. Multiple status variable names can be selected
+     * by using the wildcard <code>*</code> in the target string.  The wildcard
+     * is allowed in both fragments, but only at the end of the fragments.
+     * <p>
+     * For example, the following targets are valid:
+     * <code>com.mycomp.myapp/queue_length</code>,
+     * <code>com.mycomp.myapp/*</code>, <code>com.mycomp.&#42;/*</code>,
+     * <code>&#42;/*</code>, <code>&#42;/queue_length</code>, 
+     * <code>&#42;/queue*</code>.
+     * <p>
+     * The following targets are invalid:
+     * <code>*.myapp/queue_length</code>, <code>com.*.myapp/*</code>,
+     * <code>*</code>.
+     * <p>
+     * The <code>actions</code> parameter specifies the allowed action(s): 
+     * <code>read</code>, <code>publish</code>, <code>startjob</code>,
+     * <code>reset</code>, <code>switchevents</code>, or the combination of 
+     * these separated by commas. String constants are defined in this class for
+     * each valid action.  Passing <code>&quot;*&quot;</code> as the action 
+     * string is equivalent to listing all actions. 
+     * 
+     * @param statusVariable the identifier of the <code>StatusVariable</code>
+     *        in [Monitorable_id]/[StatusVariable_id] format 
+     * @param actions the list of allowed actions separated by commas, or
+     *        <code>*</code> for all actions
+     * @throws java.lang.IllegalArgumentException if either parameter is 
+     *         <code>null</code>, or invalid with regard to the constraints
+     *         defined above and in the documentation of the used actions 
+     */
+    public MonitorPermission(String statusVariable, String actions) 
+            throws IllegalArgumentException {
+        super(statusVariable);
+
+        if(statusVariable == null)
+            throw new IllegalArgumentException(
+                    "Invalid StatusVariable path 'null'.");
+        
+        if(actions == null)
+            throw new IllegalArgumentException(
+                    "Invalid actions string 'null'.");
+        
+        int sep = statusVariable.indexOf('/');
+        int len = statusVariable.length();
+
+        if (sep == -1)
+            throw new IllegalArgumentException(
+                    "Invalid StatusVariable path: should contain '/' separator.");
+        if (sep == 0 || sep == statusVariable.length() - 1)
+            throw new IllegalArgumentException(
+                    "Invalid StatusVariable path: empty monitorable ID or StatusVariable name.");
+
+        prefixMonId = statusVariable.charAt(sep - 1) == '*';
+        prefixVarId = statusVariable.charAt(len - 1) == '*';
+        
+        monId = statusVariable.substring(0, prefixMonId ? sep - 1 : sep);
+        varId = statusVariable.substring(sep + 1, prefixVarId ? len - 1 : len);
+
+        checkId(monId, "Monitorable ID part of the target");
+        checkId(varId, "Status Variable ID part of the target");
+
+        minJobInterval = 0;
+
+        if(actions.equals("*"))
+            mask = ALL_FLAGS;
+        else {
+            mask = 0;
+            StringTokenizer st = new StringTokenizer(actions, ",");
+            while (st.hasMoreTokens()) {
+                String action = st.nextToken();
+                if (action.equalsIgnoreCase(READ)) {
+                    addToMask(READ_FLAG, READ);
+                } else if (action.equalsIgnoreCase(RESET)) {
+                    addToMask(RESET_FLAG, RESET);
+                } else if (action.equalsIgnoreCase(PUBLISH)) {
+                    addToMask(PUBLISH_FLAG, PUBLISH);
+                } else if (action.equalsIgnoreCase(SWITCHEVENTS)) {
+                    addToMask(SWITCHEVENTS_FLAG, SWITCHEVENTS);
+                } else if (action.toLowerCase().startsWith(STARTJOB)) {
+                    minJobInterval = 0;
+    
+                    int slen = STARTJOB.length();
+                    if (action.length() != slen) {
+                        if (action.charAt(slen) != ':')
+                            throw new IllegalArgumentException(
+                                    "Invalid action '" + action + "'.");
+    
+                        try {
+                            minJobInterval = Integer.parseInt(action
+                                    .substring(slen + 1));
+                        } catch (NumberFormatException e) {
+                            throw new IllegalArgumentException(
+                                    "Invalid parameter in startjob action '"
+                                            + action + "'.");
+                        }
+                    }
+                    addToMask(STARTJOB_FLAG, STARTJOB);
+                } else
+                    throw new IllegalArgumentException("Invalid action '" + 
+                            action + "'");
+            }
+        }
+    }
+    
+    private void addToMask(int action, String actionString) {
+        if((mask & action) != 0)
+            throw new IllegalArgumentException("Invalid action string: " + 
+                    actionString + " appears multiple times.");
+        
+        mask |= action;
+    }
+
+    private void checkId(String id, String idName)
+            throws IllegalArgumentException {
+        
+        byte[] nameBytes;
+        try {
+            nameBytes = id.getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            // never happens, "UTF-8" must always be supported
+            throw new IllegalStateException(e.getMessage());
+        }
+        if(nameBytes.length > StatusVariable.MAX_ID_LENGTH)
+            throw new IllegalArgumentException(idName + " is too long (over " +
+                    StatusVariable.MAX_ID_LENGTH + " bytes in UTF-8 encoding).");
+
+        
+        if (id.equals(".") || id.equals(".."))
+            throw new IllegalArgumentException(idName + " is invalid.");
+        
+        char[] chars = id.toCharArray();
+        for (int i = 0; i < chars.length; i++)
+            if (StatusVariable.SYMBOLIC_NAME_CHARACTERS.indexOf(chars[i]) == -1)
+                throw new IllegalArgumentException(idName +
+                        " contains invalid characters.");
+    }
+    
+    /**
+     * Create an integer hash of the object. The hash codes of
+     * <code>MonitorPermission</code>s <code>p1</code> and <code>p2</code> are 
+     * the same if <code>p1.equals(p2)</code>.
+     * 
+     * @return the hash of the object
+     */
+    public int hashCode() {
+        return new Integer(mask).hashCode()
+                ^ new Integer(minJobInterval).hashCode() ^ monId.hashCode()
+                ^ new Boolean(prefixMonId).hashCode()
+                ^ varId.hashCode()
+                ^ new Boolean(prefixVarId).hashCode();
+    }
+
+    /**
+     * Determines the equality of two <code>MonitorPermission</code> objects.
+     * Two <code>MonitorPermission</code> objects are equal if their target
+     * strings are equal and the same set of actions are listed in their action
+     * strings.
+     * 
+     * @param o the object being compared for equality with this object
+     * @return <code>true</code> if the two permissions are equal
+     */
+    public boolean equals(Object o) {
+        if (!(o instanceof MonitorPermission))
+            return false;
+
+        MonitorPermission other = (MonitorPermission) o;
+
+        return mask == other.mask && minJobInterval == other.minJobInterval
+                && monId.equals(other.monId)
+                && prefixMonId == other.prefixMonId
+                && varId.equals(other.varId)
+                && prefixVarId == other.prefixVarId;
+    }
+
+    /**
+     * Get the action string associated with this permission.  The actions are
+     * returned in the following order: <code>read</code>, <code>reset</code>, 
+     * <code>publish</code>, <code>startjob</code>, <code>switchevents</code>.
+     * 
+     * @return the allowed actions separated by commas, cannot be
+     *         <code>null</code>
+     */
+    public String getActions() {
+        StringBuffer sb = new StringBuffer();
+
+        appendAction(sb, READ_FLAG,         READ);
+        appendAction(sb, RESET_FLAG,        RESET);
+        appendAction(sb, PUBLISH_FLAG,      PUBLISH);
+        appendAction(sb, STARTJOB_FLAG,     STARTJOB);
+        appendAction(sb, SWITCHEVENTS_FLAG, SWITCHEVENTS);
+
+        return sb.toString();
+    }
+
+    private void appendAction(StringBuffer sb, int flag, String actionName) {
+        if ((mask & flag) != 0) {
+            if(sb.length() != 0)
+                sb.append(',');
+            sb.append(actionName);
+            
+            if(flag == STARTJOB_FLAG && minJobInterval != 0)
+                sb.append(':').append(minJobInterval);
+        }
+    }
+
+    /**
+     * Determines if the specified permission is implied by this permission.
+     * <p>
+     * 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 <code>MonitorPermission</code>
+     * <li>it has a broader set of actions allowed than this one
+     * <li>it allows initiating time based monitoring jobs with a lower minimal
+     * sampling interval
+     * <li>the target set of <code>Monitorable</code>s is not the same nor a
+     * subset of the target set of <code>Monitorable</code>s of this permission
+     * <li>the target set of <code>StatusVariable</code>s is not the same
+     * nor a subset of the target set of <code>StatusVariable</code>s of this
+     * permission
+     * </ul>
+     * 
+     * @param p the permission to be checked
+     * @return <code>true</code> if the given permission is implied by this
+     *         permission
+     */
+    public boolean implies(Permission p) {
+        if (!(p instanceof MonitorPermission))
+            return false;
+
+        MonitorPermission other = (MonitorPermission) p;
+
+        if ((mask & other.mask) != other.mask)
+            return false;
+
+        if ((other.mask & STARTJOB_FLAG) != 0
+                && minJobInterval > other.minJobInterval)
+            return false;
+
+        return implies(monId, prefixMonId, other.monId, other.prefixMonId)
+                && implies(varId, prefixVarId, other.varId, other.prefixVarId);
+    }
+
+    private boolean implies(String id, boolean prefix, String oid,
+            boolean oprefix) {
+
+        return prefix ? oid.startsWith(id) : !oprefix && id.equals(oid);
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/Monitorable.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/Monitorable.java
new file mode 100644
index 0000000..8203ddd
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/Monitorable.java
@@ -0,0 +1,140 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/Monitorable.java,v 1.17 2006/06/16 16:31:25 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 org.osgi.service.monitor;
+
+/**
+ * A <code>Monitorable</code> can provide information about itself in the form
+ * of <code>StatusVariables</code>. Instances of this interface should
+ * register themselves at the OSGi Service Registry. The
+ * <code>MonitorAdmin</code> listens to the registration of
+ * <code>Monitorable</code> services, and makes the information they provide
+ * available also through the Device Management Tree (DMT) for remote access.
+ * <p>
+ * The monitorable service is identified by its PID string which must be a non-
+ * <code>null</code>, non-empty string that conforms to the "symbolic-name"
+ * definition in the OSGi core specification. This means that only the
+ * characters [-_.a-zA-Z0-9] may be used. The length of the PID must not exceed
+ * 20 characters.
+ * <p>
+ * A <code>Monitorable</code> may optionally support sending notifications
+ * when the status of its <code>StatusVariables</code> change. Support for
+ * change notifications can be defined per <code>StatusVariable</code>.
+ * <p>
+ * Publishing <code>StatusVariables</code> requires the presence of the
+ * <code>MonitorPermission</code> with the <code>publish</code> action
+ * string. This permission, however, is not checked during registration of the
+ * <code>Monitorable</code> service. Instead, the <code>MonitorAdmin</code>
+ * implemenatation must make sure that when a <code>StatusVariable</code> is
+ * queried, it is shown only if the <code>Monitorable</code> is authorized to
+ * publish the given <code>StatusVariable</code>.
+ */
+public interface Monitorable {
+    /**
+     * Returns the list of <code>StatusVariable</code> identifiers published
+     * by this <code>Monitorable</code>. A <code>StatusVariable</code> name
+     * is unique within the scope of a <code>Monitorable</code>. The array
+     * contains the elements in no particular order. The returned value must not
+     * be <code>null</code>.
+     * 
+     * @return the <code>StatusVariable<code> identifiers published by this 
+     *         object, or an empty array if none are published
+     */
+    public String[] getStatusVariableNames();
+    
+    /**
+     * Returns the <code>StatusVariable</code> object addressed by its
+     * identifier. The <code>StatusVariable</code> will hold the value taken
+     * at the time of this method call.
+     * <p>
+     * The given identifier does not contain the Monitorable PID, i.e. it 
+     * specifies the name and not the path of the Status Variable.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>, cannot be
+     *        <code>null</code> 
+     * @return the <code>StatusVariable</code> object
+     * @throws java.lang.IllegalArgumentException if <code>id</code> points to a
+     *         non-existing <code>StatusVariable</code>
+     */
+    public StatusVariable getStatusVariable(String id)
+            throws IllegalArgumentException;
+
+    /**
+     * Tells whether the <code>StatusVariable</code> provider is able to send
+     * instant notifications when the given <code>StatusVariable</code>
+     * changes. If the <code>Monitorable</code> supports sending change
+     * updates it must notify the <code>MonitorListener</code> when the value
+     * of the <code>StatusVariable</code> changes. The
+     * <code>Monitorable</code> finds the <code>MonitorListener</code>
+     * service through the Service Registry.
+     * <p>
+     * The given identifier does not contain the Monitorable PID, i.e. it 
+     * specifies the name and not the path of the Status Variable.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>, cannot be
+     *        <code>null</code> 
+     * @return <code>true</code> if the <code>Monitorable</code> can send
+     *         notification when the given <code>StatusVariable</code>
+     *         changes, <code>false</code> otherwise
+     * @throws java.lang.IllegalArgumentException if <code>id</code> points to a
+     *         non-existing <code>StatusVariable</code>
+     */
+    public boolean notifiesOnChange(String id) throws IllegalArgumentException;
+
+    /**
+     * Issues a request to reset a given <code>StatusVariable</code>.
+     * Depending on the semantics of the actual Status Variable this call may or
+     * may not succeed: it makes sense to reset a counter to its starting value,
+     * but for example a <code>StatusVariable</code> of type <code>String</code>
+     * might not have a meaningful default value. Note that for numeric
+     * <code>StatusVariables</code> the starting value may not necessarily be
+     * 0. Resetting a <code>StatusVariable</code> must trigger a monitor event.
+     * <p>
+     * The given identifier does not contain the Monitorable PID, i.e. it 
+     * specifies the name and not the path of the Status Variable.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>, cannot be
+     *        <code>null</code> 
+     * @return <code>true</code> if the <code>Monitorable</code> could
+     *         successfully reset the given <code>StatusVariable</code>,
+     *         <code>false</code> otherwise
+     * @throws java.lang.IllegalArgumentException if <code>id</code> points to a
+     *         non-existing <code>StatusVariable</code>
+     */
+    public boolean resetStatusVariable(String id)
+            throws IllegalArgumentException;
+    
+    /**
+     * Returns a human readable description of a <code>StatusVariable</code>.
+     * This can be used by management systems on their GUI. The 
+     * <code>null</code> return value is allowed if there is no description for
+     * the specified Status Variable.
+     * <p>
+     * The given identifier does not contain the Monitorable PID, i.e. it 
+     * specifies the name and not the path of the Status Variable.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>, cannot be
+     *        <code>null</code> 
+     * @return the human readable description of this
+     *         <code>StatusVariable</code> or <code>null</code> if it is not
+     *         set
+     * @throws java.lang.IllegalArgumentException if <code>id</code> points to a
+     *         non-existing <code>StatusVariable</code>
+     */
+    public String getDescription(String id) throws IllegalArgumentException;
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitoringJob.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitoringJob.java
new file mode 100644
index 0000000..75b9d2e
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitoringJob.java
@@ -0,0 +1,126 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/MonitoringJob.java,v 1.14 2006/06/16 16:31:25 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 org.osgi.service.monitor;
+
+/**
+ * A Monitoring Job is a request for scheduled or event based notifications on
+ * update of a set of <code>StatusVariable</code>s. The job is a data
+ * structure that holds a non-empty list of <code>StatusVariable</code> names,
+ * an identification of the initiator of the job, and the sampling parameters.
+ * There are two kinds of monitoring jobs: time based and change based. Time
+ * based jobs take samples of all <code>StatusVariable</code>s with a
+ * specified frequency. The number of samples to be taken before the job
+ * finishes may be specified. Change based jobs are only interested in the
+ * changes of the monitored <code>StatusVariable</code>s. In this case, the
+ * number of changes that must take place between two notifications can be
+ * specified.
+ * <p>
+ * The job can be started on the <code>MonitorAdmin</code> interface. Running
+ * the job (querying the <code>StatusVariable</code>s, listening to changes,
+ * and sending out notifications on updates) is the task of the
+ * <code>MonitorAdmin</code> implementation.
+ * <p>
+ * Whether a monitoring job keeps track dynamically of the
+ * <code>StatusVariable</code>s it monitors is not specified. This means that
+ * if we monitor a <code>StatusVariable</code> of a <code>Monitorable</code>
+ * service which disappears and later reappears then it is implementation
+ * specific whether we still receive updates of the <code>StatusVariable</code>
+ * changes or not.
+ */
+public interface MonitoringJob {
+    /**
+     * Stops a Monitoring Job. Note that a time based job can also stop
+     * automatically if the specified number of samples have been taken.
+     */
+    public void stop();
+
+    /**
+     * Returns the identitifier of the principal who initiated the job. This is
+     * set at the time when
+     * {@link MonitorAdmin#startJob MonitorAdmin.startJob()} method is called.
+     * This string holds the ServerID if the operation was initiated from a
+     * remote manager, or an arbitrary ID of the initiator entity in the local
+     * case (used for addressing notification events).
+     * 
+     * @return the ID of the initiator, cannot be <code>null</code>
+     */
+    public String getInitiator();
+
+    /**
+     * Returns the list of <code>StatusVariable</code> names that are the
+     * targets of this measurement job. For time based jobs, the
+     * <code>MonitorAdmin</code> will iterate through this list and query all
+     * <code>StatusVariable</code>s when its timer set by the job's frequency
+     * rate expires.
+     * 
+     * @return the target list of the measurement job in
+     *         [Monitorable_ID]/[StatusVariable_ID] format, cannot be
+     *         <code>null</code>
+     */
+    public String[] getStatusVariableNames();
+
+    /**
+     * Returns the delay (in seconds) between two samples. If this call returns
+     * N (greater than 0) then the <code>MonitorAdmin</code> queries each
+     * <code>StatusVariable</code> that belongs to this job every N seconds.
+     * The value 0 means that the job is not scheduled but event based: in this
+     * case instant notification on changes is requested (at every nth change of
+     * the value, as specified by the report count parameter).
+     * 
+     * @return the delay (in seconds) between samples, or 0 for change based
+     *         jobs
+     */
+    public int getSchedule();
+
+    /**
+     * Returns the number of times <code>MonitorAdmin</code> will query the
+     * <code>StatusVariable</code>s (for time based jobs), or the number of
+     * changes of a <code>StatusVariable</code> between notifications (for
+     * change based jobs). Time based jobs with non-zero report count will take
+     * <code>getReportCount()</code>*<code>getSchedule()</code> time to
+     * finish. Time based jobs with 0 report count and change based jobs do not
+     * stop automatically, but all jobs can be stopped with the {@link #stop}
+     * method.
+     * 
+     * @return the number of measurements to be taken, or the number of changes
+     *         between notifications
+     */
+    public int getReportCount();
+
+    /**
+     * Returns whether the job was started locally or remotely.  Jobs started by
+     * the clients of this API are always local, remote jobs can only be started
+     * using the Device Management Tree.
+     * 
+     * @return <code>true</code> if the job was started from the local device,
+     *         <code>false</code> if the job was initiated from a management 
+     *         server through the device management tree
+     */
+    public boolean isLocal();
+    
+    /**
+     * Returns whether the job is running.   A job is running until it is
+     * explicitely stopped, or, in case of time based jobs with a finite report
+     * count, until the given number of measurements have been made.
+     *   
+     * @return <code>true</code> if the job is still running, <code>false</code>
+     *         if it has finished
+     */
+    public boolean isRunning();
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/StatusVariable.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/StatusVariable.java
new file mode 100644
index 0000000..74ff9df
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/StatusVariable.java
@@ -0,0 +1,433 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/StatusVariable.java,v 1.14 2006/06/16 16:31:25 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 org.osgi.service.monitor;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+
+/**
+ * A <code>StatusVariable</code> object represents the value of a status
+ * variable taken with a certain collection method at a certain point of time.
+ * The type of the <code>StatusVariable</code> can be <code>int</code>,
+ * <code>float</code>, <code>boolean</code> or <code>String</code>.
+ * <p>
+ * A <code>StatusVariable</code> is identified by an ID string that is unique
+ * within the scope of a <code>Monitorable</code>. The ID must be a non-
+ * <code>null</code>, non-empty string that conforms to the "symbolic-name"
+ * definition in the OSGi core specification. This means that only the
+ * characters [-_.a-zA-Z0-9] may be used. The length of the ID must not exceed
+ * 32 bytes when UTF-8 encoded.
+ */
+public final class StatusVariable {
+    //----- Public constants -----//
+    /**
+     * Constant for identifying <code>int</code> data type.
+     */
+    public static final int    TYPE_INTEGER   = 0;
+
+    /**
+     * Constant for identifying <code>float</code> data type.
+     */
+    public static final int    TYPE_FLOAT = 1;
+
+    /**
+     * Constant for identifying <code>String</code> data type.
+     */
+    public static final int    TYPE_STRING = 2;
+
+    /**
+     * Constant for identifying <code>boolean</code> data type.
+     */
+   public static final int    TYPE_BOOLEAN = 3;
+
+    /**
+     * Constant for identifying 'Cumulative Counter' data collection method. 
+     */
+    public static final int    CM_CC        = 0;
+
+    /**
+     * Constant for identifying 'Discrete Event Registration' data collection
+     * method.
+     */
+    public static final int    CM_DER       = 1;
+
+    /**
+     * Constant for identifying 'Gauge' data collection method. 
+     */
+    public static final int    CM_GAUGE     = 2;
+
+    /**
+     * Constant for identifying 'Status Inspection' data collection method.
+     */
+    public static final int    CM_SI        = 3;
+
+    //----- Package private constants -----//
+
+    static final String SYMBOLIC_NAME_CHARACTERS =
+        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" +
+        "-_.";   // a subset of the characters allowed in DMT URIs 
+
+    static final int MAX_ID_LENGTH = 32;
+    
+    //----- Private fields -----//
+    private String  id;
+    private Date    timeStamp;
+    private int     cm;
+    private int     type;
+
+    private int     intData;
+    private float   floatData;
+    private String  stringData;
+    private boolean booleanData;
+
+
+    //----- Constructors -----//
+    /**
+     * Constructor for a <code>StatusVariable</code> of <code>int</code>
+     * type.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>
+     * @param cm the collection method, one of the <code>CM_</code> constants
+     * @param data the <code>int</code> value of the
+     *        <code>StatusVariable</code>
+     * @throws java.lang.IllegalArgumentException if the given <code>id</code>
+     *         is not a valid <code>StatusVariable</code> name, or if 
+     *         <code>cm</code> is not one of the collection method constants
+     * @throws java.lang.NullPointerException if the <code>id</code>
+     *         parameter is <code>null</code>
+     */
+    public StatusVariable(String id, int cm, int data) {
+        setCommon(id, cm);
+        type = TYPE_INTEGER;
+        intData = data;
+    }
+
+    /**
+     * Constructor for a <code>StatusVariable</code> of <code>float</code>
+     * type.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>
+     * @param cm the collection method, one of the <code>CM_</code> constants
+     * @param data the <code>float</code> value of the
+     *        <code>StatusVariable</code>
+     * @throws java.lang.IllegalArgumentException if the given <code>id</code>
+     *         is not a valid <code>StatusVariable</code> name, or if
+     *         <code>cm</code> is not one of the collection method constants
+     * @throws java.lang.NullPointerException if the <code>id</code> parameter
+     *         is <code>null</code>
+     */
+    public StatusVariable(String id, int cm, float data) {
+        setCommon(id, cm);
+        type = TYPE_FLOAT;
+        floatData = data;
+    }
+
+    /**
+     * Constructor for a <code>StatusVariable</code> of <code>boolean</code>
+     * type.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>
+     * @param cm the collection method, one of the <code>CM_</code> constants
+     * @param data the <code>boolean</code> value of the
+     *        <code>StatusVariable</code>
+     * @throws java.lang.IllegalArgumentException if the given <code>id</code>
+     *         is not a valid <code>StatusVariable</code> name, or if 
+     *         <code>cm</code> is not one of the collection method constants
+     * @throws java.lang.NullPointerException if the <code>id</code> parameter
+     *         is <code>null</code>
+     */
+    public StatusVariable(String id, int cm, boolean data) {
+        setCommon(id, cm);
+        type = TYPE_BOOLEAN;
+        booleanData = data;
+    }
+
+    /**
+     * Constructor for a <code>StatusVariable</code> of <code>String</code>
+     * type.
+     * 
+     * @param id the identifier of the <code>StatusVariable</code>
+     * @param cm the collection method, one of the <code>CM_</code> constants
+     * @param data the <code>String</code> value of the
+     *        <code>StatusVariable</code>, can be <code>null</code>
+     * @throws java.lang.IllegalArgumentException if the given <code>id</code>
+     *         is not a valid <code>StatusVariable</code> name, or if 
+     *         <code>cm</code> is not one of the collection method constants
+     * @throws java.lang.NullPointerException if the <code>id</code> parameter
+     *         is <code>null</code>
+     */
+    public StatusVariable(String id, int cm, String data) {
+        setCommon(id, cm);
+        type = TYPE_STRING;
+        stringData = data;
+    }
+
+    
+    // ----- Public methods -----//
+    /**
+     * Returns the ID of this <code>StatusVariable</code>. The ID is unique 
+     * within the scope of a <code>Monitorable</code>.
+     * 
+     * @return the ID of this <code>StatusVariable</code>
+     */
+    public String getID() {
+        return id;
+    }
+
+    /**
+     * Returns information on the data type of this <code>StatusVariable</code>.
+     * 
+     * @return one of the <code>TYPE_</code> constants indicating the type of
+     *         this <code>StatusVariable</code>
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * Returns the timestamp associated with the <code>StatusVariable</code>.
+     * The timestamp is stored when the <code>StatusVariable</code> instance is
+     * created, generally during the {@link Monitorable#getStatusVariable} 
+     * method call.
+     * 
+     * @return the time when the <code>StatusVariable</code> value was
+     *         queried, cannot be <code>null</code>
+     * 
+     */
+    public Date getTimeStamp() {
+        return timeStamp;
+    }
+
+    /**
+     * Returns the <code>StatusVariable</code> value if its type is
+     * <code>String</code>.
+     * 
+     * @return the <code>StatusVariable</code> value as a <code>String</code>
+     * @throws java.lang.IllegalStateException if the type of the 
+     * <code>StatusVariable</code> is not <code>String</code>
+     */
+    public String getString() throws IllegalStateException {
+        if (type != TYPE_STRING)
+            throw new IllegalStateException(
+                    "This StatusVariable does not contain a String value.");
+        return stringData;
+    }
+
+    /**
+     * Returns the <code>StatusVariable</code> value if its type is
+     * <code>int</code>.
+     * 
+     * @return the <code>StatusVariable</code> value as an <code>int</code>
+     * @throws java.lang.IllegalStateException if the type of this
+     *         <code>StatusVariable</code> is not <code>int</code>
+     */
+    public int getInteger() throws IllegalStateException {
+        if (type != TYPE_INTEGER)
+            throw new IllegalStateException(
+                    "This StatusVariable does not contain an integer value.");
+        return intData;
+    }
+
+    /**
+     * Returns the <code>StatusVariable</code> value if its type is
+     * <code>float</code>.
+     * 
+     * @return the <code>StatusVariable</code> value as a <code>float</code>
+     * @throws java.lang.IllegalStateException if the type of this
+     *         <code>StatusVariable</code> is not <code>float</code>
+     */
+    public float getFloat() throws IllegalStateException {
+        if (type != TYPE_FLOAT)
+            throw new IllegalStateException(
+                    "This StatusVariable does not contain a float value.");
+        return floatData;
+    }
+
+    /**
+     * Returns the <code>StatusVariable</code> value if its type is
+     * <code>boolean</code>.
+     * 
+     * @return the <code>StatusVariable</code> value as a <code>boolean</code>
+     * @throws java.lang.IllegalStateException if the type of this
+     *         <code>StatusVariable</code> is not <code>boolean</code>
+     */
+    public boolean getBoolean() throws IllegalStateException {
+        if (type != TYPE_BOOLEAN)
+            throw new IllegalStateException(
+                    "This StatusVariable does not contain a boolean value.");
+        return booleanData;
+    }
+    
+    /**
+     * Returns the collection method of this <code>StatusVariable</code>. See
+     * section 3.3 b) in [ETSI TS 132 403]
+     * 
+     * @return one of the <code>CM_</code> constants
+     */
+    public int getCollectionMethod() {
+        return cm;
+    }
+
+    /**
+     * Compares the specified object with this <code>StatusVariable</code>.
+     * Two <code>StatusVariable</code> objects are considered equal if their
+     * full path, collection method and type are identical, and the data
+     * (selected by their type) is equal.
+     * 
+     * @param obj the object to compare with this <code>StatusVariable</code>
+     * @return <code>true</code> if the argument represents the same
+     *         <code>StatusVariable</code> as this object
+     */
+    public boolean equals(Object obj) {
+        if (!(obj instanceof StatusVariable))
+            return false;
+        
+        StatusVariable other = (StatusVariable) obj;
+        
+        if (!equals(id, other.id) || cm != other.cm || type != other.type)
+            return false;
+        
+        switch (type) {
+        case TYPE_INTEGER: return intData == other.intData;
+        case TYPE_FLOAT:   return floatData == other.floatData;
+        case TYPE_STRING:  return equals(stringData, other.stringData);
+        case TYPE_BOOLEAN: return booleanData == other.booleanData;
+        }
+        
+        return false; // never reached
+    }
+
+    /**
+     * Returns the hash code value for this <code>StatusVariable</code>. The
+     * hash code is calculated based on the full path, collection method and
+     * value of the <code>StatusVariable</code>.
+     * 
+     * @return the hash code of this object
+     */
+    public int hashCode() {
+        int hash = hashCode(id) ^ cm;
+
+        switch (type) {
+        case TYPE_INTEGER: return hash ^ intData;
+        case TYPE_FLOAT:   return hash ^ hashCode(new Float(floatData));
+        case TYPE_BOOLEAN: return hash ^ hashCode(new Boolean(booleanData));
+        case TYPE_STRING:  return hash ^ hashCode(stringData);
+        }
+        
+        return 0; // never reached
+    }
+
+    //  String representation: StatusVariable(path, cm, time, type, value)
+    /**
+     * Returns a <code>String</code> representation of this
+     * <code>StatusVariable</code>. The returned <code>String</code>
+     * contains the full path, collection method, timestamp, type and value 
+     * parameters of the <code>StatusVariable</code> in the following format:
+     * <pre>StatusVariable(&lt;path&gt;, &lt;cm&gt;, &lt;timestamp&gt;, &lt;type&gt;, &lt;value&gt;)</pre>
+     * The collection method identifiers used in the string representation are
+     * "CC", "DER", "GAUGE" and "SI" (without the quotes).  The format of the 
+     * timestamp is defined by the <code>Date.toString</code> method, while the 
+     * type is identified by one of the strings "INTEGER", "FLOAT", "STRING" and
+     * "BOOLEAN".  The final field contains the string representation of the 
+     * value of the status variable.   
+     * 
+     * @return the <code>String</code> representation of this
+     *         <code>StatusVariable</code>
+     */
+    public String toString() {
+        String cmName = null;
+        switch (cm) {
+        case CM_CC:    cmName = "CC";    break;
+        case CM_DER:   cmName = "DER";   break;
+        case CM_GAUGE: cmName = "GAUGE"; break;
+        case CM_SI:    cmName = "SI";    break;
+        }
+        
+        String beg = "StatusVariable(" + id + ", " + cmName + ", "
+                + timeStamp + ", ";
+        
+        switch (type) {
+        case TYPE_INTEGER: return beg + "INTEGER, " + intData + ")";
+        case TYPE_FLOAT:   return beg + "FLOAT, " + floatData + ")";
+        case TYPE_STRING:  return beg + "STRING, " + stringData + ")";
+        case TYPE_BOOLEAN: return beg + "BOOLEAN, " + booleanData + ")";
+        }
+        
+        return null; // never reached
+    }
+
+    //----- Private methods -----//
+    
+    private void setCommon(String id, int cm)
+            throws IllegalArgumentException, NullPointerException {
+        checkId(id, "StatusVariable ID");
+        
+        if (cm != CM_CC && cm != CM_DER && cm != CM_GAUGE && cm != CM_SI)
+            throw new IllegalArgumentException(
+                    "Unknown data collection method constant '" + cm + "'.");
+        
+        this.id = id;
+        this.cm = cm;
+        timeStamp = new Date();
+    }
+
+    
+    private boolean equals(Object o1, Object o2) {
+        return o1 == null ? o2 == null : o1.equals(o2);
+    }
+
+    private int hashCode(Object o) {
+        return o == null ? 0 : o.hashCode();
+    }
+
+    private static void checkId(String id, String idName)
+            throws IllegalArgumentException, NullPointerException {
+        if (id == null)
+            throw new NullPointerException(idName + " is null.");
+        if(id.length() == 0)
+            throw new IllegalArgumentException(idName + " is empty.");
+        
+        byte[] nameBytes;
+        try {
+            nameBytes = id.getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            // never happens, "UTF-8" must always be supported
+            throw new IllegalStateException(e.getMessage());
+        }
+        if(nameBytes.length > MAX_ID_LENGTH)
+            throw new IllegalArgumentException(idName + " is too long " + 
+                    "(over " + MAX_ID_LENGTH + " bytes in UTF-8 encoding).");
+
+        if(id.equals(".") || id.equals(".."))
+            throw new IllegalArgumentException(idName + " is invalid.");
+        
+        if(!containsValidChars(id))
+            throw new IllegalArgumentException(idName + 
+                    " contains invalid characters.");
+    }
+    
+    private static boolean containsValidChars(String name) {
+        char[] chars = name.toCharArray();
+        for(int i = 0; i < chars.length; i++)
+            if(SYMBOLIC_NAME_CHARACTERS.indexOf(chars[i]) == -1)
+                return false;
+        
+        return true;        
+    }
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/useradmin/Authorization.java b/org.osgi.compendium/src/main/java/org/osgi/service/useradmin/Authorization.java
index 08f0205..618a0f1 100644
--- a/org.osgi.compendium/src/main/java/org/osgi/service/useradmin/Authorization.java
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/useradmin/Authorization.java
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvshome/build/org.osgi.service.useradmin/src/org/osgi/service/useradmin/Authorization.java,v 1.9 2006/07/11 00:54:01 hargrave Exp $
+ * $Header: /cvshome/build/org.osgi.service.useradmin/src/org/osgi/service/useradmin/Authorization.java,v 1.11 2007/02/07 18:53:08 hargrave Exp $
  *
- * Copyright (c) OSGi Alliance (2001, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2001, 2007). 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.
@@ -60,7 +60,7 @@
  * <code>Authorization</code> object), the service explicitly checks that the
  * calling bundle has permission to make the call.
  * 
- * @version $Revision: 1.9 $
+ * @version $Revision: 1.11 $
  */
 public interface Authorization {
 	/**
@@ -92,10 +92,10 @@
 	public boolean hasRole(String name);
 
 	/**
-	 * Gets the names of all roles encapsulated by this <code>Authorization</code>
+	 * Gets the names of all roles implied by this <code>Authorization</code>
 	 * context.
 	 * 
-	 * @return The names of all roles encapsulated by this
+	 * @return The names of all roles implied by this
 	 *         <code>Authorization</code> context, or <code>null</code> if no roles
 	 *         are in the context. The predefined role <code>user.anyone</code>
 	 *         will not be included in this list.
diff --git a/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMEICondition.java b/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMEICondition.java
new file mode 100644
index 0000000..958b55b
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMEICondition.java
@@ -0,0 +1,85 @@
+/*
+ * $Header: /cvshome/build/org.osgi.util.gsm/src/org/osgi/util/gsm/IMEICondition.java,v 1.21 2007/02/19 21:32:28 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 org.osgi.util.gsm;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.condpermadmin.Condition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+
+/**
+ * Class representing an IMEI condition. Instances of this class contain a
+ * string value that is matched against the IMEI of the device.
+ */
+public class IMEICondition {
+	private static final String ORG_OSGI_UTIL_GSM_IMEI = "org.osgi.util.gsm.imei";
+	private static final String imei ;
+		
+	static {
+		imei = (String)
+		AccessController.doPrivileged(
+				new PrivilegedAction() {
+					public Object run() {
+					return System.getProperty(ORG_OSGI_UTIL_GSM_IMEI);
+					}
+				}
+				);
+	}
+	
+	private IMEICondition() {
+	}
+
+	/**
+	 * Creates an IMEICondition object.
+	 * 
+	 * @param bundle ignored, as the IMEI number is the property of the mobile device,
+	 * 					and thus the same for all bundles.
+	 * @param conditionInfo contains the IMEI value to match the device's IMEI against. Its
+	 * 		{@link ConditionInfo#getArgs()} method should return a String array with one value, the
+	 * 		IMEI string. The IMEI is 15 digits without hypens. Limited pattern matching is allowed,
+	 * 		then the string is 0 to 14 digits, followed by an asterisk(<code>*</code>).
+	 * @return An IMEICondition object, that can tell whether its IMEI number matches that of the device.
+	 * 			If the number contains an asterisk(<code>*</code>), then the beginning
+	 * 			of the imei is compared to the pattern.
+	 * @throws NullPointerException if one of the parameters is <code>null</code>.
+	 * @throws IllegalArgumentException if the IMEI is not a string of 15 digits, or 
+	 * 		0 to 14 digits with an <code>*</code> at the end.
+	 */
+	public static Condition getCondition(Bundle bundle, ConditionInfo conditionInfo) {
+		if (bundle==null) throw new NullPointerException("bundle");
+		String imei = conditionInfo.getArgs()[0];
+		if (imei.length()>15) throw new IllegalArgumentException("imei too long: "+imei);
+		if (imei.endsWith("*")) {
+			imei = imei.substring(0,imei.length()-1);
+		} else {
+			if (imei.length()!=15) throw new IllegalArgumentException("not a valid imei: "+imei);
+		}
+		for(int i=0;i<imei.length();i++) {
+			int c = imei.charAt(i);
+			if (c<'0'||c>'9') throw new IllegalArgumentException("not a valid imei: "+imei);
+		}
+		if (IMEICondition.imei==null) {
+			System.err.println("The OSGi Reference Implementation of org.osgi.util.gsm.IMEICondition ");
+			System.err.println("needs the system property "+ORG_OSGI_UTIL_GSM_IMEI+" set.");
+			return Condition.FALSE;
+		}
+		return IMEICondition.imei.startsWith(imei)?Condition.TRUE:Condition.FALSE;
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMSICondition.java b/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMSICondition.java
new file mode 100644
index 0000000..740bda1
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMSICondition.java
@@ -0,0 +1,84 @@
+/*
+ * $Header: /cvshome/build/org.osgi.util.gsm/src/org/osgi/util/gsm/IMSICondition.java,v 1.23 2007/02/19 21:32:28 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 org.osgi.util.gsm;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.condpermadmin.Condition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+
+/**
+ * Class representing an IMSI condition. Instances of this class contain a
+ * string value that is matched against the IMSI of the subscriber.
+ */
+public class IMSICondition {
+	private static final String ORG_OSGI_UTIL_GSM_IMSI = "org.osgi.util.gsm.imsi";
+	private static final String imsi;
+	
+	static {
+		imsi = (String)
+		AccessController.doPrivileged(
+				new PrivilegedAction() {
+					public Object run() {
+					return System.getProperty(ORG_OSGI_UTIL_GSM_IMSI);
+					}
+				}
+				);
+	}
+
+	private IMSICondition() {}
+
+	/**
+	 * Creates an IMSI condition object.
+	 * 
+	 * @param bundle ignored, as the IMSI number is the same for all bundles.
+	 * @param conditionInfo contains the IMSI value to match the device's IMSI against. Its
+	 * 		{@link ConditionInfo#getArgs()} method should return a String array with one value, the
+	 * 		IMSI string. The IMSI is 15 digits without hypens. Limited pattern matching is allowed,
+	 * 		then the string is 0 to 14 digits, followed by an asterisk(<code>*</code>).
+	 * @return An IMSICondition object, that can tell whether its IMSI number matches that of the device.
+	 * 			If the number contains an asterisk(<code>*</code>), then the beginning
+	 * 			of the IMSI is compared to the pattern.
+	 * @throws NullPointerException if one of the parameters is <code>null</code>.
+	 * @throws IllegalArgumentException if the IMSI is not a string of 15 digits, or 
+	 * 		0 to 14 digits with an <code>*</code> at the end.
+	 */
+	public static Condition getCondition(Bundle bundle, ConditionInfo conditionInfo) {
+		if (bundle==null) throw new NullPointerException("bundle");
+		if (conditionInfo==null) throw new NullPointerException("conditionInfo");
+		String imsi = conditionInfo.getArgs()[0];
+		if (imsi.length()>15) throw new IllegalArgumentException("imsi too long: "+imsi);
+		if (imsi.endsWith("*")) {
+			imsi = imsi.substring(0,imsi.length()-1);
+		} else {
+			if (imsi.length()!=15) throw new IllegalArgumentException("not a valid imei: "+imsi);
+		}
+		for(int i=0;i<imsi.length();i++) {
+			int c = imsi.charAt(i);
+			if (c<'0'||c>'9') throw new IllegalArgumentException("not a valid imei: "+imsi);
+		}
+		if (IMSICondition.imsi==null) {
+			System.err.println("The OSGi Reference Implementation of org.osgi.util.gsm.IMSICondition ");
+			System.err.println("needs the system property "+ORG_OSGI_UTIL_GSM_IMSI+" set.");
+			return Condition.FALSE;
+		}
+		return (IMSICondition.imsi.startsWith(imsi))?Condition.TRUE:Condition.FALSE;
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java b/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java
new file mode 100644
index 0000000..423ba1a
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java
@@ -0,0 +1,256 @@
+/*
+ * $Header: /cvshome/build/org.osgi.util.mobile/src/org/osgi/util/mobile/UserPromptCondition.java,v 1.26 2006/07/10 08:18:30 pnagy 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 org.osgi.util.mobile;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.condpermadmin.Condition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+
+/**
+ * Class representing a user prompt condition. Instances of this class hold two
+ * values: a prompt string that is to be displayed to the user and the
+ * permission level string according to MIDP2.0 (oneshot, session, blanket).
+ *  
+ */
+public class UserPromptCondition implements Condition {
+	
+	/*
+	 * NOTE: An implementor may also choose to replace this class in
+	 * their distribution with a class that directly interfaces with the
+	 * policy implementation. This replacement class MUST NOT alter the
+	 * public/protected signature of this class.
+	 */
+	// this will need to be set by the implementation class
+	static Method factory = null;
+	Condition realUserPromptCondition;
+
+	private final Bundle bundle;
+	private final String levels;
+	private final String defaultLevel;
+	private final String catalogName;
+	private final String message;
+	
+	/**
+	 * Returns a UserPromptCondition object with the given prompt string and permission
+	 * level. The user should be given choice as to what level of permission is
+	 * given. Thus, the lifetime of the permission is controlled by the user.
+	 *
+	 * @param bundle the bundle to ask about.
+	 * @param conditionInfo the conditionInfo containing the construction information. Its 
+	 * 			{@link ConditionInfo#getArgs()} method should return a String array with 4
+	 * 			strings in it:
+	 * <ol start="0">
+	 * <li>the possible permission levels. This is a comma-separated list that can contain
+	 * 		following strings: ONESHOT SESSION BLANKET. The order is not important. This
+	 * 		parameter is case-insensitive.
+	 * 		</li>
+	 * <li>the default permission level, one chosen from the possible permission levels. If
+	 * 		it is an empty string, then there is no default. This parameter
+	 * 		is case-insensitive.</li>
+	 * <li>the message catalog base name. It will be loaded by a {@link java.util.ResourceBundle},
+	 * 		or equivalent
+	 * 		from an exporting OSGi Bundle. Thus, if the catalogName is "com.provider.messages.userprompt",
+	 * 		then there should be an OSGi Bundle exporting the "com.provider.messages" package, and inside
+	 * 		it files like "userprompt_en_US.properties".</li>
+	 * <li>textual description of the condition, to be displayed to the user. If
+	 * 		it starts with a '%' sign, then the message is looked up from the catalog specified previously.
+	 * 		The key is the rest of the string after the '%' sign.</li>
+	 * </ol>
+	 * @return The requested UserPromptCondition.
+	 * @throws IllegalArgumentException if the parameters are malformed.
+	 * @throws NullPointerException if one of the parameters is <code>null</code>.
+	 */
+	public static Condition getCondition(Bundle bundle,ConditionInfo conditionInfo)
+	{
+		String[] args = conditionInfo.getArgs();
+		if (args==null) throw new NullPointerException("args");
+		if (args.length!=4) throw new IllegalArgumentException("args.length=="+args.length+" (should be 4)");
+		if (bundle==null) throw new NullPointerException("bundle");
+		String levels = args[0];
+		String defaultLevel = args[1];
+		String catalogName = args[2];
+		String message = args[3];
+		if (levels==null) throw new NullPointerException("levels");
+		if (defaultLevel==null) throw new NullPointerException("defaultLevel");
+		if (catalogName==null) throw new NullPointerException("catalogName");
+		if (message==null) throw new NullPointerException("message");
+		
+		if (factory==null) {
+			// the bundle implementing the UserPromptCondition has not started yet.
+			// Do wrapping magick.
+			return new UserPromptCondition(bundle,levels,defaultLevel,catalogName,message);
+		} else {
+			// there is already a factory, no need to do any wrapping magic
+			try {
+				return (Condition) factory.invoke(null,new Object[]{bundle,levels,defaultLevel,catalogName,message});
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			} catch (InvocationTargetException e) {
+				Throwable original = e.getTargetException();
+				if (original instanceof NullPointerException) throw (NullPointerException) original;
+				if (original instanceof IllegalArgumentException) throw (IllegalArgumentException) original;
+				e.printStackTrace();
+			}
+			// the factory method is not working, fallback behavior:
+			factory = null;
+			return new UserPromptCondition(bundle,levels,defaultLevel,catalogName,message);
+		}
+	}
+
+	/**
+	 * Instances of the UserPromptCondition are simply store the construction parameters
+	 * until a "real" UserPromptCondition is registered in setFactory(). At that point, it
+	 * will delegate all calls there.
+	 * @param unused this parameter is here so that ConditionalPermissionAdmin would not
+	 * 		use this as the constructor instead of the getInstance
+	 * @param bundle
+	 * @param levels
+	 * @param defaultLevel
+	 * @param catalogName
+	 * @param message
+	 */
+	private UserPromptCondition(Bundle bundle,String levels,String defaultLevel,String catalogName,String message) {
+		this.bundle=bundle;
+		this.levels=levels;
+		this.defaultLevel=defaultLevel;
+		this.catalogName=catalogName;
+		this.message=message;
+	}
+	
+	/**
+	 * Check if a factory is registered, and if yes, create userprompt to delegate calls to.
+	 */
+	private void lookForImplementation() {
+		if ((realUserPromptCondition==null)&&(factory!=null)) {
+			try {
+				realUserPromptCondition = (Condition) factory.invoke(null,new Object[]{bundle,levels,defaultLevel,catalogName,message});
+				return;
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			} catch (InvocationTargetException e) {
+				e.printStackTrace();
+			}
+			// only if the factory call fails with some invocation exception
+			factory = null;
+		}
+	}
+	
+	/**
+	 * Checks if the {@link #isSatisfied()} method needs to prompt the user, thus cannot
+	 * give results instantly. 
+	 * This depends on the permission level given in 
+	 * {@link UserPromptCondition#getCondition(Bundle, ConditionInfo)}. 
+	 * <ul>
+	 * <li>ONESHOT - isPostponed always returns true. The user is prompted for question every time.</li>
+	 * <li>SESSION - isPostponed returns true until the user decides either yes or no for the current session.</li>
+	 * <li>BLANKET - isPostponed returns true until the user decides either always or never.</li>
+	 * </ul>
+	 * Regardless of the session level, the user is always given the option to reject the prompt
+	 * permanently, as if BLANKET/never was chosen. In this case, the question is not postponed
+	 * anymore, and {@link #isSatisfied()} returns false.<br/>
+	 * If the system supports an separately accessible permission management GUI,
+	 * that may reset the condition
+	 * to its initial state.
+	 * 
+	 * @return True, if user interaction is needed.
+	 */
+	public boolean isPostponed() {
+		lookForImplementation();
+		if (realUserPromptCondition!=null) {
+			return realUserPromptCondition.isPostponed();
+		} else {
+			return true;
+		}
+	}
+
+	/**
+	 * Checks whether the condition may change during the lifetime of the UserPromptCondition object.
+	 * This depends on the permission level given in 
+	 * {@link UserPromptCondition#getCondition(Bundle, ConditionInfo)}. 
+	 * <ul>
+	 * 	<li>ONESHOT - true</li>
+	 *  <li>SESSION - true, if the application model's session lifetime is
+	 *  		shorter than the UserPromptCondition object lifetime</li>
+	 *  <li>BLANKET - false</li>
+	 * </ul>
+	 * If the system supports separately accessible permission management GUI,
+	 * then this function may also return true for SESSION and BLANKET.
+	 * 
+	 * @return True, if the condition can change.
+	 */
+	public boolean isMutable() {
+		lookForImplementation();
+		if (realUserPromptCondition!=null) {
+			return realUserPromptCondition.isMutable();
+		} else {
+			// since we don't know what the actual status is, we cannot say
+			// "the condition cannot change anymore"
+			return true;
+		}
+	}
+
+	/**
+	 * Displays the prompt string to
+	 * the user and returns true if the user accepts. Depending on the
+	 * amount of levels the condition is assigned to, the prompt may have
+	 * multiple accept buttons and one of them can be selected by default (see
+	 * default level parameter at {@link UserPromptCondition#getCondition(Bundle, ConditionInfo)}).
+	 * It must always be possible for the user
+	 * to stop further prompting of this question, even with ONESHOT and SESSION levels.
+	 * In case of BLANKET
+	 * and SESSION levels, it is possible that the user has already answered the question,
+	 * in this case there will be no prompting, but immediate return with the previous answer.
+	 * 
+	 * @return True if the user accepts the prompt (or accepts any prompt in
+	 *         case there are multiple permission levels).
+	 */
+	public boolean isSatisfied() {
+		lookForImplementation();
+		if (realUserPromptCondition!=null) {
+			return realUserPromptCondition.isSatisfied();
+		} else {
+			// paranoid security option
+			return false;
+		}
+	}
+
+	/**
+	 * Checks an array of UserPrompt conditions.
+	 * 
+	 * @param conds The array containing the UserPrompt conditions to evaluate.
+	 * @param context Storage area for evaluation. The {@link org.osgi.service.condpermadmin.ConditionalPermissionAdmin}
+	 * 		may evaluate a condition several times for one permission check, so this context
+	 * 		will be used to store results of ONESHOT questions. This way asking the same question
+	 * 		twice in a row can be avoided. If context is null, temporary results will not be stored.
+	 * @return True, if all conditions are satisfied.
+	 * @throws NullPointerException if conds is null.
+	 */
+	public boolean isSatisfied(Condition[] conds, Dictionary context) {
+		lookForImplementation();
+		if (realUserPromptCondition!=null) {
+			return realUserPromptCondition.isSatisfied(conds,context);
+		} else {
+			// paranoid security option
+			return false;
+		}
+	}
+}
diff --git a/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTracker.java b/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTracker.java
index 253b9f6..c945088 100644
--- a/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTracker.java
+++ b/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTracker.java
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTracker.java,v 1.21 2006/07/12 21:05:17 hargrave Exp $
+ * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTracker.java,v 1.29 2007/02/19 19:04:33 hargrave Exp $
  * 
- * Copyright (c) OSGi Alliance (2000, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2007). 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.
@@ -40,14 +40,21 @@
  * references to the services being tracked. The <code>getService</code> and
  * <code>getServices</code> methods can be called to get the service objects
  * for the tracked service.
+ * <p>
+ * The <code>ServiceTracker</code> class is thread-safe. It does not call a
+ * <code>ServiceTrackerCustomizer</code> object while holding any locks.
+ * <code>ServiceTrackerCustomizer</code> implementations must also be
+ * thread-safe.
  * 
- * @version $Revision: 1.21 $
+ * @ThreadSafe
+ * @version $Revision: 1.29 $
  */
 public class ServiceTracker implements ServiceTrackerCustomizer {
 	/* set this to true to compile in debug messages */
 	static final boolean				DEBUG			= false;
 	/**
-	 * Bundle context against which this <code>ServiceTracker</code> object is tracking.
+	 * Bundle context against which this <code>ServiceTracker</code> object is
+	 * tracking.
 	 */
 	protected final BundleContext		context;
 	/**
@@ -80,7 +87,7 @@
 	 * Tracked services: <code>ServiceReference</code> object -> customized
 	 * Object and <code>ServiceListener</code> object
 	 */
-	private Tracked						tracked;
+	private volatile Tracked			tracked;
 	/**
 	 * Modification count. This field is initialized to zero by open, set to -1
 	 * by close and incremented by modified.
@@ -700,16 +707,17 @@
 	 * 
 	 * The tracking count is initialized to 0 when this
 	 * <code>ServiceTracker</code> object is opened. Every time a service is
-	 * added or removed from this <code>ServiceTracker</code> object the
-	 * tracking count is incremented.
+	 * added, modified or removed from this <code>ServiceTracker</code> object
+	 * the tracking count is incremented.
 	 * 
 	 * <p>
 	 * The tracking count can be used to determine if this
-	 * <code>ServiceTracker</code> object has added or removed a service by
-	 * comparing a tracking count value previously collected with the current
-	 * tracking count value. If the value has not changed, then no service has
-	 * been added or removed from this <code>ServiceTracker</code> object
-	 * since the previous tracking count was collected.
+	 * <code>ServiceTracker</code> object has added, modified or removed a
+	 * service by comparing a tracking count value previously collected with the
+	 * current tracking count value. If the value has not changed, then no
+	 * service has been added, modified or removed from this
+	 * <code>ServiceTracker</code> object since the previous tracking count
+	 * was collected.
 	 * 
 	 * @since 1.2
 	 * @return The tracking count for this <code>ServiceTracker</code> object
@@ -722,6 +730,8 @@
 	/**
 	 * Called by the Tracked object whenever the set of tracked services is
 	 * modified. Increments the tracking count and clears the cache.
+	 * 
+	 * @GuardedBy tracked
 	 */
 	/*
 	 * This method must not be synchronized since it is called by Tracked while
@@ -738,13 +748,6 @@
 	}
 
 	/**
-	 * Finalize. This method no longer performs any function but it kept to
-	 * maintain binary compatibility with prior versions of this class.
-	 */
-	protected void finalize() throws Throwable {
-	}
-
-	/**
 	 * Inner class to track services. If a <code>ServiceTracker</code> object
 	 * is reused (closed then reopened), then a new Tracked object is used. This
 	 * class is a hashtable mapping <code>ServiceReference</code> object ->
@@ -753,6 +756,7 @@
 	 * tracked services. This is not a public class. It is only for use by the
 	 * implementation of the <code>ServiceTracker</code> class.
 	 * 
+	 * @ThreadSafe
 	 */
 	class Tracked extends Hashtable implements ServiceListener {
 		static final long			serialVersionUID	= -7420065199791006079L;
@@ -767,9 +771,11 @@
 		 * 
 		 * Since the ArrayList implementation is not synchronized, all access to
 		 * this list must be protected by the same synchronized object for
-		 * thread safety.
+		 * thread-safety.
+		 * 
+		 * @GuardedBy this
 		 */
-		private ArrayList			adding;
+		private final ArrayList		adding;
 
 		/**
 		 * true if the tracked object is closed.
@@ -793,9 +799,11 @@
 		 * 
 		 * Since the LinkedList implementation is not synchronized, all access
 		 * to this list must be protected by the same synchronized object for
-		 * thread safety.
+		 * thread-safety.
+		 * 
+		 * @GuardedBy this
 		 */
-		private LinkedList			initial;
+		private final LinkedList	initial;
 
 		/**
 		 * Tracked constructor.
@@ -816,6 +824,7 @@
 		 * addServiceListener call.
 		 * 
 		 * @param references The initial list of services to be tracked.
+		 * @GuardedBy this
 		 */
 		protected void setInitialServices(ServiceReference[] references) {
 			if (references == null) {
@@ -920,7 +929,7 @@
 				case ServiceEvent.REGISTERED :
 				case ServiceEvent.MODIFIED :
 					if (listenerFilter != null) { // constructor supplied
-													// filter
+						// filter
 						track(reference);
 						/*
 						 * If the customizer throws an unchecked exception, it
@@ -959,7 +968,7 @@
 		 * 
 		 * @param reference Reference to a service to be tracked.
 		 */
-		protected void track(ServiceReference reference) {
+		private void track(ServiceReference reference) {
 			Object object;
 			synchronized (this) {
 				object = this.get(reference);
@@ -1127,6 +1136,7 @@
 	 * This class is used by the ServiceTracker if open is called with true.
 	 * 
 	 * @since 1.3
+	 * @ThreadSafe
 	 */
 	class AllTracked extends Tracked implements AllServiceListener {
 		static final long	serialVersionUID	= 4050764875305137716L;
diff --git a/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java b/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
index c6f9e20..e091078 100644
--- a/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
+++ b/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTrackerCustomizer.java,v 1.10 2006/06/16 16:31:13 hargrave Exp $
+ * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTrackerCustomizer.java,v 1.13 2007/02/19 19:04:33 hargrave Exp $
  * 
- * Copyright (c) OSGi Alliance (2000, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2007). 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.
@@ -22,25 +22,32 @@
 
 /**
  * The <code>ServiceTrackerCustomizer</code> interface allows a
- * <code>ServiceTracker</code> object to customize the service objects that are
- * tracked. The <code>ServiceTrackerCustomizer</code> object is called when a
- * service is being added to the <code>ServiceTracker</code> object. The
- * <code>ServiceTrackerCustomizer</code> can then return an object for the tracked
- * service. The <code>ServiceTrackerCustomizer</code> object is also called when a
- * tracked service is modified or has been removed from the
+ * <code>ServiceTracker</code> object to customize the service objects that
+ * are tracked. The <code>ServiceTrackerCustomizer</code> object is called
+ * when a service is being added to the <code>ServiceTracker</code> object.
+ * The <code>ServiceTrackerCustomizer</code> can then return an object for the
+ * tracked service. The <code>ServiceTrackerCustomizer</code> object is also
+ * called when a tracked service is modified or has been removed from the
  * <code>ServiceTracker</code> object.
  * 
  * <p>
  * The methods in this interface may be called as the result of a
- * <code>ServiceEvent</code> being received by a <code>ServiceTracker</code> object.
- * Since <code>ServiceEvent</code> s are synchronously delivered by the Framework,
- * it is highly recommended that implementations of these methods do not
- * register (<code>BundleContext.registerService</code>), modify (
+ * <code>ServiceEvent</code> being received by a <code>ServiceTracker</code>
+ * object. Since <code>ServiceEvent</code> s are synchronously delivered by
+ * the Framework, it is highly recommended that implementations of these methods
+ * do not register (<code>BundleContext.registerService</code>), modify (
  * <code>ServiceRegistration.setProperties</code>) or unregister (
  * <code>ServiceRegistration.unregister</code>) a service while being
  * synchronized on any object.
  * 
- * @version $Revision: 1.10 $
+ * <p>
+ * The <code>ServiceTracker</code> class is thread-safe. It does not call a
+ * <code>ServiceTrackerCustomizer</code> object while holding any locks.
+ * <code>ServiceTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 1.13 $
  */
 public interface ServiceTrackerCustomizer {
 	/**
@@ -48,17 +55,17 @@
 	 * 
 	 * <p>
 	 * This method is called before a service which matched the search
-	 * parameters of the <code>ServiceTracker</code> object is added to it. This
-	 * method should return the service object to be tracked for this
-	 * <code>ServiceReference</code> object. The returned service object is stored
-	 * in the <code>ServiceTracker</code> object and is available from the
-	 * <code>getService</code> and <code>getServices</code> methods.
+	 * parameters of the <code>ServiceTracker</code> object is added to it.
+	 * This method should return the service object to be tracked for this
+	 * <code>ServiceReference</code> object. The returned service object is
+	 * stored in the <code>ServiceTracker</code> object and is available from
+	 * the <code>getService</code> and <code>getServices</code> methods.
 	 * 
 	 * @param reference Reference to service being added to the
 	 *        <code>ServiceTracker</code> object.
 	 * @return The service object to be tracked for the
-	 *         <code>ServiceReference</code> object or <code>null</code> if the
-	 *         <code>ServiceReference</code> object should not be tracked.
+	 *         <code>ServiceReference</code> object or <code>null</code> if
+	 *         the <code>ServiceReference</code> object should not be tracked.
 	 */
 	public Object addingService(ServiceReference reference);
 
@@ -73,8 +80,7 @@
 	 * @param reference Reference to service that has been modified.
 	 * @param service The service object for the modified service.
 	 */
-	public void modifiedService(ServiceReference reference,
-			Object service);
+	public void modifiedService(ServiceReference reference, Object service);
 
 	/**
 	 * A service tracked by the <code>ServiceTracker</code> object has been
@@ -87,6 +93,5 @@
 	 * @param reference Reference to service that has been removed.
 	 * @param service The service object for the removed service.
 	 */
-	public void removedService(ServiceReference reference,
-			Object service);
+	public void removedService(ServiceReference reference, Object service);
 }
diff --git a/org.osgi.compendium/src/main/java/org/osgi/util/xml/XMLParserActivator.java b/org.osgi.compendium/src/main/java/org/osgi/util/xml/XMLParserActivator.java
index 77ba538..c1e6cd8 100644
--- a/org.osgi.compendium/src/main/java/org/osgi/util/xml/XMLParserActivator.java
+++ b/org.osgi.compendium/src/main/java/org/osgi/util/xml/XMLParserActivator.java
@@ -1,5 +1,5 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.xml/src/org/osgi/util/xml/XMLParserActivator.java,v 1.10 2006/06/21 17:41:20 hargrave Exp $
+ * $Header: /cvshome/build/org.osgi.util.xml/src/org/osgi/util/xml/XMLParserActivator.java,v 1.11 2006/10/27 18:17:06 hargrave Exp $
  * 
  * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
  * 
@@ -137,7 +137,6 @@
 	 *         bundle is marked as stopped and the Framework will remove this
 	 *         bundle's listeners, unregister all services registered by this
 	 *         bundle, and release all services used by this bundle.
-	 * @see Bundle#start
 	 */
 	public void start(BundleContext context) throws Exception {
 		this.context = context;
@@ -159,7 +158,6 @@
 	}
 
 	/**
-	 * <p>
 	 * This method has nothing to do as all active service registrations will
 	 * automatically get unregistered when the bundle stops.
 	 * 
@@ -168,7 +166,6 @@
 	 *         bundle is still marked as stopped, and the Framework will remove
 	 *         the bundle's listeners, unregister all services registered by the
 	 *         bundle, and release all services used by the bundle.
-	 * @see Bundle#stop
 	 */
 	public void stop(BundleContext context) throws Exception {
 	}