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);
+ }
+}