Author: bdekruijff at gmail.com
Date: Tue Dec 14 19:14:49 2010
New Revision: 493

Log:
[sandbox] poc code for multi container tenant isolation

Added:
   sandbox/bdekruijff/amdatu-tm/
   sandbox/bdekruijff/amdatu-tm/README.txt
   sandbox/bdekruijff/amdatu-tm/amdatu-tm/
   sandbox/bdekruijff/amdatu-tm/amdatu-tm/amdatu-web.properties
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.core.useradminstore-fs-0.1.0-SNAPSHOT.jar
   (contents, props changed)
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.shell.aucommands-0.1.0-SNAPSHOT.jar
   (contents, props changed)
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.shell.tmcommands-0.1.0-SNAPSHOT.jar
   (contents, props changed)
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.tm.tenantmanager-0.1.0-SNAPSHOT.jar
   (contents, props changed)
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.dependencymanager-3.0.0-SNAPSHOT.jar
   (contents, props changed)
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.dependencymanager.shell-3.0.0-SNAPSHOT.jar
   (contents, props changed)
   sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.main-3.0.6.jar   
(contents, props changed)
   sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.shell-1.4.2.jar   
(contents, props changed)
   
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.shell.remote-1.1.2.jar  
 (contents, props changed)
   sandbox/bdekruijff/amdatu-tm/amdatu-tm/startup.bat
   sandbox/bdekruijff/amdatu-tm/pom.xml
   sandbox/bdekruijff/amdatu-tm/tenantmanager/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/pom.xml
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/TenantApplicationContext.java
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/TenantManagerService.java
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/osgi/
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/osgi/Activator.java
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/service/
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/service/TenantManagerServiceImpl.java
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/recources/
   
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/recources/felix.properties
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/test/
   sandbox/bdekruijff/amdatu-tm/tenantmanager/src/test/java/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/pom.xml
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSGroup.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRole.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleNameList.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleStorage.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUser.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUtil.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivator.java
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/
   
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProvider.java
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/test/
   sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/test/java/

Added: sandbox/bdekruijff/amdatu-tm/README.txt
==============================================================================
--- (empty file)
+++ sandbox/bdekruijff/amdatu-tm/README.txt     Tue Dec 14 19:14:49 2010
@@ -0,0 +1,83 @@
+--------------------------------
+PoC for nested Tenant Management
+--------------------------------
+
+basic design:
+
+
+* TenantManager is a tenant aware service that 
+       1) spawns a new osgi container for each tenant registered
+       2) delegates platform packages to the tenant container
+       3) proxies (only relevant!) services (log/useradmin) to the tenant
+       4) monitors container to take action (eg refresh)
+       5) TODO: publishes tenant services in parent container (eg a REST 
service?)
+
+* This UserAdmin FS storage is now tenant aware that
+       1) create a storage dir per tenant (based on id)
+       2) leverages PAX user admin through extender pattern
+
+-> useradmin per tenant
+-> application dev model in tact
+
+       
+
+implementation notes/todos:
+
+* set of delegated packages is static (should be extendable)
+* list proxied services is static (should be extendable)
+* currently deploys four bundles into each tenant container
+       - Apache Felix Dependency Manager;
+       - Apache Felix Dependency Manager Shell
+       - Apache Felix Shell Service
+       - Apache Felix Remote Shell
+       - Amdatu UserAdmin Commands
+* currently proxies global logservice into each tenant container
+* currently proxies relevant useradmin into tenant container
+* JVM with Xmx=256 went OOM at about 500 tenants
+
+
+1) Starting this up:
+       * Copy the amdatu-tm folder into devserver layout
+       * Copy startup from this folder to devserver root
+       * startup
+
+
+2) inside parent container
+
+  -> tmcreate 245
+  tmcreate <tenantId> <tenantName>
+  -> tmcreate 245 jan
+  -> tmlist
+  '123'   - 11
+  '245'   - jan
+  -> tmdelete 123
+  Tenant deleted: 123
+  
+note: use integers for tenantid as it is used to register a remote shell on 
that port
+note: refresh in the parent container will restart all tenants
+
+3) Inside tenant container
+
+  * telnet 127.0.0.1 245
+  
+  Felix Remote Shell Console:
+  ============================
+  
+  -> ps
+  START LEVEL 1
+     ID   State         Level  Name
+  [   0] [Active     ] [    0] System Bundle (3.0.6)
+  [   1] [Resolved   ] [    1] Apache Felix Dependency Manager (3.0.0.SNAPSHOT)
+  [   2] [Active     ] [    1] Apache Felix Dependency Manager Shell 
(3.0.0.SNAPSHOT)
+  [   3] [Active     ] [    1] Apache Felix Shell Service (1.4.2)
+  [   4] [Active     ] [    1] Apache Felix Remote Shell (1.1.2)
+  [   5] [Active     ] [    1] Amdatu UserAdmin Commands (0.1.0.SNAPSHOT)
+
+  -> uacreate 123
+  User created: 123
+  -> ualist
+  '123
+  -> uadelete 123
+  User deleted: 123
+  ->
+

