Author: bdekruijff at gmail.com
Date: Tue Nov 23 18:00:06 2010
New Revision: 435

Log:
AMDATU-177 Initial commit of filebased useradminstore. Needs testing and 
integration

Added:
   
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.useradminstore-fs.cfg
   trunk/amdatu-core/useradminstore-fs/   (props changed)
   trunk/amdatu-core/useradminstore-fs/pom.xml
   trunk/amdatu-core/useradminstore-fs/src/
   trunk/amdatu-core/useradminstore-fs/src/main/
   trunk/amdatu-core/useradminstore-fs/src/main/java/
   trunk/amdatu-core/useradminstore-fs/src/main/java/org/
   trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/
   trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSGroup.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRole.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleNameList.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleStorage.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUser.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUtil.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivator.java
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/
   
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProvider.java
   trunk/amdatu-core/useradminstore-fs/src/test/
   trunk/amdatu-core/useradminstore-fs/src/test/java/
   trunk/amdatu-core/useradminstore-fs/src/test/java/org/
   trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/
   trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/osgi/
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivatorTest.java
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProviderTest.java
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockGroup.java
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockRole.java
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockUser.java
   
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockUserAdminFactory.java
Modified:
   trunk/amdatu-core/pom.xml

Added: 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.useradminstore-fs.cfg
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.useradminstore-fs.cfg
    Tue Nov 23 18:00:06 2010
@@ -0,0 +1,2 @@
+# PAX UserAdmin FS datadirectory
+datadir=work/useradminstore-fs
\ No newline at end of file

Modified: trunk/amdatu-core/pom.xml
==============================================================================
--- trunk/amdatu-core/pom.xml   (original)
+++ trunk/amdatu-core/pom.xml   Tue Nov 23 18:00:06 2010
@@ -64,6 +64,7 @@
     <module>loghandler</module>
     <module>tenant</module>
     <module>tenantstore-fs</module>
+    <module>useradminstore-fs</module>
   </modules>
 
 </project>
\ No newline at end of file

Added: trunk/amdatu-core/useradminstore-fs/pom.xml
==============================================================================
--- (empty file)
+++ trunk/amdatu-core/useradminstore-fs/pom.xml Tue Nov 23 18:00:06 2010
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu</groupId>
+    <artifactId>org.amdatu.core</artifactId>
+    <version>0.0.6-SNAPSHOT</version>
+  </parent>
+  <groupId>org.amdatu.core</groupId>
+  <artifactId>useradminstore-fs</artifactId>
+  <packaging>bundle</packaging>
+  <name>Amdatu Core - Useradmin filesystem storage</name>
+  <description>This bundle provides filebased Useradmin storage</description>
+
+  <dependencies>
+      <dependency>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>org.apache.felix.framework</artifactId>
+        <version>${org.apache.felix.main.version}</version>
+        <scope>provided</scope>
+      </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.useradmin</groupId>
+      <artifactId>pax-useradmin-service</artifactId>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            
<Bundle-Activator>org.amdatu.core.useradminstore.fs.osgi.FSUserAdminStorageProviderActivator</Bundle-Activator>
+            
<Bundle-SymbolicName>org.amdatu.core.useradminstore-fs</Bundle-SymbolicName>
+          </instructions>
+        </configuration>
+      </plugin>
+
+    </plugins>
+  </build>
+</project>

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSGroup.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSGroup.java
   Tue Nov 23 18:00:06 2010
