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"}) @SuppressWarnings({"Convert2Diamond", "Convert2Lambda"})
class DiskStorage implements Storage { class DiskStorage implements Storage {
// null if we are a read-only storage
private final DelayTimer timer; private final DelayTimer timer;
private final StorageBase storage; private final StorageBase storage;
@ -60,16 +61,16 @@ class DiskStorage implements Storage {
@Override @Override
public public
void run() { void run() {
ReentrantLock actionLock2 = DiskStorage.this.actionLock; ReentrantLock actionLock = DiskStorage.this.actionLock;
Map<StorageKey, Object> actions; Map<StorageKey, Object> actions;
try { try {
actionLock2.lock(); actionLock.lock();
// do a fast swap on the actionMap. // do a fast swap on the actionMap.
actions = DiskStorage.this.actionMap; actions = DiskStorage.this.actionMap;
DiskStorage.this.actionMap = new ConcurrentHashMap<StorageKey, Object>(); DiskStorage.this.actionMap = new ConcurrentHashMap<StorageKey, Object>();
} finally { } finally {
actionLock2.unlock(); actionLock.unlock();
} }
DiskStorage.this.storage.doActionThings(actions); DiskStorage.this.storage.doActionThings(actions);
@ -108,32 +109,13 @@ class DiskStorage implements Storage {
*/ */
@Override @Override
public final public final
boolean contains(String key) { boolean contains(StorageKey key) {
if (!this.isOpen.get()) { if (!this.isOpen.get()) {
throw new RuntimeException("Unable to act on closed storage"); 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 // check if our pending actions has it, or if our storage index has it
return this.actionMap.containsKey(wrap) || this.storage.contains(wrap); return this.actionMap.containsKey(key) || this.storage.contains(key);
}
/**
* 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));
} }
/** /**
@ -146,31 +128,7 @@ class DiskStorage implements Storage {
} }
/** /**
* Returns the saved data 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)
*/
@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.
* *
* @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) * @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) { if (source == null) {
// returned was null, so we should save the default value // returned was null, so we should save the default value
putAndSave(key, data); put(key, data);
return data; return data;
} }
else { else {
@ -236,30 +194,6 @@ class DiskStorage implements Storage {
return this.storage.get(key); 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. * Saves the given data to storage with the associated key.
* <p/> * <p/>
@ -274,34 +208,24 @@ class DiskStorage implements Storage {
} }
if (timer != null) { 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 // timer action runs on TIMER thread, not this thread
this.timer.delay(this.milliSeconds); this.timer.delay(this.milliSeconds);
} } else {
} throw new RuntimeException("Unable to put on a read-only storage");
/**
* 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;
} }
} }
@ -323,13 +247,14 @@ class DiskStorage implements Storage {
return this.storage.delete(key); return this.storage.delete(key);
} }
else { 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)} * Closes and removes this storage from the storage system. This is the same as calling {@link StorageSystem#close(Storage)}
*/ */
@Override
public public
void close() { void close() {
StorageSystem.close(this); StorageSystem.close(this);
@ -433,24 +358,6 @@ class DiskStorage implements Storage {
this.storage.setVersion(version); 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() { void increaseReference() {
this.references.incrementAndGet(); this.references.incrementAndGet();
} }
@ -483,66 +390,8 @@ class DiskStorage implements Storage {
// timer action runs on THIS thread, not timer thread // timer action runs on THIS thread, not timer thread
if (timer != null) { if (timer != null) {
this.timer.delay(0L); this.timer.delay(0L);
} } else {
} throw new RuntimeException("Unable to save on a read-only storage");
/**
* 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);
} }
} }
} }

View File

@ -45,26 +45,8 @@ class MemoryStorage implements Storage {
*/ */
@Override @Override
public public
boolean contains(final String key) { boolean contains(final StorageKey key) {
return storage.containsKey(new StorageKey(key)); return storage.containsKey(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));
} }
/** /**
@ -77,30 +59,6 @@ class MemoryStorage implements Storage {
return (T) storage.get(key); 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") @SuppressWarnings("unchecked")
@Override @Override
public public
@ -113,30 +71,6 @@ class MemoryStorage implements Storage {
return (T) o; 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. * Saves the given data to storage with the associated key.
* <p/> * <p/>
@ -149,17 +83,6 @@ class MemoryStorage implements Storage {
storage.put(key, object); 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. * Deletes an object from storage.
* *
@ -247,43 +170,6 @@ class MemoryStorage implements Storage {
// no-op // 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 * 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. * Checks if there is a object corresponding to the given key.
*/ */
boolean contains(String key); boolean contains(StorageKey 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);
/** /**
* Reads a object using the specific key, and casts it to the expected class * Reads a object using the specific key, and casts it to the expected class
*/ */
<T> T get(StorageKey key); <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. * Returns the saved data for the specified key.
* *
@ -72,22 +46,6 @@ interface Storage {
*/ */
<T> T get(StorageKey key, T data); <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. * Saves the given data to storage with the associated key.
* <p/> * <p/>
@ -96,13 +54,6 @@ interface Storage {
*/ */
void put(StorageKey key, Object data); 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. * Deletes an object from storage.
* *
@ -154,27 +105,6 @@ interface Storage {
*/ */
void save(); 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 * Closes this storage system
*/ */

View File

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