Added: sandbox/bdekruijff/amdatu-tm/amdatu-tm/amdatu-web.properties
==============================================================================
--- (empty file)
+++ sandbox/bdekruijff/amdatu-tm/amdatu-tm/amdatu-web.properties        Tue Dec 
14 19:14:49 2010
@@ -0,0 +1,137 @@
+# 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.
+
+#
+# Framework config properties.
+#
+
+# To override the packages the framework exports by default from the
+# class path, set this variable.
+#org.osgi.framework.system.packages=
+
+# To append packages to the default set of exported system packages,
+# set this value.
+# Unfortunately, Cassandra uses a cliffc lib which contains a class 
'org.cliffc.high_scale_lib.NonBlockingHashMap'
+# This class imports the com.sun package and so it must be exposed here.
+org.osgi.framework.system.packages.extra=sun.misc,com.sun.management,dalvik.system
+
+# The following property makes specified packages from the class path
+# available to all bundles. You should avoid using this property.
+#org.osgi.framework.bootdelegation=sun.*,com.sun.*
+
+# Felix tries to guess when to implicitly boot delegate in certain
+# situations to ease integration without outside code. This feature
+# is enabled by default, uncomment the following line to disable it.
+#felix.bootdelegation.implicit=false
+
+# The following property explicitly specifies the location of the bundle
+# cache, which defaults to "felix-cache" in the current working directory.
+# If this value is not absolute, then the felix.cache.rootdir controls
+# how the absolute location is calculated. (See next property)
+org.osgi.framework.storage=felix-deploy
+
+# The following property is used to convert a relative bundle cache
+# location into an absolute one by specifying the root to prepend to
+# the relative cache path. The default for this property is the
+# current working directory.
+felix.cache.rootdir=work/cache
+
+# The following property controls whether the bundle cache is flushed
+# the first time the framework is initialized. Possible values are
+# "none" and "onFirstInit"; the default is "none".
+#org.osgi.framework.storage.clean=onFirstInit
+
+# The following property determines which actions are performed when
+# processing the auto-deploy directory. It is a comma-delimited list of
+# the following values: 'install', 'start', 'update', and 'uninstall'.
+# An undefined or blank value is equivalent to disabling auto-deploy
+# processing.
+#felix.auto.deploy.action=install,start,update,uninstall
+
+# The following property specifies the directory to use as the bundle
+# auto-deploy directory; the default is 'bundle' in the working directory.
+#felix.auto.deploy.dir=amdatu-system
+
+# The following property is a space-delimited list of bundle URLs
+# to install when the framework starts. The ending numerical component
+# is the target start level. Any number of these properties may be
+# specified for different start levels.
+#felix.auto.install.5=
+
+# The following property is a space-delimited list of bundle URLs
+# to install and start when the framework starts. The ending numerical
+# component is the target start level. Any number of these properties
+# may be specified for different start levels.
+felix.auto.start.1=reference:file:amdatu-system/org.apache.felix.configadmin-1.2.4.jar
 \
+                                       
reference:file:amdatu-system/org.apache.felix.dependencymanager-3.0.0-SNAPSHOT.jar
 \
+                                       
reference:file:amdatu-system/org.apache.felix.eventadmin-1.2.2.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.fileinstall-3.0.0.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.log-1.0.0.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.main-2.0.5.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.metatype-1.0.4.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.shell.tui-1.4.1.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.shell-1.4.2.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.webconsole-3.1.2.jar \
+                                       
reference:file:amdatu-system/org.apache.sling.commons.mime-2.1.4.jar \
+                                       
reference:file:amdatu-system/org.apache.sling.commons.osgi-2.0.6.jar \
+                                       
reference:file:amdatu-system/org.osgi.compendium-4.2.0.jar \
+                                       
reference:file:amdatu-system/org.osgi.core-4.2.0.jar \
+                                       
reference:file:amdatu-system/pax-useradmin-service-0.0.1-SNAPSHOT.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.http.jetty-2.0.4.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.http.whiteboard-2.0.4.jar \
+                                       
reference:file:amdatu-system/pax-swissbox-core-1.3.0.jar \
+                                       
reference:file:amdatu-system/ops4j-base-lang-1.2.2.jar \
+                                       
reference:file:amdatu-system/org.apache.felix.scr-1.6.0.jar
+felix.auto.start.2=reference:file:amdatu-core/org.amdatu.core.config.filebased-0.1.0-SNAPSHOT.jar
 \
