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.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1412574 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java
index b093e44..512e8a8 100644
--- a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java
+++ b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/ResettableTimer.java
@@ -76,6 +76,15 @@
     }
 
     /**
+     * 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.
diff --git a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java
index 115d223..071cbba 100644
--- a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java
+++ b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStore.java
@@ -86,10 +86,6 @@
         }
     }
     
-    public void initialize() throws IOException {
-        m_entries.putAll(retrieve());
-    }
-
     public void roleChanged(UserAdminEvent event) {
         scheduleTask();
     }
@@ -110,25 +106,30 @@
     }
 
     /**
+     * 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 @@
         } 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 @@
      */
     private void scheduleTask() {
         ResettableTimer timer = (ResettableTimer) m_timerRef.get();
-        if (timer != null) {
+        if (timer != null && !timer.isShutDown()) {
             timer.schedule();
         }
     }
diff --git a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java
index f064f79..c89ecdb 100644
--- a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java
+++ b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositoryMemoryStore.java
@@ -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 @@
     
     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;
+        Role role = RoleFactory.createRole(type, roleName);
+        Object result = m_entries.putIfAbsent(roleName, role);
+        return (result == null) ? role : null;
     }
 
-    public void close() throws IOException {
-        // Nop
-    }
-
-    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;
     }
 }
diff --git a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java
index 57aca3e..e4ecaad 100644
--- a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java
+++ b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializer.java
@@ -161,6 +161,23 @@
     }
 
     /**
+     * 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 @@
             }
         }
     }
-    
+
     /**
      * Reads a (stub) group from the given input stream.
      * 
@@ -254,7 +271,7 @@
         
         return repository;
     }
-
+    
     /**
      * Reads a role from the given input stream.
      * 
@@ -269,7 +286,7 @@
         
         return role;
     }
-    
+
     /**
      * Reads a user from the given input stream.
      * 
@@ -300,7 +317,7 @@
 
         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 @@
         
         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);
             }
diff --git a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java
index edef63c..a63223f 100644
--- a/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java
+++ b/useradmin/filestore/src/main/java/org/apache/felix/useradmin/filestore/osgi/Activator.java
@@ -35,6 +35,7 @@
 
     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() };
         
diff --git a/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java b/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java
index c19699f..11bd35e 100644
--- a/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java
+++ b/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositoryFileStorePerformanceTest.java
@@ -22,9 +22,7 @@
 
 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 @@
  */
 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 @@
      * 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 @@
         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 @@
         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 @@
     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 @@
     private User createUser(int idx) {
         String name = "User" + idx;
         
-        User result = new UserImpl(name);
+        User result = RoleFactory.createUser(name);
 
         setCredentials(result);
         setProperties(result);
diff --git a/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java b/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java
index 5f1aadb..a6567ff 100644
--- a/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java
+++ b/useradmin/filestore/src/test/java/org/apache/felix/useradmin/filestore/RoleRepositorySerializerTest.java
@@ -31,9 +31,7 @@
 
 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 @@
         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 @@
         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 @@
     }
 
     /**
-     * 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 @@
         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 @@
         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 @@
         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 @@
 
         m_repository = new HashMap();
 
-        m_userAnyone = RoleRepository.USER_ANYONE;
+        m_userAnyone = RoleFactory.createRole(Role.USER_ANYONE);
         
         setProperties(m_userAnyone);
         
@@ -307,7 +297,9 @@
     }
     
     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 @@
     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 @@
     private Group createGroup(int idx) {
         String name = "Group" + idx;
         
-        Group result = new GroupImpl(name);
+        Group result = RoleFactory.createGroup(name);
 
         setCredentials(result);
         setProperties(result);
diff --git a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
index 6cf080a..39cc8e4 100644
--- a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
+++ b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
@@ -38,6 +38,7 @@
 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 @@
             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 @@
         }
         return result;
     }
+    
+    /**
+     * @return the {@link UserAdmin} service instance.
+     */
+    protected UserAdmin getUserAdmin() throws Exception {
+        return getService(UserAdmin.class.getName());
+    }
 
     /**
      * @param bsn
diff --git a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
index bfb8529..0b712b8 100644
--- a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
+++ b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
@@ -21,17 +21,16 @@
 
 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 @@
 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);
+		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);
 
-		// Wait until it becomes available...
-		awaitService(serviceName);
+		group = (Group) ua.getRole("group1");
+		assertNull(group);
+		
+		// This will not succeed: no backend to store the user in...
+		assertNull(ua.createRole("user2", Role.USER));
 
-		assertEquals(1, mockStore.m_initCount.get());
-		assertEquals(0, mockStore.m_closeCount.get());
+		fileStoreBundle.start();
 
-		serviceReg.unregister();
+		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);
 
-		Thread.sleep(100); // sleep a tiny bit to allow service to be properly unregistered...
+        Role[] members = group.getMembers();
+        assertNotNull(members);
+        assertEquals(1, members.length);
+        assertEquals("user1", members[0].getName());
 
-		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());
+        members = group.getRequiredMembers();
+        assertNotNull(members);
+        assertEquals(1, members.length);
+        assertEquals(Role.USER_ANYONE, members[0].getName());
+        
+        user = (User) ua.getRole("user2");
+        assertNull(user);
 	}
 }
diff --git a/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java b/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
index 5dc1719..8761ea6 100644
--- a/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
+++ b/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
@@ -19,13 +19,13 @@
 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 @@
     }
 
     @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 {
+    public Role[] getRoles(Filter filter) throws MongoException {
+        List<Role> roles = new ArrayList<Role>();
+        
+        DBCollection coll = getCollection();
+
+        DBCursor cursor = coll.find();
         try {
-            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();
+            while (cursor.hasNext()) {
+                roles.add(m_helper.deserialize(cursor.next()));
             }
+        } finally {
+            cursor.close();
+        }
 
-            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);
-        }
+        return roles.toArray(new Role[roles.size()]);
     }
 
     @Override
@@ -208,56 +194,26 @@
     }
 
     @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));
-
-            if (result.getLastError() != null) {
-                result.getLastError().throwOnError();
-            }
-
-            return true;
+    public Role removeRole(String roleName) throws MongoException {
+        DBCollection coll = getCollection();
+        
+        Role role = getRole(roleName);
+        if (role == null) {
+            return null;
         }
-        catch (MongoException e) {
-            m_log.log(LogService.LOG_WARNING, "Remove role failed!", e);
-            throw new IOException("RemoveRole failed!", e);
+
+        WriteResult result = coll.remove(getTemplateObject(role));
+
+        if (result.getLastError() != null) {
+            result.getLastError().throwOnError();
         }
+
+        return role;
     }
     
     @Override
@@ -394,8 +350,8 @@
      */
     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;
     }
     
diff --git a/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java b/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java
index 31af118..48e48d8 100644
--- a/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java
+++ b/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java
@@ -115,6 +115,22 @@
         
         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.
diff --git a/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java b/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java
index 632af63..b2eeee9 100644
--- a/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java
+++ b/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/osgi/Activator.java
@@ -32,26 +32,30 @@
 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;
diff --git a/useradmin/useradmin/pom.xml b/useradmin/useradmin/pom.xml
index 7531d23..3c4576d 100644
--- a/useradmin/useradmin/pom.xml
+++ b/useradmin/useradmin/pom.xml
@@ -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>
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java
index da54c9e..b145db6 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleFactory.java
@@ -20,7 +20,9 @@
 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 @@
 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 @@
             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
+    }
 }
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java
index 3296f59..365e273 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/RoleRepositoryStore.java
@@ -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.
+     * @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.
      */
-    boolean addRole(Role role) throws IOException;
+    Role addRole(String roleName, int type) throws Exception;
 
     /**
-     * Closes this store, allowing implementations to free up resources, close
-     * connections, and so on.
+     * Returns all roles in this backend matching the given filter criteria.
      * 
-     * @throws IOException in case of I/O problems.
-     */
-    void close() throws IOException;
-
-    /**
-     * Returns all available roles in this backend.
-     * 
+     * @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.
+     * @throws Exception in case of problems retrieving the requested role.
      */
-    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.
-     */
-    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;
 }
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java
index b9666db..54c8f80 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleRepository.java
@@ -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 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 @@
     }
 
     /** 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 @@
      * @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 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 @@
         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 @@
         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 @@
      * @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 @@
 
         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 @@
      * 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 @@
 	 */
 	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 @@
                 }
             }
         }
-        catch (IOException e) {
+        catch (Exception e) {
             throw new BackendException("Failed to get all roles!", e);
         }
 	}
