Author: bdekruijff at gmail.com
Date: Mon Nov 15 17:41:13 2010
New Revision: 429

Log:
AMDATU-176 Initial commit of filebased tenantstore. Needs testing and 
integration

Added:
   trunk/amdatu-core/tenantstore-fs/   (props changed)
   trunk/amdatu-core/tenantstore-fs/pom.xml
   trunk/amdatu-core/tenantstore-fs/src/
   trunk/amdatu-core/tenantstore-fs/src/main/
   trunk/amdatu-core/tenantstore-fs/src/main/java/
   trunk/amdatu-core/tenantstore-fs/src/main/java/org/
   trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/
   trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/
   trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSTenantIdList.java
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSTenantStore.java
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSUtil.java
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/osgi/
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/osgi/Activator.java
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/service/
   
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/service/FSTenantStorageProvider.java
   trunk/amdatu-core/tenantstore-fs/src/test/
   trunk/amdatu-core/tenantstore-fs/src/test/java/
   trunk/amdatu-core/tenantstore-fs/src/test/java/org/
   trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/
   trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/
   trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/tenantstore/
   
trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/tenantstore/fs/
   
trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/tenantstore/fs/service/
   
trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/tenantstore/fs/service/FSTenantStorageProviderTest.java
Modified:
   trunk/amdatu-core/pom.xml

Modified: trunk/amdatu-core/pom.xml
==============================================================================
--- trunk/amdatu-core/pom.xml   (original)
+++ trunk/amdatu-core/pom.xml   Mon Nov 15 17:41:13 2010
@@ -63,6 +63,7 @@
     <module>config-filebased</module>
     <module>loghandler</module>
     <module>tenant</module>
+    <module>tenantstore-fs</module>
   </modules>
 
 </project>
\ No newline at end of file

Added: trunk/amdatu-core/tenantstore-fs/pom.xml
==============================================================================
--- (empty file)
+++ trunk/amdatu-core/tenantstore-fs/pom.xml    Mon Nov 15 17:41:13 2010
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu</groupId>
+    <artifactId>org.amdatu.core</artifactId>
+    <version>0.0.6-SNAPSHOT</version>
+  </parent>
+  <groupId>org.amdatu.core</groupId>
+  <artifactId>tenantstore-fs</artifactId>
+  <packaging>bundle</packaging>
+  <name>Amdatu Core - Tenant Filesystem Backstore</name>
+  <description>This bundle provides a filebased Tenant backstore</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.amdatu.core</groupId>
+      <artifactId>tenant</artifactId>
+      <version>${platform.version}</version>
+      <scope>provided</scope>
+      <type>bundle</type>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            
<Bundle-Activator>org.amdatu.core.tenantstore.fs.osgi.Activator</Bundle-Activator>
+            
<Bundle-SymbolicName>org.amdatu.core.tenantstore-fs</Bundle-SymbolicName>
+          </instructions>
+        </configuration>
+      </plugin>
+
+    </plugins>
+  </build>
+</project>

Added: 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSTenantIdList.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSTenantIdList.java
  Mon Nov 15 17:41:13 2010
