diff --git a/useradmin/itest/pom.xml b/useradmin/itest/pom.xml index 88adb09dde1..fbfdf53b1b0 100644 --- a/useradmin/itest/pom.xml +++ b/useradmin/itest/pom.xml @@ -39,12 +39,6 @@ jar Integration tests for the UserAdmin OSGi compendium service. - - org.osgi - org.osgi.core - ${osgi.version} - provided - org.osgi org.osgi.compendium @@ -62,6 +56,18 @@ 1.0.3-SNAPSHOT test + + org.apache.felix + org.apache.felix.useradmin.mongodb + 1.0.2-SNAPSHOT + test + + + org.mongodb + mongo-java-driver + 2.8.0 + test + org.apache.felix org.apache.felix.dependencymanager 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 4f6746f0f61..feb6995291d 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 @@ -46,22 +46,26 @@ * * @author Felix Project Team */ -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 Option[] config() { 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 void setUp() throws Exception { * @return * @throws Exception */ - protected T awaitService(String serviceName) throws Exception { + protected 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 getService(String serviceName) throws Exception { + protected 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; - } } 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 0b712b818f5..fd854e0970f 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 @@ -36,51 +36,54 @@ * @author Felix Project Team */ @RunWith(JUnit4TestRunner.class) -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)); - - fileStoreBundle.start(); - - awaitService(ORG_APACHE_FELIX_USERADMIN_FILESTORE); - +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(); + // Start the file store bundle... + Bundle fileStoreBundle = getFileStoreBundle(); + 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); + // 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 void testStoreIsInitializedAndClosedProperlyOk() throws Exception { assertNotNull(members); assertEquals(1, members.length); assertEquals(Role.USER_ANYONE, members[0].getName()); - + user = (User) ua.getRole("user2"); assertNull(user); - } + } } diff --git a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java new file mode 100644 index 00000000000..371d0960860 --- /dev/null +++ b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/MongoDBStoreTest.java @@ -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 Felix Project Team + */ +@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; + } +} diff --git a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java index 4dd5db429ae..c09916e8bd9 100644 --- a/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java +++ b/useradmin/itest/src/test/java/org/apache/felix/useradmin/itest/UserAdminIntegrationTest.java @@ -35,108 +35,112 @@ * @author Felix Project Team */ @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 after 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 after 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]); + } } 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 1e66bffb6f2..fd80e86e692 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 @@ -147,7 +147,8 @@ public Role addRole(String roleName, int type) throws MongoException { result.getLastError().throwOnError(); } - return role; + // FELIX-4400: ensure we return the correct role... + return getRole(roleName); } /** 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 48e48d837b2..06a80052edc 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 @@ -34,26 +34,28 @@ /** * 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 null. */ - public MongoSerializerHelper(RoleProvider roleProvider) { + public MongoSerializerHelper(RoleProvider roleProvider) + { m_roleProvider = roleProvider; } @@ -63,29 +65,34 @@ public MongoSerializerHelper(RoleProvider roleProvider) { * @param object the {@link DBObject} to convert, cannot be null. * @return a {@link Role} instance, never null. */ - 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 @@ public Role deserialize(DBObject object) { * @param role the {@link Role} to serialize, cannot be null (unchecked!). * @return a {@link DBObject} representing the given {@link Role}, never null. */ - 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 @@ public DBObject serialize(Role role) { * @param type the type of the role to serialize. * @return a {@link DBObject} representing the role with the given name and type, never null. */ - 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 null. * @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 @@ public DBObject serializeUpdate(Role role) { * @return a member instance, never null. * @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 Role findExistingMember(String name) { /** * 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 null; + * @param object the {@link DBObject} to deserialize, can be null. */ - 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 null. * @return the "serialized" array, never null. */ - private List getRoleNames(Role[] members) { + private List getRoleNames(Role[] members) + { List result = new ArrayList(); - 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 null. * @return a list with {@link Role}s, never null. */ - private List getRoles(BasicDBList list) { + private List getRoles(BasicDBList list) + { List result = new ArrayList(); - 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 @@ private List getRoles(BasicDBList list) { * @param properties the {@link Dictionary} to serialize, cannot be null. * @return the serialized dictionary, never null. */ - private DBObject serializeDictionary(Dictionary properties) { + private DBObject serializeDictionary(Dictionary properties) + { BasicDBObject result = new BasicDBObject(); - + Enumeration 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; } }