Simplified storage API. Throws RuntimeException if trying to write to a

read-only storage.
This commit is contained in:
nathan 2017-08-04 16:26:32 +02:00
parent ec84231b1b
commit 85a745dc3a
4 changed files with 39 additions and 374 deletions

View File

@ -36,6 +36,7 @@ import dorkbox.util.SerializationManager;
*/
@SuppressWarnings({"Convert2Diamond", "Convert2Lambda"})
class DiskStorage implements Storage {
// null if we are a read-only storage
private final DelayTimer timer;
private final StorageBase storage;
@ -60,16 +61,16 @@ class DiskStorage implements Storage {
@Override
public
void run() {
ReentrantLock actionLock2 = DiskStorage.this.actionLock;
ReentrantLock actionLock = DiskStorage.this.actionLock;
Map<StorageKey, Object> actions;
try {
actionLock2.lock();
actionLock.lock();
// do a fast swap on the actionMap.
actions = DiskStorage.this.actionMap;
DiskStorage.this.actionMap = new ConcurrentHashMap<StorageKey, Object>();
} finally {
actionLock2.unlock();
actionLock.unlock();
}
DiskStorage.this.storage.doActionThings(actions);
@ -108,32 +109,13 @@ class DiskStorage implements Storage {
*/
@Override
public final
boolean contains(String key) {
boolean contains(StorageKey key) {
if (!this.isOpen.get()) {
throw new RuntimeException("Unable to act on closed storage");
}
final StorageKey wrap = new StorageKey(key);
// check if our pending actions has it, or if our storage index has it
return this.actionMap.containsKey(wrap) || this.storage.contains(wrap);
}
/**
* Reads a object using the specific key, and casts it to the expected class
*/
@Override
public final
<T> T get(String key) {
return get0(new StorageKey(key));
}
/**
* Reads a object using the specific key, and casts it to the expected class
*/
@Override
public final
<T> T get(byte[] key) {
return get0(new StorageKey(key));
return this.actionMap.containsKey(key) || this.storage.contains(key);
}
/**
@ -146,31 +128,7 @@ class DiskStorage implements Storage {
}
/**
* Returns the saved data for the specified key. Also saves the data.
*
* @param data If there is no object in the DB with the specified key, this value will be the default (and will be saved to the db)
*/
@Override
public
<T> T get(String key, T data) {
StorageKey wrap = new StorageKey(key);
return get(wrap, data);
}
/**
* Returns the saved data for the specified key. Also saves the data.
*
* @param data If there is no object in the DB with the specified key, this value will be the default (and will be saved to the db)
*/
@Override
public
<T> T get(byte[] key, T data) {
return get(new StorageKey(key), data);
}
/**
* Returns the saved data (or null) for the specified key. Also saves the data.
* Returns the saved data (or null) for the specified key. Also saves the data as default data.
*
* @param data If there is no object in the DB with the specified key, this value will be the default (and will be saved to the db)
*
@ -184,7 +142,7 @@ class DiskStorage implements Storage {
if (source == null) {
// returned was null, so we should save the default value
putAndSave(key, data);
put(key, data);
return data;
}
else {
@ -236,30 +194,6 @@ class DiskStorage implements Storage {
return this.storage.get(key);
}
/**
* Saves the given data to storage with the associated key.
* <p/>
* Also will update existing data. If the new contents do not fit in the original space, then the update is handled by
* deleting the old data and adding the new.
*/
@Override
public final
void put(String key, Object object) {
put(new StorageKey(key), object);
}
/**
* Saves the given data to storage with the associated key.
* <p/>
* Also will update existing data. If the new contents do not fit in the original space, then the update is handled by
* deleting the old data and adding the new.
*/
@Override
public final
void put(byte[] key, Object object) {
put(new StorageKey(key), object);
}
/**
* Saves the given data to storage with the associated key.
* <p/>
@ -274,34 +208,24 @@ class DiskStorage implements Storage {
}
if (timer != null) {
action(key, object);
try {
this.actionLock.lock();
if (object != null) {
// push action to map
this.actionMap.put(key, object);
}
else {
this.actionMap.remove(key);
}
} finally {
this.actionLock.unlock();
}
// timer action runs on TIMER thread, not this thread
this.timer.delay(this.milliSeconds);
}
}
/**
* Deletes an object from storage.
*
* @return true if the delete was successful. False if there were problems deleting the data.
*/
@Override
public final
boolean delete(String key) {
if (!this.isOpen.get()) {
throw new RuntimeException("Unable to act on closed storage");
}
StorageKey wrap = new StorageKey(key);
// timer action runs on THIS thread, not timer thread
if (timer != null) {
this.timer.delay(0L);
return this.storage.delete(wrap);
}
else {
return false;
} else {
throw new RuntimeException("Unable to put on a read-only storage");
}
}
@ -323,13 +247,14 @@ class DiskStorage implements Storage {
return this.storage.delete(key);
}
else {
return false;
throw new RuntimeException("Unable to delete on a read-only storage");
}
}
/**
* Closes and removes this storage from the storage system. This is the same as calling {@link StorageSystem#close(Storage)}
*/
@Override
public
void close() {
StorageSystem.close(this);
@ -433,24 +358,6 @@ class DiskStorage implements Storage {
this.storage.setVersion(version);
}
private
void action(StorageKey key, Object object) {
try {
this.actionLock.lock();
if (object != null) {
// push action to map
this.actionMap.put(key, object);
} else {
this.actionMap.remove(key);
}
} finally {
this.actionLock.unlock();
}
}
void increaseReference() {
this.references.incrementAndGet();
}
@ -483,66 +390,8 @@ class DiskStorage implements Storage {
// timer action runs on THIS thread, not timer thread
if (timer != null) {
this.timer.delay(0L);
}
}
/**
* Adds a key/value pair to the storage, then saves the storage to disk, immediately.
* <p/>
* This will save ALL of the pending save actions to the file
*/
@Override
public
void putAndSave(final String key, final Object object) {
if (!this.isOpen.get()) {
throw new RuntimeException("Unable to act on closed storage");
}
// timer action runs on THIS thread, not timer thread
if (timer != null) {
action(new StorageKey(key), object);
this.timer.delay(0L);
}
}
/**
* Adds a key/value pair to the storage, then save the storage to disk, immediately.
* <p/>
* This will save ALL of the pending save actions to the file
*/
@Override
public
void putAndSave(final byte[] key, final Object object) {
if (!this.isOpen.get()) {
throw new RuntimeException("Unable to act on closed storage");
}
if (timer != null) {
action(new StorageKey(key), object);
// timer action runs on THIS thread, not timer thread
this.timer.delay(0L);
}
}
/**
* Adds a key/value pair to the storage, then save the storage to disk, immediately.
* <p/>
* This will save ALL of the pending save actions to the file
*/
@Override
public
void putAndSave(final StorageKey key, final Object object) {
if (!this.isOpen.get()) {
throw new RuntimeException("Unable to act on closed storage");
}
if (timer != null) {
action(key, object);
// timer action runs on THIS thread, not timer thread
this.timer.delay(0L);
} else {
throw new RuntimeException("Unable to save on a read-only storage");
}
}
}

View File

@ -45,26 +45,8 @@ class MemoryStorage implements Storage {
*/
@Override
public
boolean contains(final String key) {
return storage.containsKey(new StorageKey(key));
}
/**
* Reads a object using the specific key, and casts it to the expected class
*/
@Override
public
<T> T get(final String key) {
return get(new StorageKey(key));
}
/**
* Reads a object using the specific key, and casts it to the expected class
*/
@Override
public
<T> T get(final byte[] key) {
return get(new StorageKey(key));
boolean contains(final StorageKey key) {
return storage.containsKey(key);
}
/**
@ -77,30 +59,6 @@ class MemoryStorage implements Storage {
return (T) storage.get(key);
}
/**
* Returns the saved data for the specified key.
*
* @param data If there is no object in the DB with the specified key, this value will be the default (and will be saved to the db)
*/
@Override
public
<T> T get(String key, T data) {
StorageKey wrap = new StorageKey(key);
return get(wrap, data);
}
/**
* Returns the saved data for the specified key.
*
* @param data If there is no object in the DB with the specified key, this value will be the default (and will be saved to the db)
*/
@Override
public
<T> T get(byte[] key, T data) {
return get(new StorageKey(key), data);
}
@SuppressWarnings("unchecked")
@Override
public
@ -113,30 +71,6 @@ class MemoryStorage implements Storage {
return (T) o;
}
/**
* Saves the given data to storage with the associated key.
* <p/>
* Also will update existing data. If the new contents do not fit in the original space, then the update is handled by
* deleting the old data and adding the new.
*/
@Override
public
void put(final String key, final Object data) {
put(new StorageKey(key), data);
}
/**
* Saves the given data to storage with the associated key.
* <p/>
* Also will update existing data. If the new contents do not fit in the original space, then the update is handled by
* deleting the old data and adding the new.
*/
@Override
public
void put(final byte[] key, final Object data) {
put(new StorageKey(key), data);
}
/**
* Saves the given data to storage with the associated key.
* <p/>
@ -149,17 +83,6 @@ class MemoryStorage implements Storage {
storage.put(key, object);
}
/**
* Deletes an object from storage.
*
* @return true if the delete was successful. False if there were problems deleting the data.
*/
@Override
public
boolean delete(final String key) {
return delete(new StorageKey(key));
}
/**
* Deletes an object from storage.
*
@ -247,43 +170,6 @@ class MemoryStorage implements Storage {
// no-op
}
/**
* Adds a key/value pair to the storage.
* <p/>
* There is no file that backs this storage, so writes are immediate and saves do nothing
*/
@Override
public
void putAndSave(final String key, final Object object) {
put(key, object);
// no-save!
}
/**
/**
* Adds a key/value pair to the storage.
* <p/>
* There is no file that backs this storage, so writes are immediate and saves do nothing
*/
@Override
public
void putAndSave(final byte[] key, final Object object) {
put(key, object);
// no save because we are in memory!
}
/**
* Adds a key/value pair to the storage.
* <p/>
* There is no file that backs this storage, so writes are immediate and saves do nothing
*/
@Override
public
void putAndSave(final StorageKey key, final Object object) {
put(key, object);
// no save because we are in memory!
}
/**
* In-memory storage systems do not have a backing file, so there is nothing to close
*/

View File

@ -31,39 +31,13 @@ interface Storage {
/**
* Checks if there is a object corresponding to the given key.
*/
boolean contains(String key);
/**
* Reads a object using the specific key, and casts it to the expected class.
*/
<T> T get(String key);
/**
* Reads a object using the specific key, and casts it to the expected class
*/
<T> T get(byte[] key);
boolean contains(StorageKey key);
/**
* Reads a object using the specific key, and casts it to the expected class
*/
<T> T get(StorageKey key);
/**
* Returns the saved data for the specified key.
*
* @param key The key used to check if data already exists.
* @param data This is the default value, and if there is no value with the key in the DB this default value will be saved.
*/
<T> T get(String key, T data);
/**
* Returns the saved data for the specified key.
*
* @param key The key used to check if data already exists.
* @param data This is the default value, and if there is no value with the key in the DB this default value will be saved.
*/
<T> T get(byte[] key, T data);
/**
* Returns the saved data for the specified key.
*
@ -72,22 +46,6 @@ interface Storage {
*/
<T> T get(StorageKey key, T data);
/**
* Saves the given data to storage with the associated key.
* <p/>
* Also will update existing data. If the new contents do not fit in the original space, then the update is handled by
* deleting the old data and adding the new.
*/
void put(String key, Object data);
/**
* Saves the given data to storage with the associated key.
* <p/>
* Also will update existing data. If the new contents do not fit in the original space, then the update is handled by
* deleting the old data and adding the new.
*/
void put(byte[] key, Object data);
/**
* Saves the given data to storage with the associated key.
* <p/>
@ -96,13 +54,6 @@ interface Storage {
*/
void put(StorageKey key, Object data);
/**
* Deletes an object from storage.
*
* @return true if the delete was successful. False if there were problems deleting the data.
*/
boolean delete(String key);
/**
* Deletes an object from storage.
*
@ -154,27 +105,6 @@ interface Storage {
*/
void save();
/**
* Adds a key/value pair to the storage, then saves the storage immediately.
* <p/>
* This will save ALL of the pending save actions to the file
*/
void putAndSave(String key, Object object);
/**
* Adds a key/value pair to the storage, then saves the storage immediately.
* <p/>
* This will save ALL of the pending save actions to the file
*/
void putAndSave(byte[] key, Object object);
/**
* Adds a key/value pair to the storage, then saves the storage immediately.
* <p/>
* This will save ALL of the pending save actions to the file
*/
void putAndSave(StorageKey key, Object object);
/**
* Closes this storage system
*/

View File

@ -286,7 +286,7 @@ class StorageTest {
// now test loading data
Data data = new Data();
String createKey = createKey(63);
StorageKey createKey = createKey(63);
makeData(data);
storage.put(createKey, data);
@ -453,7 +453,7 @@ class StorageTest {
for (int i = 0; i < total; i++) {
Data data = new Data();
makeData(data);
String createKey = createKey(i);
StorageKey createKey = createKey(i);
storage.put(createKey, data);
}
@ -466,7 +466,7 @@ class StorageTest {
.file(TEST_DB)
.build();
for (int i = 0; i < total; i++) {
String createKey = createKey(i);
StorageKey createKey = createKey(i);
Data data2;
data2 = storage.get(createKey, new Data());
@ -489,7 +489,7 @@ class StorageTest {
public static
String add(Storage storage, int number) throws IOException {
String record1Data = createData(number);
String record1Key = createKey(number);
StorageKey record1Key = createKey(number);
log("adding record " + number + "...");
storage.put(record1Key, record1Data);
@ -498,7 +498,7 @@ class StorageTest {
public static
String readRecord(Storage storage, int number) throws ClassNotFoundException, IOException {
String record1Key = createKey(number);
StorageKey record1Key = createKey(number);
log("reading record " + number + "...");
@ -509,7 +509,7 @@ class StorageTest {
public static
void deleteRecord(Storage storage, int nNumber) throws ClassNotFoundException, IOException {
String record1Key = createKey(nNumber);
StorageKey record1Key = createKey(nNumber);
log("deleting record " + nNumber + "...");
storage.delete(record1Key);
@ -517,7 +517,7 @@ class StorageTest {
private static
String updateRecord(Storage storage, int number, String newData) throws IOException {
String record1Key = createKey(number);
StorageKey record1Key = createKey(number);
log("updating record " + number + "...");
storage.put(record1Key, newData);
@ -526,8 +526,8 @@ class StorageTest {
}
private static
String createKey(int number) {
return "foo" + number;
StorageKey createKey(int number) {
return new StorageKey("foo" + number);
}