@@ -0,0 +1,130 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.internal;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+
+public class FSGroup extends FSUser implements Group {
+
+    protected Map<String, Role> m_members;
+    protected Map<String, Role> m_requiredMembers;
+
+    public FSGroup(final Group group) {
+        super(group);
+        m_type = Role.GROUP;
+        Role[] members = group.getMembers();
+        if (members != null && members.length > 0 && m_members == null) {
+            m_members = new HashMap<String, Role>();
+        }
+        for (Role member : members) {
+            m_members.put(member.getName(), member);
+        }
+        Role[] requiredMembers = group.getRequiredMembers();
+        if (requiredMembers != null && requiredMembers.length > 0 && 
m_requiredMembers == null) {
+            m_requiredMembers = new HashMap<String, Role>();
+        }
+        for (Role member : requiredMembers) {
+            m_requiredMembers.put(member.getName(), member);
+        }
+    }
+
+    public FSGroup(final String name, final Dictionary properties, final 
Dictionary credentials) {
+        super(name, properties, credentials);
+        m_type = Role.GROUP;
+    }
+
+    public boolean addMember(Role role) {
+        if (m_members == null) {
+            m_members = new HashMap<String, Role>();
+        }
+        if (m_members.containsKey(role.getName())) {
+            return false;
+        }
+        m_members.put(role.getName(), role);
+        return true;
+    }
+
+    public boolean addRequiredMember(Role role) {
+        if (m_requiredMembers == null) {
+            m_requiredMembers = new HashMap<String, Role>();
+        }
+        if (m_requiredMembers.containsKey(role.getName())) {
+            return false;
+        }
+        m_requiredMembers.put(role.getName(), role);
+        return true;
+    }
+
+    public Role[] getMembers() {
+        if (m_members == null) {
+            return new Role[] {};
+        }
+        return m_members.values().toArray(new Role[m_members.size()]);
+    }
+
+    public void setMembers(final List<FSRole> members) {
+        if (members == null) {
+            m_members = null;
+        }
+        else {
+            m_members = new HashMap<String, Role>();
+            for (FSRole role : members) {
+                m_members.put(role.getName(), role);
+            }
+        }
+    }
+
+    public Role[] getRequiredMembers() {
+        if (m_requiredMembers == null) {
+            return new Role[] {};
+        }
+        return m_requiredMembers.values().toArray(new 
Role[m_requiredMembers.size()]);
+    }
+
+    public void setRequiredMembers(final List<FSRole> requiredMembers) {
+        if (requiredMembers == null) {
+            m_requiredMembers = null;
+        }
+        else {
+            m_requiredMembers = new HashMap<String, Role>();
+            for (FSRole role : requiredMembers) {
+                m_requiredMembers.put(role.getName(), role);
+            }
+        }
+    }
+
+    public boolean removeMember(final Role role) {
+        boolean removed = false;
+        if (role != null) {
+            if (m_members != null && m_members.containsKey(role.getName())) {
+                m_members.remove(role.getName());
+                removed = true;
+            }
+            if (m_requiredMembers != null && 
m_requiredMembers.containsKey(role.getName())) {
+                m_requiredMembers.remove(role.getName());
+                removed = true;
+            }
+        }
+        return removed;
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRole.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRole.java
    Tue Nov 23 18:00:06 2010
@@ -0,0 +1,77 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.service.useradmin.Role;
+
+public abstract class FSRole implements Role {
+
+    protected int m_type;
+    protected String m_name;
+    protected Dictionary m_properties;
+
+    protected FSRoleStorage m_internalRoleFile;
+
+    public String getName() {
+        return m_name;
+    }
+
+    public Dictionary getProperties() {
+        return m_properties;
+    }
+
+    public int getType() {
+        return m_type;
+    }
+
+    public void setProperties(Dictionary dictionary) {
+        m_properties = dictionary;
+    }
+
+    public Object getProperty(final String key) {
+        if (m_properties != null) {
+            return m_properties.get(key);
+        }
+        return null;
+    }
+
+    public void setProperty(String key, Object value) {
+        if (m_properties == null) {
+            m_properties = new Hashtable();
+        }
+
+        m_properties.put(key, value);
+    }
+
+    public Object removeProperty(final String key) {
+        if (m_properties != null) {
+            return m_properties.remove(key);
+        }
+        return null;
+    }
+
+    public FSRoleStorage getInternalRoleFile() {
+        return m_internalRoleFile;
+    }
+
+    public void setInternalRoleFile(final FSRoleStorage internalRoleFile) {
+        m_internalRoleFile = internalRoleFile;
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleNameList.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleNameList.java
    Tue Nov 23 18:00:06 2010
@@ -0,0 +1,118 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.ops4j.pax.useradmin.service.spi.StorageException;
+
+/**
+ * Implementation of a persistent list of tenant identifiers on disk.
+ */
+public final class FSRoleNameList {
+
+    private final File m_file;
+    private List<String> m_roleNameList;
+
+    public FSRoleNameList(final File file) throws StorageException {
+        m_file = file;
+        m_roleNameList = new LinkedList<String>();
+        try {
+            readRoleNameList();
+        }
+        catch (IOException e) {
+            throw new StorageException(e.getMessage());
+        }
+    }
+
+    public synchronized List<String> getAll() throws StorageException {
+        return new LinkedList<String>(m_roleNameList);
+    }
+
+    public synchronized void addRoleName(final String roleName) throws 
StorageException {
+        try {
+            if (!m_roleNameList.contains(roleName)) {
+                m_roleNameList.add(roleName);
+                writeRoleNameList();
+            }
+        }
+        catch (IOException e) {
+            throw new StorageException(e.getMessage());
+        }
+    }
+
+    public synchronized void removeTenantId(final String roleName) throws 
StorageException {
+        try {
+            if (m_roleNameList.contains(roleName)) {
+                m_roleNameList.remove(roleName);
+                writeRoleNameList();
+            }
+        }
+        catch (IOException e) {
+            throw new StorageException(e.getMessage());
+        }
+    }
+
+    private void readRoleNameList() throws IOException {
+        if (!m_file.exists()) {
+            m_roleNameList.clear();
+            return;
+        }
+        FileInputStream fis = null;
+        ObjectInputStream ois = null;
+        try {
+            fis = new FileInputStream(m_file);
+            ois = new ObjectInputStream(fis);
+            final int numberOfTenantEntityIds = ois.readInt();
+            for (int i = 0; i < numberOfTenantEntityIds; i++) {
+                final String id = FSUtil.readString(ois);
+                m_roleNameList.add(id);
+            }
+        }
+        finally {
+            FSUtil.closeInputStreamsSafely(ois, fis);
+        }
+    }
+
+    private void writeRoleNameList() throws IOException {
+        if (m_roleNameList.size() == 0 && m_file.exists()) {
+            m_file.delete();
+            return;
+        }
+        FileOutputStream fos = null;
+        ObjectOutputStream oos = null;
+        try {
+            fos = new FileOutputStream(m_file);
+            oos = new ObjectOutputStream(fos);
+            oos.writeInt(m_roleNameList.size());
+            for (final String tenantId : m_roleNameList) {
+                FSUtil.writeString(oos, tenantId);
+            }
+            oos.flush();
+        }
+        finally {
+            FSUtil.closeOutputStreamsSafely(oos, fos);
+        }
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleStorage.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleStorage.java
     Tue Nov 23 18:00:06 2010
@@ -0,0 +1,228 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.ops4j.pax.useradmin.service.spi.StorageException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+public final class FSRoleStorage {
+
+    private final File m_file;
+    private final Map<String, FSRole> m_roles;
+
+    public FSRoleStorage(final File file)
+        throws StorageException {
+        m_file = file;
+        m_roles = new HashMap<String, FSRole>();
+        try {
+            readRoles();
+        }
+        catch (IOException e) {
+            throw new StorageException(e.getMessage());
+        }
+    }
+
+    public void save() throws StorageException {
+        try {
+            writeRoles();
+        }
+        catch (IOException e) {
+            throw new StorageException(e.getMessage());
+        }
+    }
+
+    public FSRole getRole(final String roleName) {
+        return m_roles.get(roleName);
+    }
+
+    public FSRole addRole(final FSRole role) {
+        return m_roles.put(role.getName(), role);
+    }
+
+    public FSRole removeRole(final FSRole role) {
+        return m_roles.remove(role.getName());
+    }
+
+    private void readRoles() throws IOException {
+        if (m_file.exists()) {
+            FileInputStream fis = null;
+            ObjectInputStream ois = null;
+            try {
+                fis = new FileInputStream(m_file);
+                ois = new ObjectInputStream(fis);
+                final int numberOfUsers = ois.readInt();
+                for (int i = 0; i < numberOfUsers; i++) {
+                    FSRole role = readRole(ois);
+                    role.setInternalRoleFile(this);
+                    m_roles.put(role.getName(), role);
+                }
+            }
+            finally {
+                FSUtil.closeInputStreamsSafely(ois, fis);
+            }
+        }
+    }
+
+    private FSRole readRole(final ObjectInputStream ois) throws IOException,
+        UnsupportedEncodingException {
+
+        final int roleType = ois.readInt();
+        final String name = FSUtil.readString(ois);
+        final Dictionary properties = readDictionary(ois);
+        final Dictionary credentials = readDictionary(ois);
+
+        if (roleType == Role.USER) {
+            return new FSUser(name, properties, credentials);
+        }
+        else if (roleType == Role.GROUP) {
+            FSGroup group = new FSGroup(name, properties, credentials);
+            group.setMembers(readMembers(ois));
+            group.setRequiredMembers(readMembers(ois));
+            return group;
+        }
+        else {
+            throw new IllegalStateException("Deserialization error: illegal 
roletype " + roleType);
+        }
+    }
+
+    private List<FSRole> readMembers(final ObjectInputStream ois) throws 
IOException {
+        final int numberOfMembers = ois.readInt();
+        if (numberOfMembers == 0) {
+            return null;
+        }
+        final List<FSRole> members = new LinkedList<FSRole>();
+        for (int i = 0; i < numberOfMembers; i++) {
+            final int memberType = ois.readInt();
+            if (memberType == Role.USER) {
+                members.add(new FSUser(FSUtil.readString(ois), null, null));
+            }
+            else if (memberType == Role.GROUP) {
+                members.add(new FSGroup(FSUtil.readString(ois), null, null));
+            }
+            else {
+                throw new IllegalStateException("Deserialization error: 
illegal membertype " + memberType);
+            }
+        }
+        return members;
+    }
+
+    private Dictionary readDictionary(final ObjectInputStream ois) throws 
IOException {
+        final int numberOfEntries = ois.readInt();
+        if (numberOfEntries == 0) {
+            return null;
+        }
+        final Dictionary dictionary = new Hashtable();
+        for (int j = 0; j < numberOfEntries; j++) {
+            final String key = FSUtil.readString(ois);
+            final int type = ois.readInt();
+            switch (type) {
+                case 0:
+                    byte[] byteValue = FSUtil.readBytes(ois);
+                    dictionary.put(key, byteValue);
+                    break;
+                case 1:
+                    String stringValue = FSUtil.readString(ois);
+                    dictionary.put(key, stringValue);
+                    break;
+                default:
+                    break;
+            }
+        }
+        return dictionary;
+    }
+
+    private void writeRoles() throws IOException {
+        FileOutputStream fos = null;
+        ObjectOutputStream oos = null;
+        try {
+            fos = new FileOutputStream(m_file);
+            oos = new ObjectOutputStream(fos);
+            oos.writeInt(m_roles.size());
+            for (FSRole role : m_roles.values()) {
+                writeRole(oos, role);
+            }
+            oos.flush();
+        }
+        finally {
+            FSUtil.closeOutputStreamsSafely(oos, fos);
+        }
+    }
+
+    private void writeRole(ObjectOutputStream oos, FSRole role) throws 
UnsupportedEncodingException,
+        IOException {
+        oos.writeInt(role.getType());
+        FSUtil.writeString(oos, role.getName());
+        writeDictionary(oos, role.getProperties());
+        writeDictionary(oos, ((User) role).getCredentials());
+        if (role.getType() == Role.GROUP) {
+            writeMembers(oos, ((FSGroup) role).getMembers());
+            writeMembers(oos, ((FSGroup) role).getRequiredMembers());
+        }
+    }
+
+    private void writeMembers(final ObjectOutputStream oos, final Role[] 
members) throws IOException {
+        if (members == null) {
+            oos.writeInt(0);
+        }
+        else {
+            oos.writeInt(members.length);
+            for (Role member : members) {
+                oos.writeInt(member.getType());
+                FSUtil.writeString(oos, member.getName());
+            }
+        }
+    }
+
+    private void writeDictionary(final ObjectOutputStream oos, final 
Dictionary dictionary) throws IOException {
+        if (dictionary == null) {
+            oos.writeInt(0);
+        }
+        else {
+            oos.writeInt(dictionary.size());
+            Enumeration keys = dictionary.keys();
+            while (keys.hasMoreElements()) {
+                String key = (String) keys.nextElement();
+                FSUtil.writeString(oos, key);
+                Object value = dictionary.get(key);
+                if (value instanceof byte[]) {
+                    oos.writeInt(0);
+                    FSUtil.writeBytes(oos, (byte[]) value);
+                }
+                else if (value instanceof String) {
+                    oos.writeInt(1);
+                    FSUtil.writeString(oos, (String) value);
+                }
+            }
+        }
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUser.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUser.java
    Tue Nov 23 18:00:06 2010
@@ -0,0 +1,79 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+public class FSUser extends FSRole implements User {
+
+    private Dictionary m_credentials;
+
+    public FSUser(final User user) {
+        m_type = Role.USER;
+        m_name = user.getName();
+        m_properties = user.getProperties();
+        m_credentials = user.getCredentials();
+    }
+
+    public FSUser(final String name, final Dictionary properties, final 
Dictionary credentials) {
+        m_type = Role.USER;
+        m_name = name;
+        m_properties = properties;
+        m_credentials = credentials;
+    }
+
+    public Object getcredential(final String key) {
+        if (m_credentials != null) {
+            return m_credentials.get(key);
+        }
+        return null;
+    }
+
+    public void setCredential(final String key, final Object value) {
+        if (m_credentials == null) {
+            m_credentials = new Hashtable();
+        }
+        m_credentials.put(key, value);
+    }
+
+    public Object removeCredential(final String key) {
+        if (m_credentials != null) {
+            return m_credentials.remove(key);
+        }
+        return null;
+    }
+
+    public Dictionary getCredentials() {
+        return m_credentials;
+    }
+
+    public void setCredentials(Dictionary credentials) {
+        m_credentials = credentials;
+    }
+
+    public boolean hasCredential(String key, Object value) {
+        Object localValue = m_credentials.get(key);
+        if (localValue == null) {
+            return false;
+        }
+        return localValue.equals(value);
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUtil.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUtil.java
    Tue Nov 23 18:00:06 2010
@@ -0,0 +1,116 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.internal;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Some generic utility methods.
+ */
+public final class FSUtil {
+
+    public static byte[] readBytes(final ObjectInputStream ois) throws 
IOException {
+        final int bytesLength = ois.readInt();
+        final byte[] bytes = new byte[bytesLength];
+        ois.readFully(bytes);
+        return bytes;
+    }
+
+    public static String readString(final ObjectInputStream ois) throws 
IOException {
+        final int keyLength = ois.readInt();
+        final byte[] keyBytes = new byte[keyLength];
+        ois.readFully(keyBytes);
+        return new String(keyBytes, "utf-8");
+    }
+
+    public static Map<String, String> readProperties(final ObjectInputStream 
ois) throws IOException {
+        final int numberOfProperties = ois.readInt();
+        if (numberOfProperties == 0) {
+            return null;
+        }
+        final Map<String, String> properties = new HashMap<String, String>();
+        for (int i = 0; i < numberOfProperties; i++) {
+            final String key = readString(ois);
+            final String value = readString(ois);
+            properties.put(key, value);
+        }
+        return properties;
+    }
+
+    public static void writeBytes(final ObjectOutputStream oos, final byte[] 
bytes) throws IOException {
+        oos.writeInt(bytes.length);
+        oos.write(bytes);
+    }
+
+    public static void writeString(final ObjectOutputStream oos, final String 
value) throws IOException {
+        String mvalue = value;
+        if (mvalue == null) {
+            mvalue = "";
+        }
+        final byte[] bytes = mvalue.getBytes("utf-8");
+        oos.writeInt(bytes.length);
+        oos.write(bytes);
+    }
+
+    public static void writeProperties(final ObjectOutputStream oos, final 
Map<String, String> properties)
+        throws IOException {
+        if (properties == null) {
+            oos.writeInt(0);
+            return;
+        }
+        oos.writeInt(properties.size());
+        for (final String key : properties.keySet()) {
+            writeString(oos, key);
+            writeString(oos, properties.get(key));
+        }
+    }
+
+    public static void closeInputStreamsSafely(ObjectInputStream ois, 
FileInputStream fis) throws IOException {
+        try {
+            if (ois != null) {
+                ois.close();
+            }
+        }
+        catch (IOException e) {
+            if (fis != null) {
+                fis.close();
+            }
+            throw e;
+        }
+    }
+
+    public static void closeOutputStreamsSafely(ObjectOutputStream oos, 
FileOutputStream fos) throws IOException {
+        try {
+            if (oos != null) {
+                oos.close();
+            }
+        }
+        catch (IOException e) {
+            if (fos != null) {
+                fos.close();
+            }
+            throw e;
+        }
+    }
+
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivator.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivator.java
   Tue Nov 23 18:00:06 2010
@@ -0,0 +1,47 @@
+/*
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.osgi;
+
+import org.amdatu.core.useradminstore.fs.service.FSUserAdminStorageProvider;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.ops4j.pax.useradmin.service.spi.StorageProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class represents the OSGi activator for the tenant service
+ * 
+ */
+public class FSUserAdminStorageProviderActivator extends 
DependencyActivatorBase {
+
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws 
Exception {
+
+        manager.add(
+                createComponent()
+                    .setImplementation(new FSUserAdminStorageProvider())
+                    .setInterface(StorageProvider.class.getName(), null)
+                    
.add(createConfigurationDependency().setPid(FSUserAdminStorageProvider.CONFIGURATION_PID))
+                    
.add(createServiceDependency().setService(LogService.class).setRequired(false)));
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) 
throws Exception {
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProvider.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProvider.java
 Tue Nov 23 18:00:06 2010
@@ -0,0 +1,403 @@
+package org.amdatu.core.useradminstore.fs.service;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.amdatu.core.useradminstore.fs.internal.FSGroup;
+import org.amdatu.core.useradminstore.fs.internal.FSRole;
+import org.amdatu.core.useradminstore.fs.internal.FSRoleNameList;
+import org.amdatu.core.useradminstore.fs.internal.FSRoleStorage;
+import org.amdatu.core.useradminstore.fs.internal.FSUser;
+import org.ops4j.pax.useradmin.service.spi.StorageException;
+import org.ops4j.pax.useradmin.service.spi.StorageProvider;
+import org.ops4j.pax.useradmin.service.spi.UserAdminFactory;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Filesystem backed implementation of the PAX <code>StorageProvider</code> 
service interface.
+ */
+public final class FSUserAdminStorageProvider implements StorageProvider {
+
+    // The PID and configuration properties
+    public static final String CONFIGURATION_PID = 
"org.amdatu.core.useradminstore-fs";
+    public final static String DATA_DIRECTORY = "datadir";
+
+    // File naming constants
+    private static final String ENTITYLIST_FILENAME = "roleNameList.ser";
+    private static final String STORAGEFILE_PREFIX = "t_";
+    private static final String STORAGEFILE_POSTFIX = ".ser";
+
+    // DM injected (option)
+    private volatile LogService m_logService;
+
+    // Storage directory
+    private File m_dataDirectory;
+
+    // Collaborator
+    private FSRoleNameList m_roleNamelist;
+
+    /*
+     * Constructors
+     */
+
+    public FSUserAdminStorageProvider() throws StorageException {
+    }
+
+    /*
+     * Accessor methods
+     */
+
+    public synchronized File getDataDirectory() {
+        return m_dataDirectory;
+    }
+
+    public synchronized void setDataDirectory(File dataDirectory) throws 
StorageException {
+        if (!dataDirectory.isAbsolute()) {
+            File userDirectory = new File(System.getProperty("user.dir"));
+            dataDirectory = new File(userDirectory, dataDirectory.getPath());
+        }
+        if (!((dataDirectory.exists() && dataDirectory.canRead() && 
dataDirectory.canWrite()) || dataDirectory
+            .mkdirs())) {
+            throw new StorageException("Unable to access data directory: "
+                + dataDirectory.getAbsolutePath());
+        }
+        m_dataDirectory = dataDirectory;
+        m_roleNamelist = new FSRoleNameList(new File(m_dataDirectory, 
ENTITYLIST_FILENAME));
+    }
+
+    /*
+     * DM Service lifecycle
+     */
+
+    public synchronized void start() throws StorageException {
+        // by contract DM ConfigurationDependency contract dataDirectory has
+        // been set through the updated method.
+    }
+
+    public synchronized void stop() {
+        m_roleNamelist = null;
+    }
+
+    /*
+     * DM ManagedService
+     */
+
+    public synchronized void updated(Dictionary dictionary) throws 
ConfigurationException {
+        if (dictionary != null) {
+            String dataDirectoryName = (String) dictionary.get(DATA_DIRECTORY);
+            if (dataDirectoryName == null || "".equals(dataDirectoryName)) {
+                throw new ConfigurationException(DATA_DIRECTORY, "Missing 
mandatory data directory configuration");
+            }
+            File dataDirectory = new File(dataDirectoryName);
+            try {
+                setDataDirectory(dataDirectory);
+            }
+            catch (StorageException e) {
+                throw new ConfigurationException(DATA_DIRECTORY, 
e.getMessage());
+            }
+        }
+    }
+
+    /*
+     * PAX StorageProvider API
+     */
+
+    public synchronized boolean addMember(final Group group, final Role role) 
throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(group.getName());
+        final FSRole internalRole = internalRoleFile.getRole(group.getName());
+        if (internalRole != null && internalRole.getType() == Role.GROUP) {
+            ((FSGroup) internalRole).addMember(role);
+            internalRoleFile.save();
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized boolean addRequiredMember(Group group, Role role) 
throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(group.getName());
+        final FSRole internalRole = internalRoleFile.getRole(group.getName());
+        if (internalRole != null && internalRole.getType() == Role.GROUP) {
+            ((FSGroup) internalRole).addRequiredMember(role);
+            internalRoleFile.save();
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized void clearRoleAttributes(final Role role) throws 
StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(role.getName());
+        final FSRole internalRole = internalRoleFile.getRole(role.getName());
+        if (internalRole != null && internalRole.getProperties() != null) {
+            internalRole.setProperties(null);
+            internalRoleFile.save();
+        }
+    }
+
+    public synchronized void clearUserCredentials(final User user) throws 
StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(user.getName());
+        final FSRole internalRole = internalRoleFile.getRole(user.getName());
+        if (internalRole != null && internalRole.getType() == Role.USER
+            && ((FSUser) internalRole).getCredentials() != null) {
+            ((FSUser) internalRole).setCredentials(null);
+            internalRoleFile.save();
+        }
+    }
+
+    public synchronized Group createGroup(final UserAdminFactory 
userAdminFactory, final String groupName)
+        throws StorageException {
+        // FIXME guard against overwrite?
+        final Group group = userAdminFactory.createGroup(groupName, null, 
null);
+        final FSRoleStorage internalRoleFile = getStorageFile(group.getName());
+        final FSRole storedRole = internalRoleFile.addRole(new FSGroup(group));
+        if (storedRole == null) {
+            m_roleNamelist.addRoleName(group.getName());
+        }
+        internalRoleFile.save();
+        return group;
+    }
+
+    public synchronized User createUser(final UserAdminFactory 
userAdminFactory, final String userName)
+        throws StorageException {
+        // FIXME guard against overwrite?
+        final User user = userAdminFactory.createUser(userName, null, null);
+        final FSRoleStorage internalRoleFile = getStorageFile(user.getName());
+        final FSRole storedRole = internalRoleFile.addRole(new FSUser(user));
+        if (storedRole == null) {
+            m_roleNamelist.addRoleName(user.getName());
+        }
+        internalRoleFile.save();
+        return user;
+    }
+
+    public boolean deleteRole(Role role) throws StorageException {
+        // FIXME ugly construct
+        final FSRoleStorage internalRoleFile = getStorageFile(role.getName());
+        final FSRole internalRole = internalRoleFile.getRole(role.getName());
+        if (internalRole != null) {
+            if (internalRole.getType() == role.USER) {
+                internalRoleFile.removeRole(new FSUser((User) role));
+            }
+            else if (internalRole.getType() == role.GROUP) {
+                internalRoleFile.removeRole(new FSGroup((Group) role));
+            }
+            m_roleNamelist.removeTenantId(role.getName());
+            internalRoleFile.save();
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized Collection<Role> findRoles(final UserAdminFactory 
userAdminFactory, final String filterString)
+        throws StorageException {
+        List<Role> matchingRoles = new LinkedList<Role>();
+        try {
+            Filter filter = FrameworkUtil.createFilter(filterString);
+            List<String> allRoleNames = m_roleNamelist.getAll();
+            for (String roleName : allRoleNames) {
+                Role role = getRole(userAdminFactory, roleName);
+                if (filter.match(role.getProperties())) {
+                    matchingRoles.add(role);
+                }
+            }
+        }
+        catch (InvalidSyntaxException e) {
+            throw new StorageException(e.getMessage());
+        }
+        return matchingRoles;
+    }
+
+    public synchronized Collection<Role> getMembers(final UserAdminFactory 
userAdminFactory, final Group group)
+        throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(group.getName());
+        final FSRole internalRole = internalRoleFile.getRole(group.getName());
+        if (internalRole != null && internalRole.getType() == Role.GROUP) {
+            Set<Role> members = new HashSet<Role>();
+            for (Role role : ((FSGroup) internalRole).getMembers()) {
+                if (role.getType() == Role.USER) {
+                    User newuser =
+                            userAdminFactory.createUser(role.getName(), 
dictionaryToMap(role.getProperties()),
+                                dictionaryToMap(((User) 
role).getCredentials()));
+                    members.add(newuser);
+                }
+                else if (role.getType() == Role.GROUP) {
+                    Group newgroup =
+                            userAdminFactory.createGroup(role.getName(), 
dictionaryToMap(role.getProperties()),
+                                dictionaryToMap(((Group) 
role).getCredentials()));
+                    members.add(newgroup);
+                }
+            }
+            return members;
+        }
+        return null;
+    }
+
+    public synchronized Collection<Role> getRequiredMembers(final 
UserAdminFactory userAdminFactory, final Group group)
+        throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(group.getName());
+        final FSRole internalRole = internalRoleFile.getRole(group.getName());
+        Set<Role> members = new HashSet<Role>();
+        if (internalRole != null && internalRole.getType() == Role.GROUP) {
+            for (Role role : ((FSGroup) internalRole).getRequiredMembers()) {
+                if (role.getType() == Role.USER) {
+                    User newuser =
+                            userAdminFactory.createUser(role.getName(), 
dictionaryToMap(role.getProperties()),
+                                dictionaryToMap(((User) 
role).getCredentials()));
+                    members.add(newuser);
+                }
+                else if (role.getType() == Role.GROUP) {
+
+                    Group newgroup = 
userAdminFactory.createGroup(internalRole.getName(),
+                        dictionaryToMap(internalRole.getProperties()),
+                        dictionaryToMap(((Group) 
internalRole).getCredentials()));
+                    for (Role member : ((FSGroup) internalRole).getMembers()) {
+                        newgroup.addMember(getRole(userAdminFactory, 
member.getName()));
+                    }
+                    for (Role member : ((FSGroup) 
internalRole).getRequiredMembers()) {
+                        newgroup.addRequiredMember(getRole(userAdminFactory, 
member.getName()));
+                    }
+                    members.add(newgroup);
+                }
+            }
+        }
+        return members;
+    }
+
+    public synchronized Role getRole(final UserAdminFactory userAdminFactory, 
final String roleName)
+        throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(roleName);
+        final FSRole internalRole = internalRoleFile.getRole(roleName);
+        if (internalRole != null) {
+            if (internalRole.getType() == Role.USER) {
+                return userAdminFactory.createUser(internalRole.getName(),
+                        dictionaryToMap(internalRole.getProperties()),
+                        dictionaryToMap(((User) 
internalRole).getCredentials()));
+            }
+            else if (internalRole.getType() == Role.GROUP) {
+                Group newgroup = 
userAdminFactory.createGroup(internalRole.getName(),
+                    dictionaryToMap(internalRole.getProperties()),
+                    dictionaryToMap(((Group) internalRole).getCredentials()));
+                for (Role member : ((FSGroup) internalRole).getMembers()) {
+                    newgroup.addMember(getRole(userAdminFactory, 
member.getName()));
+                }
+                for (Role member : ((FSGroup) 
internalRole).getRequiredMembers()) {
+                    newgroup.addRequiredMember(getRole(userAdminFactory, 
member.getName()));
+                }
+                return newgroup;
+            }
+        }
+        return null;
+    }
+
+    public synchronized User getUser(final UserAdminFactory userAdminFactory, 
final String key, final String value)
+        throws StorageException {
+        List<User> matchingUsers = new LinkedList<User>();
+        try {
+            Filter filter = FrameworkUtil.createFilter("(" + key + "=" + value 
+ ")");
+            List<String> allRoleNames = m_roleNamelist.getAll();
+            for (String roleName : allRoleNames) {
+                Role role = getRole(userAdminFactory, roleName);
+                if (role.getType() == Role.USER && 
filter.match(role.getProperties())) {
+                    matchingUsers.add((User) role);
+                }
+            }
+        }
+        catch (InvalidSyntaxException e) {
+            throw new StorageException(e.getMessage());
+        }
+        if (matchingUsers.size() > 0) {
+            return matchingUsers.get(0);
+        }
+        return null;
+    }
+
+    public synchronized boolean removeMember(final Group group, final Role 
role) throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(group.getName());
+        final FSRole internalRole = internalRoleFile.getRole(group.getName());
+        if (internalRole != null && internalRole.getType() == Role.GROUP
+            && ((FSGroup) internalRole).removeMember(role)) {
+            internalRoleFile.save();
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized void removeRoleAttribute(final Role role, final String 
key) throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(role.getName());
+        final FSRole internalRole = internalRoleFile.getRole(role.getName());
+        if (internalRole != null) {
+            Object value = internalRole.getProperties().remove(key);
+            if (value != null) {
+                internalRoleFile.save();
+            }
+        }
+    }
+
+    public synchronized void removeUserCredential(final User user, final 
String key) throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(user.getName());
+        final FSRole internalRole = internalRoleFile.getRole(user.getName());
+        if (internalRole != null && internalRole.getType() == Role.USER) {
+            Object value = ((FSUser) internalRole).removeCredential(key);
+            if (value != null) {
+                internalRoleFile.save();
+            }
+        }
+    }
+
+    public synchronized void setRoleAttribute(final Role role, final String 
key, final Object value)
+        throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(role.getName());
+        final FSRole internalRole = internalRoleFile.getRole(role.getName());
+        if (internalRole != null) {
+            internalRole.setProperty(key, value);
+            internalRoleFile.save();
+        }
+    }
+
+    public synchronized void setUserCredential(final User user, final String 
key, final Object value)
+        throws StorageException {
+        final FSRoleStorage internalRoleFile = getStorageFile(user.getName());
+        final FSRole internalRole = internalRoleFile.getRole(user.getName());
+        if (internalRole != null && internalRole.getType() == Role.USER) {
+            ((FSUser) internalRole).setCredential(key, value);
+            internalRoleFile.save();
+        }
+    }
+
+    private FSRoleStorage getStorageFile(final String entityId) throws 
StorageException {
+        final int hash = Math.abs(entityId.hashCode());
+        final String subdirName = "" + (hash % 50);
+        final File subdirFile = new File(m_dataDirectory, subdirName);
+        if (!subdirFile.exists()) {
+            subdirFile.mkdir();
+        }
+        return new FSRoleStorage(new File(subdirFile, STORAGEFILE_PREFIX + 
hash + STORAGEFILE_POSTFIX));
+    }
+
+    private Map<String, Object> dictionaryToMap(final Dictionary<String, 
Object> dictionary) {
+        if (dictionary == null) {
+            return null;
+        }
+        Map<String, Object> map = new HashMap<String, Object>();
+        Enumeration<String> keys = dictionary.keys();
+        while (keys.hasMoreElements()) {
+            String key = keys.nextElement();
+            map.put(key, dictionary.get(key));
+        }
+        return map;
+    }
+}
\ No newline at end of file

Added: 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivatorTest.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivatorTest.java
       Tue Nov 23 18:00:06 2010
@@ -0,0 +1,230 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.osgi;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import junit.framework.Assert;
+
+import org.amdatu.core.useradminstore.fs.service.FSUserAdminStorageProvider;
+import org.amdatu.core.useradminstore.fs.service.mock.MockUserAdminFactory;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.hamcrest.Description;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.api.Action;
+import org.jmock.api.Invocation;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.ops4j.pax.useradmin.service.spi.StorageProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+public final class FSUserAdminStorageProviderActivatorTest {
+
+    private static final String JAVA_IO_TMPDIR = 
System.getProperty("java.io.tmpdir");
+
+    private static File m_absoluteTestRootDirectory;
+    private static String m_relativeTestRootDirectory;
+
+    private List<Object> m_RegisteredServices;
+
+    @Rule
+    public TestName m_testName = new TestName();
+
+    @BeforeClass
+    public static void setUpOnce() {
+        Random rand = new Random();
+        int randomInt = 1 + Math.abs(rand.nextInt());
+        m_relativeTestRootDirectory = 
FSUserAdminStorageProviderActivatorTest.class.getSimpleName() + "_" + randomInt;
+        m_absoluteTestRootDirectory =
+            new File(JAVA_IO_TMPDIR + File.separator + 
m_relativeTestRootDirectory);
+        if (!m_absoluteTestRootDirectory.exists()) {
+            m_absoluteTestRootDirectory.mkdirs();
+        }
+    }
+
+    @Before
+    public void setUp() {
+        m_RegisteredServices = new LinkedList<Object>();
+    }
+
+    @Test
+    public void testActivatorUsesAbsoluteStorageDirectory() throws Exception {
+
+        final Mockery mockContext = new Mockery();
+        final BundleContext bundleContext = 
mockContext.mock(BundleContext.class);
+
+        final File bundleStorageDir = new File(m_absoluteTestRootDirectory, 
m_testName.getMethodName());
+        bundleStorageDir.mkdir();
+
+        mockContext.checking(new Expectations() {
+            {
+                // allowing all service registration calls to the bundleContext
+                
allowing(bundleContext).addServiceListener(with(aNonNull(ServiceListener.class)),
+                    with(any(String.class)));
+
+                
allowing(bundleContext).createFilter((with(aNonNull(String.class))));
+                will(returnValue(mockContext.mock(Filter.class)));
+
+                
allowing(bundleContext).getServiceReference(LogService.class.getName());
+                will(returnValue(null));
+
+                
allowing(bundleContext).getServiceReferences(with(LogService.class.getName()), 
with(any(String.class)));
+                will(returnValue(null));
+
+                // assert that DM registers a component and store it for 
callback
+                
one(bundleContext).registerService(with(ComponentDeclaration.class.getName()),
+                    with(aNonNull(Object.class)), with(any(Dictionary.class)));
+                will(addRegisteredService());
+
+                // assert that DM registers a managed service and store it for 
callback
+                
one(bundleContext).registerService(with(ManagedService.class.getName()),
+                    with(aNonNull(Object.class)), with(any(Dictionary.class)));
+                will(addRegisteredService());
+
+                // assert that the TenantStorageProvider is registered and 
store it for callback
+                
one(bundleContext).registerService(with(StorageProvider.class.getName()),
+                    with(aNonNull(FSUserAdminStorageProvider.class)), 
with(any(Dictionary.class)));
+                will(addRegisteredService());
+            }
+        });
+
+        // start thing up and execute some logic on the service
+        DependencyActivatorBase activatorBase = new 
FSUserAdminStorageProviderActivator();
+        activatorBase.start(bundleContext);
+
+        // simulate config admin callback
+        Dictionary<String, String> dict = new Hashtable<String, String>();
+        dict.put(FSUserAdminStorageProvider.DATA_DIRECTORY, 
bundleStorageDir.getAbsolutePath());
+        ((ManagedService) m_RegisteredServices.get(1)).updated(dict);
+
+        // invoke the storage provider to get it persist something
+        StorageProvider provider = (StorageProvider) 
m_RegisteredServices.get(2);
+        provider.createUser(new MockUserAdminFactory(), "Bram");
+
+        // assert that the bundleContext storage directory was used
+        String[] files = bundleStorageDir.list();
+        Assert.assertTrue(files.length > 0);
+
+        // checkerdiecheck
+        mockContext.assertIsSatisfied();
+    }
+
+    @Test
+    public void testActivatorUsesRelativeStorageDirectory() throws Exception {
+
+        final Mockery mockContext = new Mockery();
+        final BundleContext bundleContext = 
mockContext.mock(BundleContext.class);
+
+        final String relativeStorageDirectoryPath =
+            m_relativeTestRootDirectory + File.separator + 
m_testName.getMethodName();
+        final File absolutebundleStorageDirectory = new 
File(m_absoluteTestRootDirectory, m_testName.getMethodName());
+        absolutebundleStorageDirectory.mkdir();
+
+        mockContext.checking(new Expectations() {
+            {
+                // allowing all service registration calls to the bundleContext
+                
allowing(bundleContext).addServiceListener(with(aNonNull(ServiceListener.class)),
+                    with(any(String.class)));
+
+                
allowing(bundleContext).createFilter((with(aNonNull(String.class))));
+                will(returnValue(mockContext.mock(Filter.class)));
+
+                
allowing(bundleContext).getServiceReference(LogService.class.getName());
+                will(returnValue(null));
+
+                
allowing(bundleContext).getServiceReferences(with(LogService.class.getName()), 
with(any(String.class)));
+                will(returnValue(null));
+
+                // assert that DM registers a component and store it for 
callback
+                
one(bundleContext).registerService(with(ComponentDeclaration.class.getName()),
+                    with(aNonNull(Object.class)), with(any(Dictionary.class)));
+                will(addRegisteredService());
+
+                // assert that DM registers a managed service and store it for 
callback
+                
one(bundleContext).registerService(with(ManagedService.class.getName()),
+                    with(aNonNull(Object.class)), with(any(Dictionary.class)));
+                will(addRegisteredService());
+
+                // assert that the TenantStorageProvider is registered and 
store it for callback
+                
one(bundleContext).registerService(with(StorageProvider.class.getName()),
+                    with(aNonNull(FSUserAdminStorageProvider.class)), 
with(any(Dictionary.class)));
+                will(addRegisteredService());
+            }
+        });
+
+        // start thing up and execute some logic on the service
+        DependencyActivatorBase activatorBase = new 
FSUserAdminStorageProviderActivator();
+        activatorBase.start(bundleContext);
+
+        // simulate config admin callback with relative dir
+        System.setProperty("user.dir", JAVA_IO_TMPDIR);
+        Dictionary<String, String> dict = new Hashtable<String, String>();
+        dict.put(FSUserAdminStorageProvider.DATA_DIRECTORY, 
relativeStorageDirectoryPath);
+        ((ManagedService) m_RegisteredServices.get(1)).updated(dict);
+
+        // invoke the storage provider to get it persist something
+        StorageProvider provider = (StorageProvider) 
m_RegisteredServices.get(2);
+        provider.createUser(new MockUserAdminFactory(), "Bram");
+
+        // assert that the correct storage directory was actually used
+        Assert.assertEquals(((FSUserAdminStorageProvider) 
provider).getDataDirectory().getAbsolutePath(),
+            absolutebundleStorageDirectory.getAbsolutePath());
+        String[] files = ((FSUserAdminStorageProvider) 
provider).getDataDirectory().list();
+        Assert.assertTrue(files.length > 0);
+
+        // checkerdiecheck
+        mockContext.assertIsSatisfied();
+    }
+
+    private <Object> Action addRegisteredService() {
+        return new AddElementsAction<Object>((Collection<Object>) 
m_RegisteredServices);
+    }
+}
+
+class AddElementsAction<T> implements Action {
+    private Collection<T> m_services;
+
+    public AddElementsAction(Collection<T> services) {
+        m_services = services;
+    }
+
+    public void describeTo(Description description) {
+        description.appendText("adds ")
+                   .appendValueList("", ", ", "", m_services)
+                   .appendText(" to a collection");
+    }
+
+    public Object invoke(Invocation invocation) throws Throwable {
+        m_services.add((T) invocation.getParameter(1));
+        return null;
+    }
+}
\ No newline at end of file

Added: 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProviderTest.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProviderTest.java
     Tue Nov 23 18:00:06 2010
@@ -0,0 +1,262 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.service;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.util.Collection;
+import java.util.Random;
+
+import junit.framework.Assert;
+
+import org.amdatu.core.useradminstore.fs.service.mock.MockUserAdminFactory;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.ops4j.pax.useradmin.service.spi.StorageException;
+import org.ops4j.pax.useradmin.service.spi.UserAdminFactory;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+public class FSUserAdminStorageProviderTest {
+
+    private final static String JAVA_IO_TMPDIR = 
System.getProperty("java.io.tmpdir");
+
+    private static File m_absoluteTestRootDirectory;
+    private static String m_relativeTestRootDirectory;
+
+    @Rule
+    public TestName m_testName = new TestName();
+
+    private UserAdminFactory m_userAdminFactory;
+    private FSUserAdminStorageProvider m_userAdminStorageProvider;
+
+    private File m_testDirectory;
+
+    @BeforeClass
+    public static void setUpOnce() throws StorageException {
+        Random rand = new Random();
+        int randomInt = 1 + Math.abs(rand.nextInt());
+        m_relativeTestRootDirectory = 
FSUserAdminStorageProvider.class.getSimpleName() + "_" + randomInt;
+        m_absoluteTestRootDirectory =
+            new File(JAVA_IO_TMPDIR + File.separator + 
m_relativeTestRootDirectory);
+        m_absoluteTestRootDirectory.mkdirs();
+    }
+
+    @Before
+    public void setUp() throws StorageException, ConfigurationException {
+        m_testDirectory = new File(m_absoluteTestRootDirectory, 
m_testName.getMethodName());
+        m_testDirectory.mkdir();
+        m_userAdminStorageProvider = new FSUserAdminStorageProvider();
+        m_userAdminStorageProvider.setDataDirectory(m_testDirectory);
+        m_userAdminStorageProvider.start();
+        m_userAdminFactory = new MockUserAdminFactory();
+    }
+
+    /**
+     * Comprehensive test of crud operations on tenants.
+     * 
+     * @throws UnsupportedEncodingException
+     * 
+     * @throws TenantStorageException
+     */
+    @Test
+    public void testUserCreateDelete() throws StorageException, 
UnsupportedEncodingException {
+
+        User user1 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Aa");
+        Assert.assertNotNull(user1);
+        Assert.assertEquals("Aa", user1.getName());
+        user1 = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Aa");
+        Assert.assertNotNull(user1);
+        Assert.assertEquals("Aa", user1.getName());
+
+        User user2 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"BB");
+        Assert.assertNotNull(user2);
+        Assert.assertEquals("BB", user2.getName());
+        user2 = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"BB");
+        Assert.assertNotNull(user2);
+        Assert.assertEquals("BB", user2.getName());
+
+        Assert.assertTrue(m_userAdminStorageProvider.deleteRole(user2));
+        Assert.assertFalse(m_userAdminStorageProvider.deleteRole(user2));
+
+        user2 = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"BB");
+        Assert.assertNull(user2);
+    }
+
+    @Test
+    public void testUserProperties() throws StorageException, 
UnsupportedEncodingException {
+        User user = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Bram");
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertNull(user.getProperties());
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.setRoleAttribute(user, "haarkleur", 
"blond");
+        m_userAdminStorageProvider.setRoleAttribute(user, "oogkleur", "blauw");
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertEquals("blond", user.getProperties().get("haarkleur"));
+        Assert.assertEquals("blauw", user.getProperties().get("oogkleur"));
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.removeRoleAttribute(user, "haarkleur");
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertNull(user.getProperties().get("haarkleur"));
+        Assert.assertEquals("blauw", user.getProperties().get("oogkleur"));
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.clearRoleAttributes(user);
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertNull(user.getProperties());
+    }
+
+    @Test
+    public void testUserCredentials() throws StorageException, 
UnsupportedEncodingException {
+        User user = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Bram");
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertNull(user.getCredentials());
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.setUserCredential(user, "password", 
"test123");
+        m_userAdminStorageProvider.setUserCredential(user, "token", 
"test123".getBytes("utf-8"));
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertEquals("test123", user.getCredentials().get("password"));
+        Assert.assertEquals("test123", new String((byte[]) 
user.getCredentials().get("token"), "utf-8"));
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.removeUserCredential(user, "token");
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertEquals("test123", user.getCredentials().get("password"));
+        Assert.assertNull(user.getCredentials().get("token"));
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.clearUserCredentials(user);
+
+        user = (User) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"Bram");
+        Assert.assertNotNull(user);
+        Assert.assertNull(user.getCredentials());
+    }
+
+    @Test
+    public void testGroupCreateDelete() throws StorageException {
+        Group group = 
m_userAdminStorageProvider.createGroup(m_userAdminFactory, "amdatu-dev");
+        Assert.assertNotNull(group);
+        Assert.assertEquals("amdatu-dev", group.getName());
+
+        User user1 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Bram");
+        User user2 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Ivo");
+
+        m_userAdminStorageProvider.addMember(group, user1);
+        m_userAdminStorageProvider.addMember(group, user2);
+
+        User user3 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Marcel");
+        User user4 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Angelo");
+        User user5 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Mark");
+
+        m_userAdminStorageProvider.addRequiredMember(group, user3);
+        m_userAdminStorageProvider.addRequiredMember(group, user4);
+        m_userAdminStorageProvider.addRequiredMember(group, user5);
+
+        group = (Group) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"amdatu-dev");
+        Assert.assertEquals(2, group.getMembers().length);
+        Assert.assertEquals(3, group.getRequiredMembers().length);
+
+        Collection<Role> members = 
m_userAdminStorageProvider.getMembers(m_userAdminFactory, group);
+        Assert.assertEquals(2, members.size());
+        Collection<Role> requiredMembers = 
m_userAdminStorageProvider.getRequiredMembers(m_userAdminFactory, group);
+        Assert.assertEquals(3, requiredMembers.size());
+
+        Assert.assertTrue(m_userAdminStorageProvider.removeMember(group, 
user2));
+        Assert.assertFalse(m_userAdminStorageProvider.removeMember(group, 
user2));
+
+        Assert.assertTrue(m_userAdminStorageProvider.removeMember(group, 
user4));
+        Assert.assertFalse(m_userAdminStorageProvider.removeMember(group, 
user4));
+
+        members = m_userAdminStorageProvider.getMembers(m_userAdminFactory, 
group);
+        Assert.assertEquals(1, members.size());
+        requiredMembers = 
m_userAdminStorageProvider.getRequiredMembers(m_userAdminFactory, group);
+        Assert.assertEquals(2, requiredMembers.size());
+
+        group = (Group) m_userAdminStorageProvider.getRole(m_userAdminFactory, 
"amdatu-dev");
+        Assert.assertTrue(m_userAdminStorageProvider.deleteRole(group));
+        Assert.assertFalse(m_userAdminStorageProvider.deleteRole(group));
+
+    }
+
+    @Test
+    public void testFindRolesByFilter() throws StorageException {
+        User user1 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.setRoleAttribute(user1, "bedrijf", "GX");
+        m_userAdminStorageProvider.setRoleAttribute(user1, "haarkleur", 
"blond");
+        m_userAdminStorageProvider.setRoleAttribute(user1, "oogkleur", 
"blauw");
+
+        User user2 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Ivo");
+        m_userAdminStorageProvider.setRoleAttribute(user2, "bedrijf", "GX");
+        m_userAdminStorageProvider.setRoleAttribute(user2, "haarkleur", 
"zwart");
+        m_userAdminStorageProvider.setRoleAttribute(user2, "oogkleur", 
"bruin");
+
+        Collection<Role> matches1 = 
m_userAdminStorageProvider.findRoles(m_userAdminFactory, "(haarkleur=blond)");
+        Assert.assertEquals("Find returned unexpected amount of roles", 1, 
matches1.size());
+
+        Collection<Role> matches2 = 
m_userAdminStorageProvider.findRoles(m_userAdminFactory, "(bedrijf=GX)");
+        Assert.assertEquals("Find returned unexpected amount of roles", 2, 
matches2.size());
+
+        Collection<Role> matches3 = 
m_userAdminStorageProvider.findRoles(m_userAdminFactory, "(status=n00b)");
+        Assert.assertEquals("Find returned unexpected amount of roles", 0, 
matches3.size());
+    }
+
+    @Test
+    public void testGetUserByProperty() throws StorageException {
+        User user1 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Bram");
+        m_userAdminStorageProvider.setRoleAttribute(user1, "bedrijf", "GX");
+        m_userAdminStorageProvider.setRoleAttribute(user1, "haarkleur", 
"blond");
+        m_userAdminStorageProvider.setRoleAttribute(user1, "oogkleur", 
"blauw");
+
+        User user2 = m_userAdminStorageProvider.createUser(m_userAdminFactory, 
"Ivo");
+        m_userAdminStorageProvider.setRoleAttribute(user2, "bedrijf", "GX");
+        m_userAdminStorageProvider.setRoleAttribute(user2, "haarkleur", 
"zwart");
+        m_userAdminStorageProvider.setRoleAttribute(user2, "oogkleur", 
"bruin");
+
+        User matchingUser1 = 
m_userAdminStorageProvider.getUser(m_userAdminFactory, "haarkleur", "blond");
+        Assert.assertNotNull("Expected a matching user", matchingUser1);
+        Assert.assertEquals("Expected to find Bram", "Bram", 
matchingUser1.getName());
+
+        User matchingUser2 = 
m_userAdminStorageProvider.getUser(m_userAdminFactory, "oogkleur", "bruin");
+        Assert.assertNotNull("Expected a matching user", matchingUser2);
+        Assert.assertEquals("Expected to find Ivo", "Ivo", 
matchingUser2.getName());
+
+        User matchingUser3 = 
m_userAdminStorageProvider.getUser(m_userAdminFactory, "oogkleur", "groen");
+        Assert.assertNull("Expected no matching user", matchingUser3);
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockGroup.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockGroup.java
     Tue Nov 23 18:00:06 2010
@@ -0,0 +1,76 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.service.mock;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+
+public class MockGroup extends MockUser implements Group {
+
+    protected Set<Role> m_members;
+    protected Set<Role> m_requiredMembers;
+
+    public MockGroup(final String name, final Dictionary properties, final 
Dictionary credentials) {
+        super(name, properties, credentials);
+    }
+
+    public boolean addMember(Role role) {
+        if (m_members == null) {
+            m_members = new HashSet<Role>();
+        }
+        return m_members.add(role);
+    }
+
+    public boolean addRequiredMember(final Role role) {
+        if (m_requiredMembers == null) {
+            m_requiredMembers = new HashSet<Role>();
+        }
+        return m_requiredMembers.add(role);
+    }
+
+    public Role[] getMembers() {
+        if (m_members == null) {
+            return new Role[] {};
+        }
+        return m_members.toArray(new Role[m_members.size()]);
+    }
+
+    public Role[] getRequiredMembers() {
+        if (m_requiredMembers == null) {
+            return new Role[] {};
+        }
+        return m_requiredMembers.toArray(new Role[m_requiredMembers.size()]);
+    }
+
+    public boolean removeMember(final Role role) {
+        if (role != null) {
+            boolean removed = false;
+            if (m_members != null) {
+                removed = m_members.remove(role);
+            }
+            if (m_requiredMembers != null) {
+                removed = removed || m_requiredMembers.remove(role);
+            }
+            return removed;
+        }
+        return false;
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockRole.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockRole.java
      Tue Nov 23 18:00:06 2010
@@ -0,0 +1,40 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.service.mock;
+
+import java.util.Dictionary;
+
+import org.osgi.service.useradmin.Role;
+
+public abstract class MockRole implements Role {
+
+    protected int m_type;
+    protected String m_name;
+    protected Dictionary m_properties;
+
+    public String getName() {
+        return m_name;
+    }
+
+    public Dictionary getProperties() {
+        return m_properties;
+    }
+
+    public int getType() {
+        return m_type;
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockUser.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockUser.java
      Tue Nov 23 18:00:06 2010
@@ -0,0 +1,49 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.service.mock;
+
+import java.util.Dictionary;
+
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+public class MockUser extends MockRole implements User {
+
+    private Dictionary m_credentials;
+
+    public MockUser(final String name, final Dictionary properties, final 
Dictionary credentials) {
+        m_type = Role.USER;
+        m_name = name;
+        m_properties = properties;
+        m_credentials = credentials;
+    }
+
+    public Dictionary getCredentials() {
+        return m_credentials;
+    }
+
+    public boolean hasCredential(String key, Object value) {
+        if (m_credentials == null) {
+            return false;
+        }
+        Object localValue = m_credentials.get(key);
+        if (localValue == null) {
+            return false;
+        }
+        return localValue.equals(value);
+    }
+}

Added: 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockUserAdminFactory.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/useradminstore-fs/src/test/java/org/amdatu/core/useradminstore/fs/service/mock/MockUserAdminFactory.java
  Tue Nov 23 18:00:06 2010
@@ -0,0 +1,52 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.useradminstore.fs.service.mock;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.ops4j.pax.useradmin.service.spi.UserAdminFactory;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.User;
+
+public class MockUserAdminFactory implements UserAdminFactory {
+
+    public User createUser(String name, Map<String, Object> properties, 
Map<String, Object> credentials) {
+        Dictionary propertiesDictionary = null;
+        if (properties != null) {
+            propertiesDictionary = new Hashtable(properties);
+        }
+        Dictionary credentialsDictionary = null;
+        if (credentials != null) {
+            credentialsDictionary = new Hashtable(credentials);
+        }
+        return new MockUser(name, propertiesDictionary, credentialsDictionary);
+    }
+
+    public Group createGroup(String name, Map<String, Object> properties, 
Map<String, Object> credentials) {
+        Dictionary propertiesDictionary = null;
+        if (properties != null) {
+            propertiesDictionary = new Hashtable(properties);
+        }
+        Dictionary credentialsDictionary = null;
+        if (credentials != null) {
+            credentialsDictionary = new Hashtable(credentials);
+        }
+        return new MockGroup(name, propertiesDictionary, 
credentialsDictionary);
+    }
+}

Reply via email to