Author: ivol37 at gmail.com
Date: Mon Feb 14 15:45:57 2011
New Revision: 782

Log:
[AMDATU-304] Implemented tenant cache as aspect service of tenant management 
service.

Added:
   
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantConfigServiceImpl.java
   
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementCache.java
Modified:
   
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/osgi/Activator.java
   
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementServiceImpl.java
   
trunk/integration-tests/src/test/java/org/amdatu/test/integration/tests/FSTenantStorageProviderServiceTest.java

Modified: 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/osgi/Activator.java
==============================================================================
--- 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/osgi/Activator.java
   (original)
+++ 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/osgi/Activator.java
   Mon Feb 14 15:45:57 2011
@@ -19,10 +19,13 @@
 
 import org.amdatu.core.tenant.TenantManagementService;
 import org.amdatu.core.tenant.TenantStorageProvider;
+import org.amdatu.core.tenant.service.TenantConfigServiceImpl;
+import org.amdatu.core.tenant.service.TenantManagementCache;
 import org.amdatu.core.tenant.service.TenantManagementServiceImpl;
 import org.apache.felix.dm.DependencyActivatorBase;
 import org.apache.felix.dm.DependencyManager;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
 import org.osgi.service.log.LogService;
 
 /**
@@ -39,8 +42,23 @@
             .setImplementation(TenantManagementServiceImpl.class)
             .setInterface(TenantManagementService.class.getName(), null)
             
.add(createServiceDependency().setService(TenantStorageProvider.class).setRequired(true))
-            
.add(createConfigurationDependency().setPid(TenantManagementService.PID))
             
.add(createServiceDependency().setService(LogService.class).setRequired(true)));
+
+        // Create and register the Cached Tenant management service
+        String filter = "(" + Constants.OBJECTCLASS + "=" + 
TenantManagementService.class.getName() + ")";
+        manager.add(
+            createAspectService(TenantManagementService.class, filter, 10, 
null)
+            .setImplementation(TenantManagementCache.class)
+            .setInterface(TenantManagementService.class.getName(), null)
+            
.add(createServiceDependency().setService(LogService.class).setRequired(true)));
+
+        // Create and register the Tenant config service
+        manager.add(
+            createComponent()
+            .setImplementation(TenantConfigServiceImpl.class)
+            
.add(createServiceDependency().setService(LogService.class).setRequired(true).setInstanceBound(true))
+            
.add(createServiceDependency().setService(TenantManagementService.class).setRequired(true))
+            
.add(createConfigurationDependency().setPid(TenantManagementService.PID)));
     }
 
     @Override

Added: 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantConfigServiceImpl.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantConfigServiceImpl.java
  Mon Feb 14 15:45:57 2011
@@ -0,0 +1,136 @@
+/*
+    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.tenant.service;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.amdatu.core.tenant.TenantEntity;
+import org.amdatu.core.tenant.TenantException;
+import org.amdatu.core.tenant.TenantManagementService;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * This managed service listens to configuration changes in the tenant config. 
It invokes the
+ * tenant management service when new tenants are available, tenants have been 
updated or removed.
+ * Note that since the tenant management service is decorated with a cache 
aspect, the service actually
+ * invoked will be the cache service, such that the cache will be refreshed 
automatically when the
+ * tenant config has been changed.
+ * 
+ * @author ivol
+ */
+public class TenantConfigServiceImpl implements ManagedService {
+    // Service dependencies injected by the dependency manager
+    private volatile LogService m_logService;
+    private volatile TenantManagementService m_tenantService;
+
+    // List of provisioned tenants from config admin
+    private List<TenantEntity> m_configTenants;
+
+    @SuppressWarnings("unchecked")
+    public void updated(Dictionary properties) throws ConfigurationException {
+        if (properties != null) {
+            // Build a list of tenants from the configuration file
+            m_configTenants = new ArrayList<TenantEntity>();
+            Enumeration<String> keys = properties.keys();
+            while (keys.hasMoreElements()) {
+                String key = keys.nextElement();
+                if (key.endsWith(".id")) {
+                    m_configTenants.add(getTenant(properties, key));
+                }
+            }
+            if (m_logService != null && m_tenantService != null) {
+                update();
+            }
+        }
+    }
+
+    public void start() {
+        update();
+    }
+
+    private void update() {
+        try {
+            update(m_configTenants);
+        }
+        catch (TenantException e) {
+            m_logService.log(LogService.LOG_ERROR, "Could not update tenants", 
e);
+        }
+    }
+
+    // Invoked when the list of tenants need to be updated
+    private void update(List<TenantEntity> newTenants) throws TenantException {
+        m_logService.log(LogService.LOG_INFO, "Updating tenants after 
configuration update.");
+
+        // Build the list of tenants we need to update afterwardds
+        List<TenantEntity> updateTenants = new ArrayList<TenantEntity>();
+        updateTenants.addAll(newTenants);
+
+        // First add new tenants
+        List<TenantEntity> existingTenants = m_tenantService.getTenants();
+        for (TenantEntity newTenant : newTenants) {
+            if (!existingTenants.contains(newTenant)) {
+                // This tenant does not exist yet, add it
+                m_tenantService.createTenant(newTenant.getId(), 
newTenant.getName(), newTenant.getProperties());
+
+                // Since the tenant is just added, it doesn't need to be 
updated anymore
+                updateTenants.remove(newTenant);
+            }
+        }
+
+        // Now remove tenants that have been removed
+        for (TenantEntity oldTenant : existingTenants) {
+            if (!newTenants.contains(oldTenant)) {
+                // This tenant does not exist anymore, remove it
+                m_tenantService.deleteTenant(oldTenant);
+            }
+        }
+
+        // Now update all tenants
+        for (TenantEntity newTenant : updateTenants) {
+            m_tenantService.updateTenant(newTenant);
+        }
+    }
+
+    // Read a tenant configuration from config admin properties
+    @SuppressWarnings("unchecked")
+    private TenantEntity getTenant(Dictionary dictionary, String idKey) {
+        String nameKey = idKey.substring(0, idKey.lastIndexOf(".id")) + 
".name";
+        String propertiesKey = idKey.substring(0, idKey.lastIndexOf(".id")) + 
".properties";
+        String id = (String) dictionary.get(idKey);
+        String name = (String) dictionary.get(nameKey);
+        Map<String, String> properties = new HashMap<String, String>();
+        Enumeration<String> keys = dictionary.keys();
+        while (keys.hasMoreElements()) {
+            String key = keys.nextElement();
+            if (key.startsWith(propertiesKey)) {
+                String propName = key.substring(propertiesKey.length() + 1);
+                String propValue = (String) dictionary.get(key);
+                properties.put(propName, propValue);
+            }
+        }
+        return new TenantEntity(id, name, properties);
+    }
+
+
+}

Added: 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementCache.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementCache.java
    Mon Feb 14 15:45:57 2011
@@ -0,0 +1,186 @@
+/*
+    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.tenant.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.amdatu.core.tenant.TenantEntity;
+import org.amdatu.core.tenant.TenantException;
+import org.amdatu.core.tenant.TenantManagementService;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class implements a cache for the Tenant management service, 
implemented as an aspect of the
+ * original Tenant management service. Note that tenants in Amdatu are 
actually read-only; they are
+ * "provisioned" from configuration management.
+ *
+ * @author ivol
+ */
+public class TenantManagementCache implements TenantManagementService {
+    // Service dependencies injected by the dependency manager
+    private volatile TenantManagementService m_tenantService;
+    private volatile LogService m_logService;
+
+    // The tenant cache
+    private List<TenantEntity> m_tenantCache = null;
+
+    // We currently use all-exclusive access.
+    private ReentrantReadWriteLock m_lock = new ReentrantReadWriteLock();
+
+    public void start() throws TenantException {
+        // read the tenants upon startup
+        loadCache();
+
+        m_logService.log(LogService.LOG_INFO, "Tenant management cache 
started");
+    }
+
+    public TenantEntity createTenant(String id, String name) throws 
TenantException {
+        Lock writelock = m_lock.writeLock();
+        writelock.lock();
+        try {
+            // First clear the cache. This is necessary since while invoking 
createTenant
+            // on the tenant management service, getTenantById will be invoked 
immediately
+            // after the tenant has been created but before the method returns.
+            clearCache();
+
+            TenantEntity tenant = m_tenantService.createTenant(id, name);
+            return tenant;
+        } finally {
+            writelock.unlock();
+        }
+    }
+
+    public TenantEntity createTenant(String id, String name, Map<String, 
String> properties) throws TenantException {
+        Lock writelock = m_lock.writeLock();
+        writelock.lock();
+        try {
+            // First clear the cache. This is necessary since while invoking 
createTenant
+            // on the tenant management service, getTenantById will be invoked 
immediately
+            // after the tenant has been created but before the method returns.
+            clearCache();
+
+            TenantEntity tenant = m_tenantService.createTenant(id, name, 
properties);
+            return tenant;
+        } finally {
+            writelock.unlock();
+        }
+    }
+
+    public void updateTenant(TenantEntity tenant) throws TenantException {
+        Lock writelock = m_lock.writeLock();
+        writelock.lock();
+        try {
+            // First clear the cache. This is necessary since while invoking 
createTenant
+            // on the tenant management service, getTenantById will be invoked 
immediately
+            // after the tenant has been created but before the method returns.
+            clearCache();
+
+            m_tenantService.updateTenant(tenant);
+        } finally {
+            writelock.unlock();
+        }
+    }
+
+    public void deleteTenant(TenantEntity tenant) throws TenantException {
+        Lock writelock = m_lock.writeLock();
+        writelock.lock();
+        try {
+            // First clear the cache. This is necessary since while invoking 
createTenant
+            // on the tenant management service, getTenantById will be invoked 
immediately
+            // after the tenant has been created but before the method returns.
+            clearCache();
+
+            m_tenantService.deleteTenant(tenant);
+        } finally {
+            writelock.unlock();
+        }
+    }
+
+    public TenantEntity getTenantById(String id) throws TenantException {
+        if (m_tenantCache == null) {
+            loadCache();
+        }
+
+        Lock readlock = m_lock.readLock();
+        readlock.lock();
+        try {
+            for (TenantEntity tenant : m_tenantCache) {
+                if (tenant.getId().equals(id)) {
+                    return tenant;
+                }
+            }
+        } finally {
+            readlock.unlock();
+        }
+        return null;
+    }
+
+    public List<TenantEntity> getTenants() throws TenantException {
+        if (m_tenantCache == null) {
+            loadCache();
+        }
+
+        List<TenantEntity> result = new ArrayList<TenantEntity>();
+        Lock readlock = m_lock.readLock();
+        readlock.lock();
+        try {
+            result.addAll(m_tenantCache);
+        } finally {
+            readlock.unlock();
+        }
+        return result;
+    }
+
+    public List<TenantEntity> getTenants(Map<String, String> properties) 
throws TenantException {
+        if (m_tenantCache == null) {
+            loadCache();
+        }
+
+        Lock readlock = m_lock.readLock();
+        readlock.lock();
+        try {
+            List<TenantEntity> matchingTenants = new ArrayList<TenantEntity>();
+            for (TenantEntity tenant : m_tenantCache) {
+                if (tenant.matches(properties)) {
+                    matchingTenants.add(tenant);
+                }
+            }
+            return matchingTenants;
+        } finally {
+            readlock.unlock();
+        }
+    }
+
+    private void loadCache() throws TenantException {
+        Lock writelock = m_lock.writeLock();
+        writelock.lock();
+        try {
+            m_tenantCache = new ArrayList<TenantEntity>();
+            m_tenantCache.addAll(m_tenantService.getTenants());
+        } finally {
+            writelock.unlock();
+        }
+    }
+
+    private void clearCache() {
+        m_tenantCache = null;
+    }
+}

Modified: 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementServiceImpl.java
==============================================================================
--- 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementServiceImpl.java
      (original)
+++ 
trunk/amdatu-core/tenant/src/main/java/org/amdatu/core/tenant/service/TenantManagementServiceImpl.java
      Mon Feb 14 15:45:57 2011
@@ -17,8 +17,6 @@
 package org.amdatu.core.tenant.service;
 
 import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -34,253 +32,179 @@
 import org.amdatu.core.tenant.TenantStorageProvider;
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
 
 /**
  * This class implements the Tenant management service. It provides CRUD 
operations on tenants and ensures
  * that tenants are not only persisted (using the TenantStorageProvider) but 
that Tenants are also registered
  * as a service such that tenant aware services can become tenant adaptors.
+ * Note that tenants are usually read-only; they are provided by Config Admin. 
The tenantConfigService therefore
+ * is a managed service that depends on this config and will invoke this 
service when new tenants are available,
+ * tenants have been removed or updated.
  * 
  * @author ivol
  */
-public class TenantManagementServiceImpl implements TenantManagementService, 
ManagedService {
-       private volatile LogService m_logService;
-       private volatile DependencyManager m_manager;
-       private volatile TenantStorageProvider m_tenantStorageProvider;
-
-       private Map<Tenant, Component> m_tenantComponents = new HashMap<Tenant, 
Component>();
-
-       // List of tenants provisioned from config admin
-       List<TenantEntity> m_tenants = null;
-
-       // We currently use all-exclusive access.
-       private ReentrantReadWriteLock m_lock = new ReentrantReadWriteLock();
-
-       public synchronized void init() throws TenantException {
-               // Loop over the tenants provisioned by config admin and add or 
update the tenants. We do not
-               // remove existing tenants if they are not covered by an entry 
in config admin (yet).
-               m_logService.log(LogService.LOG_INFO, "Initializing 
TenantManagementService with tenants '" + m_tenants + "'.");
-               if (m_tenants != null) {
-                       for (TenantEntity tenant : m_tenants) {
-                               TenantEntity persTenant = 
getTenantById(tenant.getId());
-                               if (persTenant == null) {
-                                       // Tenant does not yet exist, persist 
it, but without registering it as Tenant service yet
-                                       persTenant = 
storeTenant(tenant.getId(), tenant.getName());
-                               }
-                               persTenant.setName(tenant.getName());
-                               for (String propKey : 
tenant.getProperties().keySet()) {
-                                       String propValue = 
tenant.getProperties().get(propKey);
-                                       persTenant.putProperty(propKey, 
propValue);
-                               }
-                               m_tenantStorageProvider.store(persTenant);
-                               m_logService.log(LogService.LOG_INFO, "Tenant 
'" + tenant.getId() + "' added.");
-                       }
-               }
-       }
-
-       /**
-        * Invoked by the Felix dependency manager.
-        */
-       public synchronized void start() throws TenantException {
-               // TODO if we cannot get our tenants now, should we retry later?
-               for (TenantEntity tenant : getTenants()) {
-                       createTenantService(tenant);
-               }    
-               m_logService.log(LogService.LOG_INFO, "TenantManagementService 
started.");
-       }
-
-       public List<TenantEntity> getTenants() throws TenantException {
-               ReadLock lock = m_lock.readLock();
-               try {
-                       lock.lock();
-                       return m_tenantStorageProvider.getAll();
-               } finally {
-                       lock.unlock();
-               }
-       }
-
-       public TenantEntity getTenantById(String id) throws TenantException {
-               ReadLock lock = m_lock.readLock();
-               try {
-                       lock.lock();
-                       return m_tenantStorageProvider.getById(id);
-               } finally {
-                       lock.unlock();
-               }
-       }
-
-       public List<TenantEntity> getTenants(Map<String, String> properties) 
throws TenantException {
-               ReadLock lock = m_lock.readLock();
-               try {
-                       lock.lock();
-                       List<TenantEntity> matchingTenants = new 
ArrayList<TenantEntity>();
-                       for (TenantEntity tenant : getTenants()) {
-                               if (tenant.matches(properties)) {
-                                       matchingTenants.add(tenant);
-                               }
-                       }
-                       return matchingTenants;
-               } finally {
-                       lock.unlock();
-               }
-       }
-
-       public TenantEntity createTenant(String id, String name) throws 
TenantException {
-               return createTenant(id, name, null);
-       }
-
-       public TenantEntity createTenant(String id, String name, Map<String, 
String> properties) throws TenantException {
-               WriteLock writeLock = m_lock.writeLock();
-               writeLock.lock();
-               try {
-                       if (getTenantById(id) != null) {
-                               throw new TenantException("Tenant with id '" + 
id + "' already exists");
-                       }
-                       TenantEntity tenant = new TenantEntity(id, name);
-                       if (properties != null) {
-                               for (String key : properties.keySet()) {
-                                       tenant.putProperty(key, 
properties.get(key));
-                               }
-                       }
-                       m_tenantStorageProvider.store(tenant);
-
-                       // Downgrade write to read lock
-                       ReadLock readLock = m_lock.readLock();
-                       try {
-                               readLock.lock();
-                               writeLock.unlock();
-                               createTenantService(tenant);
-                               return tenant;
-                       } finally {
-                               readLock.unlock();
-                       }
-               } finally {
-                       if (writeLock.isHeldByCurrentThread()) {
-                               writeLock.unlock();
-                       }
-               }
-       }
-
-       // Does the same as createTenant, but without registration of the 
stored tenant as Tenant
-       // service. Invoked in the init() method since tenant services are 
registared in the start() method.
-       private synchronized TenantEntity storeTenant(String id, String name) 
throws TenantException {
-               if (getTenantById(id) != null) {
-                       throw new TenantException("Tenant with id '" + id + "' 
already exists");
-               }
-               TenantEntity tenant = new TenantEntity(id, name);
-               m_tenantStorageProvider.store(tenant);
-               return tenant;
-       }
-
-       public void updateTenant(TenantEntity tenant) throws TenantException {
-               WriteLock writeLock = m_lock.writeLock();
-               writeLock.lock();
-               try {
-                       m_tenantStorageProvider.store(tenant);
-
-                       // Downgrade write to read lock
-                       ReadLock readLock = m_lock.readLock();
-                       try {
-                               readLock.lock();
-                               writeLock.unlock();
-                               reregisterTenantService(tenant);
-                       } finally {
-                               readLock.unlock();
-                       }
-               } finally {
-                       if (writeLock.isHeldByCurrentThread()) {
-                               writeLock.unlock();
-                       }
-               }
-       }
-
-       public void deleteTenant(TenantEntity tenant) throws TenantException {
-               WriteLock writeLock = m_lock.writeLock();
-               writeLock.lock();
-               try {
-                       if (getTenantById(tenant.getId()) == null) {
-                               throw new TenantException("Tenant with id '" + 
tenant.getId() + "' does not exist, thus cannot be deleted.");
-                       }
-                       m_tenantStorageProvider.delete(tenant);
-
-                       // Downgrade write to read lock
-                       ReadLock readLock = m_lock.readLock();
-                       try {
-                               readLock.lock();
-                               writeLock.unlock();
-                               removeTenantService(tenant);
-                       } finally {
-                               readLock.unlock();
-                       }
-               } finally {
-                       if (writeLock.isHeldByCurrentThread()) {
-                               writeLock.unlock();
-                       }
-               }
-       }
-
-       private void createTenantService(TenantEntity tenant) {
-               Component component = m_manager.createComponent()
-               .setImplementation(tenant)
-               .setInterface(Tenant.class.getName(), 
createServiceProperties(tenant));
-               m_manager.add(component);
-               m_tenantComponents.put(tenant, component);
-       }
-
-       private void reregisterTenantService(TenantEntity tenant) {
-               Component component = m_tenantComponents.get(tenant);
-               m_manager.remove(component);
-               createTenantService(tenant);
-       }
-
-       private void removeTenantService(TenantEntity tenant) {
-               m_manager.remove(m_tenantComponents.remove(tenant));
-       }
-
-       private Properties createServiceProperties(TenantEntity tenant) {
-               Properties properties = new Properties();
-               Map<String, String> tenantProperties = tenant.getProperties();
-               for (Map.Entry<String, String> entry : 
tenantProperties.entrySet()) {
-                       properties.put(Tenant.TENANT_SERVICEPROPERTY + 
entry.getKey(), entry.getValue());
-               }
-               properties.put(Tenant.TENANT_ID_SERVICEPROPERTY, 
tenant.getId());
-               properties.put(Tenant.TENANT_NAME_SERVICEPROPERTY, 
tenant.getName());
-               return properties;
-       }
-
-       @SuppressWarnings("unchecked")
-       public void updated(Dictionary properties) throws 
ConfigurationException {
-               if (properties != null) {
-                       // Build a list of tenants from the configuration file
-                       m_tenants = new ArrayList<TenantEntity>();
-                       Enumeration<String> keys = properties.keys();
-                       while (keys.hasMoreElements()) {
-                               String key = keys.nextElement();
-                               if (key.endsWith(".id")) {
-                                       m_tenants.add(getTenant(properties, 
key));
-                               }
-                       }
-               }
-       }
-
-       @SuppressWarnings("unchecked")
-       private TenantEntity getTenant(Dictionary dictionary, String idKey) {
-               String nameKey = idKey.substring(0, idKey.lastIndexOf(".id")) + 
".name";
-               String propertiesKey = idKey.substring(0, 
idKey.lastIndexOf(".id")) + ".properties";
-               String id = (String) dictionary.get(idKey);
-               String name = (String) dictionary.get(nameKey);
-               Map<String, String> properties = new HashMap<String, String>();
-               Enumeration<String> keys = dictionary.keys();
-               while (keys.hasMoreElements()) {
-                       String key = keys.nextElement();
-                       if (key.startsWith(propertiesKey)) {
-                               String propName = 
key.substring(propertiesKey.length() + 1);
-                               String propValue = (String) dictionary.get(key);
-                               properties.put(propName, propValue);
-                       }
-               }
-               return new TenantEntity(id, name, properties);
-       }
+public class TenantManagementServiceImpl implements TenantManagementService {
+    private volatile LogService m_logService;
+    private volatile DependencyManager m_manager;
+    private volatile TenantStorageProvider m_tenantStorageProvider;
+
+    private Map<Tenant, Component> m_tenantComponents = new HashMap<Tenant, 
Component>();
+
+    // We currently use all-exclusive access.
+    private ReentrantReadWriteLock m_lock = new ReentrantReadWriteLock();
+
+    public synchronized void start() throws TenantException {
+        m_logService.log(LogService.LOG_INFO, "TenantManagementService 
started.");
+    }
+
+    public List<TenantEntity> getTenants() throws TenantException {
+        ReadLock lock = m_lock.readLock();
+        try {
+            lock.lock();
+            return m_tenantStorageProvider.getAll();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public TenantEntity getTenantById(String id) throws TenantException {
+        ReadLock lock = m_lock.readLock();
+        try {
+            lock.lock();
+            return m_tenantStorageProvider.getById(id);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public List<TenantEntity> getTenants(Map<String, String> properties) 
throws TenantException {
+        ReadLock lock = m_lock.readLock();
+        try {
+            lock.lock();
+            List<TenantEntity> matchingTenants = new ArrayList<TenantEntity>();
+            for (TenantEntity tenant : getTenants()) {
+                if (tenant.matches(properties)) {
+                    matchingTenants.add(tenant);
+                }
+            }
+            return matchingTenants;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public TenantEntity createTenant(String id, String name) throws 
TenantException {
+        return createTenant(id, name, null);
+    }
+
+    public TenantEntity createTenant(String id, String name, Map<String, 
String> properties) throws TenantException {
+        WriteLock writeLock = m_lock.writeLock();
+        writeLock.lock();
+        try {
+            if (getTenantById(id) != null) {
+                throw new TenantException("Tenant with id '" + id + "' already 
exists");
+            }
+            TenantEntity tenant = new TenantEntity(id, name);
+            if (properties != null) {
+                for (String key : properties.keySet()) {
+                    tenant.putProperty(key, properties.get(key));
+                }
+            }
+            m_tenantStorageProvider.store(tenant);
+
+            // Downgrade write to read lock
+            ReadLock readLock = m_lock.readLock();
+            try {
+                readLock.lock();
+                writeLock.unlock();
+                createTenantService(tenant);
+                return tenant;
+            } finally {
+                readLock.unlock();
+            }
+        } finally {
+            if (writeLock.isHeldByCurrentThread()) {
+                writeLock.unlock();
+            }
+        }
+    }
+
+    public void updateTenant(TenantEntity tenant) throws TenantException {
+        WriteLock writeLock = m_lock.writeLock();
+        writeLock.lock();
+        try {
+            m_tenantStorageProvider.store(tenant);
+
+            // Downgrade write to read lock
+            ReadLock readLock = m_lock.readLock();
+            try {
+                readLock.lock();
+                writeLock.unlock();
+                reregisterTenantService(tenant);
+            } finally {
+                readLock.unlock();
+            }
+        } finally {
+            if (writeLock.isHeldByCurrentThread()) {
+                writeLock.unlock();
+            }
+        }
+    }
+
+    public void deleteTenant(TenantEntity tenant) throws TenantException {
+        WriteLock writeLock = m_lock.writeLock();
+        writeLock.lock();
+        try {
+            if (getTenantById(tenant.getId()) == null) {
+                throw new TenantException("Tenant with id '" + tenant.getId() 
+ "' does not exist, thus cannot be deleted.");
+            }
+            m_tenantStorageProvider.delete(tenant);
+
+            // Downgrade write to read lock
+            ReadLock readLock = m_lock.readLock();
+            try {
+                readLock.lock();
+                writeLock.unlock();
+                removeTenantService(tenant);
+            } finally {
+                readLock.unlock();
+            }
+        } finally {
+            if (writeLock.isHeldByCurrentThread()) {
+                writeLock.unlock();
+            }
+        }
+    }
+
+    private void createTenantService(TenantEntity tenant) {
+        Component component = m_manager.createComponent()
+        .setImplementation(tenant)
+        .setInterface(Tenant.class.getName(), createServiceProperties(tenant));
+        m_manager.add(component);
+        m_tenantComponents.put(tenant, component);
+    }
+
+    private void reregisterTenantService(TenantEntity tenant) {
+        removeTenantService(tenant);
+        createTenantService(tenant);
+    }
+
+    private void removeTenantService(TenantEntity tenant) {
+        Component component = m_tenantComponents.remove(tenant);
+        if (component != null) {
+            m_manager.remove(component);
+        }
+    }
+
+    private Properties createServiceProperties(TenantEntity tenant) {
+        Properties properties = new Properties();
+        Map<String, String> tenantProperties = tenant.getProperties();
+        for (Map.Entry<String, String> entry : tenantProperties.entrySet()) {
+            properties.put(Tenant.TENANT_SERVICEPROPERTY + entry.getKey(), 
entry.getValue());
+        }
+        properties.put(Tenant.TENANT_ID_SERVICEPROPERTY, tenant.getId());
+        properties.put(Tenant.TENANT_NAME_SERVICEPROPERTY, tenant.getName());
+        return properties;
+    }
 }

Modified: 
trunk/integration-tests/src/test/java/org/amdatu/test/integration/tests/FSTenantStorageProviderServiceTest.java
==============================================================================
--- 
trunk/integration-tests/src/test/java/org/amdatu/test/integration/tests/FSTenantStorageProviderServiceTest.java
     (original)
+++ 
trunk/integration-tests/src/test/java/org/amdatu/test/integration/tests/FSTenantStorageProviderServiceTest.java
     Mon Feb 14 15:45:57 2011
@@ -97,12 +97,13 @@
             m_tenantStorageProvider.delete(teDelete);
         }
 
-        TenantEntity te1 = m_tenantManagementService.createTenant("tenant1", 
"Tenant1");
+        TenantEntity te1 = new TenantEntity("tenant1", "Tenant1");
+        m_tenantStorageProvider.store(te1);
         TenantEntity te2 = m_tenantStorageProvider.getById("tenant1");
         Assert.assertNotNull("Tenant was not persisted", te2);
 
         te1.putProperty("favorite.drink", "hot choco");
-        m_tenantManagementService.updateTenant(te1);
+        m_tenantStorageProvider.store(te1);
 
         te2 = m_tenantStorageProvider.getById("tenant1");
         Assert.assertNotNull("Tenant was not persisted", te2);
@@ -110,6 +111,6 @@
         Assert.assertEquals("Tenant property was not set", "hot choco", 
te2.getProperties().get("favorite.drink"));
 
         // Remove tenant
-        m_tenantManagementService.deleteTenant(te1);
+        m_tenantStorageProvider.delete(te1);
     }
 }

Reply via email to