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
