jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r884108 [5/10] - in /jackrabbit/sandbox/JCR-1456: ./ jackrabbit-api/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/ jackrabbit-core/ jackrabbit-core/src...
Date Wed, 25 Nov 2009 14:04:50 GMT
Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Wed Nov 25 14:04:38 2009
@@ -27,8 +27,9 @@
 import org.apache.jackrabbit.core.ProtectedItemModifier;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.SessionListener;
-import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.core.security.SystemPrincipal;
+import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
@@ -41,8 +42,8 @@
 import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
-import javax.jcr.Session;
 import javax.jcr.ItemNotFoundException;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.version.VersionException;
@@ -78,8 +79,8 @@
  * <ul>
  * <li>The names of the hierarchy folders is determined from ID of the
  * authorizable to be created, consisting of the leading N chars where N is
- * the relative depth starting from the node at {@link UserConstants#USERS_PATH}
- * or {@link UserConstants#GROUPS_PATH}.</li>
+ * the relative depth starting from the node at {@link #getUsersPath()}
+ * or {@link #getGroupsPath()}.</li>
  * <li>By default 2 levels (depth == 2) are created.</li>
  * <li>Parent nodes are expected to consist of folder structure only.</li>
  * <li>If the ID contains invalid JCR chars that would prevent the creation of
@@ -144,14 +145,14 @@
         implements UserManager, UserConstants, SessionListener {
 
     /**
-     * Configuration option to change the {@link #USERS_PATH default path} for
-     * creating users.
+     * Configuration option to change the
+     * {@link UserConstants#USERS_PATH default path} for creating users.
      */
     public static final String PARAM_USERS_PATH = "usersPath";
 
     /**
-     * Configuration option to change the {@link #GROUPS_PATH default path} for
-     * creating groups.
+     * Configuration option to change the
+     * {@link UserConstants#GROUPS_PATH default path} for creating groups.
      */
     public static final String PARAM_GROUPS_PATH = "groupsPath";
 
@@ -211,19 +212,19 @@
 
     /**
      * Configuration value defining the node where User nodes will be created.
-     * Default value is {@link #USERS_PATH}.
+     * Default value is {@link UserConstants#USERS_PATH}.
      */
     private final String usersPath;
 
     /**
      * Configuration value defining the node where Group nodes will be created.
-     * Default value is {@link #GROUPS_PATH}.
+     * Default value is {@link UserConstants#GROUPS_PATH}.
      */
     private final String groupsPath;
 
     /**
-     * Flag indicating if {@link #getAuthorizable(String)} should find users or
-     * groups created with Jackrabbit < 2.0.<br>
+     * Flag indicating if {@link #getAuthorizable(String)} should be able to deal
+     * with users or groups created with Jackrabbit < 2.0.<br>
      * As of 2.0 authorizables are created using a defined logic that allows
      * to retrieve them without searching/traversing. If this flag is
      * <code>true</code> this method will try to find authorizables using the
@@ -232,22 +233,16 @@
     private final boolean compatibleJR16;
 
     /**
-     * Flat storing the batch modus.
-     * If this option is turned on changes made through this user manager will
-     * only be persisted upon an explicit call to {@link javax.jcr.Session#save()},
-     * which must be the editing session object. Otherwise (default behavior)
-     * changes are immediately persisted.
-     *
-     * @see #setPersistenceModus(boolean, Session)
+     * boolean flag indicating whether the editing session is a system session.
      */
-    boolean batchModus = false;
+    private final boolean isSystemUserManager;
 
     /**
      * Create a new <code>UserManager</code> with the default configuration.
      *
-     * @param session
-     * @param adminId
-     * @throws RepositoryException
+     * @param session The editing/reading session.
+     * @param adminId The user ID of the administrator.
+     * @throws RepositoryException If an error occurs.
      */
     public UserManagerImpl(SessionImpl session, String adminId) throws RepositoryException {
         this(session, adminId, null);
@@ -258,8 +253,8 @@
      * Currently the following configuration options are respected:
      *
      * <ul>
-     * <li>{@link #PARAM_USERS_PATH}. If missing set to {@link #USERS_PATH}.</li>
-     * <li>{@link #PARAM_GROUPS_PATH}. If missing set to {@link #GROUPS_PATH}.</li>
+     * <li>{@link #PARAM_USERS_PATH}. If missing set to {@link UserConstants#USERS_PATH}.</li>
+     * <li>{@link #PARAM_GROUPS_PATH}. If missing set to {@link UserConstants#GROUPS_PATH}.</li>
      * <li>{@link #PARAM_DEFAULT_DEPTH}. The default number of levels is 2.</li>
      * <li>{@link #PARAM_AUTO_EXPAND_TREE}. By default this option is disabled.</li>
      * <li>{@link #PARAM_AUTO_EXPAND_SIZE}. The default value is 1000.</li>
@@ -267,10 +262,10 @@
      *
      * See the overall {@link UserManagerImpl introduction} for details.
      *
-     * @param session
-     * @param adminId
-     * @param config
-     * @throws RepositoryException
+     * @param session The editing/reading session.
+     * @param adminId The user ID of the administrator.
+     * @param config The configuration parameters.
+     * @throws RepositoryException If an error occurs.
      */
     public UserManagerImpl(SessionImpl session, String adminId, Properties config) throws RepositoryException {
         this.session = session;
@@ -296,24 +291,19 @@
         }
         authResolver = nr;
         authResolver.setSearchRoots(usersPath, groupsPath);
-    }
 
-    /**
-     * @param batched If <code>true</code> changes made through this manager and
-     * to authorizables are only persisted upon explict call to {@link
-     * javax.jcr.Session#save()}.
-     * @throws RepositoryException If the passed <code>editingSession</code> is
-     * not the same this UserManager is writing to.
-     */
-    /* COMMENTED. WORK IN PROGRESS
-    public void setPersistenceModus(boolean batched, Session editingSession) throws RepositoryException {
-        if (editingSession != session) {
-            throw new RepositoryException("Cannot change persistence modus: Session mismatch.");
-        }
-        batchModus = batched;
+        /**
+         * evaluate if the editing session is a system session. since the
+         * SystemSession class is package protected the session object cannot
+         * be checked for the property instance.
+         * 
+         * workaround: compare the class name and check if the subject contains
+         * the system principal.
+         */
+        isSystemUserManager = "org.apache.jackrabbit.core.SystemSession".equals(session.getClass().getName()) && 
+                !session.getSubject().getPrincipals(SystemPrincipal.class).isEmpty();
     }
-    */
-
+    
     /**
      * Implementation specific methods releaving where users are created within
      * the content.
@@ -344,36 +334,21 @@
         if (id == null || id.length() == 0) {
             throw new IllegalArgumentException("Invalid authorizable name '" + id + "'");
         }
-        Authorizable authorz = null;
-        NodeId nodeId = buildNodeId(id);        
-        NodeImpl n = null;
-        try {
-            n = session.getNodeById(nodeId);
-        } catch (ItemNotFoundException e) {
-            if (compatibleJR16) {
-                // backwards-compatibility with JR < 2.0 user/group structure that doesn't
-                // allow to determine existance of an authorizable from the id directly.
-                // search for it the node belonging to that id
-                n = (NodeImpl) authResolver.findNode(P_USERID, id, NT_REP_USER);
-                if (n == null) {
-                    // no user -> look for group.
-                    // NOTE: JR < 2.0 always returned groupIDs that didn't contain any
-                    // illegal JCR chars. Since Group.getID() now unescapes the node
-                    // name additional escaping is required.
-                    Name nodeName = session.getQName(Text.escapeIllegalJcrChars(id));
-                    n = (NodeImpl) authResolver.findNode(nodeName, NT_REP_GROUP);
-                }
-            } // else: no matching node found -> ignore exception.
+        Authorizable a = internalGetAuthorizable(id);
+        /**
+         * Extra check for the existance of the administrator user that must
+         * always exist.
+         * In case it got removed if must be recreated using a system session.
+         * Since a regular session may lack read permission on the admin-user's
+         * node an explicit test for the current editing session being
+         * a system session is performed.
+         */
+        if (a == null && adminId.equals(id) && isSystemUserManager) {
+            log.info("Admin user does not exist.");
+            a = createAdmin();
         }
 
-        if (n != null) {
-            if (n.isNodeType(NT_REP_USER)) {
-                authorz = createUser(n);
-            } else if (n.isNodeType(NT_REP_GROUP)) {
-                authorz = createGroup(n);
-            } // else some other node but not an authorizable -> return null.
-        } // else no matching node -> return null.
-        return authorz;
+        return a;
     }
 
     /**
@@ -391,34 +366,37 @@
                 }
             }
         } else {
-            // another Principal -> search
-            String name = principal.getName();
+            // another Principal implementation.
+            // a) try short-cut that works in case of ID.equals(principalName) only.
+            // b) execute query in case of pName mismatch or exception. however, query
+            //    requires persisted user nodes (see known issue of UserImporter).
+            String name = principal.getName();           
+            try {
+                Authorizable a = internalGetAuthorizable(name);
+                if (a != null && name.equals(a.getPrincipal().getName())) {
+                    return a;
+                }
+            } catch (RepositoryException e) {
+                // ignore and execute the query.
+            }
+            // shortcut didn't work -> search.
             n = (NodeImpl) authResolver.findNode(P_PRINCIPAL_NAME, name, NT_REP_AUTHORIZABLE);
         }
         // build the corresponding authorizable object
-        if (n != null) {
-            if (n.isNodeType(NT_REP_USER)) {
-               return createUser(n);
-            } else if (n.isNodeType(NT_REP_GROUP)) {
-               return createGroup(n);
-            } else {
-                log.debug("Unexpected user nodetype " + n.getPrimaryNodeType().getName());
-            }
-        }
-        return null;
+        return getAuthorizable(n);
     }
 
     /**
      * @see UserManager#findAuthorizables(String,String)
      */
-    public Iterator findAuthorizables(String propertyName, String value) throws RepositoryException {
+    public Iterator<Authorizable> findAuthorizables(String propertyName, String value) throws RepositoryException {
         return findAuthorizables(propertyName, value, SEARCH_TYPE_AUTHORIZABLE);
     }
 
     /**
      * @see UserManager#findAuthorizables(String,String, int)
      */
-    public Iterator findAuthorizables(String propertyName, String value, int searchType)
+    public Iterator<Authorizable> findAuthorizables(String propertyName, String value, int searchType)
             throws RepositoryException {
         Name name = session.getQName(propertyName);
         Name ntName;
@@ -439,16 +417,6 @@
     }
 
     /**
-     * Creates a new Node on the repository with the specified
-     * <code>userName</code>.<br>
-     * The User will be created relative to path of the User who represents the
-     * Session this UserManager has been created for.<br>
-     * If the {@link javax.jcr.Credentials Credentials} are of type
-     * {@link javax.jcr.SimpleCredentials SimpleCredentials} they will be
-     * crypted.
-     *
-     * @param userID
-     * @param password
      * @see UserManager#createUser(String,String)
      */
     public User createUser(String userID, String password) throws RepositoryException {
@@ -456,14 +424,7 @@
     }
 
     /**
-     *
-     * @param userID
-     * @param password
-     * @param principal
-     * @param intermediatePath Is always ignored.
-     * @return
-     * @throws AuthorizableExistsException
-     * @throws RepositoryException
+     * @see UserManager#createUser(String, String, java.security.Principal, String)
      */
     public User createUser(String userID, String password,
                            Principal principal, String intermediatePath)
@@ -474,29 +435,24 @@
         if (password == null) {
             throw new IllegalArgumentException("Cannot create user: null password.");
         }
-        if (!isValidPrincipal(principal)) {
-            throw new IllegalArgumentException("Cannot create user: Principal may not be null and must have a valid name.");
-        }
-        if (getAuthorizable(userID) != null) {
+        if (internalGetAuthorizable(userID) != null) {
             throw new AuthorizableExistsException("User for '" + userID + "' already exists");
         }
-        if (hasAuthorizable(principal)) {
-            throw new AuthorizableExistsException("Authorizable for '" + principal.getName() + "' already exists");
-        }
 
         try {
             NodeImpl userNode = (NodeImpl) nodeCreator.createUserNode(userID, intermediatePath);
-
+            setPrincipal(userNode, principal);
             setProperty(userNode, P_PASSWORD, getValue(UserImpl.buildPasswordValue(password)), true);
-            setProperty(userNode, P_PRINCIPAL_NAME, getValue(principal.getName()), true);
-            if (!batchModus) {
+
+            User user = createUser(userNode);
+            if (isAutoSave()) {
                 session.save();
             }
 
             log.debug("User created: " + userID + "; " + userNode.getPath());
-            return createUser(userNode);
+            return user;
         } catch (RepositoryException e) {
-            // something went wrong -> revert changes and rethrow
+            // something went wrong -> revert changes and re-throw
             session.refresh(false);
             log.debug("Failed to create new User, reverting changes.");
             throw e;
@@ -514,7 +470,7 @@
 
     /**
      * Create a new <code>Group</code> from the given <code>principal</code>.
-     * It will be created below the this {@link #GROUPS_PATH group path}.<br>
+     * It will be created below the defined {@link #getGroupsPath() group path}.<br>
      * Non-existant elements of the Path will be created as nodes
      * of type {@link #NT_REP_AUTHORIZABLE_FOLDER rep:AuthorizableFolder}.
      * The group ID will be generated from the principal name. If the name
@@ -522,7 +478,8 @@
      * principal name != ID) the principal name is expanded by a suffix;
      * otherwise the resulting group ID equals the principal name.
      *
-     * @param principal
+     * @param principal A principal that doesn't yet represent an existing user
+     * or group.
      * @param intermediatePath Is always ignored.
      * @return A new group.
      * @throws AuthorizableExistsException
@@ -531,24 +488,20 @@
      */
     public Group createGroup(Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
         if (!isValidPrincipal(principal)) {
-            throw new IllegalArgumentException("Cannot create Group: Principal may not be null and must have a valid name.");
+            throw new IllegalArgumentException("Cannot create group: Principal may not be null and must have a valid name.");
         }
-        if (hasAuthorizable(principal)) {
-            throw new AuthorizableExistsException("Authorizable for '" + principal.getName() + "' already exists: ");
-        }
-        
         try {
             String groupID = getGroupId(principal.getName());
             NodeImpl groupNode = (NodeImpl) nodeCreator.createGroupNode(groupID, intermediatePath);
+            setPrincipal(groupNode, principal);
 
-            setProperty(groupNode, P_PRINCIPAL_NAME, getValue(principal.getName()));
-            if (!batchModus) {
+            Group group = createGroup(groupNode);
+            if (isAutoSave()) {
                 session.save();
             }
 
             log.debug("Group created: " + groupID + "; " + groupNode.getPath());
-
-            return createGroup(groupNode);
+            return group;
         } catch (RepositoryException e) {
             session.refresh(false);
             log.debug("newInstance new Group failed, revert changes on parent");
@@ -556,70 +509,171 @@
         }
     }
 
+    /**
+     * Always returns <code>true</code> as by default the autoSave behavior
+     * cannot be altered (see also {@link #autoSave(boolean)}.
+     *
+     * @return Always <code>true</code>.
+     * @see org.apache.jackrabbit.api.security.user.UserManager#isAutoSave()
+     */
+    public boolean isAutoSave() {
+        return true;
+    }
+
+    /**
+     * Always throws <code>unsupportedRepositoryOperationException</code> as
+     * modification of the autosave behavior is not supported.
+     *
+     * @see UserManager#autoSave(boolean)
+     */
+    public void autoSave(boolean enable) throws UnsupportedRepositoryOperationException, RepositoryException {
+        throw new UnsupportedRepositoryOperationException("Cannot change autosave behavior.");
+    }
+
     //--------------------------------------------------------------------------
     /**
      *
-     * @param principal
-     * @return
-     * @throws RepositoryException
+     * @param node The new user/group node.
+     * @param principal A valid non-null principal.
+     * @throws AuthorizableExistsException If there is already another user/group
+     * with the same principal name.
+     * @throws RepositoryException If another error occurs.
      */
-    private boolean hasAuthorizable(Principal principal) throws RepositoryException {
-        return getAuthorizable(principal) != null;
+    void setPrincipal(NodeImpl node, Principal principal) throws AuthorizableExistsException, RepositoryException {
+        if (!isValidPrincipal(principal)) {
+            throw new IllegalArgumentException("Cannot create Authorizable: Principal may not be null and must have a valid name.");
+        }
+        /*
+         Check if there is *another* authorizable with the same principal.
+         The additional validation (nodes not be same) is required in order to
+         circumvent problems with re-importing existing authorizable in which
+         case the original user/group node is being recreated but the search
+         used to look for an colliding authorizable still finds the persisted
+         node.
+        */
+        Authorizable existing = getAuthorizable(principal);
+        if (existing != null && !((AuthorizableImpl) existing).getNode().isSame(node)) {
+            throw new AuthorizableExistsException("Authorizable for '" + principal.getName() + "' already exists: ");
+        }
+        if (!node.isNew() || node.hasProperty(P_PRINCIPAL_NAME)) {
+            throw new RepositoryException("rep:principalName can only be set once on a new node.");
+        }
+        setProperty(node, P_PRINCIPAL_NAME, getValue(principal.getName()), true);
     }
 
     void setProtectedProperty(NodeImpl node, Name propName, Value value) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException {
         setProperty(node, propName, value);
-        if (!batchModus) {
+        if (isAutoSave()) {
             node.save();
         }
     }
 
     void setProtectedProperty(NodeImpl node, Name propName, Value[] values) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException {
         setProperty(node, propName, values);
-        if (!batchModus) {
+        if (isAutoSave()) {
             node.save();
         }
     }
 
     void setProtectedProperty(NodeImpl node, Name propName, Value[] values, int type) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException {
         setProperty(node, propName, values, type);
-        if (!batchModus) {
+        if (isAutoSave()) {
             node.save();
         }
     }
 
     void removeProtectedItem(ItemImpl item, Node parent) throws RepositoryException, AccessDeniedException, VersionException {
         removeItem(item);
-        if (!batchModus) {
+        if (isAutoSave()) {
             parent.save();
         }
     }
 
     /**
+     * Implementation specific method used to retrieve a user/group by Node.
+     * <code>Null</code> is returned if
+     * <pre>
+     * - the passed node is <code>null</code>,
+     * - doesn't have the correct node type or
+     * - isn't placed underneith the configured user/group tree.
+     * </pre>
+     *
+     * @param n A user/group node.
+     * @return An authorizable or <code>null</code>.
+     * @throws RepositoryException If an error occurs.
+     */
+    Authorizable getAuthorizable(NodeImpl n) throws RepositoryException {
+        Authorizable authorz = null;
+        if (n != null) {
+            String path = n.getPath();
+            if (n.isNodeType(NT_REP_USER) && Text.isDescendant(usersPath, path)) {
+                authorz = createUser(n);
+            } else if (n.isNodeType(NT_REP_GROUP) && Text.isDescendant(groupsPath, path)) {
+                authorz = createGroup(n);
+            } else {
+                /* else some other node type or outside of the valid user/group
+                   hierarchy  -> return null. */
+                log.debug("Unexpected user nodetype " + n.getPrimaryNodeType().getName());
+            }
+        } /* else no matching node -> return null */
+        return authorz;
+    }
+
+    /**
      * Test if a user or group exists that has the given principals name as ID,
      * which might happen if userID != principal-name.
      * In this case: generate another ID for the group to be created.
      *
      * @param principalName to be used as hint for the groupid.
      * @return a group id.
-     * @throws RepositoryException
+     * @throws RepositoryException If an error occurs.
      */
     private String getGroupId(String principalName) throws RepositoryException {
         String groupID = principalName;
         int i = 0;
-        while (getAuthorizable(groupID) != null) {
+        while (internalGetAuthorizable(groupID) != null) {
             groupID = principalName + "_" + i;
             i++;
         }
         return groupID;
     }
 
+    /**
+     * @param id The user or group ID.
+     * @return The authorizable with the given <code>id</code> or <code>null</code>.
+     * @throws RepositoryException If an error occurs.
+     */
+    private Authorizable internalGetAuthorizable(String id) throws RepositoryException {
+        NodeId nodeId = buildNodeId(id);
+        NodeImpl n = null;
+        try {
+            n = session.getNodeById(nodeId);
+        } catch (ItemNotFoundException e) {
+            if (compatibleJR16) {
+                // backwards-compatibility with JR < 2.0 user/group structure that doesn't
+                // allow to determine existence of an authorizable from the id directly.
+                // search for it the node belonging to that id
+                n = (NodeImpl) authResolver.findNode(P_USERID, id, NT_REP_USER);
+                if (n == null) {
+                    // no user -> look for group.
+                    // NOTE: JR < 2.0 always returned groupIDs that didn't contain any
+                    // illegal JCR chars. Since Group.getID() now unescapes the node
+                    // name additional escaping is required.
+                    Name nodeName = session.getQName(Text.escapeIllegalJcrChars(id));
+                    n = (NodeImpl) authResolver.findNode(nodeName, NT_REP_GROUP);
+                }
+            } // else: no matching node found -> ignore exception.
+        }
+
+        return getAuthorizable(n);
+    }
+
     private Value getValue(String strValue) throws RepositoryException {
         return session.getValueFactory().createValue(strValue);
     }
 
     /**
-     * @param userID
+     * @param userID A userID.
      * @return true if the given userID belongs to the administrator user.
      */
     boolean isAdminId(String userID) {
@@ -629,16 +683,17 @@
     /**
      * Build the User object from the given user node.
      *
-     * @param userNode
-     * @return
-     * @throws RepositoryException
+     * @param userNode The new user node.
+     * @return An instance of <code>User</code>.
+     * @throws RepositoryException If the node isn't a child of the configured
+     * usersPath-node or if another error occurs.
      */
     User createUser(NodeImpl userNode) throws RepositoryException {
         if (userNode == null || !userNode.isNodeType(NT_REP_USER)) {
             throw new IllegalArgumentException();
         }
         if (!Text.isDescendant(usersPath, userNode.getPath())) {
-            throw new IllegalArgumentException("User has to be within the User Path");
+            throw new RepositoryException("User has to be within the User Path");
         }
         return doCreateUser(userNode);
     }
@@ -648,7 +703,7 @@
      * return a custom implementation.
      *
      * @param node user node
-     * @return user object
+     * @return the user object
      * @throws RepositoryException if an error occurs
      */
     protected User doCreateUser(NodeImpl node) throws RepositoryException {
@@ -659,32 +714,100 @@
     /**
      * Build the Group object from the given group node.
      *
-     * @param groupNode
-     * @return
-     * @throws RepositoryException
+     * @param groupNode The new group node.
+     * @return An instance of <code>Group</code>.
+     * @throws RepositoryException If the node isn't a child of the configured
+     * groupsPath-node or if another error occurs.
      */
     Group createGroup(NodeImpl groupNode) throws RepositoryException {
-        return GroupImpl.create(groupNode, this);
+        if (groupNode == null || !groupNode.isNodeType(NT_REP_GROUP)) {
+            throw new IllegalArgumentException();
+        }
+        if (!Text.isDescendant(groupsPath, groupNode.getPath())) {
+            throw new RepositoryException("Group has to be within the Group Path");
+        }
+        return doCreateGroup(groupNode);
     }
 
-    private static boolean isValidPrincipal(Principal principal) {
-        return principal != null && principal.getName() != null && principal.getName().length() > 0;
+    /**
+     * Build the group object from the given group node. May be overridden to
+     * return a custom implementation.
+     *
+     * @param node group node
+     * @return A group 
+     * @throws RepositoryException if an error occurs
+     */
+    protected Group doCreateGroup(NodeImpl node) throws RepositoryException {
+        return new GroupImpl(node, this);
     }
 
     /**
+     * Create the administrator user. If the node to be created collides
+     * with an existing node (ItemExistsException) the existing node gets removed
+     * and the admin user node is (re)created.
+     * <p/>
+     * Collision with an existing node may occur under the following circumstances:
      *
-     * @param id
+     * <ul>
+     * <li>The <code>usersPath</code> has been modified in the user manager
+     * configuration after a successful repository start that already created
+     * the administrator user.</li>
+     * <li>The NodeId created by {@link #buildNodeId(String)} by conincidence
+     * collides with another NodeId created during the regular node creation
+     * process.</li>
+     * </ul>
+     *
+     * @param adminId
+     * @param pw
      * @return
      * @throws RepositoryException
      */
-    private static NodeId buildNodeId(String id) throws RepositoryException {
+    private User createAdmin() throws RepositoryException {
+        User admin;
         try {
-            UUID uuid = UUID.nameUUIDFromBytes(id.getBytes("UTF-8"));
+            admin = createUser(adminId, adminId);
+            if (!isAutoSave()) {
+                session.save();
+            }
+            log.info("... created admin user with id \'" + adminId + "\' and default pw.");
+        } catch (ItemExistsException e) {
+            NodeImpl conflictingNode = session.getNodeById(buildNodeId(adminId));
+            String conflictPath = conflictingNode.getPath();
+            log.error("Detected conflicting node " + conflictPath + " of node type " + conflictingNode.getPrimaryNodeType().getName() + ".");
+
+            // TODO move conflicting node of type rep:User instead of removing and recreating.
+            conflictingNode.remove();
+            log.info("Removed conflicting node at " + conflictPath);
+
+            admin = createUser(adminId, adminId);
+            if (!isAutoSave()) {
+                session.save();
+            }
+            log.info("Resolved conflict and (re)created admin user with id \'" + adminId + "\' and default pw.");          
+        }
+        return admin;
+    }
+
+    /**
+     * Creates a UUID from the given <code>id</code> String that is converted
+     * to lower case before.
+     *
+     * @param id The user/group id that needs to be converted to a valid NodeId.
+     * @return a new <code>NodeId</code>.
+     * @throws RepositoryException If an error occurs.
+     */
+    private NodeId buildNodeId(String id) throws RepositoryException {
+        try {
+            UUID uuid = UUID.nameUUIDFromBytes(id.toLowerCase().getBytes("UTF-8"));
             return new NodeId(uuid);
         } catch (UnsupportedEncodingException e) {
             throw new RepositoryException("Unexpected error while build ID hash", e);
         }
     }
+    
+    private static boolean isValidPrincipal(Principal principal) {
+        return principal != null && principal.getName() != null && principal.getName().length() > 0;
+    }
 
     //----------------------------------------------------< SessionListener >---
     /**
@@ -698,17 +821,17 @@
      * @see SessionListener#loggedOut(org.apache.jackrabbit.core.SessionImpl)
      */
     public void loggedOut(SessionImpl session) {
-        // and logout the session unless it is the loggedout session itself.
+        // and logout the session unless it is the logged-out session itself.
         if (session != this.session) {
             this.session.logout();
         }
     }
 
-    //--------------------------------------------------------------------------
+    //------------------------------------------------------< inner classes >---
     /**
      * Inner class
      */
-    private final class AuthorizableIterator implements Iterator {
+    private final class AuthorizableIterator implements Iterator<Authorizable> {
 
         private final Set<String> served = new HashSet<String>();
 
@@ -731,7 +854,7 @@
         /**
          * @see Iterator#next()
          */
-        public Object next() {
+        public Authorizable next() {
             Authorizable authr = next;
             if (authr == null) {
                 throw new NoSuchElementException();
@@ -753,17 +876,11 @@
                 NodeImpl node = (NodeImpl) authNodeIter.nextNode();
                 try {
                     if (!served.contains(node.getUUID())) {
-                        Authorizable authr;
-                        if (node.isNodeType(NT_REP_USER)) {
-                            authr = createUser(node);
-                        } else if (node.isNodeType(NT_REP_GROUP)) {
-                            authr = createGroup(node);
-                        } else {
-                            log.warn("Ignoring unexpected nodetype: " + node.getPrimaryNodeType().getName());
-                            continue;
-                        }
+                        Authorizable authr = getAuthorizable(node);
                         served.add(node.getUUID());
-                        return authr;
+                        if (authr != null) {
+                            return authr;
+                        }
                     }
                 } catch (RepositoryException e) {
                     log.debug(e.getMessage());
@@ -983,39 +1100,64 @@
                 folder = createIntermediateFolderNodes(id, escapedId, folder);
             }
 
-            // finally create the authorizable node
             Name nodeName = session.getQName(escapedId);
             Name ntName = (isGroup) ? NT_REP_GROUP : NT_REP_USER;
             NodeId nid = buildNodeId(id);
 
+            // check if there exists an colliding folder child node.
+            while (((NodeImpl) folder).hasNode(nodeName)) {
+                NodeImpl colliding = ((NodeImpl) folder).getNode(nodeName);
+                if (colliding.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
+                    log.warn("Existing folder node collides with user/group to be created. Expanding path: " + colliding.getPath());
+                    folder = colliding;
+                } else {
+                    // should never get here as folder creation above already
+                    // asserts that only rep:authorizable folders exist.
+                    // similarly collisions with existing authorizable have been
+                    // checked.
+                    String msg = "Failed to create authorizable with id '" + id + "' : Detected conflicting node of unexpected nodetype '" + colliding.getPrimaryNodeType().getName() + "'.";
+                    log.error(msg);
+                    throw new ConstraintViolationException(msg);
+                }
+            }
+
+            // check for collision with existing node outside of the user/group tree
+            if (session.getItemManager().itemExists(nid)) {
+                String msg = "Failed to create authorizable with id '" + id + "' : Detected conflict with existing node (NodeID: " + nid + ")";
+                log.error(msg);
+                throw new ItemExistsException(msg);
+            }
+
+            // finally create the authorizable node
             return addNode((NodeImpl) folder, nodeName, ntName, nid);
         }
 
         private Node createDefaultFolderNodes(String id, String escapedId,
                                               boolean isGroup, String intermediatePath) throws RepositoryException {
-            NodeImpl folder;
+
             String defaultPath = getDefaultFolderPath(id, isGroup, intermediatePath);
-            if (session.nodeExists(defaultPath)) {
-                folder = (NodeImpl) session.getNode(defaultPath);
-                if (!folder.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
-                    throw new RepositoryException("Invalid intermediate path. Must be of type rep:AuthorizableFolder.");
-                }
-            } else {
-                String[] segmts = defaultPath.split("/");
-                folder = (NodeImpl) session.getRootNode();
 
-                for (String segment : segmts) {
-                    if (segment.length() < 1) {
-                        continue;
-                    }
-                    if (folder.hasNode(segment)) {
-                        folder = (NodeImpl) folder.getNode(segment);
-                        if (!folder.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
-                            throw new RepositoryException("Invalid intermediate path. Must be of type rep:AuthorizableFolder.");
-                        }
-                    } else {
-                        folder = addNode(folder, session.getQName(segment), NT_REP_AUTHORIZABLE_FOLDER);
+            // make sure users/groups are never nested and exclusively created
+            // under a tree of rep:AuthorizableFolder(s) starting at usersPath
+            // or groupsPath, respectively. ancestors of the usersPath/groupsPath
+            // may or may not be rep:AuthorizableFolder(s).
+            // therefore the shortcut Session.getNode(defaultPath) is omitted.
+            String[] segmts = defaultPath.split("/");
+            NodeImpl folder = (NodeImpl) session.getRootNode();
+            String authRoot = (isGroup) ? groupsPath : usersPath;
+
+            for (String segment : segmts) {
+                if (segment.length() < 1) {
+                    continue;
+                }
+                if (folder.hasNode(segment)) {
+                    folder = (NodeImpl) folder.getNode(segment);
+                    if (Text.isDescendantOrEqual(authRoot, folder.getPath()) &&
+                            !folder.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
+                        throw new ConstraintViolationException("Invalid intermediate path. Must be of type rep:AuthorizableFolder.");
                     }
+                } else {
+                    folder = addNode(folder, session.getQName(segment), NT_REP_AUTHORIZABLE_FOLDER);
                 }
             }
 
@@ -1068,8 +1210,8 @@
 
             // additional folders needs be created if
             // - the maximal size of child nodes is reached
-            // - if the auth-node to be created potentially collides with any
-            //   of the intermediate nodes.
+            // - if the authorizable node to be created potentially collides with
+            //   any of the intermediate nodes.
             int segmLength = defaultDepth +1;
             
             while (intermediateFolderNeeded(escapedId, folder)) {
@@ -1099,7 +1241,7 @@
                         // should never get here: some other, unexpected node type
                         String msg = "Failed to create authorizable node: Detected conflict with node of unexpected nodetype '" + n.getPrimaryNodeType().getName() + "'.";
                         log.error(msg);
-                        throw new RepositoryException(msg);
+                        throw new ConstraintViolationException(msg);
                     }
                 } else {
                     // folder doesn't exist nor does another colliding child node.
@@ -1138,7 +1280,7 @@
                 // existing folder structure: a/ab
                 // authID to be created     : abt
                 // OR
-                // existing collition that would result from
+                // existing collision that would result from
                 // existing folder structure: a/ab/abt
                 // authID to be create      : abt
                 return true;

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/ReferenceChangeTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/ReferenceChangeTracker.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/ReferenceChangeTracker.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/ReferenceChangeTracker.java Wed Nov 25 14:04:38 2009
@@ -22,6 +22,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.List;
 
 /**
  * Simple helper class that can be used to keep track of node id mappings
@@ -91,4 +92,15 @@
     public Iterator<Object> getProcessedReferences() {
         return references.iterator();
     }
+
+    /**
+     * Remove the given references that have already been processed from the
+     * references list.
+     * 
+     * @param processedReferences
+     * @return <code>true</code> if the internal list of references changed.
+     */
+    public boolean removeReferences(List<Object> processedReferences) {
+        return references.removeAll(processedReferences);
+    }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java Wed Nov 25 14:04:38 2009
@@ -41,6 +41,7 @@
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
 import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.principal.UnknownPrincipal;
@@ -75,27 +76,44 @@
         ACE_NODETYPES.add(AccessControlConstants.NT_REP_GRANT_ACE);
     }
 
-    private final AccessControlManager acMgr;
     private final Stack<Integer> prevStatus = new Stack<Integer>();
-
+    
+    private AccessControlManager acMgr;
     private int status = STATUS_UNDEFINED;
     private NodeImpl parent = null;
 
     private boolean principalbased = false;
 
+    private boolean initialized = false;
+
     /**
      * the ACL for non-principal based
      */
     private JackrabbitAccessControlList acl = null;
 
-    public AccessControlImporter(JackrabbitSession session, NamePathResolver resolver,
-                                 boolean isWorkspaceImport, int uuidBehavior) throws RepositoryException {
-        super(session, resolver, isWorkspaceImport, uuidBehavior);
-
-        acMgr = session.getAccessControlManager();
+    @Override
+    public boolean init(JackrabbitSession session, NamePathResolver resolver,
+                        boolean isWorkspaceImport, int uuidBehavior,
+                        ReferenceChangeTracker referenceTracker) {
+        if (super.init(session, resolver, isWorkspaceImport, uuidBehavior, referenceTracker)) {
+            if (initialized) {
+                throw new IllegalStateException("Already initialized");
+            }
+            try {
+                acMgr = session.getAccessControlManager();
+                initialized = true;
+            } catch (RepositoryException e) {
+                // initialization failed. ac-import not possible
+            }
+        }
+        return initialized;
     }
 
+    @Override
     public boolean start(NodeImpl protectedParent) throws RepositoryException {
+        if (!initialized) {
+            throw new IllegalStateException("Not initialized");
+        }
         if (isStarted()) {
             // only ok if same parent
             if (!protectedParent.isSame(parent)) {
@@ -116,7 +134,7 @@
                 && protectedParent.isNodeType(AccessControlConstants.NT_REP_ACL)) {
             acl = getACL(protectedParent.getParent().getPath());
             if (acl == null) {
-                log.warn("AccessControlImporter cannot be started: no ACL for {}.", parent.getParent().getPath());
+                log.warn("AccessControlImporter cannot be started: no ACL for {}.", protectedParent.getParent().getPath());
                 return false;
             }
             status = STATUS_ACL;
@@ -155,6 +173,7 @@
         return acl;
     }
 
+    @Override
     public boolean start(NodeState protectedParent) throws IllegalStateException, RepositoryException {
         if (isStarted()) {
             throw new IllegalStateException();
@@ -166,16 +185,17 @@
         return false;
     }
 
+    @Override
     public void end(NodeImpl protectedParent) throws RepositoryException {
         if (!isStarted()) {
             return;
         }
 
         if (!principalbased) {
-            checkStatus(STATUS_ACL, "");
+            checkStatus(STATUS_ACL, "STATUS_ACL expected.");
             acMgr.setPolicy(acl.getPath(), acl);
         } else {
-            checkStatus(STATUS_AC_FOLDER, "");
+            checkStatus(STATUS_AC_FOLDER, "STATUS_AC_FOLDER expected.");
             if (!prevStatus.isEmpty()) {
                 throw new ConstraintViolationException("Incomplete protected item tree: "+ prevStatus.size()+ " calls to 'endChildInfo' missing.");
             }
@@ -183,10 +203,12 @@
         reset();
     }
 
+    @Override
     public void end(NodeState protectedParent) throws IllegalStateException, ConstraintViolationException, RepositoryException {
         // nothing to do. will never get here.
     }
 
+    @Override
     public void startChildInfo(NodeInfo childInfo, List<PropInfo> propInfos) throws RepositoryException {
         if (!isStarted()) {
             return;
@@ -240,9 +262,7 @@
         prevStatus.push(previousStatus);        
     }
 
-    /**
-     * @throws javax.jcr.RepositoryException
-     */
+    @Override
     public void endChildInfo() throws RepositoryException {
         if (!isStarted()) {
             return;
@@ -261,7 +281,6 @@
         status = prevStatus.pop();
     }
 
-
     private boolean isStarted() {
         return status > STATUS_UNDEFINED;
     }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedNodeImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedNodeImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedNodeImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedNodeImporter.java Wed Nov 25 14:04:38 2009
@@ -21,6 +21,7 @@
 import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
@@ -30,25 +31,24 @@
  */
 public class DefaultProtectedNodeImporter implements ProtectedNodeImporter {
 
-    protected final JackrabbitSession session;
+    protected JackrabbitSession session;
+    protected NamePathResolver resolver;
+    protected boolean isWorkspaceImport;
+    protected int uuidBehavior;
+    protected ReferenceChangeTracker referenceTracker;
 
-    protected final NamePathResolver resolver;
-
-    protected final boolean isWorkspaceImport;
-
-    protected final int uuidBehavior;
+    public DefaultProtectedNodeImporter() {
+    }
 
-    public DefaultProtectedNodeImporter(JackrabbitSession session,
-                                 NamePathResolver resolver,
-                                 boolean isWorkspaceImport,
-                                 int uuidBehavior) {
+    public boolean init(JackrabbitSession session, NamePathResolver resolver, boolean isWorkspaceImport, int uuidBehavior, ReferenceChangeTracker referenceTracker) {
         this.session = session;
         this.resolver = resolver;
         this.isWorkspaceImport = isWorkspaceImport;
         this.uuidBehavior = uuidBehavior;
+        this.referenceTracker = referenceTracker;
+        return true;
     }
 
-
     /**
      * Always returns <code>false</code>.
      *
@@ -98,4 +98,12 @@
      */
     public void endChildInfo() throws RepositoryException {
     }
+
+    /**
+     * Does nothing.
+     *
+     * @see ProtectedNodeImporter#processReferences()
+     */
+    public void processReferences() throws RepositoryException {
+    }
 }
\ No newline at end of file

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedPropertyImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedPropertyImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedPropertyImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/DefaultProtectedPropertyImporter.java Wed Nov 25 14:04:38 2009
@@ -19,6 +19,7 @@
 import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
@@ -29,18 +30,28 @@
  */
 public class DefaultProtectedPropertyImporter implements ProtectedPropertyImporter {
 
-    protected final JackrabbitSession session;
+    protected JackrabbitSession session;
 
-    protected final NamePathResolver resolver;
+    protected NamePathResolver resolver;
 
-    protected final boolean isWorkspaceImport;
+    protected boolean isWorkspaceImport;
 
-    public DefaultProtectedPropertyImporter(JackrabbitSession session,
-                                 NamePathResolver resolver,
-                                 boolean isWorkspaceImport) {
+    protected int uuidBehavior;
+
+    protected ReferenceChangeTracker referenceTracker;
+
+    public DefaultProtectedPropertyImporter() {
+    }
+
+    public boolean init(JackrabbitSession session, NamePathResolver resolver,
+                        boolean isWorkspaceImport,
+                        int uuidBehavior, ReferenceChangeTracker referenceTracker) {
         this.session = session;
         this.resolver = resolver;
         this.isWorkspaceImport = isWorkspaceImport;
+        this.uuidBehavior = uuidBehavior;
+        this.referenceTracker = referenceTracker;
+        return true;
     }
 
     /**
@@ -48,7 +59,7 @@
      *
      * @see ProtectedPropertyImporter#handlePropInfo(org.apache.jackrabbit.core.NodeImpl, PropInfo, QPropertyDefinition)
      */
-    public boolean handlePropInfo(NodeImpl parent, PropInfo protectedPropInfo, QPropertyDefinition def) {
+    public boolean handlePropInfo(NodeImpl parent, PropInfo protectedPropInfo, QPropertyDefinition def) throws RepositoryException {
         return false;
     }
 
@@ -60,4 +71,12 @@
     public boolean handlePropInfo(NodeState parent, PropInfo protectedPropInfo, QPropertyDefinition def) throws RepositoryException {
         return false;
     }
+
+    /**
+     * Always returns <code>false</code>.
+     *
+     * @see ProtectedPropertyImporter#processReferences()
+     */
+    public void processReferences() throws RepositoryException {
+    }
 }
\ No newline at end of file

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedNodeImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedNodeImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedNodeImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedNodeImporter.java Wed Nov 25 14:04:38 2009
@@ -17,7 +17,10 @@
 package org.apache.jackrabbit.core.xml;
 
 import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
@@ -33,12 +36,23 @@
  * with all subsequent child <code>NodeInfo</code>s present below the protected
  * parent until {@link #end(NodeImpl)} is called. The latter resets this importer
  * and makes it available for another protected import.
- *
- * TODO: check if references properties can be handled with the info present
  */
 public interface ProtectedNodeImporter {
 
     /**
+     * 
+     * @param session
+     * @param resolver
+     * @param isWorkspaceImport
+     * @param uuidBehavior
+     * @param referenceTracker
+     * @return
+     */
+    boolean init(JackrabbitSession session, NamePathResolver resolver,
+                 boolean isWorkspaceImport, int uuidBehavior,
+                 ReferenceChangeTracker referenceTracker);
+
+    /**
      * Notifies this importer about the existience of a protected node that
      * has either been created (NEW) or has been found to be existing.
      * This importer implementation is in charge of evaluating the nature of
@@ -149,4 +163,13 @@
      */
     void endChildInfo() throws RepositoryException;
 
+    /**
+     * Post processing protected reference properties underneith a parent
+     * node that has been handled by this importer.
+     * This method is called
+     * from {@link org.apache.jackrabbit.core.xml.Importer#end()}.
+     * 
+     * @throws RepositoryException If an error occurs.
+     */
+    void processReferences() throws RepositoryException;
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedPropertyImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedPropertyImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedPropertyImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/ProtectedPropertyImporter.java Wed Nov 25 14:04:38 2009
@@ -19,8 +19,11 @@
 import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.api.JackrabbitSession;
 
 /**
  * <code>ProtectedPropertyImporter</code> is in charge of importing single
@@ -32,6 +35,18 @@
 public interface ProtectedPropertyImporter {
 
     /**
+     * 
+     * @param session
+     * @param resolver
+     * @param isWorkspaceImport
+     * @param uuidBehavior
+     *@param referenceTracker  @return
+     */
+    boolean init(JackrabbitSession session, NamePathResolver resolver,
+                 boolean isWorkspaceImport, int uuidBehavior,
+                 ReferenceChangeTracker referenceTracker);
+
+    /**
      * Handles a single protected property.
      *
      * @param parent The affected parent node.
@@ -42,7 +57,8 @@
      * <code>false</code> otherwise.
      * @throws RepositoryException If an error occurs.
      */
-    boolean handlePropInfo(NodeImpl parent, PropInfo protectedPropInfo, QPropertyDefinition def)
+    boolean handlePropInfo(NodeImpl parent, PropInfo protectedPropInfo,
+                           QPropertyDefinition def)
             throws RepositoryException;
 
     /**
@@ -56,8 +72,17 @@
      * <code>false</code> otherwise.
      * @throws RepositoryException If an error occurs.
      */
-    boolean handlePropInfo(NodeState parent, PropInfo protectedPropInfo, QPropertyDefinition def)
+    boolean handlePropInfo(NodeState parent, PropInfo protectedPropInfo,
+                           QPropertyDefinition def)
             throws RepositoryException;
 
 
+    /**
+     * Post processing protected reference properties. This method is called
+     * from {@link org.apache.jackrabbit.core.xml.Importer#end()}.
+     *
+     * @throws RepositoryException If an error occurs.
+     */
+    void processReferences() throws RepositoryException;
+
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java Wed Nov 25 14:04:38 2009
@@ -19,6 +19,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Stack;
+import java.util.ArrayList;
 
 import javax.jcr.AccessDeniedException;
 import javax.jcr.ImportUUIDBehavior;
@@ -34,6 +35,7 @@
 
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.config.ImportConfig;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
@@ -62,15 +64,16 @@
      */
     private final ReferenceChangeTracker refTracker;
 
+    private final List<ProtectedNodeImporter> pnImporters = new ArrayList();
     /**
-     * Importer for protected nodes.
+     * Available importers for protected properties.
      */
-    private final ProtectedNodeImporter pnImporter;
+    private final List<ProtectedPropertyImporter> ppImporters = new ArrayList();
 
     /**
-     * Importer for protected properties.
+     * Currently active importer for protected nodes.
      */
-    private final ProtectedPropertyImporter ppImporter;
+    private ProtectedNodeImporter pnImporter = null;
 
     /**
      * Creates a new <code>SessionImporter</code> instance.
@@ -83,7 +86,7 @@
     public SessionImporter(NodeImpl importTargetNode,
                            SessionImpl session,
                            int uuidBehavior) {
-        this(importTargetNode, session, uuidBehavior, null, null);
+        this(importTargetNode, session, uuidBehavior, null);
     }
 
     /**
@@ -92,27 +95,47 @@
      * @param importTargetNode the target node
      * @param session session
      * @param uuidBehavior the uuid behaviro
-     * @param pnImporter importer for protected nodes
-     * @param ppImporter importer for protected properties
+     * @param config
      */
     public SessionImporter(NodeImpl importTargetNode, SessionImpl session,
-                           int uuidBehavior,
-                           ProtectedNodeImporter pnImporter,
-                           ProtectedPropertyImporter ppImporter) {
+                           int uuidBehavior, ImportConfig config) {
         this.importTargetNode = importTargetNode;
         this.session = session;
         this.uuidBehavior = uuidBehavior;
 
-        this.ppImporter = ppImporter == null
-                ? new DefaultProtectedPropertyImporter(session, session, false)
-                : ppImporter;
-        this.pnImporter = pnImporter == null
-                ? new DefaultProtectedNodeImporter(session, session, false, uuidBehavior)
-                : pnImporter;
         refTracker = new ReferenceChangeTracker();
 
         parents = new Stack<NodeImpl>();
         parents.push(importTargetNode);
+
+        if (config != null) {
+            List<ProtectedNodeImporter> ln = config.getProtectedNodeImporters();
+            for (ProtectedNodeImporter pni : ln) {
+                if (pni.init(session, session, false, uuidBehavior, refTracker)) {
+                    pnImporters.add(pni);
+                }
+            }
+            List<ProtectedPropertyImporter> lp = config.getProtectedPropertyImporters();
+            for (ProtectedPropertyImporter ppi : lp) {
+                if (ppi.init(session, session, false, uuidBehavior, refTracker)) {
+                    ppImporters.add(ppi);
+                }
+            }
+        }
+
+        // missing config -> initialize defaults.
+        if (pnImporters.isEmpty()) {
+            ProtectedNodeImporter def = new DefaultProtectedNodeImporter();
+            if (def.init(session, session, false, uuidBehavior, refTracker)) {
+                pnImporters.add(def);
+            }
+        }
+        if (ppImporters.isEmpty()) {
+            DefaultProtectedPropertyImporter def = new DefaultProtectedPropertyImporter();
+            if (def.init(session, session, false, uuidBehavior, refTracker)) {
+                ppImporters.add(def);
+            }
+        }
     }
 
     /**
@@ -272,7 +295,9 @@
             // parent node was skipped, skip this child node too
             parents.push(null); // push null onto stack for skipped node
             // notify the p-i-importer
-            pnImporter.startChildInfo(nodeInfo, propInfos);
+            if (pnImporter != null) {
+                pnImporter.startChildInfo(nodeInfo, propInfos);
+            }
             return;
         }
 
@@ -284,11 +309,16 @@
             // Notify the ProtectedNodeImporter about the start of a item
             // tree that is protected by this parent. If it potentially is
             // able to deal with it, notify it about the child node.
-            if (pnImporter.start(parent)) {
-                log.debug("Protected node -> delegated to ProtectedPropertyImporter");
-                pnImporter.startChildInfo(nodeInfo, propInfos);
-            } /* else: p-i-Importer isn't able to deal with the protected tree.
-                 skip the tree below the protected parent */
+            for (ProtectedNodeImporter pni : pnImporters) {
+                if (pni.start(parent)) {
+                    log.debug("Protected node -> delegated to ProtectedPropertyImporter");
+                    pnImporter = pni;
+                    pnImporter.startChildInfo(nodeInfo, propInfos);
+                    break;
+                } /* else: p-i-Importer isn't able to deal with the protected tree.
+                     try next. and if none can handle the passed parent the
+                     tree below will be skipped */
+            }
             return;
         }
 
@@ -375,10 +405,14 @@
                 log.debug("Skipping protected property " + pi.getName());
 
                 // notify the ProtectedPropertyImporter.
-                if (ppImporter.handlePropInfo(node, pi, def)) {
-                    // TODO: deal with reference props within the imported tree?                    
-                    log.debug("Protected property -> delegated to ProtectedPropertyImporter");
-                } // else: p-i-Importer isn't able to deal with this property
+                for (ProtectedPropertyImporter ppi : ppImporters) {
+                    if (ppi.handlePropInfo(node, pi, def)) {
+                        log.debug("Protected property -> delegated to ProtectedPropertyImporter");
+                        break;
+                    } /* else: p-i-Importer isn't able to deal with this property.
+                         try next pp-importer */
+
+                }
             } else {
                 // regular property -> create the property
                 createProperty(node, pi, def);
@@ -395,10 +429,16 @@
     public void endNode(NodeInfo nodeInfo) throws RepositoryException {
         NodeImpl parent = parents.pop();
         if (parent == null) {
-            pnImporter.endChildInfo();
+            if (pnImporter != null) {
+                pnImporter.endChildInfo();
+            }
         } else if (parent.getDefinition().isProtected()) {
-            pnImporter.end(parent);
-            // TODO: deal with reference props within the imported tree?
+            if (pnImporter != null) {
+                pnImporter.end(parent);
+                // and reset the pnImporter field waiting for the next protected
+                // parent -> selecting again from available importers
+                pnImporter = null;
+            }
         }
     }
 
@@ -410,9 +450,24 @@
          * adjust references that refer to uuid's which have been mapped to
          * newly generated uuid's on import
          */
-        Iterator iter = refTracker.getProcessedReferences();
+        // 1. let protected property/node importers handle protected ref-properties
+        //    and (protected) properties underneith a protected parent node.
+        for (ProtectedPropertyImporter ppi : ppImporters) {
+            ppi.processReferences();
+        }
+        for (ProtectedNodeImporter pni : pnImporters) {
+            pni.processReferences();
+        }
+
+        // 2. regular non-protected properties.
+        Iterator<Object> iter = refTracker.getProcessedReferences();
         while (iter.hasNext()) {
-            Property prop = (Property) iter.next();
+            Object ref = iter.next();
+            if (!(ref instanceof Property)) {
+                continue;
+            }
+
+            Property prop = (Property) ref;
             // being paranoid...
             if (prop.getType() != PropertyType.REFERENCE
                     && prop.getType() != PropertyType.WEAKREFERENCE) {

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java Wed Nov 25 14:04:38 2009
@@ -35,6 +35,7 @@
 import org.apache.jackrabbit.core.HierarchyManager;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.WorkspaceImpl;
+import org.apache.jackrabbit.core.config.ImportConfig;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.id.PropertyId;
 import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
@@ -66,7 +67,6 @@
     private final WorkspaceImpl wsp;
     private final SessionImpl session;
     private final InternalVersionManager versionManager;
-    private final NodeTypeRegistry ntReg;
     private final HierarchyManager hierMgr;
     private final BatchedItemOperations itemOps;
 
@@ -107,10 +107,40 @@
                              int uuidBehavior)
             throws PathNotFoundException, ConstraintViolationException,
             VersionException, LockException, RepositoryException {
+        this(parentPath, wsp, ntReg, uuidBehavior, null);
+    }
+
+    /**
+     * Creates a new <code>WorkspaceImporter</code> instance.
+     *
+     * @param parentPath   target path where to add the imported subtree
+     * @param wsp the workspace to operate on
+     * @param ntReg the node type registry
+     * @param uuidBehavior flag that governs how incoming UUIDs are handled
+     * @param config import configuration.
+     * @throws PathNotFoundException        if no node exists at
+     *                                      <code>parentPath</code> or if the
+     *                                      current session is not granted read
+     *                                      access.
+     * @throws ConstraintViolationException if the node at
+     *                                      <code>parentPath</code> is protected
+     * @throws VersionException             if the node at
+     *                                      <code>parentPath</code> is not
+     *                                      checked-out
+     * @throws LockException                if a lock prevents the addition of
+     *                                      the subtree
+     * @throws RepositoryException          if another error occurs
+     */
+    public WorkspaceImporter(Path parentPath,
+                             WorkspaceImpl wsp,
+                             NodeTypeRegistry ntReg,
+                             int uuidBehavior,
+                             ImportConfig config)
+            throws PathNotFoundException, ConstraintViolationException,
+            VersionException, LockException, RepositoryException {
         this.wsp = wsp;
         this.session = (SessionImpl) wsp.getSession();
         this.versionManager = session.getInternalVersionManager();
-        this.ntReg = ntReg;
         this.uuidBehavior = uuidBehavior;
 
         itemOps = new BatchedItemOperations(
@@ -128,6 +158,24 @@
 
         parents = new Stack<NodeState>();
         parents.push(importTarget);
+
+        // TODO: TOBEFIXED importer doesn't yet pass protected items to the configured importers.
+        // for the time being throw exception if an importer is configured that
+        // is expected to work with workspace import.
+        if (config != null) {
+            List<ProtectedNodeImporter> ln = config.getProtectedNodeImporters();
+            for (ProtectedNodeImporter pni : ln) {
+                if (pni.init(session, session, true, uuidBehavior, refTracker)) {
+                    throw new UnsupportedOperationException("Workspace-Import of protected nodes: Not yet implement. ");
+                }
+            }
+            List<ProtectedPropertyImporter> lp = config.getProtectedPropertyImporters();
+            for (ProtectedPropertyImporter ppi : lp) {
+                if (ppi.init(session, session, true, uuidBehavior, refTracker)) {
+                    throw new UnsupportedOperationException("Workspace-Import of protected properties: Not yet implement. ");
+                }
+            }
+        }
     }
 
     /**
@@ -660,7 +708,7 @@
              * adjust references that refer to uuid's which have been mapped to
              * newly gererated uuid's on import
              */
-            Iterator iter = refTracker.getProcessedReferences();
+            Iterator<Object> iter = refTracker.getProcessedReferences();
             while (iter.hasNext()) {
                 PropertyState prop = (PropertyState) iter.next();
                 // being paranoid...

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory Wed Nov 25 14:04:38 2009
@@ -18,4 +18,3 @@
 #
 
 org.apache.jackrabbit.core.RepositoryFactoryImpl
-org.apache.jackrabbit.core.jndi.RepositoryFactoryImpl

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd Wed Nov 25 14:04:38 2009
@@ -558,6 +558,7 @@
   abstract
 
 [rep:ACL] > rep:Policy
+  orderable
   + * (rep:ACE) = rep:GrantACE protected IGNORE
 
 [rep:ACE]
@@ -586,12 +587,10 @@
 // User Management 
 // -----------------------------------------------------------------------------
 
-[rep:Authorizable] > mix:referenceable
+[rep:Authorizable] > mix:referenceable, nt:hierarchyNode
   abstract
-  + * (rep:Authorizable) = rep:Authorizable protected VERSION
-  + * (rep:AuthorizableFolder) = rep:AuthorizableFolder protected VERSION
+  + * (nt:base) = nt:unstructured VERSION
   - rep:principalName (STRING) protected mandatory
-  - rep:groups (WEAKREFERENCE) protected multiple < 'rep:Group'
   - * (UNDEFINED)
   - * (UNDEFINED) multiple
 
@@ -603,11 +602,12 @@
   - rep:password (STRING) protected mandatory
 
 [rep:Group] > rep:Authorizable
+  - rep:members (WEAKREFERENCE) protected multiple < 'rep:Authorizable'
 
-[rep:AuthorizableFolder] > mix:referenceable
-  + * (rep:Authorizable) = rep:User protected VERSION
-  + * (rep:AuthorizableFolder) = rep:AuthorizableFolder protected VERSION
-
+[rep:AuthorizableFolder] > nt:hierarchyNode
+  + * (rep:Authorizable) = rep:User VERSION
+  + * (rep:AuthorizableFolder) = rep:AuthorizableFolder VERSION
+  
 // -----------------------------------------------------------------------------
 // J A C K R A B B I T  R E T E N T I O N  M A N A G E M E N T
 // -----------------------------------------------------------------------------

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/query/lucene/tika-config.xml
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/query/lucene/tika-config.xml?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/query/lucene/tika-config.xml (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/query/lucene/tika-config.xml Wed Nov 25 14:04:38 2009
@@ -112,20 +112,8 @@
       <mime>application/x-vnd.oasis.opendocument.formula-template</mime>
     </parser>
 
-    <parser name="parse-image" class="org.apache.tika.parser.image.ImageParser">
-      <mime>image/bmp</mime>
-      <mime>image/gif</mime>
-      <mime>image/jpeg</mime>
-      <mime>image/png</mime>
-      <mime>image/tiff</mime>
-      <mime>image/vnd.wap.wbmp</mime>
-      <mime>image/x-icon</mime>
-      <mime>image/x-psd</mime>
-      <mime>image/x-xcf</mime>
-    </parser>
-
     <parser name="parse-class" class="org.apache.tika.parser.asm.ClassParser">
-      <mime>application/x-tika-java-class</mime>
+      <mime>application/java-vm</mime>
     </parser>
 
     <parser name="parse-mp3" class="org.apache.tika.parser.mp3.Mp3Parser">
@@ -143,6 +131,14 @@
       <mime>audio/x-aiff</mime>
     </parser>
 
+    <parser name="parse-mbox" class="org.apache.tika.parser.mbox.MboxParser">
+      <mime>application/mbox</mime>
+    </parser>
+
+    <parser name="parse-epub" class="org.apache.tika.parser.epub.EpubParser">
+      <mime>application/epub+zip</mime>
+    </parser>
+
   </parsers>
 
-</properties>
\ No newline at end of file
+</properties>

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java Wed Nov 25 14:04:38 2009
@@ -27,7 +27,6 @@
 import java.security.acl.Group;
 import java.util.Enumeration;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Set;
 
 /**
@@ -145,9 +144,9 @@
         while (it.hasNext()) {
             Principal p = it.nextPrincipal();
             if (isGroup(p) && !p.equals(principalMgr.getEveryone())) {
-                Enumeration en = ((java.security.acl.Group) p).members();
+                Enumeration<? extends Principal> en = ((java.security.acl.Group) p).members();
                 while (en.hasMoreElements()) {
-                    Principal memb = (Principal) en.nextElement();
+                    Principal memb = en.nextElement();
                     assertTrue(principalMgr.hasPrincipal(memb.getName()));
                 }
             }
@@ -198,9 +197,9 @@
 
             assertTrue(isGroup(p));
 
-            Enumeration members = ((java.security.acl.Group) p).members();
+            Enumeration<? extends Principal> members = ((java.security.acl.Group) p).members();
             while (members.hasMoreElements()) {
-                Principal memb = (Principal) members.nextElement();
+                Principal memb = members.nextElement();
 
                 Principal group = null;
                 PrincipalIterator mship = principalMgr.getGroupMembership(memb);
@@ -221,7 +220,7 @@
             if (pcpl.equals(everyone)) {
                 continue;
             }
-            Iterator it = principalMgr.findPrincipals(pcpl.getName());
+            PrincipalIterator it = principalMgr.findPrincipals(pcpl.getName());
             // search must find at least a single principal
             assertTrue("findPrincipals does not find principal with filter " + pcpl.getName(), it.hasNext());
         }
@@ -236,12 +235,12 @@
             }
 
             if (isGroup(pcpl)) {
-                Iterator it = principalMgr.findPrincipals(pcpl.getName(),
+                PrincipalIterator it = principalMgr.findPrincipals(pcpl.getName(),
                         PrincipalManager.SEARCH_TYPE_GROUP);
                 // search must find at least a single matching group principal
                 assertTrue("findPrincipals does not find principal with filter " + pcpl.getName(), it.hasNext());
             } else {
-                Iterator it = principalMgr.findPrincipals(pcpl.getName(),
+                PrincipalIterator it = principalMgr.findPrincipals(pcpl.getName(),
                         PrincipalManager.SEARCH_TYPE_NOT_GROUP);
                 // search must find at least a single matching non-group principal
                 assertTrue("findPrincipals does not find principal with filter " + pcpl.getName(), it.hasNext());

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java Wed Nov 25 14:04:38 2009
@@ -34,7 +34,6 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.Set;
 import java.util.UUID;
 
@@ -45,6 +44,7 @@
 
     protected UserManager userMgr;
 
+    @Override
     protected void setUp() throws Exception {
         super.setUp();
 
@@ -56,6 +56,22 @@
         }
     }
 
+    /**
+     * Conditional save if the userManager expects an extra save() call.
+     * NOTE: only works if changes are made with UserManager affect the passed
+     * session object (i.e. if the Session hold within the UserManager is
+     * the same as the passes Session s.
+     *
+     * @param s
+     * @throws RepositoryException
+     */
+    protected static void save(Session s) throws RepositoryException, NotExecutableException {
+        UserManager umgr = getUserManager(s);
+        if (!umgr.isAutoSave() && s.hasPendingChanges()) {
+            s.save();
+        }
+    }
+
     protected static UserManager getUserManager(Session session) throws RepositoryException, NotExecutableException {
         if (!(session instanceof JackrabbitSession)) {
             throw new NotExecutableException();
@@ -70,7 +86,7 @@
     }
 
     protected static Subject buildSubject(Principal p) {
-        return new Subject(true, Collections.singleton(p), Collections.EMPTY_SET, Collections.EMPTY_SET);
+        return new Subject(true, Collections.singleton(p), Collections.emptySet(), Collections.emptySet());
     }
 
     protected Principal getTestPrincipal() throws RepositoryException {
@@ -79,14 +95,13 @@
     }
 
     protected Principal getTestPrincipal(String name) throws RepositoryException {
-        Principal p = new TestPrincipal(name);
-        return p;
+        return new TestPrincipal(name);
     }
 
     protected String buildPassword(String uid, boolean createDigest) throws IllegalArgumentException {
         if (createDigest) {
             try {
-                StringBuffer password = new StringBuffer();
+                StringBuilder password = new StringBuilder();
                 password.append("{").append(SecurityConstants.DEFAULT_DIGEST).append("}");
                 password.append(Text.digest(SecurityConstants.DEFAULT_DIGEST, uid.getBytes("UTF-8")));
                 return password.toString();
@@ -108,7 +123,7 @@
         return new SimpleCredentials(uID, pw.toCharArray());
     }
 
-    protected static Set getPrincipalSetFromSession(Session session) throws NotExecutableException {
+    protected static Set<Principal> getPrincipalSetFromSession(Session session) throws NotExecutableException {
         if (session instanceof SessionImpl) {
             return ((SessionImpl) session).getSubject().getPrincipals();
         } else {
@@ -123,13 +138,12 @@
             return (User) auth;
         }
         // should never happen. An Session should always have a corresponding User.
-        throw new RepositoryException("Unable to retrieve a User.");
+        throw new NotExecutableException("Unable to retrieve a User.");
     }
 
     protected Group getTestGroup(Session session) throws NotExecutableException, RepositoryException {
-        Set principals = getPrincipalSetFromSession(session);
-        for (Iterator it = principals.iterator(); it.hasNext();) {
-            Authorizable auth = getUserManager(session).getAuthorizable((Principal) it.next());
+        for (Principal principal : getPrincipalSetFromSession(session)) {
+            Authorizable auth = getUserManager(session).getAuthorizable(principal);
             if (auth != null && auth.isGroup()) {
                 return (Group) auth;
             }



Mime
View raw message