You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ja...@apache.org on 2014/01/23 10:06:07 UTC

svn commit: r1560612 - in /felix/trunk/useradmin: itest/ itest/src/test/java/org/apache/felix/useradmin/itest/ mongodb/src/main/java/org/apache/felix/useradmin/mongodb/

Author: jawi
Date: Thu Jan 23 09:06:06 2014
New Revision: 1560612

URL: http://svn.apache.org/r1560612
Log:
FELIX-4399, FELIX-4400: UserAdmin MongoDB store:

- when creating a new role, we should return the created role if we
  have good indications that the creation was successful;
- MongoDB returns null for empty properties, causing NPEs on several
  occassions.


Added:
    felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java   (with props)
Modified:
    felix/trunk/useradmin/itest/pom.xml
    felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
    felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
    felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java
    felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
    felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java

Modified: felix/trunk/useradmin/itest/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/pom.xml?rev=1560612&r1=1560611&r2=1560612&view=diff
==============================================================================
--- felix/trunk/useradmin/itest/pom.xml (original)
+++ felix/trunk/useradmin/itest/pom.xml Thu Jan 23 09:06:06 2014
@@ -41,12 +41,6 @@
 	<dependencies>
 		<dependency>
 			<groupId>org.osgi</groupId>
-			<artifactId>org.osgi.core</artifactId>
-			<version>${osgi.version}</version>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.compendium</artifactId>
 			<version>${osgi.version}</version>
 		</dependency>
@@ -64,6 +58,18 @@
 		</dependency>
 		<dependency>
 			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.useradmin.mongodb</artifactId>
+			<version>1.0.2-SNAPSHOT</version>
+			<scope>test</scope>
+		</dependency>
+        <dependency>
+        	<groupId>org.mongodb</groupId>
+        	<artifactId>mongo-java-driver</artifactId>
+        	<version>2.8.0</version>
+			<scope>test</scope>
+        </dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
 			<artifactId>org.apache.felix.dependencymanager</artifactId>
 			<version>3.0.0</version>
 			<scope>test</scope>