+                                       
reference:file:amdatu-core/org.amdatu.core.loghandler-0.1.0-SNAPSHOT.jar \
+                                       
reference:file:amdatu-core/org.amdatu.core.tenant-0.1.0-SNAPSHOT.jar \
+                                       
reference:file:amdatu-core/org.amdatu.core.tenantstore-fs-0.1.0-SNAPSHOT.jar \
+                                       
reference:file:amdatu-core/org.amdatu.core.config.templates-0.1.0-SNAPSHOT.jar \
+                                       
reference:file:amdatu-tm/org.amdatu.core.useradminstore-fs-0.1.0-SNAPSHOT.jar 
+felix.auto.start.3=reference:file:amdatu-application/org.amdatu.web.httpcontext-0.1.0-SNAPSHOT.jar
 \
+                                       
reference:file:amdatu-application/org.amdatu.web.jsp-0.1.0-SNAPSHOT.jar \
+                                       
reference:file:amdatu-application/org.amdatu.web.rest.jaxrs-0.1.0-SNAPSHOT.jar \
+                                       
reference:file:amdatu-application/org.amdatu.web.rest.wink-0.1.0-SNAPSHOT.jar 
+felix.auto.start.4=reference:file:amdatu-tm/org.amdatu.tm.tenantmanager-0.1.0-SNAPSHOT.jar
 \
+                                       
reference:file:amdatu-tm/org.amdatu.shell.tmcommands-0.1.0-SNAPSHOT.jar 
+
+
+# Sets the initial start level of the framework upon startup.
+org.osgi.framework.startlevel.beginning=30
+
+# Sets the start level of newly installed bundles.
+felix.startlevel.bundle=30
+
+# Felix installs a stream and content handler factories by default,
+# uncomment the following line to not install them.
+#felix.service.urlhandlers=false
+
+# The launcher registers a shutdown hook to cleanly stop the framework
+# by default, uncomment the following line to disable it.
+#felix.shutdown.hook=false
+
+# Config of file install
+felix.fileinstall.poll=3000
+felix.fileinstall.dir=deploy
+felix.fileinstall.debug=1
+felix.fileinstall.bundles.new.start=true
+felix.fileinstall.filter=.*
+felix.fileinstall.tmpdir=work/tmp/web/org/apache/felix/fileinstall
+felix.fileinstall.start.level=30
+
+#Config of config admin
+felix.cm.dir=${user.dir}/work/configadmin
\ No newline at end of file

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.core.useradminstore-fs-0.1.0-SNAPSHOT.jar
==============================================================================
Binary file. No diff available.

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.shell.aucommands-0.1.0-SNAPSHOT.jar
==============================================================================
Binary file. No diff available.

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.shell.tmcommands-0.1.0-SNAPSHOT.jar
==============================================================================
Binary file. No diff available.

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.amdatu.tm.tenantmanager-0.1.0-SNAPSHOT.jar
==============================================================================
Binary file. No diff available.

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.dependencymanager-3.0.0-SNAPSHOT.jar
==============================================================================
Binary file. No diff available.

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.dependencymanager.shell-3.0.0-SNAPSHOT.jar
==============================================================================
Binary file. No diff available.

Added: sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.main-3.0.6.jar
==============================================================================
Binary file. No diff available.

Added: sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.shell-1.4.2.jar
==============================================================================
Binary file. No diff available.

Added: 
sandbox/bdekruijff/amdatu-tm/amdatu-tm/org.apache.felix.shell.remote-1.1.2.jar
==============================================================================
Binary file. No diff available.

Added: sandbox/bdekruijff/amdatu-tm/amdatu-tm/startup.bat
==============================================================================
--- (empty file)
+++ sandbox/bdekruijff/amdatu-tm/amdatu-tm/startup.bat  Tue Dec 14 19:14:49 2010
@@ -0,0 +1,11 @@
+rem Open a debug port
+set JAVA_OPTS=-Xdebug 
-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
+
+rem Set memory options
+set JAVA_OPTS=%JAVA_OPTS% -Xms256m -Xmx1024m -XX:MaxPermSize=256m
+
+rem Felix property file
+
+set JAVA_OPTS=%JAVA_OPTS% 
-Dfelix.config.properties=file:amdatu-tm/amdatu-web.properties
+java %JAVA_OPTS% -jar amdatu-tm/org.apache.felix.main-3.0.6.jar 
+

