felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j...@apache.org
Subject svn commit: r1412574 [1/2] - in /felix/trunk/useradmin: filestore/src/main/java/org/apache/felix/useradmin/filestore/ filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/ filestore/src/test/java/org/apache/felix/useradmin/filestore/ itest...
Date Thu, 22 Nov 2012 14:57:58 GMT
Author: jawi
Date: Thu Nov 22 14:57:51 2012
New Revision: 1412574

URL: http://svn.apache.org/viewvc?rev=1412574&view=rev
Log:
FELIX-3777: simplify RoleRepositoryStore API.

- allow any kind of exception to be thrown from the store implementation;
- remove the need for initialize() and close(). Stores needing to initialize
  themselves do so by using the standard OSGi lifecycle methods;
- allow the store to directly filter for roles;
- several other dings and dents fixed.


Added:
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java   (with props)
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java   (with props)
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableUser.java   (with props)
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/CustomRoleImplTest.java   (with props)
Modified:
    felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java
    felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java
    felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java
    felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java
    felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java
    felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java
    felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java
    felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
    felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
    felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
    felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java
    felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java
    felix/trunk/useradmin/useradmin/pom.xml
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/RoleImpl.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/UserImpl.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/RoleRepositoryStoreHelper.java
    felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/ServiceContext.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/AuthorizationImplTest.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/EventDispatcherTest.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/MemoryRoleRepositoryStore.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleCheckerTest.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositorySecurityTest.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositoryTest.java
    felix/trunk/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/UserAdminImplTest.java

Modified: felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java (original)
+++ felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java Thu Nov 22 14:57:51 2012
@@ -76,6 +76,15 @@ final class ResettableTimer {
     }
 
     /**
+     * Returns the state of this timer.
+     * 
+     * @return <code>true</code> if this timer is shut down, <code>false</code> otherwise.
+     */
+    public boolean isShutDown() {
+        return m_executor.isShutdown();
+    }
+    
+    /**
      * Schedules the task for execution with the contained timeout. If a task 
      * is already pending or running, it will be cancelled (not interrupted). 
      * The new task will be scheduled to run in now + timeout.

Modified: felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java (original)
+++ felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java Thu Nov 22 14:57:51 2012
@@ -86,10 +86,6 @@ public class RoleRepositoryFileStore ext
         }
     }
     
-    public void initialize() throws IOException {
-        m_entries.putAll(retrieve());
-    }
-
     public void roleChanged(UserAdminEvent event) {
         scheduleTask();
     }
@@ -110,25 +106,30 @@ public class RoleRepositoryFileStore ext
     }
 
     /**
+     * Starts this store by reading the latest version from disk.
+     * 
+     * @throws IOException in case of I/O problems retrieving the store.
+     */
+    public void start() throws IOException {
+        m_entries.putAll(retrieve());
+    }
+
+    /**
      * Stops this store service.
      */