@@ -0,0 +1,119 @@
+/*
+ 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.tenantstore.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.amdatu.core.tenant.TenantStorageException;
+
+/**
+ * Implementation of a persistent list of tenant identifiers on disk.
+ */
+public final class FSTenantIdList {
+
+    private final File m_file;
+
+    public FSTenantIdList(final File file) throws TenantStorageException {
+        m_file = file;
+    }
+
+    public synchronized List<String> getAll() throws TenantStorageException {
+        try {
+            return readTenantIdList();
+        }
+        catch (IOException e) {
+            throw new TenantStorageException(e);
+        }
+    }
+
+    public synchronized void addTenantId(final String entityId) throws 
TenantStorageException {
+        try {
+            final List<String> tenantEntityIdList = readTenantIdList();
+            if (!tenantEntityIdList.contains(entityId)) {
+                tenantEntityIdList.add(entityId);
+                writeTenantIdList(m_file, tenantEntityIdList);
+            }
+        }
+        catch (IOException e) {
+            throw new TenantStorageException(e);
+        }
+    }
+
+    public synchronized void removeTenantId(final String entityId) throws 
TenantStorageException {
+        try {
+            final List<String> tenantEntityIdList = readTenantIdList();
+            if (tenantEntityIdList.contains(entityId)) {
+                tenantEntityIdList.remove(entityId);
+                writeTenantIdList(m_file, tenantEntityIdList);
+            }
+        }
+        catch (IOException e) {
+            throw new TenantStorageException(e);
+        }
+    }
+
+    private List<String> readTenantIdList() throws IOException {
+        final List<String> tenantEntityIdList = new LinkedList<String>();
+        if (!m_file.exists()) {
+            return tenantEntityIdList;
+        }
+        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 int idLength = ois.readInt();
+                final byte[] idBytes = new byte[idLength];
+                ois.readFully(idBytes);
+                final String id = new String(idBytes, "utf-8");
+                tenantEntityIdList.add(id);
+            }
+            return tenantEntityIdList;
+        }
+        finally {
+            FSUtil.closeInputStreamsSafely(ois, fis);
+        }
+    }
+
+    private void writeTenantIdList(final File file, final List<String> 
tenantEntityIdList) throws IOException {
+        FileOutputStream fos = null;
+        ObjectOutputStream oos = null;
+        try {
+            fos = new FileOutputStream(file);
+            oos = new ObjectOutputStream(fos);
+            oos.writeInt(tenantEntityIdList.size());
+            for (String tenantEntityId : tenantEntityIdList) {
+                final byte[] idBytes = tenantEntityId.getBytes("utf-8");
+                oos.writeInt(idBytes.length);
+                oos.write(idBytes);
+            }
+            oos.flush();
+        }
+        finally {
+            FSUtil.closeOutputStreamsSafely(oos, fos);
+        }
+    }
+}

Added: 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSTenantStore.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSTenantStore.java
   Mon Nov 15 17:41:13 2010
@@ -0,0 +1,174 @@
+/*
+    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.tenantstore.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.HashMap;
+import java.util.Map;
+
+import org.amdatu.core.tenant.TenantEntity;
+import org.amdatu.core.tenant.TenantStorageException;
+
+/**
+ * Implementation of a persistent tenant store (containing 0 or more tenants) 
on disk.
+ */
+public final class FSTenantStore {
+
+    private final File m_file;
+    private Map<String, TenantEntity> m_entities;
+
+    public FSTenantStore(File file) throws TenantStorageException {
+        m_file = file;
+        m_entities = new HashMap<String, TenantEntity>();
+        try {
+            readEntities();
+        }
+        catch (IOException e) {
+            throw new TenantStorageException(e);
+        }
+    }
+
+    public synchronized TenantEntity addEntity(final TenantEntity entity) {
+        return m_entities.put(entity.getId(), entity);
+    }
+
+    public synchronized TenantEntity removeEntity(final String entityId) {
+        return m_entities.remove(entityId);
+    }
+
+    public synchronized TenantEntity getEntity(final String entityId) {
+        return m_entities.get(entityId);
+    }
+
+    public synchronized void save() throws TenantStorageException {
+        try {
+            writeEntities();
+        }
+        catch (IOException e) {
+            throw new TenantStorageException(e);
+        }
+    }
+
+    private void readEntities() throws IOException {
+        if (!m_file.exists()) {
+            return;
+        }
+        m_entities.clear();
+
+        FileInputStream fis = null;
+        ObjectInputStream ois = null;
+
+        try {
+            fis = new FileInputStream(m_file);
+            ois = new ObjectInputStream(fis);
+
+            final int numberOfTenants = ois.readInt();
+            for (int i = 0; i < numberOfTenants; i++) {
+                final TenantEntity tenantEntity = readEntity(ois);
+                m_entities.put(tenantEntity.getId(), tenantEntity);
+            }
+        }
+        finally {
+            FSUtil.closeInputStreamsSafely(ois, fis);
+        }
+    }
+
+    private TenantEntity readEntity(ObjectInputStream ois) throws IOException,
+        UnsupportedEncodingException {
+        final int idLength = ois.readInt();
+        final byte[] idBytes = new byte[idLength];
+        ois.readFully(idBytes);
+        final String id = new String(idBytes, "utf-8");
+
+        final int nameLength = ois.readInt();
+        final byte[] nameBytes = new byte[nameLength];
+        ois.readFully(nameBytes);
+        final String name = new String(nameBytes, "utf-8");
+
+        final int numberOfProperties = ois.readInt();
+        final Map<String, String> properties = new HashMap<String, String>();
+
+        for (int j = 0; j < numberOfProperties; j++) {
+
+            final int keyLength = ois.readInt();
+            final byte[] keyBytes = new byte[keyLength];
+            ois.readFully(keyBytes);
+            final String key = new String(keyBytes, "utf-8");
+
+            final int valueLength = ois.readInt();
+            final byte[] valueBytes = new byte[valueLength];
+            ois.readFully(valueBytes);
+            final String value = new String(valueBytes, "utf-8");
+
+            properties.put(key, value);
+        }
+        return new TenantEntity(id, name, properties);
+    }
+
+    private void writeEntities() throws IOException {
+        if ((m_entities == null || m_entities.size() == 0) && m_file.exists()) 
{
+            m_file.delete();
+        }
+
+        FileOutputStream fos = null;
+        ObjectOutputStream oos = null;
+        try {
+            fos = new FileOutputStream(m_file);
+            oos = new ObjectOutputStream(fos);
+
+            oos.writeInt(m_entities.size());
+            for (TenantEntity entity : m_entities.values()) {
+                writeEntity(oos, entity);
+            }
+            oos.flush();
+        }
+        finally {
+            FSUtil.closeOutputStreamsSafely(oos, fos);
+        }
+    }
+
+    private void writeEntity(ObjectOutputStream oos, TenantEntity entity) 
throws UnsupportedEncodingException,
+        IOException {
+
+        final byte[] id = entity.getId().getBytes("utf-8");
+        oos.writeInt(id.length);
+        oos.write(id);
+
+        final byte[] name = entity.getName().getBytes("utf-8");
+        oos.writeInt(name.length);
+        oos.write(name);
+
+        oos.writeInt(entity.getProperties().size());
+
+        for (String propKey : entity.getProperties().keySet()) {
+
+            final byte[] key = propKey.getBytes("utf-8");
+            oos.writeInt(key.length);
+            oos.write(key);
+
+            final byte[] value = 
entity.getProperties().get(propKey).getBytes("utf-8");
+            oos.writeInt(value.length);
+            oos.write(value);
+        }
+    }
+}