@@ -370,10 +359,11 @@
      * 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 @@
      * 
      * @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;
     }
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java
index 9e51c52..dce1081 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminImpl.java
@@ -19,7 +19,6 @@
 
 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 @@
      * {@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 @@
      * {@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);
     }
 
     /**
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java
index 497cbce..a1a6117 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/GroupImpl.java
@@ -25,14 +25,16 @@
 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 @@
             result = m_members.put(name, role);
         }
 
-        if (result == null) {
-            // Notify our (optional) listener...
-            entryAdded(BASIC_MEMBER, role);
-        }
-        
         return (result == null);
     }
 
@@ -91,11 +88,6 @@
             result = m_requiredMembers.put(name, role);
         }
 
-        if (result == null) {
-            // Notify our (optional) listener...
-            entryAdded(REQUIRED_MEMBER, role);
-        }
-        
         return (result == null);
     }
 
@@ -133,25 +125,17 @@
 
         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;
     }
     
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java
index a41ae14..7d167c5 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableDictionary.java
@@ -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 @@
  * 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 @@
         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 @@
      * 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 @@
         }
         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 @@
         }
 
         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 @@
             checkPermissions(getAsPermissionKey(key), m_getAction);
         }
 
-        return m_properties.get(key);
+        return m_dictionary.get(key);
     }
 
     /**
@@ -198,7 +143,7 @@
     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 @@
      * {@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 @@
             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 @@
             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 @@
      * {@inheritDoc}
      */
     public int size() {
-        return m_properties.size();
+        return m_dictionary.size();
     }
 
     /**
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java
new file mode 100644
index 0000000..0042ade
--- /dev/null
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableGroup.java
@@ -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();
+    }
+}
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java
index de05dc9..f0d25f9 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableProperties.java
@@ -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 @@
      * Creates a new, empty, {@link ObservableProperties} instance.
      */
     public ObservableProperties(String getAction, String changeAction) {
-        super(getAction, changeAction);
+        this(getAction, changeAction, new Properties());
     }
 
     /**
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java
new file mode 100644
index 0000000..ee3ef45
--- /dev/null
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableRole.java
@@ -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();
+    }
+}
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableUser.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableUser.java
new file mode 100644
index 0000000..64c0aec
--- /dev/null
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/ObservableUser.java
@@ -0,0 +1,67 @@
+/**
+ * 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.util.Dictionary;
+
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminPermission;
+
+/**
+ * Provides an adapter for all {@link User}s, allowing its changes to be 
+ * externally observed.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ObservableUser extends ObservableRole implements User {
+    
+    private static final long serialVersionUID = 344574927109701877L;
+    
+    private final ObservableProperties m_credentials;
+    
+    /**
+     * Creates a new {@link ObservableUser} instance.
+     * 
+     * @param user the user-role to observe for changes, cannot be <code>null</code>.
+     * @throws IllegalArgumentException in case the given user-role was <code>null</code>.
+     */
+    public ObservableUser(User user) {
+        super(user);
+        
+        m_credentials = new ObservableProperties(UserAdminPermission.GET_CREDENTIAL, UserAdminPermission.CHANGE_CREDENTIAL, user.getCredentials());
+        m_credentials.setDictionaryChangeListener(this);
+    }
+
+    public Dictionary getCredentials() {
+        return m_credentials;
+    }
+
+    public boolean hasCredential(String key, Object value) {
+        // Will throw a SecurityException if we're not allowed to do this!
+        m_credentials.get(key);
+        // We're allowed to do this; let the original implementation figure out
+        // whether or not the test holds...
+        return ((User) m_delegate).hasCredential(key, value);
+    }
+    
+    public String toString()
+    {
+        return m_delegate.toString();
+    }
+}
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/RoleImpl.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/RoleImpl.java
index 576eee2..6d78d8e 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/RoleImpl.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/RoleImpl.java
@@ -18,25 +18,25 @@
 
 import java.io.Serializable;
 import java.util.Dictionary;
+import java.util.Properties;
 
-
-import org.apache.felix.useradmin.impl.RoleChangeListener;
-import org.apache.felix.useradmin.impl.role.ObservableDictionary.DictionaryChangeListener;
 import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdminPermission;
 
 /**
- * Provides an implementation of {@link Role}.
+ * Provides a default implementation of {@link Role} that is not security-aware 
+ * and does not keep track of changes to its properties.
+ * <p>
+ * This implementation should be wrapped in an {@link ObservableRole} when 
+ * returned from the UserAdmin implementation. 
+ * </p>
  */