-    public void stop()  {
+    public void stop() throws IOException {
         ResettableTimer timer = (ResettableTimer) m_timerRef.get();
         if (timer != null) {
-            // Shutdown and await termination...
-            timer.shutDown();
+            if (!timer.isShutDown()) {
+                // Shutdown and await termination...
+                timer.shutDown();
+            }
             // Clear reference...
             m_timerRef.compareAndSet(timer, null);
         }
-        
-        try {
-            // Write the latest version to disk...
-            flush();
-        }
-        catch (IOException e) {
-            // Nothing we can do about this here...
-            e.printStackTrace();
-        }
+
+        // Write the latest version to disk...
+        flush();
     }
 
     /**
@@ -198,6 +199,9 @@ public class RoleRepositoryFileStore ext
         } catch (FileNotFoundException exception) {
             // Don't bother; file does not exist...
             return Collections.emptyMap();
+        } catch (IOException exception) {
+            exception.printStackTrace();
+            throw exception;
         } finally {
             closeSafely(is);
         }
@@ -251,7 +255,7 @@ public class RoleRepositoryFileStore ext
      */
     private void scheduleTask() {
         ResettableTimer timer = (ResettableTimer) m_timerRef.get();
-        if (timer != null) {
+        if (timer != null && !timer.isShutDown()) {
             timer.schedule();
         }
     }

Modified: felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java (original)
+++ felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java Thu Nov 22 14:57:51 2012
@@ -16,12 +16,16 @@
  */
 package org.apache.felix.useradmin.filestore;
 
-import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.felix.useradmin.RoleFactory;
 import org.apache.felix.useradmin.RoleRepositoryStore;
+import org.osgi.framework.Filter;
 import org.osgi.service.useradmin.Role;
 
 
@@ -32,39 +36,44 @@ public class RoleRepositoryMemoryStore i
     
     protected final ConcurrentMap m_entries = new ConcurrentHashMap();
 
-    public boolean addRole(Role role) throws IOException {
-        if (role == null) {
-            throw new IllegalArgumentException("Role cannot be null!");
+    public Role addRole(String roleName, int type) {
+        if (roleName == null) {
+            throw new IllegalArgumentException("Name cannot be null!");
         }
-        Object result = m_entries.putIfAbsent(role.getName(), role);
-        return result == null;
-    }
-
-    public void close() throws IOException {
-        // Nop
+        Role role = RoleFactory.createRole(type, roleName);
+        Object result = m_entries.putIfAbsent(roleName, role);
+        return (result == null) ? role : null;
     }
 
-    public Role[] getAllRoles() throws IOException {
+    public Role[] getRoles(Filter filter) {
         Collection roles = m_entries.values();
-        Role[] result = new Role[roles.size()];
-        return (Role[]) roles.toArray(result);
+        
+        List matchingRoles = new ArrayList();
+        Iterator rolesIter = roles.iterator();
+        while (rolesIter.hasNext()) {
+            Role role = (Role) rolesIter.next();
+            if ((filter == null) || filter.match(role.getProperties())) {
+                matchingRoles.add(role);
+            }
+        }
+
+        Role[] result = new Role[matchingRoles.size()];
+        return (Role[]) matchingRoles.toArray(result);
     }
 
-    public Role getRoleByName(String roleName) throws IOException {
+    public Role getRoleByName(String roleName) {
         if (roleName == null) {
             throw new IllegalArgumentException("Role name cannot be null!");
         }
         return (Role) m_entries.get(roleName);
     }
-    
-    public void initialize() throws IOException {
-        // Nop
-    }
 
-    public boolean removeRole(Role role) throws IOException {
-        if (role == null) {
-            throw new IllegalArgumentException("Role cannot be null!");
+    public Role removeRole(String roleName) {
+        if (roleName == null) {
+            throw new IllegalArgumentException("Name cannot be null!");
         }
-        return m_entries.remove(role.getName(), role);
+        Role role = getRoleByName(roleName);
+        boolean result = m_entries.remove(roleName, role);
+        return result ? role : null;
     }
 }

Modified: felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java (original)
+++ felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java Thu Nov 22 14:57:51 2012
@@ -161,6 +161,23 @@ final class RoleRepositorySerializer {
     }
 
     /**
+     * Returns the role with the given name from the given repository.
+     * 
+     * @param repository the repository to obtain the roles from, cannot be <code>null</code>;
+     * @param name the name of the role to retrieve, cannot be <code>null</code>.
+     * @return a role matching the given name, or <code>null</code> if no such role exists.
+     */
+    private Role getRoleFromRepository(Map repository, String name) {
+        Role role;
+        if (Role.USER_ANYONE.equals(name)) {
+            role = RoleFactory.createRole(Role.USER_ANYONE);
+        } else {
+            role = (Role) repository.get(name);
+        }
+        return role;
+    }
+    
+    /**
      * Reads and fills a given dictionary.
      * 
      * @param dict the dictionary to read & fill, cannot be <code>null</code>;
@@ -188,7 +205,7 @@ final class RoleRepositorySerializer {
             }
         }
     }
-    
+
     /**
      * Reads a (stub) group from the given input stream.
      * 
@@ -254,7 +271,7 @@ final class RoleRepositorySerializer {
         
         return repository;
     }
-
+    
     /**
      * Reads a role from the given input stream.
      * 
@@ -269,7 +286,7 @@ final class RoleRepositorySerializer {
         
         return role;
     }
-    
+
     /**
      * Reads a user from the given input stream.
      * 
@@ -300,7 +317,7 @@ final class RoleRepositorySerializer {
 
         for (int i = 0; i < size; i++) {
             String name = (String) names.get(i);
-            Role role = (Role) repository.get(name);
+            Role role = getRoleFromRepository(repository, name);
             if (role == null) {
                 throw new IOException("Unable to find referenced basic member: " + name);
             }
@@ -312,7 +329,7 @@ final class RoleRepositorySerializer {
         
         for (int i = 0; i < size; i++) {
             String name = (String) names.get(i);
-            Role role = (Role) repository.get(name);
+            Role role = getRoleFromRepository(repository, name);
             if (role == null) {
                 throw new IOException("Unable to find referenced required member: " + name);
             }

Modified: felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java (original)
+++ felix/trunk/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java Thu Nov 22 14:57:51 2012
@@ -35,6 +35,7 @@ public class Activator implements Bundle
 
     public void start(BundleContext context) throws Exception {
         m_store = new RoleRepositoryFileStore(context.getDataFile(""));
+        m_store.start();
         
         String[] interfaces = { RoleRepositoryStore.class.getName(), UserAdminListener.class.getName(), ManagedService.class.getName() };
         

Modified: felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java (original)
+++ felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java Thu Nov 22 14:57:51 2012
@@ -22,9 +22,7 @@ import java.util.Map;
 
 import junit.framework.TestCase;
 
-import org.apache.felix.useradmin.impl.RoleRepository;
-import org.apache.felix.useradmin.impl.role.GroupImpl;
-import org.apache.felix.useradmin.impl.role.UserImpl;
+import org.apache.felix.useradmin.RoleFactory;
 import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
@@ -34,9 +32,10 @@ import org.osgi.service.useradmin.User;
  */
 public class RoleRepositoryFileStorePerformanceTest extends TestCase {
 
-    private static final int USER_COUNT = 15000;
+    private static final int USER_COUNT = 25000;
     private static final int GROUP_COUNT = 500;
 
+    private Role m_anyone;
     private Group[] m_groups;
     private User[] m_users;
     
@@ -60,14 +59,14 @@ public class RoleRepositoryFileStorePerf
      * Does a very simple performance test for a large number of users spread over several groups.
      */
     protected void readRepositoryPerformanceTest() throws Exception {
-        long r_st = System.currentTimeMillis();
+        long r_st = System.nanoTime();
         Map result = m_store.retrieve();
-        long r_time = System.currentTimeMillis() - r_st;
+        long r_time = System.nanoTime() - r_st;
 
         assertNotNull(result);
         assertEquals(GROUP_COUNT + USER_COUNT + 1, result.size());
 
-        System.out.println("Read time : " + (r_time / 1000.0) + "s.");
+        System.out.println("Read time : " + (r_time / 1.0e9) + "s.");
     }
 
     /**
@@ -79,19 +78,20 @@ public class RoleRepositoryFileStorePerf
         m_store = new RoleRepositoryFileStore(new File(System.getProperty("java.io.tmpdir")), false /* disable background writes */);
 
         m_repository = new HashMap(USER_COUNT + GROUP_COUNT + 1);
+        m_anyone = RoleFactory.createRole(Role.USER_ANYONE);
 
-        addToRepository(RoleRepository.USER_ANYONE);
+        addToRepository(m_anyone);
     }
     
     /**
      * Does a very simple performance test for writing a large number of users spread over several groups.
      */
     protected void writeRepositoryPerformanceTest() throws Exception {
-        long w_st = System.currentTimeMillis();
+        long w_st = System.nanoTime();
         m_store.store(m_repository);
-        long w_time = System.currentTimeMillis() - w_st;
+        long w_time = System.nanoTime() - w_st;
 
-        System.out.println("Write time: " + (w_time / 1000.0) + "s.");
+        System.out.println("Write time: " + (w_time / 1.0e9) + "s.");
     }
 
     private void addToRepository(Role role) {
@@ -105,7 +105,7 @@ public class RoleRepositoryFileStorePerf
         m_groups = new Group[GROUP_COUNT];
         for (int i = 0; i < m_groups.length; i++) {
             m_groups[i] = createGroup(i+1);
-            m_groups[i].addRequiredMember(RoleRepository.USER_ANYONE);
+            m_groups[i].addRequiredMember(m_anyone);
 
             addToRepository(m_groups[i]);
         }
@@ -124,7 +124,7 @@ public class RoleRepositoryFileStorePerf
     private Group createGroup(int idx) {
         String name = "Group" + idx;
         
-        Group result = new GroupImpl(name);
+        Group result = RoleFactory.createGroup(name);
 
         setCredentials(result);
         setProperties(result);
@@ -135,7 +135,7 @@ public class RoleRepositoryFileStorePerf
     private User createUser(int idx) {
         String name = "User" + idx;
         
-        User result = new UserImpl(name);
+        User result = RoleFactory.createUser(name);
 
         setCredentials(result);
         setProperties(result);

Modified: felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java (original)
+++ felix/trunk/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java Thu Nov 22 14:57:51 2012
@@ -31,9 +31,7 @@ import java.util.Map;
 
 import junit.framework.TestCase;
 
-import org.apache.felix.useradmin.impl.RoleRepository;
-import org.apache.felix.useradmin.impl.role.GroupImpl;
-import org.apache.felix.useradmin.impl.role.UserImpl;
+import org.apache.felix.useradmin.RoleFactory;
 import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
@@ -70,7 +68,6 @@ public class RoleRepositorySerializerTes
         m_group3.addMember(m_user4);
         m_group3.addRequiredMember(m_userAnyone);
         
-        addToRepository(m_userAnyone);
         addToRepository(m_user1);
         addToRepository(m_user2);
         addToRepository(m_user3);
@@ -90,8 +87,7 @@ public class RoleRepositorySerializerTes
         Map result = m_serializer.deserialize(dis);
         assertNotNull(result);
 
-        assertEquals(8, result.size());
-        assertEquals(m_userAnyone, (Role) result.get(m_userAnyone.getName()));
+        assertEquals(7, result.size());
         assertEquals(m_user1, (User) result.get(m_user1.getName()));
         assertEquals(m_user2, (User) result.get(m_user2.getName()));
         assertEquals(m_user3, (User) result.get(m_user3.getName()));
@@ -124,11 +120,9 @@ public class RoleRepositorySerializerTes
     }
 
     /**
-     * Tests that writing and reading a repository with a single role works as expected.
+     * Tests that writing and reading a repository without roles works as expected.
      */
-    public void testRWRepositoryWithSingleRoleOk() throws Exception {
-        addToRepository(m_userAnyone);
-        
+    public void testRWRepositoryWithoutRolesOk() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream dos = new DataOutputStream(baos);
         
@@ -140,9 +134,7 @@ public class RoleRepositorySerializerTes
         Map result = m_serializer.deserialize(dis);
         assertNotNull(result);
         
-        assertEquals(1, result.size());
-
-        assertEquals(m_userAnyone, (Role) result.get(m_userAnyone.getName()));
+        assertEquals(0, result.size());
     }
 
     /**
@@ -226,8 +218,7 @@ public class RoleRepositorySerializerTes
         m_group1.addMember(m_userAnyone);
         m_group1.addRequiredMember(m_user1);
 
-        // "Forget" to add the user.anyone!
-        addToRepository(m_user1);
+        // "Forget" to add the user1
         addToRepository(m_group1);
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -254,8 +245,7 @@ public class RoleRepositorySerializerTes
         m_group1.addRequiredMember(m_userAnyone);
         m_group1.addMember(m_user1);
         
-        // "Forget" to add the user.anyone!
-        addToRepository(m_user1);
+        // "Forget" to add the user1!
         addToRepository(m_group1);
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -285,7 +275,7 @@ public class RoleRepositorySerializerTes
 
         m_repository = new HashMap();
 
-        m_userAnyone = RoleRepository.USER_ANYONE;
+        m_userAnyone = RoleFactory.createRole(Role.USER_ANYONE);
         
         setProperties(m_userAnyone);
         
@@ -307,7 +297,9 @@ public class RoleRepositorySerializerTes
     }
     
     private void addToRepository(Role role) {
-        m_repository.put(role.getName(), role);
+        if (!Role.USER_ANYONE.equals(role.getName())) {
+            m_repository.put(role.getName(), role);
+        }
     }
     
     private void assertEquals(Dictionary expected, Dictionary obtained) {
@@ -380,7 +372,7 @@ public class RoleRepositorySerializerTes
     private User createUser(int idx) {
         String name = "User" + idx;
         
-        User result = new UserImpl(name);
+        User result = RoleFactory.createUser(name);
 
         setCredentials(result);
         setProperties(result);
@@ -391,7 +383,7 @@ public class RoleRepositorySerializerTes
     private Group createGroup(int idx) {
         String name = "Group" + idx;
         
-        Group result = new GroupImpl(name);
+        Group result = RoleFactory.createGroup(name);
 
         setCredentials(result);
         setProperties(result);

Modified: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java (original)
+++ felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java Thu Nov 22 14:57:51 2012
@@ -38,6 +38,7 @@ import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.junit.Configuration;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 
 /**
@@ -77,7 +78,7 @@ public abstract class BaseIntegrationTes
             url("link:classpath:META-INF/links/org.ops4j.pax.swissbox.framework.link").startLevel(START_LEVEL_SYSTEM_BUNDLES),
             url("link:classpath:META-INF/links/org.apache.geronimo.specs.atinject.link").startLevel(START_LEVEL_SYSTEM_BUNDLES),
 
-            mavenBundle("org.osgi", "org.osgi.core").version("4.2.0").startLevel(START_LEVEL_SYSTEM_BUNDLES),
+//            mavenBundle("org.osgi", "org.osgi.core").version("4.2.0").startLevel(START_LEVEL_SYSTEM_BUNDLES),
             mavenBundle("org.osgi", "org.osgi.compendium").version("4.2.0").startLevel(START_LEVEL_SYSTEM_BUNDLES),
             mavenBundle("org.apache.felix", "org.apache.felix.log").version("1.0.1").startLevel(START_LEVEL_SYSTEM_BUNDLES),
             mavenBundle("org.apache.felix", ORG_APACHE_FELIX_USERADMIN).version("1.0.3-SNAPSHOT").startLevel(START_LEVEL_SYSTEM_BUNDLES),
@@ -130,6 +131,13 @@ public abstract class BaseIntegrationTes
         }
         return result;
     }
+    
+    /**
+     * @return the {@link UserAdmin} service instance.
+     */
+    protected UserAdmin getUserAdmin() throws Exception {
+        return getService(UserAdmin.class.getName());
+    }
 
     /**
      * @param bsn

Modified: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java (original)
+++ felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java Thu Nov 22 14:57:51 2012
@@ -21,17 +21,16 @@ package org.apache.felix.useradmin.itest
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.felix.useradmin.RoleRepositoryStore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
 import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
 
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
@@ -40,92 +39,62 @@ import org.osgi.service.useradmin.Role;
 public class FileStoreInitializationTest extends BaseIntegrationTest {
 
 	/**
-	 * Provides a mock file store that does nothing but track the number of
-	 * times the {@link #initialize()} and {@link #close()} methods are called.
-	 */
-	public static class MockRoleRepositoryStore implements RoleRepositoryStore {
-
-		final AtomicInteger m_initCount = new AtomicInteger(0);
-		final AtomicInteger m_closeCount = new AtomicInteger(0);
-
-		@Override
-		public boolean addRole(Role role) throws IOException {
-			return false;
-		}
-
-		@Override
-		public void close() throws IOException {
-			m_closeCount.incrementAndGet();
-		}
-
-		@Override
-		public Role[] getAllRoles() throws IOException {
-			return null;
-		}
-
-		@Override
-		public Role getRoleByName(String roleName) throws IOException {
-			return null;
-		}
-
-		@Override
-		public void initialize() throws IOException {
-			m_initCount.incrementAndGet();
-		}
-
-		@Override
-		public boolean removeRole(Role role) throws IOException {
-			return false;
-		}
-	}
-
-	/**
 	 * Tests that initialization and closing of the repository store is
 	 * performed correctly.
 	 */
 	@Test
 	public void testStoreIsInitializedAndClosedProperlyOk() throws Exception {
-		final String serviceName = RoleRepositoryStore.class.getName();
-		final MockRoleRepositoryStore mockStore = new MockRoleRepositoryStore();
+	    UserAdmin ua = getUserAdmin();
+	    
+	    // Create two roles...
+	    User user = (User) ua.createRole("user1", Role.USER);
+	    assertNotNull(user);
+	    
+	    Group group = (Group) ua.createRole("group1", Role.GROUP);
+	    assertNotNull(group);
+	    
+	    group.addMember(user);
+	    group.addRequiredMember(ua.getRole(Role.USER_ANYONE));
 
-		// Stop the file store...
+		// Stop the file store; should persist the two roles...
 		Bundle fileStoreBundle = findBundle(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
 		assertNotNull(fileStoreBundle);
 		fileStoreBundle.stop();
 
-		// Manually register our mock store...
-		ServiceRegistration serviceReg = m_context.registerService(serviceName, mockStore, null);
-
-		// Wait until it becomes available...
-		awaitService(serviceName);
-
-		assertEquals(1, mockStore.m_initCount.get());
-		assertEquals(0, mockStore.m_closeCount.get());
-
-		serviceReg.unregister();
-
-		Thread.sleep(100); // sleep a tiny bit to allow service to be properly unregistered...
-
-		assertEquals(1, mockStore.m_initCount.get());
-		assertEquals(1, mockStore.m_closeCount.get());
-
-		// Re-register the service again...
-		serviceReg = m_context.registerService(serviceName, mockStore, null);
-
-		assertEquals(2, mockStore.m_initCount.get());
-		assertEquals(1, mockStore.m_closeCount.get());
-
-		// Stop & start the UserAdmin bundle to verify the initialization is
-		// still only performed once...
-		Bundle userAdminBundle = findBundle(ORG_APACHE_FELIX_USERADMIN);
-		assertNotNull(userAdminBundle);
-		userAdminBundle.stop();
-
-		Thread.sleep(100); // sleep a tiny bit to allow service to be properly unregistered...
-
-		userAdminBundle.start();
-
-		assertEquals(3, mockStore.m_initCount.get());
-		assertEquals(2, mockStore.m_closeCount.get());
+		Thread.sleep(100); // Wait a little until the bundle is really stopped...
+		
+		// Retrieve the roles again; should both yield null due to the store not being available...
+		user = (User) ua.getRole("user1");
+		assertNull(user);
+
+		group = (Group) ua.getRole("group1");
+		assertNull(group);
+		
+		// This will not succeed: no backend to store the user in...
+		assertNull(ua.createRole("user2", Role.USER));
+
+		fileStoreBundle.start();
+
+		awaitService(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
+        
+        // Retrieve the roles again; should both yield valid values...
+        user = (User) ua.getRole("user1");
+        assertNotNull(user);
+        
+        group = (Group) ua.getRole("group1");
+        assertNotNull(group);
+
+        Role[] members = group.getMembers();
+        assertNotNull(members);
+        assertEquals(1, members.length);
+        assertEquals("user1", members[0].getName());
+
+        members = group.getRequiredMembers();
+        assertNotNull(members);
+        assertEquals(1, members.length);
+        assertEquals(Role.USER_ANYONE, members[0].getName());
+        
+        user = (User) ua.getRole("user2");
+        assertNull(user);
 	}
 }

Modified: felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java (original)
+++ felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java Thu Nov 22 14:57:51 2012
@@ -19,13 +19,13 @@ package org.apache.felix.useradmin.mongo
 import static org.apache.felix.useradmin.mongodb.MongoSerializerHelper.NAME;
 import static org.apache.felix.useradmin.mongodb.MongoSerializerHelper.TYPE;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.felix.useradmin.RoleRepositoryStore;
+import org.osgi.framework.Filter;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
@@ -124,71 +124,57 @@ public class MongoDBStore implements Rol
     }
 
     @Override
-    public boolean addRole(Role role) throws IOException {
-        if (role == null) {
+    public Role addRole(String roleName, int type) throws MongoException {
+        if (roleName == null) {
             throw new IllegalArgumentException("Role cannot be null!");
         }
         
-        try {
-            DBCollection coll = getCollection();
-            
-            DBCursor cursor = coll.find(getTemplateObject(role));
-            try {
-                if (cursor.hasNext()) {
-                    // Role already exists...
-                    return false;
-                }
-            } finally {
-                cursor.close();
-            }
-            
-            // Role does not exist; insert it...
-            DBObject data = m_helper.serialize(role);
-            
-            WriteResult result = coll.insert(data);
-            
-            if (result.getLastError() != null) {
-                result.getLastError().throwOnError();
-            }
+        DBCollection coll = getCollection();
 
-            return true;
+        Role role = getRole(roleName);
+        if (role != null) {
+            return null;
         }
-        catch (MongoException e) {
-            m_log.log(LogService.LOG_WARNING, "Add role failed!", e);
-            throw new IOException("AddRole failed!", e);
+
+        // Role does not exist; insert it...
+        DBObject data = m_helper.serialize(roleName, type);
+
+        WriteResult result = coll.insert(data);
+        
+        if (result.getLastError() != null) {
+            result.getLastError().throwOnError();
         }
+
+        return role;
     }
 
-    @Override
-    public void close() throws IOException {
+    /**
+     * Closes this store and disconnects from the MongoDB backend.
+     */
+    public void close() {
         MongoDB mongoDB = m_mongoDbRef.get();
         if (mongoDB != null) {
             mongoDB.disconnect();
         }
+        m_mongoDbRef.set(null);
     }
 
     @Override
-    public Role[] getAllRoles() throws IOException {
-        try {
-            List<Role> roles = new ArrayList<Role>();
-            
-            DBCollection coll = getCollection();
+    public Role[] getRoles(Filter filter) throws MongoException {
+        List<Role> roles = new ArrayList<Role>();
+        
+        DBCollection coll = getCollection();
 
-            DBCursor cursor = coll.find();
-            try {
-                while (cursor.hasNext()) {
-                    roles.add(m_helper.deserialize(cursor.next()));
-                }
-            } finally {
-                cursor.close();
+        DBCursor cursor = coll.find();
+        try {
+            while (cursor.hasNext()) {
+                roles.add(m_helper.deserialize(cursor.next()));
             }
-
-            return roles.toArray(new Role[roles.size()]);
-        }
-        catch (MongoException e) {
-            m_log.log(LogService.LOG_WARNING, "Get all roles failed!", e);
-            throw new IOException("GetAllRoles failed!", e);
+        } finally {
+            cursor.close();
         }
+
+        return roles.toArray(new Role[roles.size()]);
     }
 
     @Override
@@ -208,56 +194,26 @@ public class MongoDBStore implements Rol
     }
 
     @Override
-    public Role getRoleByName(String name) throws IOException {
-        try {
-            return getRole(name);
-        }
-        catch (MongoException e) {
-            m_log.log(LogService.LOG_WARNING, "Get role by name failed!", e);
-            throw new IOException("GetRoleByName failed!", e);
-        }
-    }
-    
-    @Override
-    public void initialize() throws IOException {
-        // Check whether we need to connect to MongoDB, or that this is
-        // already done by the #updated method...
-        MongoDB oldMongoDB = m_mongoDbRef.get();
-        if (oldMongoDB == null) {
-            MongoDB mongoDB = new MongoDB(DEFAULT_MONGODB_SERVER, DEFAULT_MONGODB_DBNAME, DEFAULT_MONGODB_COLLECTION);
-            
-            do {
-                oldMongoDB = m_mongoDbRef.get();
-            } 
-            while (!m_mongoDbRef.compareAndSet(oldMongoDB, mongoDB));
-            
-            try {
-                connectToDB(mongoDB, DEFAULT_MONGODB_USERNAME, DEFAULT_MONGODB_PASSWORD);
-            }
-            catch (MongoException e) {
-                m_log.log(LogService.LOG_WARNING, "Initialization failed!", e);
-                throw new IOException("Initialization failed!", e);
-            }
-        }
+    public Role getRoleByName(String name) throws MongoException {
+        return getRole(name);
     }
 
     @Override
-    public boolean removeRole(Role role) throws IOException {
-        try {
-            DBCollection coll = getCollection();
-
-            WriteResult result = coll.remove(getTemplateObject(role));
+    public Role removeRole(String roleName) throws MongoException {
+        DBCollection coll = getCollection();
+        
+        Role role = getRole(roleName);
+        if (role == null) {
+            return null;
+        }
 
-            if (result.getLastError() != null) {
-                result.getLastError().throwOnError();
-            }
+        WriteResult result = coll.remove(getTemplateObject(role));
 
-            return true;
-        }
-        catch (MongoException e) {
-            m_log.log(LogService.LOG_WARNING, "Remove role failed!", e);
-            throw new IOException("RemoveRole failed!", e);
+        if (result.getLastError() != null) {
+            result.getLastError().throwOnError();
         }
+
+        return role;
     }
     
     @Override
@@ -394,8 +350,8 @@ public class MongoDBStore implements Rol
      */
     private DBObject getTemplateObject(Role role) {
         BasicDBObject query = new BasicDBObject();
-        query.put(TYPE, role.getType());
         query.put(NAME, role.getName());
+        query.put(TYPE, role.getType());
         return query;
     }
     

Modified: felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java (original)
+++ felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java Thu Nov 22 14:57:51 2012
@@ -115,6 +115,22 @@ final class MongoSerializerHelper {
         
         return data;
     }
+
+    /**
+     * Creates a {@link DBObject} with the given role and name. 
+     * 
+     * @param roleName the name of the role to serialize, cannot be <code>null</code> or empty (unchecked!);
+     * @param type the type of the role to serialize.
+     * @return a {@link DBObject} representing the role with the given name and type, never <code>null</code>.
+     */
+    public DBObject serialize(String roleName, int type) {
+        BasicDBObject data = new BasicDBObject();
+        
+        data.put(TYPE, type);
+        data.put(NAME, roleName);
+        
+        return data;
+    }
     
     /**
      * Creates a serialized version of the given {@link Role} to be used in an update statement.

Modified: felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java (original)
+++ felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java Thu Nov 22 14:57:51 2012
@@ -32,26 +32,30 @@ import org.osgi.service.useradmin.UserAd
 public class Activator implements BundleActivator {
 
     private LogServiceHelper m_logServiceHelper;
+    private MongoDBStore m_store;
 
     @Override
     public void start(BundleContext context) throws Exception {
         m_logServiceHelper = new LogServiceHelper(context);
         m_logServiceHelper.open();
         
-        MongoDBStore store = new MongoDBStore();
-        store.setLogService(m_logServiceHelper);
+        m_store = new MongoDBStore();
+        m_store.setLogService(m_logServiceHelper);
         
         Properties props = new Properties();
         props.put(Constants.SERVICE_PID, MongoDBStore.PID);
 
-        String[] serviceNames = { RoleRepositoryStore.class.getName(), 
-            UserAdminListener.class.getName(), ManagedService.class.getName() };
+        String[] serviceNames = { RoleRepositoryStore.class.getName(), UserAdminListener.class.getName(), ManagedService.class.getName() };
 
-        context.registerService(serviceNames, store, props);
+        context.registerService(serviceNames, m_store, props);
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
+        if (m_store != null) {
+            m_store.close();
+            m_store = null;
+        }
         if (m_logServiceHelper != null) {
             m_logServiceHelper.close();
             m_logServiceHelper = null;

Modified: felix/trunk/useradmin/useradmin/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/pom.xml?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/pom.xml (original)
+++ felix/trunk/useradmin/useradmin/pom.xml Thu Nov 22 14:57:51 2012
@@ -32,12 +32,6 @@
 	<dependencies>
 		<dependency>
 			<groupId>org.osgi</groupId>
-			<artifactId>org.osgi.core</artifactId>
-			<version>4.0.0</version>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.compendium</artifactId>
 			<version>4.0.0</version>
 		</dependency>
@@ -45,7 +39,6 @@
             <groupId>org.apache.felix</groupId>
 			<artifactId>org.apache.felix.framework</artifactId>
 			<version>4.0.2</version>
-			<scope>test</scope>
 		</dependency>
 	</dependencies>
 	<build>

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java Thu Nov 22 14:57:51 2012
@@ -20,7 +20,9 @@ package org.apache.felix.useradmin;
 import org.apache.felix.useradmin.impl.role.GroupImpl;
 import org.apache.felix.useradmin.impl.role.RoleImpl;
 import org.apache.felix.useradmin.impl.role.UserImpl;
+import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
 
 /**
  * Provides a factory for creating the various role instances, can be used by external 
@@ -29,18 +31,21 @@ import org.osgi.service.useradmin.Role;
 public final class RoleFactory {
 
     /**
-     * Creates a new instance of {@link RoleFactory}, not used.
+     * Creates a new group-role instance.
+     * 
+     * @param name the name of the group to create.
+     * @return a new {@link Group} instance denoting the requested role, never <code>null</code>.
      */
-    private RoleFactory() {
-        // Nop
+    public static Group createGroup(String name) {
+        return (Group) createRole(Role.GROUP, name);
     }
 
     /**
-     * Creates a new role instance.
+     * Creates a new role instance with the given type and name.
      * 
      * @param type the type of the role to create;
      * @param name the name of the role to create.
-     * @return a new {@link RoleImpl} instance denoting the requested role, never <code>null</code>.
+     * @return a new {@link Role} instance denoting the requested role, never <code>null</code>.
      */
     public static Role createRole(int type, String name) {
         if (type == Role.USER) {
@@ -54,4 +59,32 @@ public final class RoleFactory {
             return result;
         }
     }
+
+    /**
+     * Creates a new role instance.
+     * 
+     * @param type the type of the role to create;
+     * @param name the name of the role to create.
+     * @return a new {@link Role} instance denoting the requested role, never <code>null</code>.
+     */
+    public static Role createRole(String name) {
+        return createRole(Role.ROLE, name);
+    }
+
+    /**
+     * Creates a new user-role instance.
+     * 
+     * @param name the name of the user to create.
+     * @return a new {@link User} instance denoting the requested role, never <code>null</code>.
+     */
+    public static User createUser(String name) {
+        return (User) createRole(Role.USER, name);
+    }
+
+    /**
+     * Creates a new instance of {@link RoleFactory}, not used.
+     */
+    private RoleFactory() {
+        // Nop
+    }
 }

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java Thu Nov 22 14:57:51 2012
@@ -16,79 +16,68 @@
  */
 package org.apache.felix.useradmin;
 
-import java.io.Closeable;
-import java.io.IOException;
-
+import org.osgi.framework.Filter;
 import org.osgi.service.useradmin.Role;
 
 /**
  * Provides an abstraction to store and retrieve a role repository.
+ * <p>
+ * The role object returned by this backend <em>can</em> be a different
+ * implementation than <tt>org.apache.felix.useradmin.impl.role.RoleImpl</tt>,
+ * in which case the backend is made fully responsible for keeping track of the
+ * changes in the role object and persisting them!
+ * </p>
  */
-public interface RoleRepositoryStore extends Closeable {
-    
+public interface RoleRepositoryStore {
+
     /**
      * Adds a given role to this backend.
      * <p>
      * If the given role is already contained by this backed, this method 
-     * should not do anything and return <code>false</code> to denote this.
+     * should not do anything and return <code>null</code> to denote this.
      * </p>
      * 
-     * @param role the role to add, cannot be <code>null</code>.
-     * @return <code>true</code> if the role was successfully added, <code>false</code> otherwise.
-     * @throws IllegalArgumentException in case the given argument was <code>null</code>;
-     * @throws IOException in case of I/O problems.
-     */
-    boolean addRole(Role role) throws IOException;
-
-    /**
-     * Closes this store, allowing implementations to free up resources, close
-     * connections, and so on.
-     * 
-     * @throws IOException in case of I/O problems.
+     * @param roleName the name of the role to add, cannot be <code>null</code>;
+     * @param type the type of role to add, either {@link Role#USER} or {@link Role#GROUP}.
+     * @return the role added, or <code>null</code> in case the role already 
+     *         exists.
+     * @throws IllegalArgumentException in case the given name was <code>null</code>;
+     * @throws Exception in case of problems adding the role.
      */
-    void close() throws IOException;
+    Role addRole(String roleName, int type) throws Exception;
 
     /**
-     * Returns all available roles in this backend.
+     * Returns all roles in this backend matching the given filter criteria.
      * 
+     * @param filter the optional filter to apply, can be <code>null</code> in which case all roles are to be returned.
      * @return an array with all roles, never <code>null</code>, but can be empty.
-     * @throws IOException in case of I/O problems.
+     * @throws Exception in case of problems retrieving the set of roles.
      */
-    Role[] getAllRoles() throws IOException;
-    
+    Role[] getRoles(Filter filter) throws Exception;
+
     /**
      * Returns a {@link Role} by its name.
      * 
      * @param roleName the name of the role to return, cannot be <code>null</code>.
      * @return the role with the given name, or <code>null</code> if no such role exists.
      * @throws IllegalArgumentException in case the given argument was <code>null</code>;
-     * @throws IOException in case of I/O problems.
-     */
-    Role getRoleByName(String roleName) throws IOException;
-
-    /**
-     * Called once before any other method of this interface is being called.
-     * <p>
-     * Implementations can use this method to create a connection to the 
-     * backend, or load the initial set of roles, and so on.
-     * </p>
-     * 
-     * @throws IOException in case of I/O problems.
+     * @throws Exception in case of problems retrieving the requested role.
      */
-    void initialize() throws IOException;
+    Role getRoleByName(String roleName) throws Exception;
 
     /**
      * Removes a given role from this backend.
      * <p>
      * If the given role is not contained by this backed, this method 
-     * should not do anything and return <code>false</code> to denote this.
+     * should not do anything and return <code>null</code> to denote this.
      * </p>
      * 
-     * @param role the role to remove, cannot be <code>null</code>.
-     * @return <code>true</code> if the role was successfully removed, <code>false</code> otherwise.
+     * @param roleName the name of the role to remove, cannot be <code>null</code>.
+     * @return the removed role, if it was successfully removed from this 
+     *         backend, or <code>null</code> if the role was not contained by
+     *         this backend or could not be removed.
      * @throws IllegalArgumentException in case the given argument was <code>null</code>;
-     * @throws IOException in case of I/O problems.
+     * @throws Exception in case of problems removing the requested role.
      */
-    boolean removeRole(Role role) throws IOException;
-    
+    Role removeRole(String roleName) throws Exception;
 }

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java Thu Nov 22 14:57:51 2012
@@ -14,12 +14,9 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-
 package org.apache.felix.useradmin.impl;
 
-import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Dictionary;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -27,8 +24,9 @@ import java.util.concurrent.CopyOnWriteA
 import org.apache.felix.useradmin.BackendException;
 import org.apache.felix.useradmin.RoleFactory;
 import org.apache.felix.useradmin.RoleRepositoryStore;
-import org.apache.felix.useradmin.impl.role.RoleImpl;
+import org.apache.felix.useradmin.impl.role.ObservableRole;
 import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
 import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.UserAdminPermission;
@@ -94,7 +92,7 @@ public final class RoleRepository {
     }
 
     /** The single predefined role. */
-    public static final Role USER_ANYONE = RoleFactory.createRole(Role.ROLE, Role.USER_ANYONE);
+    private static final Role USER_ANYONE = RoleFactory.createRole(Role.USER_ANYONE);
 
     private final RoleRepositoryStore m_store;
     private final CopyOnWriteArrayList m_listeners;
@@ -118,26 +116,27 @@ public final class RoleRepository {
      * @param role the role to add, cannot be <code>null</code>. If it is already contained by this manager, this method will not do anything.
      * @return the given role if added, <code>null</code> otherwise.
      */
-    public Role addRole(Role role) {
-        if (role == null) {
-            throw new IllegalArgumentException("Role cannot be null!");
+    public Role addRole(String name, int type) {
+        if ((name == null) || "".equals(name.trim())) {
+            throw new IllegalArgumentException("Name cannot be null or empty!");
         }
-        if (!(role instanceof RoleImpl)) {
+        if (type != Role.GROUP && type != Role.USER) {
             throw new IllegalArgumentException("Invalid role type!");
         }
 
         checkPermissions();
 
         try {
-            if (m_store.addRole(role)) {
-                m_roleChangeReflector.roleAdded(role);
-                return wireChangeListener(role);
+            Role result = m_store.addRole(name, type);
+            if (result != null) {
+                result = wireChangeListener(result);
+                m_roleChangeReflector.roleAdded(result);
             }
 
-            return null;
+            return result;
         }
-        catch (IOException e) {
-            throw new BackendException("Adding role " + role.getName() + " failed!", e);
+        catch (Exception e) {
+            throw new BackendException("Adding role " + name + " failed!", e);
         }
     }
 
@@ -163,9 +162,15 @@ public final class RoleRepository {
      */
     public Role getRoleByName(String roleName) {
         try {
-            return wireChangeListener(m_store.getRoleByName(roleName));
+            Role result;
+            if (isPredefinedRole(roleName)) {
+                result = getPredefinedRole(roleName);
+            } else {
+                result = m_store.getRoleByName(roleName);
+            }
+            return wireChangeListener(result);
         }
-        catch (IOException e) {
+        catch (Exception e) {
             throw new BackendException("Failed to get role by name: " + roleName + "!", e);
         }
     }
@@ -180,15 +185,15 @@ public final class RoleRepository {
         List matchingRoles = new ArrayList();
 
         try {
-            Role[] roles = m_store.getAllRoles();
+            Role[] roles = m_store.getRoles(filter);
             for (int i = 0; i < roles.length; i++) {
                 Role role = roles[i];
-                if (!isPredefinedRole(role) && ((filter == null) || filter.match(role.getProperties()))) {
+                if (!isPredefinedRole(role.getName())) {
                     matchingRoles.add(wireChangeListener(role));
                 }
             }
         }
-        catch (IOException e) {
+        catch (Exception e) {
             throw new BackendException("Failed to get roles!", e);
         }
 
@@ -213,16 +218,18 @@ public final class RoleRepository {
         List matchingRoles = new ArrayList();
 
         try {
-            Role[] roles = m_store.getAllRoles();
+            String criteria = "(".concat(key).concat("=").concat(value).concat(")");
+            Filter filter = FrameworkUtil.createFilter(criteria);
+
+            Role[] roles = m_store.getRoles(filter);
             for (int i = 0; i < roles.length; i++) {
                 Role role = roles[i];
-                Dictionary dict = role.getProperties();
-                if (!isPredefinedRole(role) && value.equals(dict.get(key))) {
+                if (!isPredefinedRole(role.getName())) {
                     matchingRoles.add(wireChangeListener(role));
                 }
             }
         }
-        catch (IOException e) {
+        catch (Exception e) {
             throw new BackendException("Failed to get roles!", e);
         }
 
@@ -235,36 +242,34 @@ public final class RoleRepository {
      * @param role the role to remove, cannot be <code>null</code>.
      * @return <code>true</code> if the role was removed (i.e., it was managed by this manager), or <code>false</code> if it was not found.
      */
-    public boolean removeRole(Role role) {
-        if (role == null) {
-            throw new IllegalArgumentException("Role cannot be null!");
-        }
-        if (!(role instanceof RoleImpl)) {
-            throw new IllegalArgumentException("Invalid role type!");
+    public boolean removeRole(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Name cannot be null!");
         }
 
         checkPermissions();
 
         // Cannot remove predefined roles...
-        if (isPredefinedRole(role)) {
+        if (isPredefinedRole(name)) {
             return false;
         }
 
         try {
-            if (m_store.removeRole(role)) {
+            Role result = m_store.removeRole(name);
+            if (result !=  null) {
             	// FELIX-3755: Remove the role as (required)member from all groups...
-            	removeRoleFromAllGroups(role);
+            	removeRoleFromAllGroups(result);
             	
-                unwireChangeListener(role);
-                m_roleChangeReflector.roleRemoved(role);
+                unwireChangeListener(result);
+                m_roleChangeReflector.roleRemoved(result);
                 
                 return true;
             }
 
             return false;
         }
-        catch (IOException e) {
-            throw new BackendException("Failed to remove role " + role.getName() + "!", e);
+        catch (Exception e) {
+            throw new BackendException("Failed to remove role " + name + "!", e);
         }
     }
 
@@ -281,33 +286,6 @@ public final class RoleRepository {
 
         m_listeners.remove(listener);
     }
-
-	/**
-     * Starts this repository.
-     */
-    public void start() {
-        try {
-            // The sole predefined role we've got...
-            m_store.addRole(USER_ANYONE);
-
-            m_store.initialize();
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * Stops this repository, allowing it to clean up.
-     */
-    public void stop() {
-        try {
-            m_store.close();
-        }
-        catch (IOException e) {
-            // Ignore; nothing we can do about this here...
-        }
-    }
     
     /**
      * Creates a new iterator for iterating over all listeners.
@@ -336,11 +314,22 @@ public final class RoleRepository {
      * Currently, there's only a single predefined role: {@link Role#USER_ANYONE}.
      * </p>
      * 
-     * @param role the role to check, may be <code>null</code>.
+     * @param roleName the role name to check, may be <code>null</code>.
      * @return <code>true</code> if the given role is predefined, <code>false</code> otherwise.
      */
-    private boolean isPredefinedRole(Role role) {
-        return Role.USER_ANYONE.equals(role.getName());
+    private boolean isPredefinedRole(String roleName) {
+        return Role.USER_ANYONE.equals(roleName);
+    }
+
+    /**
+     * Returns the predefined role with the given name.
+     * 
+     * @param roleName the name of the predefined role to return, cannot be <code>null</code>.
+     * @return a predefined role instance, never <code>null</code>.
+     * @see #isPredefinedRole(String)
+     */
+    private Role getPredefinedRole(String roleName) {
+        return USER_ANYONE;
     }
     
     /**
@@ -351,7 +340,7 @@ public final class RoleRepository {
 	 */
 	private void removeRoleFromAllGroups(Role removedRole) {
         try {
-            Role[] roles = m_store.getAllRoles();
+            Role[] roles = m_store.getRoles(null);
             for (int i = 0; i < roles.length; i++) {
                 if (roles[i].getType() == Role.GROUP) {
                 	Group group = (Group) roles[i];
@@ -361,7 +350,7 @@ public final class RoleRepository {
                 }
             }
         }
-        catch (IOException e) {
+        catch (Exception e) {
             throw new BackendException("Failed to get all roles!", e);
         }
 	}
@@ -370,10 +359,11 @@ public final class RoleRepository {
      * Unwires the given role to this repository so it no longer listens for its changes.
      * 
      * @param role the role to unwire, cannot be <code>null</code>.
-     * @throws IllegalArgumentException in case the given object was not a {@link RoleImpl} instance.
      */
     private void unwireChangeListener(Object role) {
-        ((RoleImpl) role).setRoleChangeListener(null);
+        if (role instanceof ObservableRole) {
+            ((ObservableRole) role).setRoleChangeListener(null);
+        }
     }
 
     /**
@@ -381,12 +371,13 @@ public final class RoleRepository {
      * 
      * @param role the role to listen for its changes, cannot be <code>null</code>.
      * @return the given role.
-     * @throws IllegalArgumentException in case the given object was not a {@link RoleImpl} instance.
      */
-    private Role wireChangeListener(Object role) {
-        RoleImpl result = (RoleImpl) role;
-        if (result != null) {
-            result.setRoleChangeListener(m_roleChangeReflector);
+    private Role wireChangeListener(Role role) {
+        Role result = ObservableRole.wrap(role);
+        if (result instanceof ObservableRole) {
+            // Keep track of all changes made to the given role, to fire the 
+            // proper events to everyone interested...
+            ((ObservableRole) result).setRoleChangeListener(m_roleChangeReflector);
         }
         return result;
     }

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java Thu Nov 22 14:57:51 2012
@@ -19,7 +19,6 @@ package org.apache.felix.useradmin.impl;
 
 import java.util.List;
 
-import org.apache.felix.useradmin.RoleFactory;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
@@ -69,14 +68,7 @@ public class UserAdminImpl implements Se
      * {@inheritDoc}
      */
     public Role createRole(String name, int type) {
-        if ((type != Role.USER) && (type != Role.GROUP)) {
-            throw new IllegalArgumentException("Invalid type, must by either Role.USER or Role.GROUP!");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("Invalid name, must be non-null and non-empty!");
-        }
-
-        return m_roleRepository.addRole(RoleFactory.createRole(type, name));
+        return m_roleRepository.addRole(name, type);
     }
 
     /**
@@ -154,12 +146,7 @@ public class UserAdminImpl implements Se
      * {@inheritDoc}
      */
     public boolean removeRole(String name) {
-        Role role = getRole(name);
-        if (role == null) {
-            return false;
-        }
-
-        return m_roleRepository.removeRole(role);
+        return m_roleRepository.removeRole(name);
     }
 
     /**

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java Thu Nov 22 14:57:51 2012
@@ -25,14 +25,16 @@ import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.UserAdminPermission;
 
 /**
- * Provides an implementation of {@link Group}. 
+ * Provides a default implementation of {@link Group} that is not security-aware 
+ * and does not keep track of changes to its properties. 
+ * <p>
+ * This implementation should be wrapped in an {@link ObservableGroup} when 
+ * returned from the UserAdmin implementation. 
+ * </p>
  */
 public class GroupImpl extends UserImpl implements Group {
 
-    private static final long serialVersionUID = 1515097730006454140L;
-    
-	private static final String BASIC_MEMBER = "basicMember";
-    private static final String REQUIRED_MEMBER = "requiredMember";
+    private static final long serialVersionUID = 5543895177109398569L;
 
     private final Object m_lock = new Object();
 
@@ -67,11 +69,6 @@ public class GroupImpl extends UserImpl 
             result = m_members.put(name, role);
         }
 
-        if (result == null) {
-            // Notify our (optional) listener...
-            entryAdded(BASIC_MEMBER, role);
-        }
-        
         return (result == null);
     }
 
@@ -91,11 +88,6 @@ public class GroupImpl extends UserImpl 
             result = m_requiredMembers.put(name, role);
         }
 
-        if (result == null) {
-            // Notify our (optional) listener...
-            entryAdded(REQUIRED_MEMBER, role);
-        }
-        
         return (result == null);
     }
 
@@ -133,25 +125,17 @@ public class GroupImpl extends UserImpl 
 
         String name = role.getName();
 
-        String key = null;
         Object result = null;
-        
+
         synchronized (m_lock) {
 			if (m_requiredMembers.containsKey(name)) {
-                key = REQUIRED_MEMBER;
                 result = m_requiredMembers.remove(name);
             }
             else if (m_members.containsKey(name)) {
-                key = BASIC_MEMBER;
                 result = m_members.remove(name);
             }
         }
 
-        if (result != null) {
-            // Notify our (optional) listener...
-            entryRemoved(key);
-        }
-        
         return result != null;
     }
     

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java Thu Nov 22 14:57:51 2012
@@ -17,13 +17,9 @@
 package org.apache.felix.useradmin.impl.role;
 
 import java.io.Serializable;
-import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.Hashtable;
 
 import org.osgi.service.useradmin.UserAdminPermission;
 
@@ -31,10 +27,13 @@ import org.osgi.service.useradmin.UserAd
  * Provides an observable {@link Dictionary} implementation that emits change 
  * events for the put and remove operations aside checking for security 
  * permissions for all accessor methods.
+ * <p>
+ * This class is <b>not</b> guaranteed to be thread-safe!
+ * </p>
  */
 class ObservableDictionary extends Dictionary implements Serializable {
 
-    private static final long serialVersionUID = 9223154895541178975L;
+    private static final long serialVersionUID = 3161552287666253189L;
 
     /**
      * Provides a listener for changes to a {@link ObservableDictionary}.
@@ -66,58 +65,7 @@ class ObservableDictionary extends Dicti
         void entryRemoved(Object key);
     }
 
-    /**
-     * Provides a wrapper to convert an {@link Iterator} to an {@link Enumeration} implementation.
-     */
-    static final class IteratorEnumeration implements Enumeration {
-        
-        private final Iterator m_iterator;
-
-        /**
-         * Creates a new {@link IteratorEnumeration}.
-         * 
-         * @param iterator the {@link Iterator} to convert to a {@link Enumeration}, cannot be <code>null</code>.
-         */
-        public IteratorEnumeration(Iterator iterator) {
-            m_iterator = iterator;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public boolean hasMoreElements() {
-            return m_iterator.hasNext();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public Object nextElement() {
-            return m_iterator.next();
-        }
-    }
-    
-    /**
-     * Converts a given {@link Dictionary} implementation to a {@link Map} implementation.
-     * 
-     * @param dictionary the dictionary to convert, cannot be <code>null</code>.
-     * @return a {@link Map} instance with all the same key-value pairs as the given dictionary, never <code>null</code>.
-     */
-    private static ConcurrentMap convertToMap(Dictionary dictionary) {
-        ConcurrentMap result = new ConcurrentHashMap();
-        if (dictionary instanceof Map) {
-            result.putAll((Map) dictionary);
-        } else {
-            Enumeration keyEnum = dictionary.keys();
-            while (keyEnum.hasMoreElements()) {
-                Object key = keyEnum.nextElement();
-                result.put(key, dictionary.get(key));
-            }
-        }
-        return result;
-    }
-    
-    private final ConcurrentMap m_properties;
+    private final Dictionary m_dictionary;
     private final String m_getAction;
     private final String m_changeAction;
 
@@ -127,9 +75,7 @@ class ObservableDictionary extends Dicti
      * Creates a new, empty, {@link ObservableDictionary} instance.
      */
     public ObservableDictionary(String getAction, String changeAction) {
-        m_getAction = getAction;
-        m_changeAction = changeAction;
-        m_properties = new ConcurrentHashMap();
+        this(getAction, changeAction, new Hashtable());
     }
 
     /**
@@ -143,15 +89,14 @@ class ObservableDictionary extends Dicti
         }
         m_getAction = getAction;
         m_changeAction = changeAction;
-        m_properties = convertToMap(dictionary);
+        m_dictionary = dictionary;
     }
 
     /**
      * {@inheritDoc}
      */
     public Enumeration elements() {
-        Collection values = m_properties.values();
-        return new IteratorEnumeration(values.iterator());
+        return m_dictionary.elements();
     }
 
     /**
@@ -166,11 +111,11 @@ class ObservableDictionary extends Dicti
         }
 
         ObservableDictionary other = (ObservableDictionary) object;
-        if (m_properties == null) {
-            if (other.m_properties != null) {
+        if (m_dictionary == null) {
+            if (other.m_dictionary != null) {
                 return false;
             }
-        } else if (!m_properties.equals(other.m_properties)) {
+        } else if (!m_dictionary.equals(other.m_dictionary)) {
             return false;
         }
 
@@ -189,7 +134,7 @@ class ObservableDictionary extends Dicti
             checkPermissions(getAsPermissionKey(key), m_getAction);
         }
 
-        return m_properties.get(key);
+        return m_dictionary.get(key);
     }
 
     /**
@@ -198,7 +143,7 @@ class ObservableDictionary extends Dicti
     public int hashCode() {
         final int prime = 37;
         int result = 1;
-        result = prime * result + ((m_properties == null) ? 0 : m_properties.hashCode());
+        result = prime * result + ((m_dictionary == null) ? 0 : m_dictionary.hashCode());
         return result;
     }
 
@@ -206,15 +151,14 @@ class ObservableDictionary extends Dicti
      * {@inheritDoc}
      */
     public boolean isEmpty() {
-        return m_properties.isEmpty();
+        return m_dictionary.isEmpty();
     }
 
     /**
      * {@inheritDoc}
      */
     public Enumeration keys() {
-        Collection keys = m_properties.keySet();
-        return new IteratorEnumeration(keys.iterator());
+        return m_dictionary.keys();
     }
 
     /**
@@ -232,7 +176,7 @@ class ObservableDictionary extends Dicti
             checkPermissions(getAsPermissionKey(key), m_changeAction);
         }
 
-        Object oldValue = m_properties.put(key, value);
+        Object oldValue = m_dictionary.put(key, value);
         
         final DictionaryChangeListener listener = m_listener;
         if (listener != null) {
@@ -258,7 +202,7 @@ class ObservableDictionary extends Dicti
             checkPermissions(getAsPermissionKey(key), m_changeAction);
         }
 
-        Object oldValue = m_properties.remove(key);
+        Object oldValue = m_dictionary.remove(key);
         final DictionaryChangeListener listener = m_listener;
         if (listener != null) {
             listener.entryRemoved(key);
@@ -280,7 +224,7 @@ class ObservableDictionary extends Dicti
      * {@inheritDoc}
      */
     public int size() {
-        return m_properties.size();
+        return m_dictionary.size();
     }
 
     /**

Added: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java?rev=1412574&view=auto
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java (added)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java Thu Nov 22 14:57:51 2012
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.useradmin.impl.role;
+
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+
+/**
+ * Provides an adapter for all {@link Group}s, allowing its changes to be 
+ * externally observed.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ObservableGroup extends ObservableUser implements Group {
+    
+    private static final long serialVersionUID = 4012536225870565500L;
+    
+    private static final String BASIC_MEMBER = "basicMember";
+    private static final String REQUIRED_MEMBER = "requiredMember";
+    
+    /**
+     * Creates a new {@link ObservableGroup} instance.
+     * 
+     * @param group the group-role to observe for changes, cannot be <code>null</code>.
+     * @throws IllegalArgumentException in case the given group-role was <code>null</code>.
+     */
+    public ObservableGroup(Group group) {
+        super(group);
+    }
+
+    public boolean addMember(Role role) {
+        boolean result = ((Group) m_delegate).addMember(role);
+        if (result) {
+            // Notify our (optional) listener...
+            entryAdded(BASIC_MEMBER, role);
+        }
+        return result;
+    }
+
+    public boolean addRequiredMember(Role role) {
+        boolean result = ((Group) m_delegate).addRequiredMember(role);
+        if (result) {
+            // Notify our (optional) listener...
+            entryAdded(REQUIRED_MEMBER, role);
+        }
+        
+        return result;
+    }
+
+    public boolean removeMember(Role role) {
+        // Take a snapshot of the current set of members...
+        Role[] members = getRequiredMembers();
+        boolean result = ((Group) m_delegate).removeMember(role);
+        if (result) {
+            // Notify our (optional) listener...
+            String key = BASIC_MEMBER;
+            for (int i = 0; (members != null) && (i < members.length); i++) {
+                if (members[i].equals(role)) {
+                    key = REQUIRED_MEMBER;
+                    break;
+                }
+            }
+            entryRemoved(key);
+        }
+        return result;
+    }
+
+    public Role[] getMembers() {
+        Role[] members = ((Group) m_delegate).getMembers();
+        if (members == null) {
+            return null;
+        }
+        Role[] result = new Role[members.length];
+        for (int i = 0; i < members.length; i++) {
+            result[i] = ObservableRole.wrap(members[i]);
+        }
+        return result;
+    }
+
+    public Role[] getRequiredMembers() {
+        Role[] requiredMembers = ((Group) m_delegate).getRequiredMembers();
+        if (requiredMembers == null) {
+            return null;
+        }
+        Role[] result = new Role[requiredMembers.length];
+        for (int i = 0; i < requiredMembers.length; i++) {
+            result[i] = ObservableRole.wrap(requiredMembers[i]);
+        }
+        return requiredMembers;
+    }
+    
+    public String toString() {
+        return m_delegate.toString();
+    }
+}

Propchange: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java?rev=1412574&r1=1412573&r2=1412574&view=diff
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java (original)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java Thu Nov 22 14:57:51 2012
@@ -17,10 +17,14 @@
 package org.apache.felix.useradmin.impl.role;
 
 import java.util.Dictionary;
+import java.util.Properties;
 
 /**
  * Provides an stricter variant of the {@link ObservableDictionary} that only 
  * permits string keys and values of either String or byte[]. 
+ * <p>
+ * This class is <b>not</b> guaranteed to be thread-safe!
+ * </p>
  */
 final class ObservableProperties extends ObservableDictionary {
 
@@ -30,7 +34,7 @@ final class ObservableProperties extends
      * Creates a new, empty, {@link ObservableProperties} instance.
      */
     public ObservableProperties(String getAction, String changeAction) {
-        super(getAction, changeAction);
+        this(getAction, changeAction, new Properties());
     }
 
     /**

Added: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java?rev=1412574&view=auto
==============================================================================
--- felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java (added)
+++ felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java Thu Nov 22 14:57:51 2012
@@ -0,0 +1,173 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.useradmin.impl.role;
+
+import java.io.Serializable;
+import java.util.Dictionary;
+
+import org.apache.felix.useradmin.impl.RoleChangeListener;
+import org.apache.felix.useradmin.impl.role.ObservableDictionary.DictionaryChangeListener;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminPermission;
+
+/**
+ * Provides an adapter for all {@link Role}s, allowing its changes to be 
+ * externally observed.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ObservableRole implements Serializable, Role, DictionaryChangeListener {
+
+    private static final long serialVersionUID = -3706363718282775516L;
+
+    protected final Role m_delegate;
+
+    private final ObservableProperties m_properties;
+
+    private volatile RoleChangeListener m_listener;
+
+    /**
+     * Creates a new {@link ObservableRole} instance.
+     * 
+     * @param role the role to observe for changes, cannot be <code>null</code>.
+     * @throws IllegalArgumentException in case the given role was <code>null</code>.
+     */
+    public ObservableRole(Role role) {
+        if (role == null) {
+            throw new IllegalArgumentException("Role cannot be null!");
+        }
+
+        m_delegate = role;
+        m_properties = new ObservableProperties(null, UserAdminPermission.CHANGE_PROPERTY, m_delegate.getProperties());
+        m_properties.setDictionaryChangeListener(this);
+    }
+    
+    /**
+     * Wraps the given role as an (subclass of) {@link ObservableRole}.
+     * <p>
+     * If the given role is already an instance of {@link ObservableRole}, this
+     * method simply returns the given role.
+     * </p>
+     * 
+     * @param role the role to wrap, can be <code>null</code>.
+     * @return an {@link ObservableRole} instance wrapping the given role, or 
+     *         <code>null</code> if the given role was <code>null</code>.
+     */
+    public static ObservableRole wrap(Role role) {
+        if (role == null) {
+            return null;
+        }
+        if (role instanceof ObservableRole) {
+            return (ObservableRole) role;
+        }
+        int type = role.getType();
+        switch (type) {
+            case Role.GROUP:
+                return new ObservableGroup((Group) role);
+                
+            case Role.USER:
+                return new ObservableUser((User) role);
+            
+            default:
+                return new ObservableRole(role);
+        }        
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final void entryAdded(Object key, Object value) {
+        RoleChangeListener listener = m_listener;
+        if (listener != null) {
+            listener.propertyAdded(this, key, value);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final void entryChanged(Object key, Object oldValue, Object newValue) {
+        RoleChangeListener listener = m_listener;
+        if (listener != null) {
+            listener.propertyChanged(this, key, oldValue, newValue);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final void entryRemoved(Object key) {
+        RoleChangeListener listener = m_listener;
+        if (listener != null) {
+            listener.propertyRemoved(this, key);
+        }
+    }
+
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if ((object == null) || (getClass() != object.getClass())) {
+            return false;
+        }
+        
+        ObservableRole other = (ObservableRole) object;
+        if (m_delegate == null) {
+            if (other.m_delegate != null) {
+                return false;
+            }
+        }
+        else if (!m_delegate.equals(other.m_delegate)) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    public String getName() {
+        return m_delegate.getName();
+    }
+
+    public Dictionary getProperties() {
+        return m_properties;
+    }
+
+    public int getType() {
+        return m_delegate.getType();
+    }
+
+    public int hashCode() {
+        return 31 + ((m_delegate == null) ? 0 : m_delegate.hashCode());
+    }
+
+    /**
+     * Sets the {@link RoleChangeListener} for this role implementation.
+     * 
+     * @param listener the listener to set, may be <code>null</code> to stop listening.
+     */
+    public void setRoleChangeListener(RoleChangeListener listener) {
+        m_listener = listener;
+    }
+    
+    public String toString() {
+        return m_delegate.toString();
+    }
+}

Propchange: felix/trunk/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message