Modified: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java?rev=1560612&r1=1560611&r2=1560612&view=diff
==============================================================================
--- felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java (original)
+++ felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/BaseIntegrationTest.java Thu Jan 23 09:06:06 2014
@@ -46,22 +46,26 @@ import org.osgi.util.tracker.ServiceTrac
  *  
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public abstract class BaseIntegrationTest {
+public abstract class BaseIntegrationTest
+{
 
-	private static final int DEFAULT_TIMEOUT = 10000;
-	
-	protected static final String ORG_APACHE_FELIX_USERADMIN = "org.apache.felix.useradmin";
-	protected static final String ORG_APACHE_FELIX_USERADMIN_FILESTORE = "org.apache.felix.useradmin.filestore";
+    private static final int DEFAULT_TIMEOUT = 10000;
+
+    protected static final String ORG_APACHE_FELIX_USERADMIN = "org.apache.felix.useradmin";
+    protected static final String ORG_APACHE_FELIX_USERADMIN_FILESTORE = "org.apache.felix.useradmin.filestore";
+    protected static final String ORG_APACHE_FELIX_USERADMIN_MONGODBSTORE = "org.apache.felix.useradmin.mongodb";
+    protected static final String ORG_MONGODB_MONGO_JAVA_DRIVER = "org.mongodb.mongo-java-driver";
 
     @Inject
     protected volatile BundleContext m_context;
 
     @Configuration
-    public Option[] config() {
+    public Option[] config()
+    {
         return options(
             bootDelegationPackage("sun.*"),
             cleanCaches(),
-            CoreOptions.systemProperty("logback.configurationFile").value("file:src/test/resources/logback.xml"),
+            CoreOptions.systemProperty("logback.configurationFile").value("file:src/test/resources/logback.xml"), //
 //            CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"),
 
             mavenBundle("org.slf4j", "slf4j-api").version("1.6.5").startLevel(START_LEVEL_SYSTEM_BUNDLES),
@@ -79,15 +83,15 @@ public abstract class BaseIntegrationTes
             url("link:classpath:META-INF/links/org.apache.geronimo.specs.atinject.link").startLevel(START_LEVEL_SYSTEM_BUNDLES),
 
             mavenBundle("org.apache.felix", ORG_APACHE_FELIX_USERADMIN).versionAsInProject().startLevel(START_LEVEL_SYSTEM_BUNDLES),
-            mavenBundle("org.apache.felix", ORG_APACHE_FELIX_USERADMIN_FILESTORE).versionAsInProject().startLevel(START_LEVEL_SYSTEM_BUNDLES),
-            
-            junitBundles(),
-            frameworkStartLevel(START_LEVEL_TEST_BUNDLE),
-            felix());
+            mavenBundle("org.apache.felix", ORG_APACHE_FELIX_USERADMIN_FILESTORE).versionAsInProject().noStart(),
+            mavenBundle("org.apache.felix", ORG_APACHE_FELIX_USERADMIN_MONGODBSTORE).versionAsInProject().noStart(), mavenBundle("org.mongodb", "mongo-java-driver").versionAsInProject().noStart(),
+
+            junitBundles(), frameworkStartLevel(START_LEVEL_TEST_BUNDLE), felix());
     }
 
     @Before
-    public void setUp() throws Exception {
+    public void setUp() throws Exception
+    {
         assertNotNull("No bundle context?!", m_context);
     }
 
@@ -97,55 +101,86 @@ public abstract class BaseIntegrationTes
      * @return
      * @throws Exception
      */
-    protected <T> T awaitService(String serviceName) throws Exception {
+    protected <T> T awaitService(String serviceName) throws Exception
+    {
         ServiceTracker tracker = new ServiceTracker(m_context, serviceName, null);
         tracker.open();
         T result;
-        try {
+        try
+        {
             result = (T) tracker.waitForService(DEFAULT_TIMEOUT);
         }
-        finally {
+        finally
+        {
             tracker.close();
         }
         return result;
     }
 
     /**
+     * @param bsn
+     * @return
+     */
+    protected Bundle findBundle(String bsn)
+    {
+        for (Bundle bundle : m_context.getBundles())
+        {
+            if (bsn.equals(bundle.getSymbolicName()))
+            {
+                return bundle;
+            }
+        }
+        return null;
+    }
+
+    protected Bundle getFileStoreBundle()
+    {
+        Bundle b = findBundle(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
+        assertNotNull("Filestore bundle not found?!", b);
+        return b;
+    }
+
+    protected Bundle getMongoDBStoreBundle()
+    {
+        Bundle b = findBundle(ORG_APACHE_FELIX_USERADMIN_MONGODBSTORE);
+        assertNotNull("MongoDB store bundle not found?!", b);
+        return b;
+    }
+
+    protected Bundle getMongoDBBundle()
+    {
+        Bundle b = findBundle(ORG_MONGODB_MONGO_JAVA_DRIVER);
+        assertNotNull("MongoDB bundle not found?!", b);
+        return b;
+    }
+
+    /**
      * Obtains a service without waiting for it to become available.
      * @param serviceName
      * @return
      * @throws Exception
      */
-    protected <T> T getService(String serviceName) throws Exception {
+    protected <T> T getService(String serviceName) throws Exception
+    {
         ServiceTracker tracker = new ServiceTracker(m_context, serviceName, null);
         tracker.open();
         T result;
-        try {
+        try
+        {
             result = (T) tracker.getService();
         }
-        finally {
+        finally
+        {
             tracker.close();
         }
         return result;
     }
-    
+
     /**
      * @return the {@link UserAdmin} service instance.
      */
-    protected UserAdmin getUserAdmin() throws Exception {
+    protected UserAdmin getUserAdmin() throws Exception
+    {
         return getService(UserAdmin.class.getName());
     }
-
-    /**
-     * @param bsn
-     * @return
-     */
-    protected Bundle findBundle(String bsn) {
-    	for (Bundle bundle : m_context.getBundles()) {
-    		if (bsn.equals(bundle.getSymbolicName())) {
-    			return bundle;
-    		}
-    	}
-    	return null;
-    }
 }

Modified: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java?rev=1560612&r1=1560611&r2=1560612&view=diff
==============================================================================
--- felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java (original)
+++ felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/FileStoreInitializationTest.java Thu Jan 23 09:06:06 2014
@@ -36,51 +36,54 @@ import org.osgi.service.useradmin.UserAd
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @RunWith(JUnit4TestRunner.class)
-public class FileStoreInitializationTest extends BaseIntegrationTest {
+public class FileStoreInitializationTest extends BaseIntegrationTest
+{
 
-	/**
-	 * Tests that initialization and closing of the repository store is
-	 * performed correctly.
-	 */
-	@Test
-	public void testStoreIsInitializedAndClosedProperlyOk() throws Exception {
-	    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; should persist the two roles...
-		Bundle fileStoreBundle = findBundle(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
-		assertNotNull(fileStoreBundle);
-		fileStoreBundle.stop();
-
-		Thread.sleep(100); // Wait a little until the bundle is really stopped...
-		
-		// Retrieve the roles again; should both yield null due to the store not being available...
-		user = (User) ua.getRole("user1");
-		assertNull(user);
-
-		group = (Group) ua.getRole("group1");
-		assertNull(group);
-		
-		// This will not succeed: no backend to store the user in...
-		assertNull(ua.createRole("user2", Role.USER));
+    /**
+     * Tests that initialization and closing of the repository store is
+     * performed correctly.
+     */
+    @Test
+    public void testStoreIsInitializedAndClosedProperlyOk() throws Exception
+    {
+        UserAdmin ua = getUserAdmin();
+        // Start the file store bundle...
+        Bundle fileStoreBundle = getFileStoreBundle();
+        fileStoreBundle.start();
 
-		fileStoreBundle.start();
+        // 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; should persist the two roles...
+        fileStoreBundle.stop();
+
+        Thread.sleep(100); // Wait a little until the bundle is really stopped...
+
+        // Retrieve the roles again; should both yield null due to the store not being available...
+        user = (User) ua.getRole("user1");
+        assertNull(user);
+
+        group = (Group) ua.getRole("group1");
+        assertNull(group);
+
+        // This will not succeed: no backend to store the user in...
+        assertNull(ua.createRole("user2", Role.USER));
+
+        fileStoreBundle.start();
+
+        awaitService(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
 
-		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);
 
@@ -93,8 +96,8 @@ public class FileStoreInitializationTest
         assertNotNull(members);
         assertEquals(1, members.length);
         assertEquals(Role.USER_ANYONE, members[0].getName());
-        
+
         user = (User) ua.getRole("user2");
         assertNull(user);
-	}
+    }
 }

Added: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java?rev=1560612&view=auto
==============================================================================
--- felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java (added)
+++ felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java Thu Jan 23 09:06:06 2014
@@ -0,0 +1,201 @@
+/*
+ * 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.itest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+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.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.CommandResult;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.Mongo;
+import com.mongodb.WriteConcern;
+
+/**
+ * Main integration test for the user admin service.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@RunWith(JUnit4TestRunner.class)
+public class MongoDBStoreTest extends BaseIntegrationTest
+{
+    /**
+     * Tests that fetching an empty role without properties or other roles does not cause a NPE.
+     */
+    @Test
+    public void testFelix4399_FetchEmptyRoleOk() throws Exception
+    {
+        UserAdmin ua = getUserAdmin();
+
+        String roleName = "emptyRole";
+
+        if (canRunTest())
+        {
+            Role emptyRole = ua.createRole(roleName, Role.USER);
+            assertNotNull("Collection not empty?!", emptyRole);
+
+            Role readRole = ua.getRole(roleName);
+
+            assertNotNull("Unable to read back created empty role?!", readRole);
+            assertEquals("Names not equal?!", emptyRole.getName(), readRole.getName());
+            assertEquals("Types not equal?!", emptyRole.getType(), readRole.getType());
+
+            Role[] readRoles = ua.getRoles(null);
+
+            assertNotNull("Unable to read back created empty role?!", readRoles);
+            assertEquals(1, readRoles.length);
+        }
+    }
+
+    /**
+     * Tests that creating a new role returns the actual created role.
+     */
+    @Test
+    public void testFelix4400_CreateRoleReturnsNonNullOk() throws Exception
+    {
+        UserAdmin ua = getUserAdmin();
+
+        String roleName = "newRole";
+
+        if (canRunTest())
+        {
+            Role newRole = ua.createRole(roleName, Role.USER);
+            assertNotNull("Felix-4400 not resolved?!", newRole);
+
+            assertEquals("Names not equal?!", roleName, newRole.getName());
+            assertEquals("Types not equal?!", Role.USER, newRole.getType());
+        }
+    }
+
+    /**
+     * Tests that removing a role works correctly.
+     */
+    @Test
+    public void testRemoveRoleOk() throws Exception
+    {
+        UserAdmin ua = getUserAdmin();
+
+        String roleName = "newRole";
+        Role[] readRoles;
+
+        if (canRunTest())
+        {
+            Role role = ua.createRole(roleName, Role.USER);
+            assertNotNull("Collection not empty?!", role);
+
+            readRoles = ua.getRoles(null);
+
+            assertNotNull("No roles stored?!", readRoles);
+            assertEquals(1, readRoles.length);
+
+            ua.removeRole(roleName);
+
+            readRoles = ua.getRoles(null);
+
+            assertNull("Still roles stored?!", readRoles);
+        }
+    }
+
+    /**
+     * Tests that removing a role works correctly.
+     */
+    @Test
+    public void testUpdateRoleOk() throws Exception
+    {
+        UserAdmin ua = getUserAdmin();
+
+        String roleName = "role1";
+        Role[] readRoles;
+
+        if (canRunTest())
+        {
+            User role = (User) ua.createRole(roleName, Role.USER);
+            assertNotNull("Collection not empty?!", role);
+
+            readRoles = ua.getRoles(null);
+
+            assertNotNull("No roles stored?!", readRoles);
+            assertEquals(1, readRoles.length);
+
+            role.getProperties().put("key", "value");
+
+            Thread.sleep(100); // Wait a little to ensure everything is written...
+
+            readRoles = ua.getRoles("(key=value)");
+
+            assertNotNull("Role not updated?!", readRoles);
+            assertEquals(1, readRoles.length);
+        }
+    }
+
+    /**
+     * Sets up MongoDB and tries to clear the useradmin collection. When this fails, it is assumed that no MongoDB service is available.
+     */
+    private boolean canRunTest() throws BundleException
+    {
+        Bundle mongoBundle = getMongoDBBundle();
+        mongoBundle.start();
+
+        Bundle mongoStoreBundle = getMongoDBStoreBundle();
+        mongoStoreBundle.start();
+
+        // Provision an empty configuration...
+        BundleContext context = mongoStoreBundle.getBundleContext();
+
+        ServiceReference serviceRef = context.getServiceReference(ManagedService.class.getName());
+        assertNotNull(serviceRef);
+
+        ManagedService service = (ManagedService) context.getService(serviceRef);
+        try
+        {
+            service.updated(null);
+
+            Mongo mongo = new Mongo();
+            DB db = mongo.getDB("ua_repo");
+            DBCollection collection = db.getCollection("useradmin");
+            // we always get a collection back, regardless if there is an actual MongoDB listening, hence we should do
+            // some actual calls that cause a connection to MongoDB to be created...
+            collection.remove(new BasicDBObject(), WriteConcern.SAFE);
+
+            CommandResult lastError = db.getLastError();
+
+            return (lastError.getException() == null && collection.getCount() == 0L);
+        }
+        catch (Exception e)
+        {
+            // Ignore; apparently, we failed to connect to MongoDB...
+        }
+
+        return false;
+    }
+}

Propchange: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java?rev=1560612&r1=1560611&r2=1560612&view=diff
==============================================================================
--- felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java (original)
+++ felix/trunk/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java Thu Jan 23 09:06:06 2014
@@ -35,108 +35,112 @@ import org.osgi.service.useradmin.UserAd
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @RunWith(JUnit4TestRunner.class)
-public class UserAdminIntegrationTest extends BaseIntegrationTest {
-	
-	/**
-	 * Tests that stopping a filled store and starting it again will cause it to
-	 * properly restore its state.
-	 */
-	@Test
-	public void testFelix3735_StopRunningStoreRetainsDataOk() throws Exception {
-		final String userName = "testUser";
-		final String groupName = "testGroup";
-
-		UserAdmin userAdmin = awaitService(UserAdmin.class.getName());
-
-		// Fill the user admin with some data...
-		User testUser = (User) userAdmin.createRole(userName, Role.USER);
-		testUser.getProperties().put("key", "value");
-
-		Group testGroup = (Group) userAdmin.createRole(groupName, Role.GROUP);
-		testGroup.addMember(testUser);
-
-		// Stop the file store...
-		Bundle fileStoreBundle = findBundle(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
-		assertNotNull(fileStoreBundle);
-		fileStoreBundle.stop();
-
-		// retrieve the useradmin again...
-		userAdmin = awaitService(UserAdmin.class.getName());
-
-		// Verify the user + group are gone (no store available)...
-		assertNull(userAdmin.getRole(userName));
-		assertNull(userAdmin.getRole(groupName));
-
-		// Start the file store...
-		fileStoreBundle.start();
-
-		// Verify the user + group are gone (no store available)...
-		User readUser = (User) userAdmin.getRole(userName);
-		assertNotNull(readUser);
-		assertEquals(userName, readUser.getName());
-		assertEquals("value", readUser.getProperties().get("key"));
-
-		Group readGroup = (Group) userAdmin.getRole(groupName);
-		assertNotNull(readGroup);
-		assertEquals(groupName, readGroup.getName());
-		assertEquals(1, readGroup.getMembers().length);
-		assertEquals(readUser, readGroup.getMembers()[0]);
-	}
-
-	/**
-	 * Tests that starting the file store <em>after</em> the user admin service
-	 * is started will cause it to be properly initialized.
-	 */
-	@Test
-	public void testFelix3735_StartStoreAfterUserAdminInitializesOk() throws Exception {
-		final String userName = "anotherTestUser";
-		final String groupName = "anotherTestGroup";
-
-		UserAdmin userAdmin = awaitService(UserAdmin.class.getName());
-
-		// Fill the user admin with some data...
-		User testUser = (User) userAdmin.createRole(userName, Role.USER);
-		testUser.getProperties().put("key", "value");
-
-		Group testGroup = (Group) userAdmin.createRole(groupName, Role.GROUP);
-		testGroup.addMember(testUser);
-
-		// Stop the file store...
-		Bundle fileStoreBundle = findBundle(ORG_APACHE_FELIX_USERADMIN_FILESTORE);
-		assertNotNull(fileStoreBundle);
-		fileStoreBundle.stop();
-
-		Bundle userAdminBundle = findBundle(ORG_APACHE_FELIX_USERADMIN);
-		assertNotNull(userAdminBundle);
-		userAdminBundle.stop();
-
-		// Obtain user admin service again; shouldn't be available...
-		userAdmin = getService(UserAdmin.class.getName());
-		assertNull(userAdmin);
-
-		userAdminBundle.start();
-
-		// Obtain user admin service again; should be available now...
-		userAdmin = awaitService(UserAdmin.class.getName());
-		assertNotNull(userAdmin);
-
-		// Verify the user + group are gone (no store available)...
-		assertNull(userAdmin.getRole(userName));
-		assertNull(userAdmin.getRole(groupName));
-
-		// Start the file store...
-		fileStoreBundle.start();
-
-		// Verify the user + group are gone (no store available)...
-		User readUser = (User) userAdmin.getRole(userName);
-		assertNotNull(readUser);
-		assertEquals(userName, readUser.getName());
-		assertEquals("value", readUser.getProperties().get("key"));
-
-		Group readGroup = (Group) userAdmin.getRole(groupName);
-		assertNotNull(readGroup);
-		assertEquals(groupName, readGroup.getName());
-		assertEquals(1, readGroup.getMembers().length);
-		assertEquals(readUser, readGroup.getMembers()[0]);
-	}
+public class UserAdminIntegrationTest extends BaseIntegrationTest
+{
+    /**
+     * Tests that stopping a filled store and starting it again will cause it to
+     * properly restore its state.
+     */
+    @Test
+    public void testFelix3735_StopRunningStoreRetainsDataOk() throws Exception
+    {
+        final String userName = "testUser";
+        final String groupName = "testGroup";
+
+        UserAdmin userAdmin = awaitService(UserAdmin.class.getName());
+        Bundle fileStoreBundle = getFileStoreBundle();
+        // Start a suitable storage service...
+        fileStoreBundle.start();
+
+        // Fill the user admin with some data...
+        User testUser = (User) userAdmin.createRole(userName, Role.USER);
+        testUser.getProperties().put("key", "value");
+
+        Group testGroup = (Group) userAdmin.createRole(groupName, Role.GROUP);
+        testGroup.addMember(testUser);
+
+        // Stop the file store...
+        fileStoreBundle.stop();
+
+        // retrieve the useradmin again...
+        userAdmin = awaitService(UserAdmin.class.getName());
+
+        // Verify the user + group are gone (no store available)...
+        assertNull(userAdmin.getRole(userName));
+        assertNull(userAdmin.getRole(groupName));
+
+        // Start the file store...
+        fileStoreBundle.start();
+
+        // Verify the user + group are gone (no store available)...
+        User readUser = (User) userAdmin.getRole(userName);
+        assertNotNull(readUser);
+        assertEquals(userName, readUser.getName());
+        assertEquals("value", readUser.getProperties().get("key"));
+
+        Group readGroup = (Group) userAdmin.getRole(groupName);
+        assertNotNull(readGroup);
+        assertEquals(groupName, readGroup.getName());
+        assertEquals(1, readGroup.getMembers().length);
+        assertEquals(readUser, readGroup.getMembers()[0]);
+    }
+
+    /**
+     * Tests that starting the file store <em>after</em> the user admin service
+     * is started will cause it to be properly initialized.
+     */
+    @Test
+    public void testFelix3735_StartStoreAfterUserAdminInitializesOk() throws Exception
+    {
+        final String userName = "anotherTestUser";
+        final String groupName = "anotherTestGroup";
+
+        UserAdmin userAdmin = awaitService(UserAdmin.class.getName());
+        Bundle fileStoreBundle = getFileStoreBundle();
+        // Start a suitable storage service...
+        fileStoreBundle.start();
+
+        // Fill the user admin with some data...
+        User testUser = (User) userAdmin.createRole(userName, Role.USER);
+        testUser.getProperties().put("key", "value");
+
+        Group testGroup = (Group) userAdmin.createRole(groupName, Role.GROUP);
+        testGroup.addMember(testUser);
+
+        // Stop the file store...
+        fileStoreBundle.stop();
+
+        Bundle userAdminBundle = findBundle(ORG_APACHE_FELIX_USERADMIN);
+        assertNotNull(userAdminBundle);
+        userAdminBundle.stop();
+
+        // Obtain user admin service again; shouldn't be available...
+        userAdmin = getService(UserAdmin.class.getName());
+        assertNull(userAdmin);
+
+        userAdminBundle.start();
+
+        // Obtain user admin service again; should be available now...
+        userAdmin = awaitService(UserAdmin.class.getName());
+        assertNotNull(userAdmin);
+
+        // Verify the user + group are gone (no store available)...
+        assertNull(userAdmin.getRole(userName));
+        assertNull(userAdmin.getRole(groupName));
+
+        // Start the file store...
+        fileStoreBundle.start();
+
+        // Verify the user + group are gone (no store available)...
+        User readUser = (User) userAdmin.getRole(userName);
+        assertNotNull(readUser);
+        assertEquals(userName, readUser.getName());
+        assertEquals("value", readUser.getProperties().get("key"));
+
+        Group readGroup = (Group) userAdmin.getRole(groupName);
+        assertNotNull(readGroup);
+        assertEquals(groupName, readGroup.getName());
+        assertEquals(1, readGroup.getMembers().length);
+        assertEquals(readUser, readGroup.getMembers()[0]);
+    }
 }

Modified: felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java?rev=1560612&r1=1560611&r2=1560612&view=diff
==============================================================================
--- felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java (original)
+++ felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoDBStore.java Thu Jan 23 09:06:06 2014
@@ -147,7 +147,8 @@ public class MongoDBStore implements Rol
             result.getLastError().throwOnError();
         }
 
-        return role;
+        // FELIX-4400: ensure we return the correct role...
+        return getRole(roleName);
     }
 
     /**

Modified: felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java
URL: http://svn.apache.org/viewvc/felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java?rev=1560612&r1=1560611&r2=1560612&view=diff
==============================================================================
--- felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java (original)
+++ felix/trunk/useradmin/mongodb/src/main/java/org/apache/felix/useradmin/mongodb/MongoSerializerHelper.java Thu Jan 23 09:06:06 2014
@@ -34,26 +34,28 @@ import com.mongodb.MongoException;
 /**
  * Provides a helper class for (de)serializing data to/from MongoDB.
  */
-final class MongoSerializerHelper {
-    
+final class MongoSerializerHelper
+{
+
     static final String TYPE = "type";
     static final String NAME = "name";
-    
+
     static final String PROPERTIES = "properties";
     static final String CREDENTIALS = "credentials";
     static final String MEMBERS = "members";
     static final String REQUIRED_MEMBERS = "requiredMembers";
-    
+
     static final String SET = "$set";
-    
+
     private final RoleProvider m_roleProvider;
-    
+
     /**
      * Creates a new {@link MongoSerializerHelper} instance.
      * 
      * @param roleProvider the role provider to use, cannot be <code>null</code>.
      */
-    public MongoSerializerHelper(RoleProvider roleProvider) {
+    public MongoSerializerHelper(RoleProvider roleProvider)
+    {
         m_roleProvider = roleProvider;
     }
 
@@ -63,29 +65,34 @@ final class MongoSerializerHelper {
      * @param object the {@link DBObject} to convert, cannot be <code>null</code>.
      * @return a {@link Role} instance, never <code>null</code>.
      */
-    public Role deserialize(DBObject object) {
+    public Role deserialize(DBObject object)
+    {
         int type = ((Integer) object.get(TYPE)).intValue();
         String name = (String) object.get(NAME);
 
         Role result = RoleFactory.createRole(type, name);
         // Read the generic properties of the role...
         deserializeDictionary(result.getProperties(), (DBObject) object.get(PROPERTIES));
-        
-        if ((Role.GROUP == type) || (Role.USER == type)) {
+
+        if ((Role.GROUP == type) || (Role.USER == type))
+        {
             // This is safe, as Group extends from User...
             deserializeDictionary(((User) result).getCredentials(), (DBObject) object.get(CREDENTIALS));
 
-            if (Role.GROUP == type) {
-                for (Role member : getRoles((BasicDBList) object.get(MEMBERS))) {
+            if (Role.GROUP == type)
+            {
+                for (Role member : getRoles((BasicDBList) object.get(MEMBERS)))
+                {
                     ((Group) result).addMember(member);
                 }
 
-                for (Role member : getRoles((BasicDBList) object.get(REQUIRED_MEMBERS))) {
+                for (Role member : getRoles((BasicDBList) object.get(REQUIRED_MEMBERS)))
+                {
                     ((Group) result).addRequiredMember(member);
                 }
             }
         }
-        
+
         return result;
     }
 
@@ -95,24 +102,27 @@ final class MongoSerializerHelper {
      * @param role the {@link Role} to serialize, cannot be <code>null</code> (unchecked!).
      * @return a {@link DBObject} representing the given {@link Role}, never <code>null</code>.
      */
-    public DBObject serialize(Role role) {
+    public DBObject serialize(Role role)
+    {
         BasicDBObject data = new BasicDBObject();
-        
+
         int type = role.getType();
-        
+
         data.put(TYPE, type);
         data.put(NAME, role.getName());
 
         data.put(PROPERTIES, serializeDictionary(role.getProperties()));
-        if ((Role.GROUP == type) || (Role.USER == type)) {
+        if ((Role.GROUP == type) || (Role.USER == type))
+        {
             data.put(CREDENTIALS, serializeDictionary(((User) role).getCredentials()));
 
-            if (Role.GROUP == type) {
+            if (Role.GROUP == type)
+            {
                 data.put(MEMBERS, getRoleNames(((Group) role).getMembers()));
                 data.put(REQUIRED_MEMBERS, getRoleNames(((Group) role).getRequiredMembers()));
             }
         }
-        
+
         return data;
     }
 
@@ -123,36 +133,40 @@ final class MongoSerializerHelper {
      * @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) {
+    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.
      * 
      * @param role the {@link Role} to update, cannot be <code>null</code>.
      * @return a {@link DBObject} representing an update statement for the given {@link Role}.
      */
-    public DBObject serializeUpdate(Role role) {
+    public DBObject serializeUpdate(Role role)
+    {
         int type = role.getType();
-        
+
         BasicDBObject changeSet = new BasicDBObject();
-        
+
         changeSet.put(PROPERTIES, serializeDictionary(role.getProperties()));
-        if ((Role.GROUP == type) || (Role.USER == type)) {
+        if ((Role.GROUP == type) || (Role.USER == type))
+        {
             changeSet.put(CREDENTIALS, serializeDictionary(((User) role).getCredentials()));
 
-            if (Role.GROUP == type) {
+            if (Role.GROUP == type)
+            {
                 changeSet.put(MEMBERS, getRoleNames(((Group) role).getMembers()));
                 changeSet.put(REQUIRED_MEMBERS, getRoleNames(((Group) role).getRequiredMembers()));
             }
         }
-        
+
         return new BasicDBObject(SET, changeSet);
     }
 
@@ -163,9 +177,11 @@ final class MongoSerializerHelper {
      * @return a member instance, never <code>null</code>.
      * @throws MongoException in case the requested member was not found (or any other MongoDB exception).
      */
-    final Role findExistingMember(String name) {
+    final Role findExistingMember(String name)
+    {
         Role result = m_roleProvider.getRole(name);
-        if (result == null) {
+        if (result == null)
+        {
             throw new MongoException("No such role: " + name);
         }
         return result;
@@ -174,40 +190,53 @@ final class MongoSerializerHelper {
     /**
      * Deserializes the given {@link DBObject} into the given {@link Dictionary}.
      * 
-     * @param dictionary the dictionary to fill;
-     * @param object the {@link DBObject} to deserialize.
+     * @param dictionary the dictionary to fill, cannot be <code>null</code>;
+     * @param object the {@link DBObject} to deserialize, can be <code>null</code>.
      */
-    private void deserializeDictionary(Dictionary dictionary, DBObject object) {
-        for (String key : object.keySet()) {
-            dictionary.put(KeyCodec.decode(key), object.get(key));
+    private void deserializeDictionary(Dictionary dictionary, DBObject object)
+    {
+        // FELIX-4399: MongoDB does return null for empty properties...
+        if (object != null)
+        {
+            for (String key : object.keySet())
+            {
+                dictionary.put(KeyCodec.decode(key), object.get(key));
+            }
         }
     }
-    
+
     /**
      * Serializes a given array of {@link Role}s to an list for storing in a {@link DBObject}.
      * 
      * @param members the {@link Role}s to serialize, cannot be <code>null</code>.
      * @return the "serialized" array, never <code>null</code>.
      */
-    private List<String> getRoleNames(Role[] members) {
+    private List<String> getRoleNames(Role[] members)
+    {
         List<String> result = new ArrayList<String>();
-        if (members != null) {
-            for (Role member : members) {
+        if (members != null)
+        {
+            for (Role member : members)
+            {
                 result.add(member.getName());
             }
         }
         return result;
     }
-    
+
     /**
      * Returns all roles mentioned in the given list.
      * 
-     * @param list the list with role names to convert.
+     * @param list the list with role names to convert, can be <code>null</code>.
      * @return a list with {@link Role}s, never <code>null</code>.
      */
-    private List<Role> getRoles(BasicDBList list) {
+    private List<Role> getRoles(BasicDBList list)
+    {
         List<Role> result = new ArrayList<Role>();
-        for (int i = 0, size = list.size(); i < size; i++) {
+        // FELIX-4399: MongoDB does return null for empty properties...
+        int size = (list == null) ? 0 : list.size();
+        for (int i = 0; i < size; i++)
+        {
             final String memberName = (String) list.get(i);
             result.add(findExistingMember(memberName));
         }
@@ -220,17 +249,19 @@ final class MongoSerializerHelper {
      * @param properties the {@link Dictionary} to serialize, cannot be <code>null</code>.
      * @return the serialized dictionary, never <code>null</code>. 
      */
-    private DBObject serializeDictionary(Dictionary properties) {
+    private DBObject serializeDictionary(Dictionary properties)
+    {
         BasicDBObject result = new BasicDBObject();
-        
+
         Enumeration<String> keysEnum = properties.keys();
-        while (keysEnum.hasMoreElements()) {
+        while (keysEnum.hasMoreElements())
+        {
             String key = keysEnum.nextElement();
             Object value = properties.get(key);
-            
+
             result.append(KeyCodec.encode(key), value);
         }
-        
+
         return result;
     }
 }