This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new c34f187 Config API refactoring.
c34f187 is described below
commit c34f1870a57be83f430ea00e04110b5f43bb51b8
Author: JamesBognar <[email protected]>
AuthorDate: Fri Feb 16 14:41:24 2018 -0500
Config API refactoring.
---
.../org/apache/juneau/config/store/FileStore.java | 218 ++++++++++++++-------
.../juneau/config/store/FileStoreBuilder.java | 42 ++++
.../apache/juneau/config/store/MemoryStore.java | 22 ++-
.../java/org/apache/juneau/config/store/Store.java | 2 +-
.../{MemoryStore.java => WatcherSensitivity.java} | 73 ++-----
.../apache/juneau/config/store/FileStoreTest.java | 134 +++++++++++++
.../juneau/config/store/MemoryStoreTest.java} | 103 +++++-----
7 files changed, 409 insertions(+), 185 deletions(-)
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStore.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStore.java
index d8ee346..75f50ec 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStore.java
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStore.java
@@ -13,6 +13,8 @@
package org.apache.juneau.config.store;
import static java.nio.file.StandardWatchEventKinds.*;
+import static java.nio.file.StandardOpenOption.*;
+import static org.apache.juneau.internal.StringUtils.*;
import java.io.*;
import java.nio.*;
@@ -107,6 +109,32 @@ public class FileStore extends Store {
public static final String FILESTORE_useWatcher = PREFIX +
"useWatcher.s";
/**
+ * Configuration property: Watcher sensitivity.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"FileStore.watcherSensitivity.s"</js>
+ * <li><b>Data type:</b> {@link WatcherSensitivity}
+ * <li><b>Default:</b> {@link WatcherSensitivity#MEDIUM}
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
FileStoreBuilder#watcherSensitivity(WatcherSensitivity)}
+ * <li class='jm'>{@link
FileStoreBuilder#watcherSensitivity(String)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Determines how frequently the file system is polled for updates.
+ *
+ * <h5 class='section'>Notes:</h5>
+ * <ul class='spaced-list'>
+ * <li>This relies on internal Sun packages and may not work on
all JVMs.
+ * </ul>
+ */
+ public static final String FILESTORE_watcherSensitivity = PREFIX +
"watcherSensitivity.s";
+
+ /**
* Configuration property: Config file extension.
*
* <h5 class='section'>Property:</h5>
@@ -126,6 +154,19 @@ public class FileStore extends Store {
*/
public static final String FILESTORE_ext = PREFIX + "ext.s";
+
+
//-------------------------------------------------------------------------------------------------------------------
+ // Predefined instances
+
//-------------------------------------------------------------------------------------------------------------------
+
+ /** Default file store, all default values.*/
+ public static final FileStore DEFAULT = FileStore.create().build();
+
+
+
//-------------------------------------------------------------------------------------------------------------------
+ // Instance
+
//-------------------------------------------------------------------------------------------------------------------
+
/**
* Create a new builder for this object.
*
@@ -155,52 +196,137 @@ public class FileStore extends Store {
super(ps);
try {
dir = new File(getStringProperty(FILESTORE_directory,
".")).getCanonicalFile();
+ dir.mkdirs();
ext = getStringProperty(FILESTORE_ext, "cfg");
charset = getProperty(FILESTORE_charset, Charset.class,
Charset.defaultCharset());
- watcher = getBooleanProperty(FILESTORE_useWatcher,
false) ? new WatcherThread(dir) : null;
+ WatcherSensitivity ws =
getProperty(FILESTORE_watcherSensitivity, WatcherSensitivity.class,
WatcherSensitivity.MEDIUM);
+ watcher = getBooleanProperty(FILESTORE_useWatcher,
false) ? new WatcherThread(dir, ws) : null;
if (watcher != null)
watcher.start();
- } catch (IOException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
}
+ @Override /* Store */
+ public synchronized String read(String name) throws Exception {
+ String s = cache.get(name);
+ if (s != null)
+ return s;
+
+ dir.mkdirs();
+ Path p = dir.toPath().resolve(name + '.' + ext);
+ if (! Files.exists(p))
+ return null;
+ try (FileChannel fc = FileChannel.open(p, READ, WRITE, CREATE))
{
+ try (FileLock lock = fc.lock()) {
+ ByteBuffer buf = ByteBuffer.allocate(1024);
+ StringBuilder sb = new StringBuilder();
+ while (fc.read(buf) != -1) {
+
sb.append(charset.decode((ByteBuffer)(buf.flip())));
+ buf.clear();
+ }
+ s = sb.toString();
+ cache.put(name, s);
+ }
+ }
+
+ return cache.get(name);
+ }
+
+ @Override /* Store */
+ public synchronized boolean write(String name, String oldContents,
String newContents) throws Exception {
+ dir.mkdirs();
+ Path p = dir.toPath().resolve(name + '.' + ext);
+ boolean exists = Files.exists(p);
+ if (oldContents != null && ! exists)
+ return false;
+ try (FileChannel fc = FileChannel.open(p, READ, WRITE, CREATE))
{
+ try (FileLock lock = fc.lock()) {
+ String currentContents = null;
+ if (exists) {
+ ByteBuffer buf =
ByteBuffer.allocate(1024);
+ StringBuilder sb = new StringBuilder();
+ while (fc.read(buf) != -1) {
+
sb.append(charset.decode((ByteBuffer)(buf.flip())));
+ buf.clear();
+ }
+ currentContents = sb.toString();
+ }
+ if (! isEquals(oldContents, currentContents)) {
+ if (currentContents == null)
+ cache.remove(name);
+ else
+ cache.put(name,
currentContents);
+ return false;
+ }
+ fc.position(0);
+ fc.write(charset.encode(newContents));
+ cache.put(name, newContents);
+ }
+ }
+ return true;
+ }
+
+ @Override /* Store */
+ public synchronized FileStore update(String name, String newContents) {
+ cache.put(name, newContents);
+ super.update(name, newContents);
+ return this;
+ }
+
@Override /* Closeable */
public synchronized void close() {
if (watcher != null)
watcher.interrupt();
}
+
//---------------------------------------------------------------------------------------------
// WatcherThread
//---------------------------------------------------------------------------------------------
final class WatcherThread extends Thread {
private final WatchService watchService;
-
- WatcherThread(File dir) throws IOException {
+
+ WatcherThread(File dir, WatcherSensitivity s) throws Exception {
watchService =
FileSystems.getDefault().newWatchService();
- dir.toPath().register(watchService, ENTRY_CREATE,
ENTRY_DELETE, ENTRY_MODIFY);
+ WatchEvent.Kind<?>[] kinds = new
WatchEvent.Kind[]{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY};
+ WatchEvent.Modifier modifier = lookupModifier(s);
+ dir.toPath().register(watchService, kinds, modifier);
+ }
+
+ @SuppressWarnings("restriction")
+ private WatchEvent.Modifier lookupModifier(WatcherSensitivity
s) {
+ try {
+ switch(s) {
+ case LOW: return
com.sun.nio.file.SensitivityWatchEventModifier.LOW;
+ case MEDIUM: return
com.sun.nio.file.SensitivityWatchEventModifier.MEDIUM;
+ case HIGH: return
com.sun.nio.file.SensitivityWatchEventModifier.HIGH;
+ }
+ } catch (Exception e) {
+ /* Ignore */
+ }
+ return null;
+
}
@SuppressWarnings("unchecked")
@Override /* Thread */
public void run() {
try {
- while (true) {
- WatchKey key = watchService.take();
-
- for (WatchEvent<?> event: key.pollEvents())
{
+ WatchKey key;
+ while ((key = watchService.take()) != null) {
+ for (WatchEvent<?> event :
key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
-
if (kind != OVERFLOW)
FileStore.this.onFileEvent(((WatchEvent<Path>)event));
- }
-
+ }
if (! key.reset())
break;
}
} catch (Exception e) {
+ e.printStackTrace();
throw new RuntimeException(e);
}
};
@@ -217,65 +343,25 @@ public class FileStore extends Store {
}
}
- synchronized void onFileEvent(WatchEvent<Path> e) throws IOException {
- File f = e.context().toFile();
- String fn = f.getName();
+ /**
+ * Gets called when the watcher service on this store is triggered with
a file system change.
+ *
+ * @param e The file system event.
+ * @throws Exception
+ */
+ protected synchronized void onFileEvent(WatchEvent<Path> e) throws
Exception {
+ String fn = e.context().getFileName().toString();
String bn = FileUtils.getBaseName(fn);
String ext = FileUtils.getExtension(fn);
- if (ext.equals(ext)) {
- String newContents = IOUtils.read(f);
+
+ if (isEquals(this.ext, ext)) {
String oldContents = cache.get(bn);
- if (! StringUtils.isEquals(oldContents, newContents)) {
- onChange(bn, newContents);
- cache.put(bn, newContents);
+ cache.remove(bn);
+ String newContents = read(bn);
+ if (! isEquals(oldContents, newContents)) {
+ update(bn, newContents);
}
}
}
- @Override
- public synchronized String read(String name) throws Exception {
- String s = cache.get(name);
- if (s != null)
- return s;
-
- File f = new File(dir, name + '.' + ext);
- if (f.exists()) {
- try (FileInputStream fis = new FileInputStream(f)) {
- try (FileLock lock = fis.getChannel().lock()) {
- try (Reader r = new
InputStreamReader(fis, charset)) {
- String contents =
IOUtils.read(r);
- cache.put(name, contents);
- }
- }
- }
- }
-
- return cache.get(name);
- }
-
- @Override
- public synchronized boolean write(String name, String oldContents,
String newContents) throws Exception {
- File f = new File(dir, name + '.' + ext);
- try (FileChannel fc = FileChannel.open(f.toPath(),
StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- try (FileLock lock = fc.lock()) {
- ByteBuffer buf = ByteBuffer.allocate(1024);
- StringBuilder sb = new StringBuilder();
- while (fc.read(buf) != -1) {
- sb.append(charset.decode(buf));
-
sb.append(charset.decode((ByteBuffer)(buf.flip())));
- buf.clear();
- }
- String s = sb.toString();
- if (! StringUtils.isEquals(oldContents,
sb.toString())) {
- cache.put(name, s);
- return false;
- }
- fc.position(0);
- fc.write(charset.encode(newContents));
- cache.put(name, newContents);
- return true;
- }
-
- }
- }
}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStoreBuilder.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStoreBuilder.java
index f949085..0ae98c0 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStoreBuilder.java
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/FileStoreBuilder.java
@@ -169,6 +169,48 @@ public class FileStoreBuilder extends StoreBuilder {
}
/**
+ * Configuration property: Watcher sensitivity.
+ *
+ * <p>
+ * Determines how frequently the file system is polled for updates.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_watcherSensitivity}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is {@link WatcherSensitivity#MEDIUM}
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder watcherSensitivity(WatcherSensitivity value) {
+ super.set(FILESTORE_watcherSensitivity, value);
+ return this;
+ }
+
+ /**
+ * Configuration property: Watcher sensitivity.
+ *
+ * <p>
+ * Determines how frequently the file system is polled for updates.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_watcherSensitivity}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is {@link WatcherSensitivity#MEDIUM}
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder watcherSensitivity(String value) {
+ super.set(FILESTORE_watcherSensitivity, value);
+ return this;
+ }
+
+ /**
* Configuration property: Config file extension.
*
* <p>
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
index f24e642..8dde322 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
@@ -27,6 +27,18 @@ import org.apache.juneau.*;
*/
public class MemoryStore extends Store {
+
//-------------------------------------------------------------------------------------------------------------------
+ // Predefined instances
+
//-------------------------------------------------------------------------------------------------------------------
+
+ /** Default memory store, all default values.*/
+ public static final MemoryStore DEFAULT = MemoryStore.create().build();
+
+
+
//-------------------------------------------------------------------------------------------------------------------
+ // Instance
+
//-------------------------------------------------------------------------------------------------------------------
+
/**
* Create a new builder for this object.
*
@@ -66,12 +78,20 @@ public class MemoryStore extends Store {
if (! isEquals(s, newContents)) {
cache.put(name, newContents);
- onChange(name, newContents);
+ update(name, newContents);
}
return true;
}
+
+ @Override /* Store */
+ public synchronized MemoryStore update(String name, String newContents)
{
+ cache.put(name, newContents);
+ super.update(name, newContents);
+ return this;
+ }
+
/**
* No-op.
*/
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/Store.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/Store.java
index 7e16c21..6b8488c 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/Store.java
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/Store.java
@@ -93,7 +93,7 @@ public abstract class Store extends Context implements
Closeable {
* @param contents The new contents.
* @return This object (for method chaining).
*/
- protected Store onChange(String name, String contents) {
+ public Store update(String name, String contents) {
for (StoreListener l : listeners)
l.onChange(name, contents);
return this;
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/WatcherSensitivity.java
similarity index 52%
copy from
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
copy to
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/WatcherSensitivity.java
index f24e642..ad789a5 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/WatcherSensitivity.java
@@ -12,71 +12,22 @@
//
***************************************************************************************************************************
package org.apache.juneau.config.store;
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.io.*;
-import java.util.concurrent.*;
-
-import org.apache.juneau.*;
-
/**
- * Filesystem-based storage location for configuration files.
+ * Determines how often the file system is polled by the watcher in {@link
FileStore}.
*
- * <p>
- * Points to a file system directory containing configuration files.
+ * <h5 class='section'>Notes:</h5>
+ * <ul class='spaced-list'>
+ * <li>This relies on internal Sun packages and may not work on all JVMs.
+ * </ul>
*/
-public class MemoryStore extends Store {
-
- /**
- * Create a new builder for this object.
- *
- * @return A new builder for this object.
- */
- public static MemoryStoreBuilder create() {
- return new MemoryStoreBuilder();
- }
+public enum WatcherSensitivity {
- @Override /* Context */
- public MemoryStoreBuilder builder() {
- return new MemoryStoreBuilder(getPropertyStore());
- }
-
- private final ConcurrentHashMap<String,String> cache = new
ConcurrentHashMap<>();
+ /** 30 seconds */
+ LOW,
- /**
- * Constructor.
- *
- * @param ps The settings for this content store.
- */
- protected MemoryStore(PropertyStore ps) {
- super(ps);
- }
+ /** 10 seconds */
+ MEDIUM,
- @Override /* Store */
- public synchronized String read(String name) throws Exception {
- return cache.get(name);
- }
-
- @Override /* Store */
- public synchronized boolean write(String name, String oldContents,
String newContents) throws Exception {
- String s = cache.get(name);
-
- if (! isEquals(s, oldContents))
- return false;
-
- if (! isEquals(s, newContents)) {
- cache.put(name, newContents);
- onChange(name, newContents);
- }
-
- return true;
- }
-
- /**
- * No-op.
- */
- @Override /* Closeable */
- public void close() throws IOException {
- // No-op
- }
+ /** 2 seconds */
+ HIGH;
}
diff --git
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/store/FileStoreTest.java
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/store/FileStoreTest.java
new file mode 100644
index 0000000..97b76e9
--- /dev/null
+++
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/store/FileStoreTest.java
@@ -0,0 +1,134 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance *
+// * with the License. You may obtain a copy of the License at
*
+// *
*
+// * http://www.apache.org/licenses/LICENSE-2.0
*
+// *
*
+// * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
+// * specific language governing permissions and limitations under the
License. *
+//
***************************************************************************************************************************
+package org.apache.juneau.config.store;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.internal.*;
+import org.junit.*;
+
+public class FileStoreTest {
+
+ private static final File DIR = new File("./config");
+
+ @After
+ public void cleanUp() {
+ FileUtils.delete(DIR);
+ }
+
+ @Test
+ public void testNoFile() throws Exception {
+ FileStore fs = FileStore.create().directory(DIR).build();
+ assertEquals(null, fs.read("X"));
+ assertFileNotExists("X.cfg");
+ }
+
+ @Test
+ public void testDifferentExtension() throws Exception {
+ FileStore fs =
FileStore.create().directory(DIR).ext("ini").build();
+ assertEquals(null, fs.read("X"));
+ assertFileNotExists("X.ini");
+ }
+
+ @Test
+ public void testSimpleCreate() throws Exception {
+ FileStore fs = FileStore.create().directory(DIR).build();
+ assertTrue(fs.write("X", null, "foo"));
+ assertEquals("foo", fs.read("X"));
+ assertFileExists("X.cfg");
+ }
+
+ @Test
+ public void testFailOnMismatch() throws Exception {
+ FileStore fs = FileStore.create().directory(DIR).build();
+ assertFalse(fs.write("X", "xxx", "foo"));
+ assertEquals(null, fs.read("X"));
+ assertFileNotExists("X.cfg");
+ assertTrue(fs.write("X", null, "foo"));
+ assertEquals("foo", fs.read("X"));
+ assertFalse(fs.write("X", "xxx", "foo"));
+ assertEquals("foo", fs.read("X"));
+ assertTrue(fs.write("X", "foo", "bar"));
+ assertEquals("bar", fs.read("X"));
+ }
+
+ @Test
+ public void testCharset() throws Exception {
+ FileStore fs =
FileStore.create().directory(DIR).charset("UTF-8").build();
+ assertTrue(fs.write("X", null, "foo"));
+ assertEquals("foo", fs.read("X"));
+ }
+
+ @Test
+ public void testWatcher() throws Exception {
+ FileStore fs =
FileStore.create().directory(DIR).useWatcher().watcherSensitivity(WatcherSensitivity.HIGH).build();
+
+ final CountDownLatch latch = new CountDownLatch(2);
+ final boolean[] error = {false};
+ fs.register(new StoreListener() {
+ @Override
+ public void onChange(String name, String contents) {
+ if ("X".equals(name) && "xxx".equals(contents))
+ latch.countDown();
+ else if ("Y".equals(name) &&
"yyy".equals(contents))
+ latch.countDown();
+ else
+ error[0] = true;
+ }
+ });
+ IOUtils.write(new File(DIR, "Z.ini"), new StringReader("zzz"));
+ IOUtils.write(new File(DIR, "X.cfg"), new StringReader("xxx"));
+ IOUtils.write(new File(DIR, "Y.cfg"), new StringReader("yyy"));
+ if (! latch.await(10, TimeUnit.SECONDS))
+ throw new Exception("CountDownLatch never reached
zero.");
+ assertFalse(error[0]);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ FileStore fs = FileStore.create().directory(DIR).build();
+
+ final CountDownLatch latch = new CountDownLatch(2);
+ final boolean[] error = {false};
+ fs.register(new StoreListener() {
+ @Override
+ public void onChange(String name, String contents) {
+ if ("X".equals(name) && "xxx".equals(contents))
+ latch.countDown();
+ else if ("Y".equals(name) &&
"yyy".equals(contents))
+ latch.countDown();
+ else
+ error[0] = true;
+ }
+ });
+
+ fs.update("X", "xxx");
+ fs.update("Y", "yyy");
+ if (! latch.await(10, TimeUnit.SECONDS))
+ throw new Exception("CountDownLatch never reached
zero.");
+ assertFalse(error[0]);
+ }
+
+
+ private void assertFileExists(String name) {
+ assertTrue(new File(DIR, name).exists());
+ }
+
+ private void assertFileNotExists(String name) {
+ assertTrue(! new File(DIR, name).exists());
+ }
+
+}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/store/MemoryStoreTest.java
similarity index 51%
copy from
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
copy to
juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/store/MemoryStoreTest.java
index f24e642..ac38dff 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/MemoryStore.java
+++
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/store/MemoryStoreTest.java
@@ -12,71 +12,62 @@
//
***************************************************************************************************************************
package org.apache.juneau.config.store;
-import static org.apache.juneau.internal.StringUtils.*;
+import static org.junit.Assert.*;
-import java.io.*;
import java.util.concurrent.*;
-import org.apache.juneau.*;
+import org.junit.*;
-/**
- * Filesystem-based storage location for configuration files.
- *
- * <p>
- * Points to a file system directory containing configuration files.
- */
-public class MemoryStore extends Store {
-
- /**
- * Create a new builder for this object.
- *
- * @return A new builder for this object.
- */
- public static MemoryStoreBuilder create() {
- return new MemoryStoreBuilder();
- }
+public class MemoryStoreTest {
- @Override /* Context */
- public MemoryStoreBuilder builder() {
- return new MemoryStoreBuilder(getPropertyStore());
+ @Test
+ public void testNoFile() throws Exception {
+ MemoryStore fs = MemoryStore.create().build();
+ assertEquals(null, fs.read("X"));
}
- private final ConcurrentHashMap<String,String> cache = new
ConcurrentHashMap<>();
-
- /**
- * Constructor.
- *
- * @param ps The settings for this content store.
- */
- protected MemoryStore(PropertyStore ps) {
- super(ps);
- }
-
- @Override /* Store */
- public synchronized String read(String name) throws Exception {
- return cache.get(name);
+ @Test
+ public void testSimpleCreate() throws Exception {
+ MemoryStore fs = MemoryStore.create().build();
+ assertTrue(fs.write("X", null, "foo"));
+ assertEquals("foo", fs.read("X"));
}
- @Override /* Store */
- public synchronized boolean write(String name, String oldContents,
String newContents) throws Exception {
- String s = cache.get(name);
-
- if (! isEquals(s, oldContents))
- return false;
-
- if (! isEquals(s, newContents)) {
- cache.put(name, newContents);
- onChange(name, newContents);
- }
-
- return true;
+ @Test
+ public void testFailOnMismatch() throws Exception {
+ MemoryStore fs = MemoryStore.create().build();
+ assertFalse(fs.write("X", "xxx", "foo"));
+ assertEquals(null, fs.read("X"));
+ assertTrue(fs.write("X", null, "foo"));
+ assertEquals("foo", fs.read("X"));
+ assertFalse(fs.write("X", "xxx", "foo"));
+ assertEquals("foo", fs.read("X"));
+ assertTrue(fs.write("X", "foo", "bar"));
+ assertEquals("bar", fs.read("X"));
}
+
+ @Test
+ public void testUpdate() throws Exception {
+ MemoryStore fs = MemoryStore.create().build();
- /**
- * No-op.
- */
- @Override /* Closeable */
- public void close() throws IOException {
- // No-op
- }
+ final CountDownLatch latch = new CountDownLatch(2);
+ final boolean[] error = {false};
+ fs.register(new StoreListener() {
+ @Override
+ public void onChange(String name, String contents) {
+ if ("X".equals(name) && "xxx".equals(contents))
+ latch.countDown();
+ else if ("Y".equals(name) &&
"yyy".equals(contents))
+ latch.countDown();
+ else
+ error[0] = true;
+ }
+ });
+
+ fs.update("X", "xxx");
+ fs.update("Y", "yyy");
+ if (! latch.await(10, TimeUnit.SECONDS))
+ throw new Exception("CountDownLatch never reached
zero.");
+ assertFalse(error[0]);
+ }
}
--
To stop receiving notification emails like this one, please contact
[email protected].