-public class RoleImpl implements Serializable, Role, DictionaryChangeListener {
-	
-    private static final long serialVersionUID = -6292833161748591485L;
+public class RoleImpl implements Serializable, Role {
 
-	private final ObservableProperties m_properties;
+	private static final long serialVersionUID = 6403795608776837916L;
+	
+    private final Properties m_properties;
     private final String m_name;
     private final int m_type;
-    
-    private volatile RoleChangeListener m_listener;
 
     /**
      * Creates a new {@link RoleImpl} instance of type {@link Role#ROLE} and a given name.
@@ -59,38 +59,7 @@
         }
         m_type = type;
         m_name = name;
-        m_properties = new ObservableProperties(null, UserAdminPermission.CHANGE_PROPERTY);
-        m_properties.setDictionaryChangeListener(this);
-    }
-
-    /**
-     * {@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 entryRemoved(Object key) {
-        RoleChangeListener listener = m_listener;
-        if (listener != null) {
-            listener.propertyRemoved(this, key);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final void entryChanged(Object key, Object oldValue, Object newValue) {
-        RoleChangeListener listener = m_listener;
-        if (listener != null) {
-            listener.propertyChanged(this, key, oldValue, newValue);
-        }
+        m_properties = new Properties();
     }
     
     /**
@@ -152,15 +121,6 @@
         result = prime * result + m_type;
         return result;
     }
-
-    /**
-     * 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;
-    }
     
     /**
      * {@inheritDoc}
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/UserImpl.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/UserImpl.java
index d152af3..e19874c 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/UserImpl.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/impl/role/UserImpl.java
@@ -18,19 +18,24 @@
 
 import java.util.Arrays;
 import java.util.Dictionary;
+import java.util.Properties;
 
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminPermission;
 
 /**
- * Provides an implementation of {@link User}.
+ * Provides a default implementation of {@link User} that is not security-aware 
+ * and does not keep track of changes to its properties.
+ * <p>
+ * This implementation should be wrapped in an {@link ObservableUser} when 
+ * returned from the UserAdmin implementation. 
+ * </p>
  */
 public class UserImpl extends RoleImpl implements User {
 
-    private static final long serialVersionUID = 8639414204247841034L;
-    
-	private final ObservableProperties m_credentials;
+	private static final long serialVersionUID = 7332249440557443008L;
+	
+    private final Properties m_credentials;
 
     /**
      * Creates a new {@link UserImpl} instance with type {@link Role#USER}.
@@ -50,8 +55,7 @@
     protected UserImpl(int type, String name) {
         super(type, name);
 
-        m_credentials = new ObservableProperties(UserAdminPermission.GET_CREDENTIAL, UserAdminPermission.CHANGE_CREDENTIAL);
-        m_credentials.setDictionaryChangeListener(this);
+        m_credentials = new Properties();
     }
 
     /**
@@ -65,7 +69,6 @@
      * {@inheritDoc}
      */
     public boolean hasCredential(String key, Object value) {
-        // Will throw a SecurityException if we're not allowed to do this!
         Object result = m_credentials.get(key);
 
         // Be a bit more lenient with the various results we can get...
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/RoleRepositoryStoreHelper.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/RoleRepositoryStoreHelper.java
index 824d520..26cde1a 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/RoleRepositoryStoreHelper.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/RoleRepositoryStoreHelper.java
@@ -16,12 +16,9 @@
  */
 package org.apache.felix.useradmin.osgi;
 
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 import org.apache.felix.useradmin.RoleRepositoryStore;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Filter;
 import org.osgi.service.useradmin.Role;
 import org.osgi.util.tracker.ServiceTracker;
 
@@ -34,8 +31,6 @@
  */
 class RoleRepositoryStoreHelper extends ServiceTracker implements RoleRepositoryStore {
 	
-	private final AtomicBoolean m_initialized = new AtomicBoolean(false);
-    
     /**
      * Creates a new {@link RoleRepositoryStoreHelper} instance.
      * 
@@ -45,50 +40,25 @@
         super(context, RoleRepositoryStore.class.getName(), null /* customizer */);
     }
 
-    public Object addingService(ServiceReference reference) {
-    	// FELIX-3735: store can also become available *after* this bundle is started...
-    	RoleRepositoryStore store = (RoleRepositoryStore) super.addingService(reference);
-    	try {
-    		initializeStore(store);
-		} catch (IOException e) {
-            // Ignore; nothing we can do about this here...
-		}
-    	return store;
-    }
-    
-    public boolean addRole(Role role) throws IOException {
+    public Role addRole(String roleName, int type) throws Exception {
         RoleRepositoryStore store = getStore();
         if (store != null) {
-            return store.addRole(role);
+            return store.addRole(roleName, type);
         }
 
-        return false;
-    }
-    
-    public synchronized void close() {
-        try {
-            RoleRepositoryStore store = getStore();
-            if (store != null) {
-                closeStore(store);
-            }
-        }
-        catch (IOException e) {
-            // Ignore; nothing we can do about this here...
-        } finally {
-            super.close();
-        }
+        return null;
     }
 
-    public Role[] getAllRoles() throws IOException {
+    public Role[] getRoles(Filter filter) throws Exception {
         RoleRepositoryStore store = getStore();
         if (store != null) {
-            return store.getAllRoles();
+            return store.getRoles(filter);
         }
 
         return new Role[0];
     }
 
-    public Role getRoleByName(String roleName) throws IOException {
+    public Role getRoleByName(String roleName) throws Exception {
         RoleRepositoryStore store = getStore();
         if (store != null) {
             return store.getRoleByName(roleName);
@@ -96,33 +66,15 @@
 
         return null;
     }
-    
-    public void initialize() throws IOException {
-        RoleRepositoryStore store = getStore();
-        if (store != null) {
-        	initializeStore(store);
-        }
-    }
 
-    public void removedService(ServiceReference reference, Object service) {
-        RoleRepositoryStore removedStore = (RoleRepositoryStore) service;
-        try {
-        	closeStore(removedStore);
-        }
-        catch (IOException e) {
-            // Ignore; nothing we can do about this here...
-        }
-        super.removedService(reference, service);
-    }
-
-    public boolean removeRole(Role role) throws IOException {
+    public Role removeRole(String roleName) throws Exception {
         // and possibly also from our tracked store...
         RoleRepositoryStore store = getStore();
         if (store != null) {
-            return store.removeRole(role);
+            return store.removeRole(roleName);
         }
 
-        return false;
+        return null;
     }
 
     /**
@@ -133,41 +85,4 @@
     private RoleRepositoryStore getStore() {
         return (RoleRepositoryStore) getService();
     }
-
-	/**
-	 * Closes the given store.
-	 * 
-	 * @param store the store to close, cannot be <code>null</code>.
-	 * @throws IOException in case initialization failed.
-	 */
-	private void closeStore(RoleRepositoryStore store) throws IOException {
-		// Only close the store if its initialized...
-		boolean initialized = m_initialized.get();
-		if (initialized) {
-			store.close();
-
-			do {
-				initialized = m_initialized.get();
-			} while (!m_initialized.compareAndSet(initialized, false));
-		}
-	}
-
-	/**
-	 * Initializes the given store.
-	 * 
-	 * @param store the store to initialize, cannot be <code>null</code>.
-	 * @throws IOException in case initialization failed.
-	 */
-	private void initializeStore(RoleRepositoryStore store) throws IOException {
-		// FELIX-3735: store can also become available *after* this bundle is started; 
-		// hence we need to ensure we do not initialize the store twice...
-		boolean initialized = m_initialized.get();
-		if (!initialized) {
-			store.initialize();
-
-			do {
-				initialized = m_initialized.get();
-			} while (!m_initialized.compareAndSet(initialized, true));
-		}
-	}
 }
diff --git a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/ServiceContext.java b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/ServiceContext.java
index 1d87ffd..79f7dbe 100644
--- a/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/ServiceContext.java
+++ b/useradmin/useradmin/src/main/java/org/apache/felix/useradmin/osgi/ServiceContext.java
@@ -51,7 +51,6 @@
         m_eventAdmin.open();
         m_listenerList.open(true /* trackAllServices */);
         m_eventDispatcher.start();
-        m_roleRepository.start();
         m_store.open();
     }
     
@@ -59,7 +58,6 @@
      * Stops/closes all helpers.
      */
     public void stop() {
-        m_roleRepository.stop();
         m_eventDispatcher.stop();
         m_listenerList.close();
         m_eventAdmin.close();
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/AuthorizationImplTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/AuthorizationImplTest.java
index 7cc1c39..766fab5 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/AuthorizationImplTest.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/AuthorizationImplTest.java
@@ -22,16 +22,18 @@
 
 import junit.framework.TestCase;
 
-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;
 
 /**
  * Test cases for {@link AuthorizationImpl}.
  */
 public class AuthorizationImplTest extends TestCase {
 
-    private RoleRepository m_roleManager;
+    private RoleRepository m_roleRepository;
+    private Role m_anyone;
 
     /**
      * {@inheritDoc}
@@ -39,7 +41,9 @@
     protected void setUp() throws Exception {
         super.setUp();
         
-        m_roleManager = new RoleRepository(new MemoryRoleRepositoryStore());
+        m_roleRepository = new RoleRepository(new MemoryRoleRepositoryStore());
+        
+        m_anyone = m_roleRepository.getRoleByName(Role.USER_ANYONE);
     }
     
     /**
@@ -62,26 +66,26 @@
         Group residents = createGroup("Residents");
         
         // Users
-        UserImpl elmer = new UserImpl("Elmer");
-        UserImpl fudd = new UserImpl("Fudd");
-        UserImpl marvin = new UserImpl("Marvin");
-        UserImpl pepe = new UserImpl("Pepe");
-        UserImpl daffy = new UserImpl("Daffy");
-        UserImpl foghorn = new UserImpl("Foghorn");
+        User elmer = RoleFactory.createUser("Elmer");
+        User fudd = RoleFactory.createUser("Fudd");
+        User marvin = RoleFactory.createUser("Marvin");
+        User pepe = RoleFactory.createUser("Pepe");
+        User daffy =RoleFactory.createUser("Daffy");
+        User foghorn = RoleFactory.createUser("Foghorn");
         
         // Not explicitly mentioned; but needed to comply with the semantics
-        alarmSystemControl.addRequiredMember(RoleRepository.USER_ANYONE);
-        internetAccess.addRequiredMember(RoleRepository.USER_ANYONE);
-        temperatureControl.addRequiredMember(RoleRepository.USER_ANYONE);
-        photoAlbumEdit.addRequiredMember(RoleRepository.USER_ANYONE);
-        photoAlbumView.addRequiredMember(RoleRepository.USER_ANYONE);
-        portForwarding.addRequiredMember(RoleRepository.USER_ANYONE);
+        alarmSystemControl.addRequiredMember(m_anyone);
+        internetAccess.addRequiredMember(m_anyone);
+        temperatureControl.addRequiredMember(m_anyone);
+        photoAlbumEdit.addRequiredMember(m_anyone);
+        photoAlbumView.addRequiredMember(m_anyone);
+        portForwarding.addRequiredMember(m_anyone);
 
-        administrators.addRequiredMember(RoleRepository.USER_ANYONE);
-        buddies.addRequiredMember(RoleRepository.USER_ANYONE);
-        children.addRequiredMember(RoleRepository.USER_ANYONE);
-        adults.addRequiredMember(RoleRepository.USER_ANYONE);
-        residents.addRequiredMember(RoleRepository.USER_ANYONE);
+        administrators.addRequiredMember(m_anyone);
+        buddies.addRequiredMember(m_anyone);
+        children.addRequiredMember(m_anyone);
+        adults.addRequiredMember(m_anyone);
+        residents.addRequiredMember(m_anyone);
 
         // Table 107.1
         residents.addMember(elmer);
@@ -121,7 +125,7 @@
         portForwarding.addRequiredMember(administrators);
 
         // Test with the user "foghorn"...
-        AuthorizationImpl auth = new AuthorizationImpl(foghorn, m_roleManager);
+        AuthorizationImpl auth = new AuthorizationImpl(foghorn, m_roleRepository);
 
         assertFalse(auth.hasRole(alarmSystemControl.getName()));
         assertFalse(auth.hasRole(internetAccess.getName()));
@@ -131,7 +135,7 @@
         assertFalse(auth.hasRole(portForwarding.getName()));
 
         // Test with the user "fudd"...
-        auth = new AuthorizationImpl(fudd, m_roleManager);
+        auth = new AuthorizationImpl(fudd, m_roleRepository);
 
         assertFalse(auth.hasRole(alarmSystemControl.getName()));
         assertTrue(auth.hasRole(internetAccess.getName()));
@@ -141,7 +145,7 @@
         assertFalse(auth.hasRole(portForwarding.getName()));
 
         // Test with the user "elmer"...
-        auth = new AuthorizationImpl(elmer, m_roleManager);
+        auth = new AuthorizationImpl(elmer, m_roleRepository);
 
         assertTrue(auth.hasRole(alarmSystemControl.getName()));
         assertTrue(auth.hasRole(internetAccess.getName()));
@@ -155,54 +159,54 @@
      * Test that the tests for membership work correctly. 
      */
     public void testHasRoleOk() {
-        GroupImpl citizens = createGroup("citizen");
-        citizens.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group citizens = createGroup("citizen");
+        citizens.addRequiredMember(m_anyone);
         
-        GroupImpl adults = createGroup("adult");
-        adults.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group adults = createGroup("adult");
+        adults.addRequiredMember(m_anyone);
         
-        GroupImpl voters = createGroup("voter");
+        Group voters = createGroup("voter");
         voters.addRequiredMember(citizens);
         voters.addRequiredMember(adults);
-        voters.addMember(RoleRepository.USER_ANYONE);
+        voters.addMember(m_anyone);
         
         // Elmer belongs to the citizens and adults...
-        UserImpl elmer = createUser("elmer");
+        User elmer = createUser("elmer");
         citizens.addMember(elmer);
         adults.addMember(elmer);
         
         // Pepe belongs to the citizens, but is not an adult...
-        UserImpl pepe = createUser("pepe");
+        User pepe = createUser("pepe");
         citizens.addMember(pepe);
         
         // Bugs is an adult, but is not a citizen...
-        UserImpl bugs = createUser("bugs");
+        User bugs = createUser("bugs");
         adults.addMember(bugs);
         
         // Daffy is not an adult, neither a citizen...
-        UserImpl daffy = createUser("daffy");
+        User daffy = createUser("daffy");
         
         AuthorizationImpl auth;
 
-        auth = new AuthorizationImpl(elmer, m_roleManager);
+        auth = new AuthorizationImpl(elmer, m_roleRepository);
         assertTrue(auth.hasRole("adult"));
         assertTrue(auth.hasRole("citizen"));
         assertTrue(auth.hasRole("voter"));
         assertFalse(auth.hasRole("non-existing-role"));
 
-        auth = new AuthorizationImpl(pepe, m_roleManager);
+        auth = new AuthorizationImpl(pepe, m_roleRepository);
         assertFalse(auth.hasRole("adult"));
         assertTrue(auth.hasRole("citizen"));
         assertFalse(auth.hasRole("voter"));
         assertFalse(auth.hasRole("non-existing-role"));
 
-        auth = new AuthorizationImpl(bugs, m_roleManager);
+        auth = new AuthorizationImpl(bugs, m_roleRepository);
         assertTrue(auth.hasRole("adult"));
         assertFalse(auth.hasRole("citizen"));
         assertFalse(auth.hasRole("voter"));
         assertFalse(auth.hasRole("non-existing-role"));
 
-        auth = new AuthorizationImpl(daffy, m_roleManager);
+        auth = new AuthorizationImpl(daffy, m_roleRepository);
         assertFalse(auth.hasRole("adult"));
         assertFalse(auth.hasRole("citizen"));
         assertFalse(auth.hasRole("voter"));
@@ -213,51 +217,51 @@
      * Test that the tests for membership work correctly. 
      */
     public void testGetRolesOk() {
-        GroupImpl citizens = createGroup("citizen");
-        citizens.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group citizens = createGroup("citizen");
+        citizens.addRequiredMember(m_anyone);
         
-        GroupImpl adults = createGroup("adult");
-        adults.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group adults = createGroup("adult");
+        adults.addRequiredMember(m_anyone);
         
-        GroupImpl voters = createGroup("voter");
+        Group voters = createGroup("voter");
         voters.addRequiredMember(citizens);
         voters.addRequiredMember(adults);
-        voters.addMember(RoleRepository.USER_ANYONE);
+        voters.addMember(m_anyone);
         
         // Elmer belongs to the citizens and adults...
-        UserImpl elmer = createUser("elmer");
+        User elmer = createUser("elmer");
         citizens.addMember(elmer);
         adults.addMember(elmer);
         
         // Pepe belongs to the citizens, but is not an adult...
-        UserImpl pepe = createUser("pepe");
+        User pepe = createUser("pepe");
         citizens.addMember(pepe);
         
         // Bugs is an adult, but is not a citizen...
-        UserImpl bugs = createUser("bugs");
+        User bugs = createUser("bugs");
         adults.addMember(bugs);
 
         // Daffy is not an adult, neither a citizen...
-        UserImpl daffy = createUser("daffy");
+        User daffy = createUser("daffy");
 
-        // Daffy is not an adult, neither a citizen...
-        UserImpl donald = new UserImpl("donald");
+        // Donald is not an adult, neither a citizen...
+        User donald = RoleFactory.createUser("donald");
         
         AuthorizationImpl auth;
 
-        auth = new AuthorizationImpl(elmer, m_roleManager);
+        auth = new AuthorizationImpl(elmer, m_roleRepository);
         assertSameRoles(new String[]{ "elmer", "adult", "citizen", "voter" }, auth.getRoles());
 
-        auth = new AuthorizationImpl(pepe, m_roleManager);
+        auth = new AuthorizationImpl(pepe, m_roleRepository);
         assertSameRoles(new String[]{ "pepe", "citizen" }, auth.getRoles());
 
-        auth = new AuthorizationImpl(bugs, m_roleManager);
+        auth = new AuthorizationImpl(bugs, m_roleRepository);
         assertSameRoles(new String[]{ "bugs", "adult" }, auth.getRoles());
 
-        auth = new AuthorizationImpl(daffy, m_roleManager);
+        auth = new AuthorizationImpl(daffy, m_roleRepository);
         assertSameRoles(new String[]{ "daffy" }, auth.getRoles());
 
-        auth = new AuthorizationImpl(donald, m_roleManager);
+        auth = new AuthorizationImpl(donald, m_roleRepository);
         assertNull(auth.getRoles());
     }
 
@@ -271,15 +275,11 @@
         assertTrue("Not seen: " + e, e.isEmpty());
     }
 
-    private GroupImpl createGroup(String name) {
-        GroupImpl result = new GroupImpl(name);
-        m_roleManager.addRole(result);
-        return result;
+    private Group createGroup(String name) {
+        return (Group) m_roleRepository.addRole(name, Role.GROUP);
     }
 
-    private UserImpl createUser(String name) {
-        UserImpl result = new UserImpl(name);
-        m_roleManager.addRole(result);
-        return result;
+    private User createUser(String name) {
+        return (User) m_roleRepository.addRole(name, Role.USER);
     }
 }
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/CustomRoleImplTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/CustomRoleImplTest.java
new file mode 100644
index 0000000..ef4ae15
--- /dev/null
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/CustomRoleImplTest.java
@@ -0,0 +1,486 @@
+/**
+ *  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;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.useradmin.RoleRepositoryStore;
+import org.osgi.framework.Filter;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Tests whether using custom roles in the backend work as expected and yield the proper events.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CustomRoleImplTest extends TestCase
+{
+    static class MyGroupImpl extends MyUserImpl implements Group {
+        private final Map m_members;
+        private final Map m_requiredMembers;
+        
+        public MyGroupImpl(String name) {
+            super(name, GROUP);
+            m_members = new HashMap();
+            m_requiredMembers = new HashMap();
+        }
+
+        public boolean addMember(Role role) {
+            String name = role.getName();
+            if (m_requiredMembers.containsKey(name) || m_members.containsKey(name)) {
+                return false;
+            }
+            return m_members.put(name, role) == null;
+        }
+
+        public boolean addRequiredMember(Role role) {
+            String name = role.getName();
+            if (m_requiredMembers.containsKey(name) || m_members.containsKey(name)) {
+                return false;
+            }
+            return m_requiredMembers.put(name, role) == null;
+        }
+
+        public Role[] getMembers() {
+            Role[] result = new Role[m_members.size()];
+            return (Role[]) m_members.values().toArray(result);
+        }
+
+        public Role[] getRequiredMembers() {
+            Role[] result = new Role[m_requiredMembers.size()];
+            return (Role[]) m_requiredMembers.values().toArray(result);
+        }
+
+        public boolean removeMember(Role role) {
+            String name = role.getName();
+            if (m_requiredMembers.remove(name) != null) {
+                return true;
+            } else if (m_members.remove(name) != null) {
+                return true;
+            }
+            return false;
+        }
+    }
+    
+    static class MyRoleImpl implements Role {
+        private final String m_name;
+        private final int m_type;
+        private final Hashtable m_props;
+        
+        public MyRoleImpl(String name, int type) {
+            m_name = name;
+            m_type = type;
+            m_props = new Hashtable();
+        }
+
+        public String getName() {
+            return m_name;
+        }
+
+        public Dictionary getProperties() {
+            return m_props;
+        }
+
+        public int getType() {
+            return m_type;
+        }
+    }
+    
+    static class MyRoleRepositoryStore implements RoleRepositoryStore {
+        private final ConcurrentMap m_entries = new ConcurrentHashMap();
+
+        public Role addRole(String roleName, int type) throws IOException {
+            if (roleName == null) {
+                throw new IllegalArgumentException("Name cannot be null!");
+            }
+
+            Role role = createRole(roleName, type);
+
+            Object result = m_entries.putIfAbsent(roleName, role);
+            return (result == null) ? role : null;
+        }
+
+        public void close() throws IOException {
+            // Nop
+        }
+
+        public Role getRoleByName(String roleName) throws IOException {
+            if (roleName == null) {
+                throw new IllegalArgumentException("Role name cannot be null!");
+            }
+            return (Role) m_entries.get(roleName);
+        }
+
+        public Role[] getRoles(Filter filter) throws IOException {
+            Collection roles = m_entries.values();
+            
+            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 void initialize() throws IOException {
+            // Nop
+        }
+
+        public Role removeRole(String roleName) throws IOException {
+            if (roleName == null) {
+                throw new IllegalArgumentException("Name cannot be null!");
+            }
+            Role role = getRoleByName(roleName);
+            boolean result = m_entries.remove(roleName, role);
+            return result ? role : null;
+        }
+
+        private Role createRole(String roleName, int type) {
+            Role role;
+            if (type == Role.USER) {
+                role = new MyUserImpl(roleName);
+            } else if (type == Role.GROUP) {
+                role = new MyGroupImpl(roleName);
+            } else {
+                throw new IllegalArgumentException("Invalid role type!");
+            }
+            return role;
+        }
+    }
+    
+    static class MyUserImpl extends MyRoleImpl implements User {
+        private final Hashtable m_creds;
+        
+        public MyUserImpl(String name) {
+            this(name, USER);
+        }
+        
+        protected MyUserImpl(String name, int type) {
+            super(name, type);
+            m_creds = new Hashtable();
+        }
+
+        public Dictionary getCredentials() {
+            return m_creds;
+        }
+
+        public boolean hasCredential(String key, Object value) {
+            if (m_creds.containsKey(key)) {
+                Object v = m_creds.get(key);
+                return ((v == value) || ((v != null) && v.equals(value)));
+            }
+            return false;
+        }
+    }
+    
+    private CountDownLatch m_latch;
+    private RoleRepository m_repository;
+
+    /**
+     * Tests that adding a basic member to a group-role yields an event. 
+     */
+    public void testAddBasicGroupMemberYieldsEvent() throws Exception {
+        final Group role = (Group) m_repository.addRole("testGroup", Role.GROUP);
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                Role anyone = m_repository.getRoleByName(Role.USER_ANYONE);
+                assertTrue(role.addMember(anyone));
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that adding an existing role does not yield an event. 
+     */
+    public void testAddExistingRoleDoesNotYieldEvent() throws Exception {
+        assertNotNull(m_repository.addRole("testUser", Role.USER));
+
+        m_latch = new CountDownLatch(1);
+
+        new Thread(new Runnable() {
+            public void run() {
+                assertNull(m_repository.addRole("testUser", Role.USER));
+            };
+        }).start();
+
+        assertFalse(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that adding a new role yields an event. 
+     */
+    public void testAddNewRoleYieldsEvent() throws Exception {
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                assertNotNull(m_repository.addRole("testUser", Role.USER));
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that adding a required member to a group-role yields an event. 
+     */
+    public void testAddRequiredGroupMemberYieldsEvent() throws Exception {
+        final Group role = (Group) m_repository.addRole("testGroup", Role.GROUP);
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                Role anyone = m_repository.getRoleByName(Role.USER_ANYONE);
+                assertTrue(role.addRequiredMember(anyone));
+            };
+        }).start();
+
+        assertTrue(m_latch.await(100, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that changing the properties of a user-role yields an event. 
+     */
+    public void testAddRolePropertiesYieldsEvent() throws Exception {
+        final Role role = m_repository.addRole("testUser", Role.USER);
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                role.getProperties().put("key", "value");
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that changing the credentials of a user-role yields an event. 
+     */
+    public void testAddUserCredentialsYieldsEvent() throws Exception {
+        final User role = (User) m_repository.addRole("testUser", Role.USER);
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                role.getCredentials().put("key", "value");
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that changing the credentials of a user-role yields an event. 
+     */
+    public void testChangePropertiesYieldsEvent() throws Exception {
+        final Role role = m_repository.addRole("testUser", Role.USER);
+        role.getProperties().put("key", "value1");
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                role.getProperties().put("key", "value2");
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that changing the credentials of a user-role yields an event. 
+     */
+    public void testChangeUserCredentialsYieldsEvent() throws Exception {
+        final User role = (User) m_repository.addRole("testUser", Role.USER);
+        role.getCredentials().put("key", "value1");
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                role.getCredentials().put("key", "value2");
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that removing an existing role does yield an event. 
+     */
+    public void testRemoveExistingRoleYieldsEvent() throws Exception {
+        assertNotNull(m_repository.addRole("testUser", Role.USER));
+
+        m_latch = new CountDownLatch(1);
+
+        new Thread(new Runnable() {
+            public void run() {
+                assertTrue(m_repository.removeRole("testUser"));
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that removing a non-existing role does not yield an event. 
+     */
+    public void testRemoveNonExistingRoleDoesNotYieldEvent() throws Exception {
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                assertFalse(m_repository.removeRole("testUser"));
+            };
+        }).start();
+
+        assertFalse(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that changing the properties of a user-role yields an event. 
+     */
+    public void testRemoveRolePropertiesYieldsEvent() throws Exception {
+        final Role role = m_repository.addRole("testUser", Role.USER);
+        role.getProperties().put("key", "value");
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                role.getProperties().remove("key");
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that changing the credentials of a user-role yields an event. 
+     */
+    public void testRemoveUserCredentialsYieldsEvent() throws Exception {
+        final User role = (User) m_repository.addRole("testUser", Role.USER);
+        role.getCredentials().put("key", "value");
+        assertNotNull(role);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                role.getCredentials().remove("key");
+            };
+        }).start();
+
+        assertTrue(m_latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Tests that removing a member from a group-role yields an event. 
+     */
+    public void testRemovingGroupMemberYieldsEvent() throws Exception {
+        final Role anyone = m_repository.getRoleByName(Role.USER_ANYONE);
+        final Group role = (Group) m_repository.addRole("testGroup", Role.GROUP);
+        assertNotNull(role);
+        role.addRequiredMember(anyone);
+        
+        m_latch = new CountDownLatch(1);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                assertTrue(role.removeMember(anyone));
+            };
+        }).start();
+
+        assertTrue(m_latch.await(100, TimeUnit.SECONDS));
+    }
+    
+    /**
+     * Set up for all test cases.
+     */
+    protected void setUp() throws Exception
+    {
+        m_repository = new RoleRepository(new MyRoleRepositoryStore());
+        m_repository.addRoleChangeListener(new RoleChangeListener() {
+            public void propertyAdded(Role role, Object key, Object value) {
+                if (m_latch != null) {
+                    m_latch.countDown();
+                }
+            }
+
+            public void propertyChanged(Role role, Object key, Object oldValue, Object newValue) {
+                if (m_latch != null) {
+                    m_latch.countDown();
+                }
+            }
+            
+            public void propertyRemoved(Role role, Object key) {
+                if (m_latch != null) {
+                    m_latch.countDown();
+                }
+            }
+
+            public void roleAdded(Role role) {
+                if (m_latch != null) {
+                    m_latch.countDown();
+                }
+            }
+
+            public void roleRemoved(Role role) {
+                if (m_latch != null) {
+                    m_latch.countDown();
+                }
+            }
+        });
+    }
+}
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/EventDispatcherTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/EventDispatcherTest.java
index 4f89d12..943d281 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/EventDispatcherTest.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/EventDispatcherTest.java
@@ -23,9 +23,9 @@
 import junit.framework.TestCase;
 
 
+import org.apache.felix.useradmin.RoleFactory;
 import org.apache.felix.useradmin.impl.EventDispatcher;
 import org.apache.felix.useradmin.impl.UserAdminListenerList;
-import org.apache.felix.useradmin.impl.role.UserImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.event.Event;
@@ -99,6 +99,10 @@
                 m_props.put(properties[i], properties[i + 1]);
             }
         }
+        
+        public int compareTo(Object reference) {
+            return 0;
+        }
 
         public Bundle getBundle() {
             return null;
@@ -289,7 +293,7 @@
      * @return
      */
     private UserAdminEvent createMockEvent(int type) {
-        Role user = new UserImpl("user-" + System.currentTimeMillis());
+        Role user = RoleFactory.createUser("user-" + System.currentTimeMillis());
         ServiceReference ref = new FakeServiceReference();
         return new UserAdminEvent(ref, type, user);
     }
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/MemoryRoleRepositoryStore.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/MemoryRoleRepositoryStore.java
index 8a0b7c3..80a4f50 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/MemoryRoleRepositoryStore.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/MemoryRoleRepositoryStore.java
@@ -17,11 +17,16 @@
 package org.apache.felix.useradmin.impl;
 
 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;
 
 
@@ -30,28 +35,35 @@
  */
 public class MemoryRoleRepositoryStore implements RoleRepositoryStore {
     
-    protected final ConcurrentMap m_entries = new ConcurrentHashMap();
+    private 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) throws IOException {
+        if (roleName == null) {
+            throw new IllegalArgumentException("Name cannot be null!");
         }
-        Object result = m_entries.putIfAbsent(role.getName(), role);
-        return result == null;
+        Role role = RoleFactory.createRole(type, roleName);
+        Object result = m_entries.putIfAbsent(roleName, role);
+        return (result == null) ? role : null;
     }
 
     public void close() throws IOException {
         // Nop
     }
 
-    public void flush() throws IOException {
-        // Nop
-    }
-
-    public Role[] getAllRoles() throws IOException {
+    public Role[] getRoles(Filter filter) throws IOException {
         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 {
@@ -65,10 +77,12 @@
         // Nop
     }
 
-    public boolean removeRole(Role role) throws IOException {
-        if (role == null) {
-            throw new IllegalArgumentException("Role cannot be null!");
+    public Role removeRole(String roleName) throws IOException {
+        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;
     }
 }
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleCheckerTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleCheckerTest.java
index 191f6f1..c656860 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleCheckerTest.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleCheckerTest.java
@@ -16,13 +16,13 @@
  */
 package org.apache.felix.useradmin.impl;
 
-import org.apache.felix.useradmin.impl.RoleChecker;
-import org.apache.felix.useradmin.impl.RoleRepository;
-import org.apache.felix.useradmin.impl.role.GroupImpl;
-import org.apache.felix.useradmin.impl.role.UserImpl;
-
 import junit.framework.TestCase;
 
+import org.apache.felix.useradmin.RoleFactory;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
 
 /**
  * Test cases for {@link RoleChecker}.
@@ -30,12 +30,13 @@
 public class RoleCheckerTest extends TestCase {
     
     private RoleChecker m_roleChecker;
+    private Role m_anyone;
 
     /**
      * Tests that a user always implies itself.
      */
     public void testUserAlwaysImpliesItself() {
-        UserImpl user = new UserImpl("foo");
+        User user = RoleFactory.createUser("foo");
         
         assertTrue(m_roleChecker.isImpliedBy(user, user));
     }
@@ -44,8 +45,8 @@
      * Test that a user does never imply a group to which it is not a member.
      */
     public void testUserDoesNotImplyNotImpliedGroup() {
-        UserImpl user = new UserImpl("foo");
-        GroupImpl group = new GroupImpl("bar");
+        User user = RoleFactory.createUser("foo");
+        Group group = RoleFactory.createGroup("bar");
         
         assertFalse(m_roleChecker.isImpliedBy(user, group));
     }
@@ -54,10 +55,10 @@
      * Test that a user does never imply a group to which it is not a member.
      */
     public void testUserImpliesImpliedGroup() {
-        UserImpl user = new UserImpl("foo");
+        User user = RoleFactory.createUser("foo");
         
-        GroupImpl group = new GroupImpl("bar");
-        group.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group group = RoleFactory.createGroup("bar");
+        group.addRequiredMember(m_anyone);
         group.addMember(user);
 
         assertTrue(m_roleChecker.isImpliedBy(group, user));
@@ -67,9 +68,9 @@
      * Test that a user does never imply a group to which it is not a member.
      */
     public void testGroupDoesNotImplyNotImpliedUser() {
-        UserImpl user = new UserImpl("foo");
+        User user = RoleFactory.createUser("foo");
         
-        GroupImpl group = new GroupImpl("bar");
+        Group group = RoleFactory.createGroup("bar");
         group.addMember(user);
         
         assertFalse(m_roleChecker.isImpliedBy(user, group));
@@ -79,9 +80,9 @@
      * Test that a group does never imply a group to which it is a required member.
      */
     public void testGroupDoesNotImplySameRequiredGroup() {
-        UserImpl user = new UserImpl("foo");
+        User user = RoleFactory.createUser("foo");
         
-        GroupImpl group = new GroupImpl("bar");
+        Group group = RoleFactory.createGroup("bar");
         group.addRequiredMember(group);
         group.addMember(user);
         
@@ -92,9 +93,9 @@
      * Test that a group does never imply a group to which it is a basic member.
      */
     public void testGroupDoesNotImplySameGroup() {
-        UserImpl user = new UserImpl("foo");
+        User user = RoleFactory.createUser("foo");
         
-        GroupImpl group = new GroupImpl("bar");
+        Group group = RoleFactory.createGroup("bar");
         group.addMember(group);
         group.addMember(user);
         
@@ -105,25 +106,25 @@
      * Test that a membership can be implied for users belonging to multiple required groups.
      */
     public void testRequiredRolesMultipleRequiredGroupsOk() {
-        UserImpl elmer = new UserImpl("elmer");
-        UserImpl pepe = new UserImpl("pepe");
-        UserImpl bugs = new UserImpl("bugs");
-        UserImpl daffy = new UserImpl("daffy");
+        User elmer = RoleFactory.createUser("elmer");
+        User pepe = RoleFactory.createUser("pepe");
+        User bugs = RoleFactory.createUser("bugs");
+        User daffy = RoleFactory.createUser("daffy");
         
-        GroupImpl administrators = new GroupImpl("administrators");
-        administrators.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group administrators = RoleFactory.createGroup("administrators");
+        administrators.addRequiredMember(m_anyone);
         administrators.addMember(elmer);
         administrators.addMember(pepe);
         administrators.addMember(bugs);
 
-        GroupImpl family = new GroupImpl("family");
-        family.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group family = RoleFactory.createGroup("family");
+        family.addRequiredMember(m_anyone);
         family.addMember(elmer);
         family.addMember(pepe);
         family.addMember(daffy);
 
-        GroupImpl alarmSystemActivation = new GroupImpl("alarmSystemActivation");
-        alarmSystemActivation.addMember(RoleRepository.USER_ANYONE);
+        Group alarmSystemActivation = RoleFactory.createGroup("alarmSystemActivation");
+        alarmSystemActivation.addMember(m_anyone);
         alarmSystemActivation.addRequiredMember(administrators);
         alarmSystemActivation.addRequiredMember(family);
 
@@ -137,25 +138,25 @@
      * Test that a membership can be implied for users belonging to multiple non-required groups.
      */
     public void testRequiredRolesMultipleGroupsOk() {
-        UserImpl elmer = new UserImpl("elmer");
-        UserImpl pepe = new UserImpl("pepe");
-        UserImpl bugs = new UserImpl("bugs");
-        UserImpl daffy = new UserImpl("daffy");
+        User elmer = RoleFactory.createUser("elmer");
+        User pepe = RoleFactory.createUser("pepe");
+        User bugs = RoleFactory.createUser("bugs");
+        User daffy = RoleFactory.createUser("daffy");
         
-        GroupImpl administrators = new GroupImpl("administrators");
-        administrators.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group administrators = RoleFactory.createGroup("administrators");
+        administrators.addRequiredMember(m_anyone);
         administrators.addMember(elmer);
         administrators.addMember(pepe);
         administrators.addMember(bugs);
 
-        GroupImpl family = new GroupImpl("family");
-        family.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group family = RoleFactory.createGroup("family");
+        family.addRequiredMember(m_anyone);
         family.addMember(elmer);
         family.addMember(pepe);
         family.addMember(daffy);
 
-        GroupImpl alarmSystemActivation = new GroupImpl("alarmSystemActivation");
-        alarmSystemActivation.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group alarmSystemActivation = RoleFactory.createGroup("alarmSystemActivation");
+        alarmSystemActivation.addRequiredMember(m_anyone);
         alarmSystemActivation.addMember(administrators);
         alarmSystemActivation.addMember(family);
 
@@ -169,33 +170,33 @@
      * Test that a membership can be implied for users belonging to multiple non-required groups.
      */
     public void testVotersRequiredMembersOk() {
-        GroupImpl citizens = new GroupImpl("citizen");
-        citizens.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group citizens = RoleFactory.createGroup("citizen");
+        citizens.addRequiredMember(m_anyone);
         
-        GroupImpl adults = new GroupImpl("adult");
-        adults.addRequiredMember(RoleRepository.USER_ANYONE);
+        Group adults = RoleFactory.createGroup("adult");
+        adults.addRequiredMember(m_anyone);
         
-        GroupImpl voters = new GroupImpl("voter");
+        Group voters = RoleFactory.createGroup("voter");
         voters.addRequiredMember(citizens);
         voters.addRequiredMember(adults);
-        voters.addMember(RoleRepository.USER_ANYONE);
+        voters.addMember(m_anyone);
         
         
         // Elmer belongs to the citizens and adults...
-        UserImpl elmer = new UserImpl("elmer");
+        User elmer = RoleFactory.createUser("elmer");
         citizens.addMember(elmer);
         adults.addMember(elmer);
         
         // Pepe belongs to the citizens, but is not an adult...
-        UserImpl pepe = new UserImpl("pepe");
+        User pepe = RoleFactory.createUser("pepe");
         citizens.addMember(pepe);
         
         // Bugs is an adult, but is not a citizen...
-        UserImpl bugs = new UserImpl("bugs");
+        User bugs = RoleFactory.createUser("bugs");
         adults.addMember(bugs);
         
         // Daffy is not an adult, neither a citizen...
-        UserImpl daffy = new UserImpl("daffy");
+        User daffy = RoleFactory.createUser("daffy");
 
         assertTrue(m_roleChecker.isImpliedBy(voters, elmer));
         assertFalse(m_roleChecker.isImpliedBy(voters, pepe));
@@ -209,6 +210,8 @@
     protected void setUp() throws Exception {
         super.setUp();
         
+        m_anyone = RoleFactory.createRole(Role.USER_ANYONE);
+
         m_roleChecker = new RoleChecker();
     }
 }
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositorySecurityTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositorySecurityTest.java
index c6f1185..6ab743b 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositorySecurityTest.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositorySecurityTest.java
@@ -29,6 +29,8 @@
  */
 public class RoleRepositorySecurityTest extends TestCase {
 
+    private static final String TEST_USER_NAME = "testUser";
+    
     private RoleRepository m_roleManager;
     private TestSecurityManager m_securityManager;
 
@@ -37,8 +39,8 @@
      */
     public void testAddRoleWithPermissionsOk() throws SecurityException {
         m_securityManager.m_allowed = true;
-       
-        m_roleManager.addRole(RoleRepository.USER_ANYONE);
+
+        m_roleManager.addRole(TEST_USER_NAME, Role.USER);
     }
 
     /**
@@ -46,7 +48,7 @@
      */
     public void testAddRoleWithoutPermissionsFails() throws SecurityException {
         try {
-            m_roleManager.addRole(RoleRepository.USER_ANYONE);
+            m_roleManager.addRole(TEST_USER_NAME, Role.USER);
             
             fail("Expected SecurityException!");
         } catch (SecurityException e) {
@@ -81,7 +83,7 @@
     public void testRemoveRoleWithPermissionsOk() throws SecurityException {
         m_securityManager.m_allowed = true;
         
-        m_roleManager.removeRole(RoleRepository.USER_ANYONE);
+        m_roleManager.removeRole(TEST_USER_NAME);
     }
 
     /**
@@ -89,7 +91,7 @@
      */
     public void testRemoveRoleWithoutPermissionsFails() throws SecurityException {
         try {
-            m_roleManager.removeRole(RoleRepository.USER_ANYONE);
+            m_roleManager.removeRole(TEST_USER_NAME);
             
             fail("Expected SecurityException!");
         } catch (SecurityException e) {
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositoryTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositoryTest.java
index 1003bfa..6edd1c8 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositoryTest.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/RoleRepositoryTest.java
@@ -20,39 +20,38 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Dictionary;
 import java.util.List;
-import java.util.Properties;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import junit.framework.TestCase;
 
 import org.apache.felix.framework.FilterImpl;
-import org.apache.felix.useradmin.impl.role.GroupImpl;
-import org.apache.felix.useradmin.impl.role.UserImpl;
 import org.osgi.framework.Filter;
+import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
 
 /**
  * Test cases for {@link RoleRepository}.
  */
 public class RoleRepositoryTest extends TestCase {
 
-    private RoleRepository m_roleManager;
+    private RoleRepository m_roleRepository;
     private CountDownLatch m_latch;
     
     /**
      * Tests whether adding a new role to a group causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testAddBasicRoleYieldsEventOk() throws Exception {
-        final GroupImpl role = (GroupImpl) m_roleManager.addRole(new GroupImpl("foo"));
+        final Group role = (Group) m_roleRepository.addRole("foo", Role.GROUP);
         
         m_latch = new CountDownLatch(1);
         
         new Thread(new Runnable() {
             public void run() {
-                role.addMember(RoleRepository.USER_ANYONE);
+                Role anyone = m_roleRepository.getRoleByName(Role.USER_ANYONE);
+                role.addMember(anyone);
             };
         }).start();
 
@@ -64,7 +63,7 @@
      */
     public void testAddNullRoleChangeListenerFail() throws Exception {
         try {
-            m_roleManager.addRoleChangeListener(null);
+            m_roleRepository.addRoleChangeListener(null);
             fail("Expected IllegalArgumentException!");
         } catch (IllegalArgumentException e) {
             // Ok; expected...
@@ -75,22 +74,28 @@
      * Tests that adding a predefined role is not allowed.
      */
     public void testAddPredefineRoleFails() {
-        Role role = RoleRepository.USER_ANYONE;
-        assertNull(m_roleManager.addRole(role));
-        assertEquals(0, m_roleManager.getRoles(null).size());
+        try {
+            assertNull(m_roleRepository.addRole(Role.USER_ANYONE, Role.ROLE));
+            fail("IllegalArgumentException expected!");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // Ok; expected
+        }
     }
 
     /**
      * Tests whether adding a new role to a group causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testAddRequiredRoleYieldsEventOk() throws Exception {
-        final GroupImpl role = (GroupImpl) m_roleManager.addRole(new GroupImpl("foo"));
+        final Group role = (Group) m_roleRepository.addRole("foo", Role.GROUP);
 
         m_latch = new CountDownLatch(1);
         
         new Thread(new Runnable() {
             public void run() {
-                role.addRequiredMember(RoleRepository.USER_ANYONE);
+                Role anyone = m_roleRepository.getRoleByName(Role.USER_ANYONE);
+                role.addRequiredMember(anyone);
             };
         }).start();
 
@@ -102,7 +107,7 @@
      */
     public void testAddRoleChangeListenerOk() throws Exception {
         // Should succeed...
-        m_roleManager.addRoleChangeListener(new RoleChangeListener() {
+        m_roleRepository.addRoleChangeListener(new RoleChangeListener() {
             public void propertyAdded(Role role, Object key, Object value) {
             }
             
@@ -124,22 +129,19 @@
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#addRole(Role)}.
      */
     public void testAddRoleOfSameTypeTwiceFail() {
-        UserImpl role = new UserImpl("foo");
+        assertNotNull(m_roleRepository.addRole("foo", Role.USER));
+        assertEquals(1, m_roleRepository.getRoles(null).size());
 
-        assertSame(role, m_roleManager.addRole(role));
-        assertEquals(1, m_roleManager.getRoles(null).size());
-
-        assertNull(m_roleManager.addRole(role));
-        assertEquals(1, m_roleManager.getRoles(null).size());
+        assertNull(m_roleRepository.addRole("foo", Role.USER));
+        assertEquals(1, m_roleRepository.getRoles(null).size());
     }
 
     /**
      * Tests that adding a role works.
      */
     public void testAddRoleOk() {
-        UserImpl role = new UserImpl("foo");
-        assertSame(role, m_roleManager.addRole(role));
-        assertEquals(1, m_roleManager.getRoles(null).size());
+        assertNotNull(m_roleRepository.addRole("foo", Role.USER));
+        assertEquals(1, m_roleRepository.getRoles(null).size());
     }
 
     /**
@@ -148,7 +150,7 @@
     public void testAddRolePropertyYieldsEventOk() throws Exception {
         m_latch = new CountDownLatch(1);
         
-        final Role role = m_roleManager.addRole(new UserImpl("john.doe"));
+        final Role role = m_roleRepository.addRole("john.doe", Role.USER);
         
         new Thread(new Runnable() {
             public void run() {
@@ -160,21 +162,11 @@
     }
 
     /**
-     * Tests that adding a role that does not inherit from RoleImpl does not work and yields an exception.
+     * Tests that adding a null-role does not work and yields an exception.
      */
-    public void testAddRoleWithInvalidRoleFail() {
+    public void testAddRoleWithNullRoleFail() {
         try {
-            m_roleManager.addRole(new Role() {
-                public String getName() {
-                    return "A User";
-                }
-                public Dictionary getProperties() {
-                    return new Properties();
-                }
-                public int getType() {
-                    return Role.USER;
-                }
-            });
+            m_roleRepository.addRole(null, Role.USER);
             
             fail("Exception expected!");
         } catch (IllegalArgumentException e) {
@@ -183,11 +175,11 @@
     }
 
     /**
-     * Tests that adding a null-role does not work and yields an exception.
+     * Tests that adding a role with an invalid type does not work and yields an exception.
      */
-    public void testAddRoleWithNullRoleFail() {
+    public void testAddRoleWithInvalidRoleTypeFail() {
         try {
-            m_roleManager.addRole(null);
+            m_roleRepository.addRole("role", Role.ROLE);
             
             fail("Exception expected!");
         } catch (IllegalArgumentException e) {
@@ -199,14 +191,11 @@
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#addRole(Role)}.
      */
     public void testAddRoleWithSameNameTwiceFail() {
-        UserImpl role1 = new UserImpl("foo");
-        GroupImpl role2 = new GroupImpl("foo");
+        assertNotNull(m_roleRepository.addRole("foo", Role.USER));
+        assertEquals(1, m_roleRepository.getRoles(null).size());
 
-        assertSame(role1, m_roleManager.addRole(role1));
-        assertEquals(1, m_roleManager.getRoles(null).size());
-
-        assertNull(m_roleManager.addRole(role2));
-        assertEquals(1, m_roleManager.getRoles(null).size());
+        assertNull(m_roleRepository.addRole("foo", Role.GROUP));
+        assertEquals(1, m_roleRepository.getRoles(null).size());
     }
 
     /**
@@ -215,7 +204,7 @@
     public void testAddUserCredentialYieldsEventOk() throws Exception {
         m_latch = new CountDownLatch(1);
         
-        final UserImpl role = (UserImpl) m_roleManager.addRole(new UserImpl("john.doe"));
+        final User role = (User) m_roleRepository.addRole("john.doe", Role.USER);
         
         new Thread(new Runnable() {
             public void run() {
@@ -230,7 +219,7 @@
      * Tests whether changing an existing property to a role causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testChangeRolePropertyYieldsEventOk() throws Exception {
-        final Role role = m_roleManager.addRole(new UserImpl("john.doe"));
+        final Role role = m_roleRepository.addRole("john.doe", Role.USER);
         role.getProperties().put("key", "value");
         
         m_latch = new CountDownLatch(1);
@@ -248,7 +237,7 @@
      * Tests whether changing an existing credential for a user causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testChangeUserCredentialYieldsEventOk() throws Exception {
-        final UserImpl role = (UserImpl) m_roleManager.addRole(new UserImpl("john.doe"));
+        final User role = (User) m_roleRepository.addRole("john.doe", Role.USER);
         role.getCredentials().put("key", "value");
         
         m_latch = new CountDownLatch(1);
@@ -266,85 +255,88 @@
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#getRoleByName(java.lang.String)}.
      */
     public void testGetRoleByName() {
-        Role role1 = m_roleManager.addRole(new UserImpl("foo"));
-        Role role2 = m_roleManager.addRole(new GroupImpl("bar"));
+        Role role1 = m_roleRepository.addRole("foo", Role.USER);
+        Role role2 = m_roleRepository.addRole("bar", Role.GROUP);
 
-        assertSame(role1, m_roleManager.getRoleByName("foo"));
-        assertSame(role2, m_roleManager.getRoleByName("bar"));
-        assertNull(m_roleManager.getRoleByName("qux"));
+        assertEquals(role1, m_roleRepository.getRoleByName("foo"));
+        assertEquals(role2, m_roleRepository.getRoleByName("bar"));
+        assertNull(m_roleRepository.getRoleByName("qux"));
     }
 
     /**
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#getRoles(org.osgi.framework.Filter)}.
      */
     public void testGetRolesWithFilterOk() throws Exception {
-        Role role1 = m_roleManager.addRole(new UserImpl("foo"));
+        Role role1 = m_roleRepository.addRole("foo", Role.USER);
         role1.getProperties().put("key", "value1");
         role1.getProperties().put("keyA", "valueA");
-        Role role2 = m_roleManager.addRole(new GroupImpl("bar"));
+        Role role2 = m_roleRepository.addRole("bar", Role.GROUP);
         role2.getProperties().put("key", "value2");
         role2.getProperties().put("keyB", "value1");
         
         Filter filter;
 
         filter = new FilterImpl("(key=value1)");
-        assertSameRoles(new Role[]{ role1 }, m_roleManager.getRoles(filter));
+        assertSameRoles(new Role[]{ role1 }, m_roleRepository.getRoles(filter));
 
         filter = new FilterImpl("(key=value2)");
-        assertSameRoles(new Role[]{ role2 }, m_roleManager.getRoles(filter));
+        assertSameRoles(new Role[]{ role2 }, m_roleRepository.getRoles(filter));
 
         filter = new FilterImpl("(key=value*)");
-        assertSameRoles(new Role[]{ role1, role2 }, m_roleManager.getRoles(filter));
+        assertSameRoles(new Role[]{ role1, role2 }, m_roleRepository.getRoles(filter));
 
         filter = new FilterImpl("(|(key=value1)(keyB=value1))");
-        assertSameRoles(new Role[]{ role1, role2 }, m_roleManager.getRoles(filter));
+        assertSameRoles(new Role[]{ role1, role2 }, m_roleRepository.getRoles(filter));
     }
 
     /**
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#getRoles(String, String)}.
      */
     public void testGetRolesWithKeyValuePairOk() throws Exception {
-        Role role1 = m_roleManager.addRole(new UserImpl("foo"));
+        Role role1 = m_roleRepository.addRole("foo", Role.USER);
         role1.getProperties().put("key", "value1");
         role1.getProperties().put("keyA", "valueA");
-        Role role2 = m_roleManager.addRole(new GroupImpl("bar"));
+        Role role2 = m_roleRepository.addRole("bar", Role.GROUP);
         role2.getProperties().put("key", "value2");
         role2.getProperties().put("keyB", "value1");
-        
-        assertSameRoles(new Role[]{ role1 }, m_roleManager.getRoles("key", "value1"));
-        assertSameRoles(new Role[]{ role2 }, m_roleManager.getRoles("key", "value2"));
-        assertSameRoles(new Role[0], m_roleManager.getRoles("key", "value"));
+
+        assertSameRoles(new Role[]{ role1 }, m_roleRepository.getRoles("key", "value1"));
+        assertSameRoles(new Role[]{ role2 }, m_roleRepository.getRoles("key", "value2"));
+        assertSameRoles(new Role[0], m_roleRepository.getRoles("key", "value"));
     }
 
     /**
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#getRoles(org.osgi.framework.Filter)}.
      */
     public void testGetRolesWithoutFilterOk() {
-        Role role1 = m_roleManager.addRole(new UserImpl("foo"));
-        Role role2 = m_roleManager.addRole(new GroupImpl("bar"));
+        Role role1 = m_roleRepository.addRole("foo", Role.USER);
+        Role role2 = m_roleRepository.addRole("bar", Role.GROUP);
         
-        assertSameRoles(new Role[]{ role2, role1 }, m_roleManager.getRoles(null));
+        assertSameRoles(new Role[]{ role2, role1 }, m_roleRepository.getRoles(null));
     }
 
     /**
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#getRoleByName(java.lang.String)}.
      */
     public void testGetUserAnyoneRoleByName() {
-        assertSame(RoleRepository.USER_ANYONE, m_roleManager.getRoleByName(Role.USER_ANYONE));
+        Role anyone = m_roleRepository.getRoleByName(Role.USER_ANYONE);
+        assertEquals(Role.USER_ANYONE, anyone.getName());
+        assertEquals(Role.ROLE, anyone.getType());
     }
 
     /**
      * Tests whether removing a role from a group causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testRemoveBasicRoleYieldsEventOk() throws Exception {
-        final GroupImpl role = (GroupImpl) m_roleManager.addRole(new GroupImpl("foo"));
-        role.addMember(RoleRepository.USER_ANYONE);
+        final Role anyone = m_roleRepository.getRoleByName(Role.USER_ANYONE);
+        final Group role = (Group) m_roleRepository.addRole("bar", Role.GROUP);
+        role.addMember(anyone);
         
         m_latch = new CountDownLatch(1);
         
         new Thread(new Runnable() {
             public void run() {
-                role.removeMember(RoleRepository.USER_ANYONE);
+                role.removeMember(anyone);
             };
         }).start();
 
@@ -355,23 +347,20 @@
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#removeRole(Role)}.
      */
     public void testRemoveExistingRoleOk() {
-        UserImpl role = new UserImpl("foo");
-        assertSame(role, m_roleManager.addRole(role));
+        assertNotNull(m_roleRepository.addRole("foo", Role.USER));
         
-        assertTrue(m_roleManager.removeRole(role));
-        assertEquals(0, m_roleManager.getRoles(null).size());
+        assertTrue(m_roleRepository.removeRole("foo"));
+        assertEquals(0, m_roleRepository.getRoles(null).size());
     }
 
     /**
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#removeRole(Role)}.
      */
     public void testRemoveNonExistingRoleOk() {
-        UserImpl role1 = new UserImpl("foo");
-        UserImpl role2 = new UserImpl("bar");
-        assertSame(role1, m_roleManager.addRole(role1));
+        assertNotNull(m_roleRepository.addRole("foo", Role.USER));
 
-        assertFalse(m_roleManager.removeRole(role2));
-        assertEquals(1, m_roleManager.getRoles(null).size());
+        assertFalse(m_roleRepository.removeRole("qux"));
+        assertEquals(1, m_roleRepository.getRoles(null).size());
     }
 
     /**
@@ -379,7 +368,7 @@
      */
     public void testRemoveNullRoleChangeListenerFail() throws Exception {
         try {
-            m_roleManager.removeRoleChangeListener(null);
+            m_roleRepository.removeRoleChangeListener(null);
             fail("Expected IllegalArgumentException!");
         } catch (IllegalArgumentException e) {
             // Ok; expected...
@@ -390,26 +379,26 @@
      * Test method for {@link org.apache.felix.useradmin.impl.RoleRepository#removeRole(Role)}.
      */
     public void testRemovePredefinedRoleFails() {
-        m_roleManager.addRole(new UserImpl("foo"));
+        m_roleRepository.addRole("foo", Role.USER);
 
-        Role role = RoleRepository.USER_ANYONE;
-        assertFalse(m_roleManager.removeRole(role));
+        assertFalse(m_roleRepository.removeRole(Role.USER_ANYONE));
 
-        assertEquals(1, m_roleManager.getRoles(null).size());
+        assertEquals(1, m_roleRepository.getRoles(null).size());
     }
 
     /**
      * Tests whether removing a role from a group causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testRemoveRequiredRoleYieldsEventOk() throws Exception {
-        final GroupImpl role = (GroupImpl) m_roleManager.addRole(new GroupImpl("foo"));
-        role.addRequiredMember(RoleRepository.USER_ANYONE);
+        final Role anyone = m_roleRepository.getRoleByName(Role.USER_ANYONE);
+        final Group role = (Group) m_roleRepository.addRole("bar", Role.GROUP);
+        role.addRequiredMember(anyone);
         
         m_latch = new CountDownLatch(1);
         
         new Thread(new Runnable() {
             public void run() {
-                role.removeMember(RoleRepository.USER_ANYONE);
+                role.removeMember(anyone);
             };
         }).start();
 
@@ -421,7 +410,7 @@
      */
     public void testRemoveRoleChangeListenerOk() throws Exception {
         // Should succeed...
-        m_roleManager.removeRoleChangeListener(new RoleChangeListener() {
+        m_roleRepository.removeRoleChangeListener(new RoleChangeListener() {
             public void propertyAdded(Role role, Object key, Object value) {
             }
             
@@ -443,7 +432,7 @@
      * Tests whether removing a property from a role causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testRemoveRolePropertyYieldsEventOk() throws Exception {
-        final Role role = m_roleManager.addRole(new UserImpl("john.doe"));
+        final Role role = m_roleRepository.addRole("john.doe", Role.USER);
         role.getProperties().put("key", "value");
         
         m_latch = new CountDownLatch(1);
@@ -458,34 +447,11 @@
     }
 
     /**
-     * Tests that adding a role that does not inherit from RoleImpl does not work and yields an exception.
-     */
-    public void testRemoveRoleWithInvalidRoleFail() {
-        try {
-            m_roleManager.removeRole(new Role() {
-                public String getName() {
-                    return "A User";
-                }
-                public Dictionary getProperties() {
-                    return new Properties();
-                }
-                public int getType() {
-                    return Role.USER;
-                }
-            });
-            
-            fail("Exception expected!");
-        } catch (IllegalArgumentException e) {
-            // Ok; expected
-        }
-    }
-
-    /**
      * Tests that removing a null-role does not work and yields an exception.
      */
     public void testRemoveRoleWithNullRoleFail() {
         try {
-            m_roleManager.removeRole(null);
+            m_roleRepository.removeRole(null);
             
             fail("Exception expected!");
         } catch (IllegalArgumentException e) {
@@ -497,7 +463,7 @@
      * Tests whether removing a credential from a user causes an event to be emitted to the {@link RoleRepository}.
      */
     public void testRemoveUserCredentialYieldsEventOk() throws Exception {
-        final UserImpl role = (UserImpl) m_roleManager.addRole(new UserImpl("john.doe"));
+        final User role = (User) m_roleRepository.addRole("john.doe", Role.USER);
         role.getCredentials().put("key", "value");
         
         m_latch = new CountDownLatch(1);
@@ -517,8 +483,8 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        m_roleManager = new RoleRepository(new MemoryRoleRepositoryStore());
-        m_roleManager.addRoleChangeListener(new RoleChangeListener() {
+        m_roleRepository = new RoleRepository(new MemoryRoleRepositoryStore());
+        m_roleRepository.addRoleChangeListener(new RoleChangeListener() {
             public void propertyAdded(Role role, Object key, Object value) {
                 if (m_latch != null) {
                     m_latch.countDown();
@@ -549,8 +515,6 @@
                 }
             }
         });
-        
-        m_roleManager.start();
     }
 
     /**
diff --git a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/UserAdminImplTest.java b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/UserAdminImplTest.java
index 9bca211..610e7dd 100644
--- a/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/UserAdminImplTest.java
+++ b/useradmin/useradmin/src/test/java/org/apache/felix/useradmin/impl/UserAdminImplTest.java
@@ -22,7 +22,7 @@
 import junit.framework.TestCase;
 
 import org.apache.felix.framework.FilterImpl;
-import org.apache.felix.useradmin.impl.role.UserImpl;
+import org.apache.felix.useradmin.RoleFactory;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.event.Event;
@@ -347,7 +347,8 @@
      * Tests that obtaining the authorization for a non-existing user yields null.
      */
     public void testGetAuthorizationForNonExistingUserOk() {
-        Authorization auth = m_userAdmin.getAuthorization(new UserImpl("foo"));
+        User nonExistingUser = RoleFactory.createUser("non-existing-user");
+        Authorization auth = m_userAdmin.getAuthorization(nonExistingUser);
 
         assertNotNull(auth);
 
@@ -865,8 +866,6 @@
                 return new FilterImpl(filter);
             }
         };
-
-        m_roleRepository.start();
         m_dispatcher.start();
     }
     
@@ -874,7 +873,6 @@
      * {@inheritDoc}
      */
     protected void tearDown() throws Exception {
-        m_roleRepository.stop();
         m_dispatcher.stop();
         
         super.tearDown();