Added: 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSUtil.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/internal/FSUtil.java
  Mon Nov 15 17:41:13 2010
@@ -0,0 +1,58 @@
+/*
+    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.tenantstore.fs.internal;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Some generic utility methods.
+ */
+public final class FSUtil {
+
+    public static void closeInputStreamsSafely(ObjectInputStream ois, 
FileInputStream fis) throws IOException {
+        try {
+            if (ois != null) {
+                ois.close();
+            }
+        }
+        catch (IOException e) {
+            if (fis != null) {
+                fis.close();
+            }
+            throw e;
+        }
+    }
+
+    public static void closeOutputStreamsSafely(ObjectOutputStream oos, 
FileOutputStream fos) throws IOException {
+        try {
+            if (oos != null) {
+                oos.close();
+            }
+        }
+        catch (IOException e) {
+            if (fos != null) {
+                fos.close();
+            }
+            throw e;
+        }
+    }
+
+}

Added: 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/osgi/Activator.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/osgi/Activator.java
   Mon Nov 15 17:41:13 2010
@@ -0,0 +1,66 @@
+/*
+/*
+    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.tenantstore.fs.osgi;
+
+import java.io.File;
+
+import org.amdatu.core.tenant.TenantStorageException;
+import org.amdatu.core.tenant.TenantStorageProvider;
+import org.amdatu.core.tenantstore.fs.service.FSTenantStorageProvider;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class represents the OSGi activator for the tenant service fs storage 
provider. By default it will use the
+ * bundle storage, but can be configured to use an alternative directory by 
setting the system property specified
+ * by <code>FSTenantStorageProvider.STORAGEDIR_PROPERTYNAME</code>.
+ */
+public final class Activator extends DependencyActivatorBase {
+
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws 
Exception {
+
+        File storageDirectory;
+        String dirProperty = 
System.getProperty(FSTenantStorageProvider.STORAGEDIR_PROPERTYNAME);
+        if (dirProperty == null && !"".equals(dirProperty)) {
+            storageDirectory = new File(dirProperty);
+        }
+        else {
+            // Default bundle storage
+            storageDirectory = context.getDataFile("");
+        }
+
+        // Check accessibility
+        if (storageDirectory == null || !storageDirectory.exists() || 
!storageDirectory.canRead()
+            || !storageDirectory.canWrite()) {
+            throw new TenantStorageException("Unable to access storage 
directory:" + storageDirectory.getAbsolutePath());
+        }
+
+        manager.add(
+                createComponent()
+                    .setImplementation(new 
FSTenantStorageProvider(storageDirectory))
+                    .setInterface(TenantStorageProvider.class.getName(), null)
+                    
.add(createServiceDependency().setService(LogService.class).setRequired(false)));
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) 
throws Exception {
+    }
+}

Added: 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/service/FSTenantStorageProvider.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenantstore-fs/src/main/java/org/amdatu/core/tenantstore/fs/service/FSTenantStorageProvider.java
  Mon Nov 15 17:41:13 2010
@@ -0,0 +1,96 @@
+/*
+ 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.tenantstore.fs.service;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.amdatu.core.tenant.TenantEntity;
+import org.amdatu.core.tenant.TenantStorageException;
+import org.amdatu.core.tenant.TenantStorageProvider;
+import org.amdatu.core.tenantstore.fs.internal.FSTenantIdList;
+import org.amdatu.core.tenantstore.fs.internal.FSTenantStore;
+
+/**
+ * Filesystem backed implementation of the <code>TenantStorageProvider</code> 
service interface.
+ */
+public class FSTenantStorageProvider implements TenantStorageProvider {
+
+    public static final String STORAGEDIR_PROPERTYNAME = 
"org.amdatu.core.tenant.storagedir";
+
+    private static final String ENTITYLIST_FILENAME = "tenantIdList.ser";
+    private static final String STORAGEFILE_PREFIX = "t_";
+    private static final String STORAGEFILE_POSTFIX = ".ser";
+
+    private final FSTenantIdList m_FSEntityIdList;
+    private final File m_RootDirectory;
+
+    public FSTenantStorageProvider(final File rootDirectory) throws 
TenantStorageException {
+        if (!rootDirectory.isDirectory() || !rootDirectory.canRead() || 
!rootDirectory.canWrite()) {
+            throw new TenantStorageException("Unable to access root 
directory");
+        }
+        m_RootDirectory = rootDirectory;
+        m_FSEntityIdList = new FSTenantIdList(new File(m_RootDirectory, 
ENTITYLIST_FILENAME));
+    }
+
+    public FSTenantStorageProvider() throws TenantStorageException {
+        this(new File("" + 
System.getProperty(FSTenantStorageProvider.STORAGEDIR_PROPERTYNAME)));
+    }
+
+    public synchronized void store(final TenantEntity entity) throws 
TenantStorageException {
+        FSTenantStore tenantStorageFile = getStorageFile(entity.getId());
+        TenantEntity storedEntity = tenantStorageFile.addEntity(entity);
+        if (storedEntity == null) {
+            m_FSEntityIdList.addTenantId(entity.getId());
+        }
+        tenantStorageFile.save();
+    }
+
+    public synchronized void delete(final TenantEntity entity) throws 
TenantStorageException {
+        FSTenantStore tenantStorageFile = getStorageFile(entity.getId());
+        TenantEntity storeEntity = 
tenantStorageFile.removeEntity(entity.getId());
+        if (storeEntity != null) {
+            m_FSEntityIdList.removeTenantId(entity.getId());
+            tenantStorageFile.save();
+        }
+    }
+
+    public synchronized TenantEntity getById(final String entityId) throws 
TenantStorageException {
+        FSTenantStore tenantStorageFile = getStorageFile(entityId);
+        return tenantStorageFile.getEntity(entityId);
+    }
+
+    public synchronized List<TenantEntity> getAll() throws 
TenantStorageException {
+        final List<String> entityIdList = m_FSEntityIdList.getAll();
+        final List<TenantEntity> tenantEntityList = new 
LinkedList<TenantEntity>();
+        for (String tenantEntityId : entityIdList) {
+            tenantEntityList.add(getById(tenantEntityId));
+        }
+        return tenantEntityList;
+    }
+
+    private FSTenantStore getStorageFile(final String entityId) throws 
TenantStorageException {
+        final int hash = Math.abs(entityId.hashCode());
+        final String subdirName = "" + (hash % 50);
+        final File subdirFile = new File(m_RootDirectory, subdirName);
+        if (!subdirFile.exists()) {
+            subdirFile.mkdir();
+        }
+        return new FSTenantStore(new File(subdirFile, STORAGEFILE_PREFIX + 
hash + STORAGEFILE_POSTFIX));
+    }
+}

Added: 
trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/tenantstore/fs/service/FSTenantStorageProviderTest.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/tenantstore-fs/src/test/java/org/amdatu/core/tenantstore/fs/service/FSTenantStorageProviderTest.java
      Mon Nov 15 17:41:13 2010
@@ -0,0 +1,196 @@
+package org.amdatu.core.tenantstore.fs.service;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.amdatu.core.tenant.TenantEntity;
+import org.amdatu.core.tenant.TenantStorageException;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class FSTenantStorageProviderTest {
+
+    final String JAVA_IO_TMPDIR = System.getProperty("java.io.tmpdir");
+
+    private FSTenantStorageProvider m_TenantBundleStorageProvider;
+    private File m_RootDirectory;
+
+    @Before
+    public void setUp() throws TenantStorageException {
+
+        Random rand = new Random();
+        int randomInt = 1 + rand.nextInt();
+
+        m_RootDirectory =
+            new File(JAVA_IO_TMPDIR + File.separator + 
"FSTenantStorageProviderTest_" + randomInt);
+        if (!m_RootDirectory.exists()) {
+            m_RootDirectory.mkdirs();
+        }
+        m_RootDirectory.deleteOnExit();
+
+        m_TenantBundleStorageProvider = new 
FSTenantStorageProvider(m_RootDirectory);
+    }
+
+    @After
+    public void tearDown() {
+        if (m_RootDirectory != null && m_RootDirectory.exists()) {
+            m_RootDirectory.delete();
+        }
+    }
+
+    /**
+     * Comprehensive test of crud operations on tenants.
+     * 
+     * @throws TenantStorageException
+     */
+    @Test
+    public void testExtensiveCRUD() throws TenantStorageException {
+
+        final int testSize = 179;
+
+        // creating
+        for (int i = 0; i < testSize; i++) {
+            TenantEntity tenantEntity;
+            if (i % 3 == 0) {
+                // mod 3 entities get properties
+                Map<String, String> properties = new HashMap<String, String>();
+                properties.put("key_" + i, "value_" + i);
+                properties.put("key_" + (i + 1), "value_" + (i + 1));
+                tenantEntity = new TenantEntity("" + i, "Bram_" + i, 
properties);
+            }
+            else {
+                tenantEntity = new TenantEntity("" + i, "Bram_" + i);
+            }
+            m_TenantBundleStorageProvider.store(tenantEntity);
+        }
+
+        // testing
+        for (int i = 0; i < testSize; i++) {
+            TenantEntity tenantEntity = 
m_TenantBundleStorageProvider.getById("" + i);
+            Assert.assertEquals("" + i, tenantEntity.getId());
+            Assert.assertEquals("Bram_" + i, tenantEntity.getName());
+            if (i % 3 == 0) {
+                // mod 3 entities have properties
+                Assert.assertEquals(2, tenantEntity.getProperties().size());
+                Assert.assertEquals("value_" + i, 
tenantEntity.getProperties().get("key_" + i));
+                Assert.assertEquals("value_" + (i + 1), 
tenantEntity.getProperties().get("key_" + (i + 1)));
+            }
+        }
+
+        List<TenantEntity> tenantEntityList = 
m_TenantBundleStorageProvider.getAll();
+        Assert.assertEquals(testSize, tenantEntityList.size());
+
+        // updating
+        for (int i = 0; i < testSize; i++) {
+            if (i % 4 == 0) {
+                // mod 4 entities get updated
+                TenantEntity tenantEntity = 
m_TenantBundleStorageProvider.getById("" + i);
+                tenantEntity.setName(tenantEntity.getName() + "_updated");
+                if (i % 12 == 0) {
+                    // mod 12 entities get (new) properties
+                    tenantEntity.putProperty("key_" + i, "newvalue_" + i);
+                }
+
+                m_TenantBundleStorageProvider.store(tenantEntity);
+            }
+        }
+
+        // testing
+        for (int i = 0; i < testSize; i++) {
+            if (i % 4 == 0) {
+                TenantEntity tenantEntity = 
m_TenantBundleStorageProvider.getById("" + i);
+                Assert.assertEquals("Bram_" + i + "_updated", 
tenantEntity.getName());
+                if (i % 3 == 0) {
+                    if (i % 12 != 0) {
+                        Assert.assertEquals("value_" + i, 
tenantEntity.getProperties().get("key_" + i));
+                    }
+                    else {
+                        // mod 12 entities have new properties
+                        Assert.assertEquals("newvalue_" + i, 
tenantEntity.getProperties().get("key_" + i));
+                    }
+                    Assert.assertEquals("value_" + (i + 1), 
tenantEntity.getProperties().get("key_" + (i + 1)));
+                }
+            }
+        }
+
+        List<TenantEntity> tenantEntityList2 = 
m_TenantBundleStorageProvider.getAll();
+        Assert.assertEquals(testSize, tenantEntityList2.size());
+
+        // deleting
+        int deleted = 0;
+        for (int i = 0; i < testSize; i++) {
+            if (i % 5 == 0) {
+                TenantEntity tenantEntity = 
m_TenantBundleStorageProvider.getById("" + i);
+                m_TenantBundleStorageProvider.delete(tenantEntity);
+                deleted++;
+            }
+        }
+
+        // testing
+        for (int i = 0; i < testSize; i++) {
+            if (i % 5 == 0) {
+                TenantEntity tenantEntity = 
m_TenantBundleStorageProvider.getById("" + i);
+                Assert.assertNull(tenantEntity);
+            }
+        }
+
+        List<TenantEntity> tenantEntityList3 = 
m_TenantBundleStorageProvider.getAll();
+        Assert.assertEquals(testSize - deleted, tenantEntityList3.size());
+
+        // test lenient behavior on delete of non existing
+        TenantEntity tenantEntity5 = m_TenantBundleStorageProvider.getById("" 
+ 6);
+        Assert.assertNotNull(tenantEntity5);
+        m_TenantBundleStorageProvider.delete(tenantEntity5);
+        m_TenantBundleStorageProvider.delete(tenantEntity5);
+    }
+
+    /**
+     * Testing behavior on tenantEntities with same hashcode() for the id. 
This is based on knowledge of the
+     * implementation but as it is a likely pitfall let's test it anyway to 
catch future mistakes.
+     * 
+     * @throws TenantStorageException
+     */
+    @Test
+    public void testWithEqualHashcodes() throws TenantStorageException {
+
+        // Assert the reason for this test
+        Assert.assertEquals("BB".hashCode(), "Aa".hashCode());
+
+        TenantEntity tenantEntity1 = new TenantEntity("Aa", "Bram");
+        m_TenantBundleStorageProvider.store(tenantEntity1);
+
+        TenantEntity tenantEntity2 = new TenantEntity("BB", "Pipo");
+        m_TenantBundleStorageProvider.store(tenantEntity2);
+
+        TenantEntity tenantEntity3 = 
m_TenantBundleStorageProvider.getById("Aa");
+        Assert.assertEquals("Aa", tenantEntity3.getId());
+        Assert.assertEquals("Bram", tenantEntity3.getName());
+
+        TenantEntity tenantEntity4 = 
m_TenantBundleStorageProvider.getById("BB");
+        Assert.assertEquals("BB", tenantEntity4.getId());
+        Assert.assertEquals("Pipo", tenantEntity4.getName());
+
+        List<TenantEntity> tenantEntityList1 = 
m_TenantBundleStorageProvider.getAll();
+        Assert.assertEquals(2, tenantEntityList1.size());
+
+        m_TenantBundleStorageProvider.delete(tenantEntity4);
+
+        List<TenantEntity> tenantEntityList2 = 
m_TenantBundleStorageProvider.getAll();
+        Assert.assertEquals(1, tenantEntityList2.size());
+
+        TenantEntity tenantEntity5 = 
m_TenantBundleStorageProvider.getById("Aa");
+        Assert.assertEquals("Aa", tenantEntity5.getId());
+        Assert.assertEquals("Bram", tenantEntity5.getName());
+
+        TenantEntity tenantEntity6 = 
m_TenantBundleStorageProvider.getById("BB");
+        Assert.assertNull(tenantEntity6);
+
+        // test lenient behavior on delete of non existing
+        m_TenantBundleStorageProvider.delete(tenantEntity4);
+    }
+}

Reply via email to