Hi, Good to see we have a file-based storage for our tenants now!
Still, there are some things I don't really understand. - I noticed the storage provider can be configured using a system property; why not use the ConfigurationAdmin for this? - The FSTSP synchronizes all its methods; this it not necessary, as the TenantStorageProvider javadoc states 'The TenantStorageProvider can assume that synchronization will be handled externally.' - It seems you use finally a little differently than I do (e.g., in FSTentantIdList, the m_file is final, but the m_tenantIdList is not). Our coding guideline does not state anything about this; should we include that? Something like "members should be made final if they can, and local variables should only be made final when they have to be". Angelo On Nov 15, 2010, at 5:41 PM, <subversion at amdatu.org> <subversion at amdatu.org> wrote: > 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); > + } > +} > _______________________________________________ > Amdatu-commits mailing list > Amdatu-commits at amdatu.org > http://lists.amdatu.org/mailman/listinfo/amdatu-commits