Added: sandbox/bdekruijff/amdatu-tm/pom.xml
==============================================================================
--- (empty file)
+++ sandbox/bdekruijff/amdatu-tm/pom.xml        Tue Dec 14 19:14:49 2010
@@ -0,0 +1,20 @@
+<?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>amdatu</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.amdatu.tm</groupId>
+  <artifactId>tm</artifactId>
+  <packaging>pom</packaging>
+  <name>Amdatu Tenant Manager</name>
+  <description>This project provides Tenant Management</description>
+
+  <modules>
+    <module>tenantmanager</module>
+    <module>useradminstore-fs</module>
+  </modules>
+</project>

Added: sandbox/bdekruijff/amdatu-tm/tenantmanager/pom.xml
==============================================================================
--- (empty file)
+++ sandbox/bdekruijff/amdatu-tm/tenantmanager/pom.xml  Tue Dec 14 19:14:49 2010
@@ -0,0 +1,65 @@
+<?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.tm</groupId>
+    <artifactId>tm</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>tenantmanager</artifactId>
+  <packaging>bundle</packaging>
+  <name>Amdatu Tenant Manager</name>
+  <description>This bundle provides a Tenant Manager</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.framework</artifactId>
+      <type>bundle</type>
+      <scope>compile</scope>
+      <version>3.0.6</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.shell</artifactId>
+      <type>bundle</type>
+      <scope>provided</scope>
+      <version>1.4.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.core</groupId>
+      <artifactId>tenant</artifactId>
+      <scope>provided</scope>
+      <type>bundle</type>
+      <version>0.1.0-SNAPSHOT</version>
+    </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.tm.tenantmanager.osgi.Activator</Bundle-Activator>
+            
<Bundle-SymbolicName>org.amdatu.tm.tenantmanager</Bundle-SymbolicName>
+            
<Embed-Dependency>*;scope=compile;inline=default.properties|org/apache/felix/**</Embed-Dependency>
+            <Embed-Transitive>true</Embed-Transitive>
+            <Bundle-ClassPath>.</Bundle-ClassPath>
+            <Import-Package>*,org.osgi.service.useradmin, org.osgi.service.cm, 
org.osgi.service.event, android.dalvik;resolution:=optional</Import-Package>
+            <!--
+              <DynamicImport-Package>*</DynamicImport-Package>
+            -->
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>

Added: 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/TenantApplicationContext.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/TenantApplicationContext.java
  Tue Dec 14 19:14:49 2010
@@ -0,0 +1,5 @@
+package org.amdatu.tm.tenantmanager;
+
+public interface TenantApplicationContext {
+
+}

Added: 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/TenantManagerService.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/TenantManagerService.java
      Tue Dec 14 19:14:49 2010
@@ -0,0 +1,6 @@
+package org.amdatu.tm.tenantmanager;
+
+
+public interface TenantManagerService {
+
+}

Added: 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/osgi/Activator.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/osgi/Activator.java
    Tue Dec 14 19:14:49 2010
@@ -0,0 +1,50 @@
+/*
+/*
+    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.tm.tenantmanager.osgi;
+
+import org.amdatu.core.tenant.Tenant;
+import org.amdatu.tm.tenantmanager.TenantManagerService;
+import org.amdatu.tm.tenantmanager.service.TenantManagerServiceImpl;
+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.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ */
+public final class Activator extends DependencyActivatorBase {
+
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws 
Exception {
+
+        manager.add(
+            createAdapterService(Tenant.class, null)
+                .setImplementation(TenantManagerServiceImpl.class)
+                .setInterface(TenantManagerService.class.getName(), null)
+                
.add(createServiceDependency().setService(LogService.class).setRequired(true)));
+
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) 
throws Exception {
+    }
+}

Added: 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/service/TenantManagerServiceImpl.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/java/org/amdatu/tm/tenantmanager/service/TenantManagerServiceImpl.java
  Tue Dec 14 19:14:49 2010
@@ -0,0 +1,324 @@
+/*
+ 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.tm.tenantmanager.service;
+
+import java.util.Properties;
+
+import org.amdatu.core.tenant.Tenant;
+import org.amdatu.tm.tenantmanager.TenantManagerService;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * I am responsible for managing a tenant container
+ * The tenant is injected by the dependencymanager
+ * I proxy all platform packages to the tenant container
+ * I can proxy global en tenant aware platform services
+ * I need to restart the container when proxied packages change
+ * I can install tenant specific bundles as well
+ * I use a cache directory per tenant
+ * I should support multiple frameworks
+ * I could decorate proxied services for management/qos concerns
+ */
+public class TenantManagerServiceImpl implements TenantManagerService {
+
+    // injected
+    private volatile BundleContext m_bundleContext;
+    private volatile DependencyManager m_dependencyManager;
+    private volatile Component m_component;
+    private volatile LogService m_logService;
+    private volatile Tenant m_tenant;
+    private volatile UserAdmin m_useradmin;
+
+    // collaborators
+    private final FrameworkListener m_parentFrameworkListener = new 
ParentFrameworkMonitor();
+    private final FrameworkListener m_tenantFrameworkListener = new 
TenantFrameworkMonitor();
+    private final ServiceListener m_tenantServiceListener = new 
TenantServiceMonitor();
+
+    // administration
+    private Framework m_framework;
+    private Properties m_properties;
+    private Object m_logServiceRegistration; // using object to hide from 
depman
+    private Object m_userAdminRegistration; // using object to hide from depman
+    private volatile boolean isStarted = false;;
+
+    public TenantManagerServiceImpl() {
+    }
+
+    public synchronized void init() {
+        m_bundleContext.addFrameworkListener(new ParentFrameworkMonitor());
+
+        // add a dependency for this tenants useradmin
+        m_component.add(m_dependencyManager.createServiceDependency()
+            .setService(UserAdmin.class, 
"(org.ops4j.pax.useradmin.storageprovider.type=" + m_tenant.getId() + ")")
+            .setCallbacks("userAdminAdded", "userAdminRemoved"));
+    }
+
+    public synchronized void destroy() {
+        m_bundleContext.removeFrameworkListener(m_parentFrameworkListener);
+    }
+
+    public synchronized void start() throws BundleException {
+
+        // FIXME blindly delegating parent export packages to tenant container
+        m_properties = new Properties();
+        m_properties.put("org.osgi.framework.system.packages",
+            (String) 
m_bundleContext.getBundle(0).getHeaders().get("Export-Package"));
+
+        // FIXME blindly delegating compendium packages to tenant container
+        Bundle[] bundles = m_bundleContext.getBundles();
+        for (Bundle bundle : bundles) {
+            if 
(bundle.getHeaders().get("Bundle-SymbolicName").equals("osgi.cmpn")) {
+                m_properties.put("org.osgi.framework.system.packages.extra", 
(String) bundle
+                    .getHeaders().get("Export-Package") + ", 
javax.servlet.http, javax.servlet");
+            }
+        }
+
+        m_properties.put("org.osgi.framework.startlevel.beginning", "1");
+        m_properties.put("org.osgi.framework.storage", "felix-deploy");
+        m_properties.put("felix.startlevel", "1");
+        m_properties.put("felix.cache.rootdir", "work/cache_" + 
m_tenant.getId());
+        m_properties.put("felix.config.properties", "");
+
+        // FIXME assuming tenantid is a valid telnet port
+        m_properties.put("osgi.shell.telnet.ip", "127.0.0.1");
+        m_properties.put("osgi.shell.telnet.port", m_tenant.getId());
+
+        // FIXME assuming tenantid is a valid http port
+        m_properties.put("org.osgi.service.http.port", "8" + m_tenant.getId());
+
+        startFramework();
+        isStarted = true;
+        m_logService.log(LogService.LOG_ERROR, "Started Tenant '" + 
m_tenant.getId() + "'");
+    }
+
+    public synchronized void stop() throws BundleException {
+        m_logService.log(LogService.LOG_ERROR, "Stopping Tenant '" + 
m_tenant.getId() + "'");
+        isStarted = false;
+        stopFramework();
+    }
+
+    public synchronized void restart() throws BundleException {
+        m_logService.log(LogService.LOG_ERROR, "Restarting Tenant '" + 
m_tenant.getId() + "'");
+        stopFramework();
+        startFramework();
+    }
+
+    public synchronized void userAdminAdded(ServiceReference sr, Object svc) {
+        m_useradmin = (UserAdmin) svc;
+        proxyPlatformServices();
+    }
+
+    public synchronized void userAdminRemoved(ServiceReference sr, Object svc) 
{
+        unproxyPlatformServices();
+    }
+
+    private void startFramework() throws BundleException {
+
+        m_framework = new Felix(new StringMap(m_properties, false));
+        m_framework.start();
+
+        m_framework.getBundleContext().addFrameworkListener(
+            m_tenantFrameworkListener);
+        m_framework.getBundleContext().addServiceListener(
+            m_tenantServiceListener);
+
+        proxyPlatformServices();
+
+        // just for kicks as we don't actually use this atm
+        m_framework.getBundleContext().installBundle(
+            
"reference:file:amdatu-tm/org.apache.felix.dependencymanager-3.0.0-SNAPSHOT.jar");
+        m_framework.getBundleContext().installBundle(
+            
"reference:file:amdatu-tm/org.apache.felix.dependencymanager.shell-3.0.0-SNAPSHOT.jar").start();
+
+        // shell so we can have a look inside a tenant container
+        m_framework.getBundleContext().installBundle(
+            
"reference:file:amdatu-tm/org.apache.felix.shell-1.4.2.jar").start();
+        m_framework.getBundleContext().installBundle(
+            
"reference:file:amdatu-tm/org.apache.felix.shell.remote-1.1.2.jar").start();
+
+        // commands for testing useradmin in the container
+        m_framework.getBundleContext().installBundle(
+            
"reference:file:amdatu-tm/org.amdatu.shell.aucommands-0.1.0-SNAPSHOT.jar").start();
+    }
+
+    private void stopFramework() throws BundleException {
+        unproxyPlatformServices();
+
+        m_framework.getBundleContext().removeFrameworkListener(
+            m_tenantFrameworkListener);
+        m_framework.getBundleContext().removeServiceListener(
+            m_tenantServiceListener);
+
+        m_framework.stop();
+        try {
+            m_framework.waitForStop(10000);
+        }
+        catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    private void proxyPlatformServices() {
+        if (m_framework != null && m_framework.getBundleContext() != null) {
+            if (m_logServiceRegistration == null && m_logService != null) {
+                m_logServiceRegistration =
+                    
m_framework.getBundleContext().registerService(LogService.class.getName(), 
m_logService, null);
+            }
+            if (m_userAdminRegistration == null && m_useradmin != null) {
+                m_userAdminRegistration =
+                    
m_framework.getBundleContext().registerService(UserAdmin.class.getName(), 
m_useradmin, null);
+            }
+        }
+    }
+
+    private void unproxyPlatformServices() {
+        if (m_userAdminRegistration != null) {
+            ((ServiceRegistration) m_userAdminRegistration).unregister();
+            m_userAdminRegistration = null;
+        }
+        if (m_logServiceRegistration != null) {
+            ((ServiceRegistration) m_logServiceRegistration).unregister();
+            m_logServiceRegistration = null;
+        }
+    }
+
+    class ParentFrameworkMonitor implements FrameworkListener {
+
+        public void frameworkEvent(FrameworkEvent event) {
+            switch (event.getType()) {
+                case FrameworkEvent.STARTED:
+                    break;
+                case FrameworkEvent.ERROR:
+                    break;
+                case FrameworkEvent.INFO:
+                    break;
+                case FrameworkEvent.PACKAGES_REFRESHED:
+                    // FIXME not sure if this is the right action
+                    if (isStarted) {
+                        try {
+                            restart();
+                        }
+                        catch (BundleException e) {}
+                    }
+                    break;
+                case FrameworkEvent.STARTLEVEL_CHANGED:
+                    break;
+                case FrameworkEvent.STOPPED:
+                    break;
+                case FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED:
+                    break;
+                case FrameworkEvent.STOPPED_UPDATE:
+                    break;
+                case FrameworkEvent.WAIT_TIMEDOUT:
+                    break;
+                case FrameworkEvent.WARNING:
+                    break;
+                default:
+                    break;
+            }
+
+        }
+    }
+
+    class TenantFrameworkMonitor implements FrameworkListener {
+
+        public void frameworkEvent(FrameworkEvent event) {
+            System.err.println("EVENT in tenant container");
+            switch (event.getType()) {
+                case FrameworkEvent.STARTED:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " STARTED");
+                    break;
+                case FrameworkEvent.ERROR:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " ERROR");
+                    break;
+                case FrameworkEvent.INFO:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " INFO");
+                    break;
+                case FrameworkEvent.PACKAGES_REFRESHED:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " PACKAGES_REFRESHED");
+                    break;
+                case FrameworkEvent.STARTLEVEL_CHANGED:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " STARTLEVEL_CHANGED");
+                    break;
+                case FrameworkEvent.STOPPED:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " STOPPED");
+                    break;
+                case FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId()
+                        + " STOPPED_BOOTCLASSPATH_MODIFIED");
+                    break;
+                case FrameworkEvent.STOPPED_UPDATE:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " STOPPED_UPDATE");
+                    break;
+                case FrameworkEvent.WAIT_TIMEDOUT:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " WAIT_TIMEDOUT");
+                    break;
+                case FrameworkEvent.WARNING:
+                    m_logService.log(LogService.LOG_ERROR, "Tenant " + 
m_tenant.getId() + " WARNING");
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    class TenantServiceMonitor implements ServiceListener {
+
+        public void serviceChanged(ServiceEvent event) {
+            switch (event.getType()) {
+                case ServiceEvent.REGISTERED:
+                    m_logService.log(LogService.LOG_ERROR, ""
+                        + 
event.getServiceReference().getBundle().getSymbolicName() + " registered 
service "
+                        + event.getSource());
+                    break;
+                case ServiceEvent.UNREGISTERING:
+                    m_logService.log(LogService.LOG_ERROR, ""
+                        + 
event.getServiceReference().getBundle().getSymbolicName() + " unregistering 
service "
+                        + event.getSource());
+                    break;
+                case ServiceEvent.MODIFIED:
+                    m_logService.log(LogService.LOG_ERROR, ""
+                        + 
event.getServiceReference().getBundle().getSymbolicName() + " modified service "
+                        + event.getSource());
+                    break;
+                case ServiceEvent.MODIFIED_ENDMATCH:
+                    m_logService.log(LogService.LOG_ERROR, ""
+                        + 
event.getServiceReference().getBundle().getSymbolicName() + " modified_endmatch 
service "
+                        + event.getSource());
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}

Added: 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/recources/felix.properties
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/tenantmanager/src/main/recources/felix.properties  
    Tue Dec 14 19:14:49 2010
@@ -0,0 +1,61 @@
+#org.osgi.framework.system.packages=
+org.osgi.framework.system.packages.extra=sun.misc,com.sun.management
+org.osgi.framework.startlevel.beginning=30
+org.osgi.framework.storage=felix-deploy
+felix.startlevel.bundle=30
+felix.cache.rootdir=work/cache
+
+
+#org.osgi.framework.bootdelegation=sun.*,com.sun.*
+#felix.bootdelegation.implicit=false
+#org.osgi.framework.storage.clean=onFirstInit
+#felix.auto.deploy.action=install,start,update,uninstall
+#felix.auto.deploy.dir=amdatu-system
+#felix.auto.install.5=
+
+
+# Sets the start level of newly installed bundles.
+
+# Felix installs a stream and content handler factories by default,
+# uncomment the following line to not install them.
+#felix.service.urlhandlers=false
+# The launcher registers a shutdown hook to cleanly stop the framework
+# by default, uncomment the following line to disable it.
+#felix.shutdown.hook=false
+# Config of file install
+#felix.fileinstall.poll=3000
+#felix.fileinstall.dir=deploy
+#felix.fileinstall.debug=1
+#felix.fileinstall.bundles.new.start=true
+#felix.fileinstall.filter=.*
+#felix.fileinstall.tmpdir=work/tmp/web/org/apache/felix/fileinstall
+#felix.fileinstall.start.level=30
+#Config of config admin
+#felix.cm.dir=${user.dir}/work/configadmin
+#felix.auto.start.1=reference:file:amdatu-system/org.apache.felix.configadmin-1.2.4.jar
 \
+#                                      
reference:file:amdatu-system/org.apache.felix.dependencymanager-3.0.0-SNAPSHOT.jar
 \
+#                                      
reference:file:amdatu-system/org.apache.felix.eventadmin-1.2.2.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.fileinstall-3.0.0.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.log-1.0.0.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.main-2.0.5.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.metatype-1.0.4.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.shell.tui-1.4.1.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.shell-1.4.2.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.webconsole-3.1.2.jar \
+#                                      
reference:file:amdatu-system/org.apache.sling.commons.mime-2.1.4.jar \
+#                                      
reference:file:amdatu-system/org.apache.sling.commons.osgi-2.0.6.jar \
+#                                      
reference:file:amdatu-system/org.osgi.compendium-4.2.0.jar \
+#                                      
reference:file:amdatu-system/org.osgi.core-4.2.0.jar \
+#                                      
reference:file:amdatu-system/pax-useradmin-service-0.0.1-SNAPSHOT.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.http.jetty-2.0.4.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.http.whiteboard-2.0.4.jar \
+#                                      
reference:file:amdatu-system/pax-swissbox-core-1.3.0.jar \
+#                                      
reference:file:amdatu-system/ops4j-base-lang-1.2.2.jar \
+#                                      
reference:file:amdatu-system/org.apache.felix.scr-1.6.0.jar
+#felix.auto.start.2=reference:file:amdatu-core/org.amdatu.core.config.filebased-0.1.0-SNAPSHOT.jar
 \
+#                                      
reference:file:amdatu-core/org.amdatu.core.loghandler-0.1.0-SNAPSHOT.jar \
+#                                      
reference:file:amdatu-core/org.amdatu.core.tenant-0.1.0-SNAPSHOT.jar \
+#                                      
reference:file:amdatu-core/org.amdatu.core.tenantstore-fs-0.1.0-SNAPSHOT.jar \
+#                                      
reference:file:amdatu-core/org.amdatu.core.config.templates-0.1.0-SNAPSHOT.jar \
+#                                      
reference:file:amdatu-core/org.amdatu.core.useradminstore-fs-0.1.0-SNAPSHOT.jar 
+#
\ No newline at end of file

Added: sandbox/bdekruijff/amdatu-tm/useradminstore-fs/pom.xml
==============================================================================
--- (empty file)
+++ sandbox/bdekruijff/amdatu-tm/useradminstore-fs/pom.xml      Tue Dec 14 
19:14:49 2010
@@ -0,0 +1,46 @@
+<?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.1.0-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.amdatu.core</groupId>
+      <artifactId>tenant</artifactId>
+      <scope>provided</scope>
+      <type>bundle</type>
+      <version>0.1.0-SNAPSHOT</version>
+    </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: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSGroup.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSGroup.java
        Tue Dec 14 19:14:49 2010
@@ -0,0 +1,134 @@
+/*
+    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 (m_members == null) {
+            m_members = new HashMap<String, Role>();
+        }
+        if (members != null) {
+            for (Role member : members) {
+                m_members.put(member.getName(), member);
+            }
+        }
+        Role[] requiredMembers = group.getRequiredMembers();
+        if (m_requiredMembers == null) {
+            m_requiredMembers = new HashMap<String, Role>();
+        }
+        if (requiredMembers != null) {
+            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: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRole.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRole.java
 Tue Dec 14 19:14:49 2010
@@ -0,0 +1,67 @@
+/*
+    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;
+
+    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;
+    }
+}

Added: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleNameList.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleNameList.java
 Tue Dec 14 19:14:49 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 role names 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 removeRoleName(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: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleStorage.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSRoleStorage.java
  Tue Dec 14 19:14:49 2010
@@ -0,0 +1,227 @@
+/*
+    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);
+                    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: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUser.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUser.java
 Tue Dec 14 19:14:49 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: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUtil.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/internal/FSUtil.java
 Tue Dec 14 19:14:49 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: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivator.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/osgi/FSUserAdminStorageProviderActivator.java
        Tue Dec 14 19:14:49 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.tenant.Tenant;
+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(
+                createAdapterService(Tenant.class, null)
+                    .setImplementation(FSUserAdminStorageProvider.class)
+                    .setInterface(StorageProvider.class.getName(), null)
+                    
.add(createServiceDependency().setService(LogService.class).setRequired(false)));
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) 
throws Exception {
+    }
+}

Added: 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProvider.java
==============================================================================
--- (empty file)
+++ 
sandbox/bdekruijff/amdatu-tm/useradminstore-fs/src/main/java/org/amdatu/core/useradminstore/fs/service/FSUserAdminStorageProvider.java
      Tue Dec 14 19:14:49 2010
@@ -0,0 +1,390 @@
+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.tenant.Tenant;
+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.apache.felix.dm.Component;
+import org.ops4j.pax.useradmin.service.UserAdminConstants;
+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.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+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 Component m_component;
+    private volatile BundleContext m_bundleContext;
+    private volatile Tenant m_tenant;
+    private volatile LogService m_logService;
+
+    // Storage directory
+    private File m_dataDirectory;
+
+    // Collaborator
+    private FSRoleNameList m_roleNamelist;
+
+    /*
+     * Constructors
+     */
+
+    public FSUserAdminStorageProvider() throws StorageException {
+    }
+
+    /*
+     * DM Service lifecycle
+     */
+
+    public synchronized void init() {
+        Dictionary props = m_component.getServiceProperties();
+        props.put("tenant_id", m_tenant.getId());
+        props.put("tenant_name", m_tenant.getName());
+        props.put(UserAdminConstants.STORAGEPROVIDER_TYPE, m_tenant.getId());
+        props.put("service_ranking", 0);
+        m_component.setServiceProperties(props);
+    }
+
+    public synchronized void start() throws StorageException {
+        File rootDir = m_bundleContext.getDataFile("storage");
+        rootDir.mkdir();
+        m_dataDirectory = new File(rootDir, m_tenant.getId());
+        m_dataDirectory.mkdir();
+        m_roleNamelist = new FSRoleNameList(new File(m_dataDirectory, 
ENTITYLIST_FILENAME));
+
+        // by contract DM ConfigurationDependency contract dataDirectory has
+        // been set through the updated method.
+        if (m_logService != null)
+            m_logService.log(LogService.LOG_INFO, "Filesystem UserAdmin 
storage provider started");
+    }
+
+    public synchronized void stop() {
+        m_roleNamelist = null;
+
+        if (m_logService != null)
+            m_logService.log(LogService.LOG_INFO, "Filesystem UserAdmin 
storage provider stopped");
+    }
+
+    /*
+     * 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.removeRoleName(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 {
+            List<String> allRoleNames = m_roleNamelist.getAll();
+            Filter filter = null;
+            if (filterString != null && !"".equals(filterString)) {
+                filter = FrameworkUtil.createFilter(filterString);
+            }
+            for (String roleName : allRoleNames) {
+                Role role = getRole(userAdminFactory, roleName);
+                if (filter == null || 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 new HashSet<Role>();
+    }
+
+    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

Reply via email to