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 e830ffd Config API enhancements
e830ffd is described below
commit e830ffddefee7651e87d3c73e9c9c59232f82e41
Author: JamesBognar <[email protected]>
AuthorDate: Wed Feb 14 20:10:48 2018 -0500
Config API enhancements
---
.../org/apache/juneau/config/ConfigSourceFile.java | 133 ----------
.../apache/juneau/config/ConfigSourceMemory.java | 131 ----------
.../apache/juneau/config/ConfigSourceSettings.java | 103 --------
.../org/apache/juneau/config/source/FileStore.java | 281 +++++++++++++++++++++
.../juneau/config/source/FileStoreBuilder.java | 191 ++++++++++++++
.../apache/juneau/config/source/MemoryStore.java | 82 ++++++
.../juneau/config/source/MemoryStoreBuilder.java | 42 +++
.../{ConfigSource.java => source/Store.java} | 103 +++++---
.../apache/juneau/config/source/StoreBuilder.java | 40 +++
.../apache/juneau/config/source/StoreListener.java | 27 ++
10 files changed, 736 insertions(+), 397 deletions(-)
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceFile.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceFile.java
deleted file mode 100644
index 4a03964..0000000
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceFile.java
+++ /dev/null
@@ -1,133 +0,0 @@
-//
***************************************************************************************************************************
-// * 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;
-
-import java.io.*;
-import java.nio.channels.*;
-import java.nio.charset.*;
-import java.util.*;
-import java.util.concurrent.*;
-
-import org.apache.juneau.internal.*;
-
-/**
- * Implementation of a configuration source that's a file on the local file
system.
- */
-public class ConfigSourceFile extends ConfigSource {
-
- private ConcurrentHashMap<String,CacheEntry> cache = new
ConcurrentHashMap<>();
-
- /**
- * Constructor.
- *
- * @param settings
- * The settings for this config source.
- */
- public ConfigSourceFile(ConfigSourceSettings settings) {
- super(settings);
- }
-
- @Override /* ConfigSource */
- public synchronized String read(String name) throws Exception {
- CacheEntry fe = cache.get(name);
-
- if (fe == null || fe.hasBeenModified()) {
- File f = findFile(name);
- try (FileInputStream fis = new FileInputStream(f)) {
- try (FileLock lock = fis.getChannel().lock()) {
- try (Reader r = new
InputStreamReader(fis, Charset.defaultCharset())) {
- String contents =
IOUtils.read(r);
- long lastModified =
f.lastModified();
- fe = new CacheEntry(f,
lastModified, contents);
- cache.put(name, fe);
- }
- }
- }
- }
-
- return fe.contents;
- }
-
- @Override /* ConfigSource */
- public synchronized boolean write(String name, String contents) throws
Exception {
- if (hasBeenModified(name))
- return false;
-
- CacheEntry fe = cache.get(name);
- File f = fe != null ? fe.file : findFile(name);
-
- try (FileOutputStream fos = new FileOutputStream(f)) {
- try (FileLock lock = fos.getChannel().lock()) {
- if (hasBeenModified(name))
- return false;
- try (Writer w = new OutputStreamWriter(fos,
Charset.defaultCharset())) {
- IOUtils.pipe(contents, w);
- }
- fe = new CacheEntry(f, f.lastModified(),
contents);
- cache.put(name, fe);
- return true;
- }
- }
- }
-
- @Override /* ConfigSource */
- public boolean hasBeenModified(String name) throws Exception {
- CacheEntry fe = cache.get(name);
- return (fe != null && fe.hasBeenModified());
- }
-
- private static class CacheEntry {
- final File file;
- final long lastModified;
- final String contents;
-
- CacheEntry(File file, long lastModified, String contents) {
- this.file = file;
- this.lastModified = lastModified;
- this.contents = contents;
- }
-
- boolean hasBeenModified() {
- return file.lastModified() != lastModified;
- }
- }
-
- private File findFile(String name) throws IOException {
-
- List<String> searchPaths = getSettings().getSearchPaths();
-
- if (searchPaths.isEmpty())
- throw new FileNotFoundException("No search paths
specified in ConfigFileBuilder.");
-
- // Handle paths relative to search paths.
- for (String sp : searchPaths) {
- File pf = new File(sp);
- File f = new File(pf, name);
- if (f.exists())
- return f;
- }
-
- if (getSettings().isCreateIfNotExists()) {
- for (String sf : searchPaths) {
- File pf = new File(sf);
- if (pf.exists() && pf.isDirectory() &&
pf.canWrite()) {
- File f = new File(pf, name);
- if (f.createNewFile())
- return f;
- }
- }
- }
-
- throw new FileNotFoundException("Could not find config file
'"+name+"'");
- }
-}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceMemory.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceMemory.java
deleted file mode 100644
index b6d1b0e..0000000
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceMemory.java
+++ /dev/null
@@ -1,131 +0,0 @@
-//
***************************************************************************************************************************
-// * 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;
-
-import java.io.*;
-import java.util.*;
-import java.util.concurrent.*;
-
-/**
- * Implementation of a configuration source entirely in memory.
- */
-public class ConfigSourceMemory extends ConfigSource {
-
- private static final ConcurrentHashMap<String,MemoryFile> MEMORY = new
ConcurrentHashMap<>();
-
- private ConcurrentHashMap<String,CacheEntry> cache = new
ConcurrentHashMap<>();
-
- /**
- * Constructor.
- *
- * @param settings
- * The settings for this config source.
- */
- public ConfigSourceMemory(ConfigSourceSettings settings) {
- super(settings);
- }
-
- @Override /* ConfigSource */
- public synchronized String read(String name) throws Exception {
- CacheEntry ce = cache.get(name);
-
- if (ce == null || ce.hasBeenModified()) {
- MemoryFile f = findFile(name);
- synchronized(f) {
- ce = new CacheEntry(f, f.lastModified,
f.contents);
- cache.put(name, ce);
- }
- }
-
- return ce.contents;
- }
-
- @Override /* ConfigSource */
- public synchronized boolean write(String name, String contents) throws
Exception {
- if (hasBeenModified(name))
- return false;
-
- CacheEntry ce = cache.get(name);
- MemoryFile f = ce != null ? ce.file : findFile(name);
-
- synchronized(f) {
- if (hasBeenModified(name))
- return false;
- f.contents = contents;
- f.lastModified = System.currentTimeMillis();
- ce = new CacheEntry(f, f.lastModified, f.contents);
- cache.put(name, ce);
- }
-
- return true;
- }
-
- @Override /* ConfigSource */
- public boolean hasBeenModified(String name) throws Exception {
- CacheEntry ce = cache.get(name);
- return (ce != null && ce.hasBeenModified());
- }
-
- private MemoryFile findFile(String name) throws IOException {
-
- List<String> searchPaths = getSettings().getSearchPaths();
-
- if (searchPaths.isEmpty())
- throw new FileNotFoundException("No search paths
specified in ConfigFileBuilder.");
-
- // Handle paths relative to search paths.
- for (String sp : searchPaths) {
- String pf = sp + '/' + name;
- MemoryFile mf = MEMORY.get(pf);
- if (mf != null)
- return mf;
- }
-
- if (getSettings().isCreateIfNotExists()) {
- for (String sf : searchPaths) {
- String path = sf + '/' + name;
- MemoryFile mf = new MemoryFile("");
- MEMORY.putIfAbsent(path, mf);
- return MEMORY.get(path);
- }
- }
-
- throw new FileNotFoundException("Could not find config file
'"+name+"'");
- }
-
- private static class MemoryFile {
- String contents;
- long lastModified;
-
- MemoryFile(String contents) {
- this.contents = contents;
- this.lastModified = System.currentTimeMillis();
- }
- }
-
- private static class CacheEntry {
- final MemoryFile file;
- final long lastModified;
- final String contents;
-
- CacheEntry(MemoryFile file, long lastModified, String contents)
{
- this.file = file;
- this.lastModified = lastModified;
- this.contents = contents;
- }
-
- boolean hasBeenModified() {
- return file.lastModified != lastModified;
- }
- }
-}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceSettings.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceSettings.java
deleted file mode 100644
index bfc66a3..0000000
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSourceSettings.java
+++ /dev/null
@@ -1,103 +0,0 @@
-//
***************************************************************************************************************************
-// * 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;
-
-import java.nio.charset.*;
-import java.util.*;
-
-/**
- * Configuration settings for the {@link ConfigSource} class.
- */
-public class ConfigSourceSettings {
-
- private final List<String> searchPaths;
- private final Charset charset;
- private final boolean readonly, createIfNotExists;
-
- static final class Builder {
- List<String> searchPaths = Arrays.asList(new String[]{"."});
- Charset charset = Charset.defaultCharset();
- boolean readonly = false, createIfNotExists = true;
-
- Builder searchPaths(String[] searchPaths) {
- this.searchPaths = Arrays.asList(searchPaths);
- return this;
- }
-
- Builder charset(Charset charset) {
- this.charset = charset;
- return this;
- }
-
- Builder readonly(boolean readonly) {
- this.readonly = readonly;
- return this;
- }
-
- Builder createIfNotExists(boolean createIfNotExists) {
- this.createIfNotExists = createIfNotExists;
- return this;
- }
-
- ConfigSourceSettings build() {
- return new ConfigSourceSettings(this);
- }
- }
-
- ConfigSourceSettings(Builder b) {
- this.searchPaths = b.searchPaths;
- this.charset = b.charset;
- this.readonly = b.readonly;
- this.createIfNotExists = b.createIfNotExists;
- }
-
- /**
- * Returns the paths to search to find config files.
- *
- * @return The paths to search to find config files.
- */
- public List<String> getSearchPaths() {
- return searchPaths;
- }
-
- /**
- * Returns the charset of the config file.
- *
- * @return The charset of the config file.
- */
- public Charset getCharset() {
- return charset;
- }
-
- /**
- * Specifies whether the config file should be opened in read-only mode.
- *
- * @return <jk>true</jk> if the config file should be opened in
read-only mode.
- */
- public boolean isReadonly() {
- return readonly;
- }
-
- /**
- * Specifies whether config files should be created if they're not
found in the search paths.
- *
- * <p>
- * Note that the first writable path will be used for the location of
the file.
- *
- * @return <jk>true</jk> if the config file should be created if not
found.
- */
- public boolean isCreateIfNotExists() {
- return createIfNotExists;
- }
-}
-
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/FileStore.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/FileStore.java
new file mode 100644
index 0000000..e5a9dc5
--- /dev/null
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/FileStore.java
@@ -0,0 +1,281 @@
+//
***************************************************************************************************************************
+// * 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.source;
+
+import static java.nio.file.StandardWatchEventKinds.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.charset.*;
+import java.nio.file.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Filesystem-based storage location for configuration files.
+ *
+ * <p>
+ * Points to a file system directory containing configuration files.
+ */
+public class FileStore extends Store {
+
+
//-------------------------------------------------------------------------------------------------------------------
+ // Configurable properties
+
//-------------------------------------------------------------------------------------------------------------------
+
+ private static final String PREFIX = "FileStore.";
+
+ /**
+ * Configuration property: Local file system directory.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"FileStore.directory.s"</js>
+ * <li><b>Data type:</b> <code>String</code>
+ * <li><b>Default:</b> <js>"."</js>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
FileStoreBuilder#directory(String)}
+ * <li class='jm'>{@link
FileStoreBuilder#directory(File)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Identifies the path of the directory containing the configuration
files.
+ */
+ public static final String FILESTORE_directory = PREFIX + "directory.s";
+
+ /**
+ * Configuration property: Charset.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"FileStore.charset.s"</js>
+ * <li><b>Data type:</b> {@link Charset}
+ * <li><b>Default:</b> {@link Charset#defaultCharset()}
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
FileStoreBuilder#charset(String)}
+ * <li class='jm'>{@link
FileStoreBuilder#charset(Charset)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Identifies the charset of external files.
+ */
+ public static final String FILESTORE_charset = PREFIX + "charset.s";
+
+ /**
+ * Configuration property: Use watcher.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"FileStore.useWatcher.b"</js>
+ * <li><b>Data type:</b> <code>Boolean</code>
+ * <li><b>Default:</b> <jk>false</jk>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
FileStoreBuilder#useWatcher()}
+ * <li class='jm'>{@link
FileStoreBuilder#useWatcher(boolean)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Use a file system watcher for file system changes.
+ *
+ * <h5 class='section'>Notes:</h5>
+ * <ul class='spaced-list'>
+ * <li>Calling {@link #close()} on this object closes the watcher.
+ * </ul>
+ */
+ public static final String FILESTORE_useWatcher = PREFIX +
"useWatcher.s";
+
+ /**
+ * Configuration property: Config file extension.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"FileStore.ext.s"</js>
+ * <li><b>Data type:</b> <code>String</code>
+ * <li><b>Default:</b> <js>"cfg"</js>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
FileStoreBuilder#ext(String)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * File extension identifier for config files.
+ */
+ public static final String FILESTORE_ext = PREFIX + "ext.s";
+
+ /**
+ * Create a new builder for this object.
+ *
+ * @return A new builder for this object.
+ */
+ public static FileStoreBuilder create() {
+ return new FileStoreBuilder();
+ }
+
+ @Override /* Context */
+ public FileStoreBuilder builder() {
+ return new FileStoreBuilder(getPropertyStore());
+ }
+
+ private final File dir;
+ private final String ext;
+ private final Charset charset;
+ private final WatcherThread watcher;
+ private final ConcurrentHashMap<String,String> cache = new
ConcurrentHashMap<>();
+
+ /**
+ * Constructor.
+ *
+ * @param ps The settings for this content store.
+ */
+ protected FileStore(PropertyStore ps) {
+ super(ps);
+ try {
+ dir = new File(getStringProperty(FILESTORE_directory,
".")).getCanonicalFile();
+ ext = getStringProperty(FILESTORE_ext, "cfg");
+ charset = getProperty(FILESTORE_charset, Charset.class,
Charset.defaultCharset());
+ watcher = getBooleanProperty(FILESTORE_useWatcher,
false) ? new WatcherThread(dir) : null;
+ if (watcher != null)
+ watcher.start();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @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 {
+ watchService =
FileSystems.getDefault().newWatchService();
+ dir.toPath().register(watchService, ENTRY_CREATE,
ENTRY_DELETE, ENTRY_MODIFY);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override /* Thread */
+ public void run() {
+ try {
+ while (true) {
+ WatchKey key = watchService.take();
+
+ 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) {
+ throw new RuntimeException(e);
+ }
+ };
+
+ @Override /* Thread */
+ public void interrupt() {
+ try {
+ watchService.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ super.interrupt();
+ }
+ }
+ }
+
+ synchronized void onFileEvent(WatchEvent<Path> e) throws IOException {
+ File f = e.context().toFile();
+ String fn = f.getName();
+ String bn = FileUtils.getBaseName(fn);
+ String ext = FileUtils.getExtension(fn);
+ if (ext.equals(ext)) {
+ String newContents = IOUtils.read(f);
+ String oldContents = cache.get(bn);
+ if (! StringUtils.isEquals(oldContents, newContents)) {
+ onChange(bn, newContents);
+ cache.put(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/source/FileStoreBuilder.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/FileStoreBuilder.java
new file mode 100644
index 0000000..1030f94
--- /dev/null
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/FileStoreBuilder.java
@@ -0,0 +1,191 @@
+//
***************************************************************************************************************************
+// * 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.source;
+
+import static org.apache.juneau.config.source.FileStore.*;
+
+import java.io.*;
+import java.nio.charset.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Builder for {@link FileStore} objects.
+ */
+public class FileStoreBuilder extends StoreBuilder {
+
+ /**
+ * Constructor, default settings.
+ */
+ public FileStoreBuilder() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param ps The initial configuration settings for this builder.
+ */
+ public FileStoreBuilder(PropertyStore ps) {
+ super(ps);
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // Properties
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Configuration property: Local file system directory.
+ *
+ * <p>
+ * Identifies the path of the directory containing the configuration
files.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_directory}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is <js>"."</js>.
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder directory(String value) {
+ super.set(FILESTORE_directory, value);
+ return this;
+ }
+
+ /**
+ * Configuration property: Local file system directory.
+ *
+ * <p>
+ * Identifies the path of the directory containing the configuration
files.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_directory}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is <js>"."</js>.
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder directory(File value) {
+ super.set(FILESTORE_directory, value);
+ return this;
+ }
+
+ /**
+ * Configuration property: Charset.
+ *
+ * <p>
+ * Identifies the charset of external files.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_charset}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is <js>"."</js>.
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder charset(String value) {
+ super.set(FILESTORE_charset, value);
+ return this;
+ }
+
+ /**
+ * Configuration property: Charset.
+ *
+ * <p>
+ * Identifies the charset of external files.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_charset}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is <js>"."</js>.
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder charset(Charset value) {
+ super.set(FILESTORE_charset, value);
+ return this;
+ }
+
+ /**
+ * Configuration property: Use watcher.
+ *
+ * <p>
+ * Use a file system watcher for file system changes.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_useWatcher}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is <jk>false</jk>.
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder useWatcher(boolean value) {
+ super.set(FILESTORE_useWatcher, value);
+ return this;
+ }
+
+ /**
+ * Configuration property: Use watcher.
+ *
+ * <p>
+ * Shortcut for calling <code>useWatcher(<jk>true</jk>)</code>.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link FileStore#FILESTORE_useWatcher}
+ * </ul>
+ *
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder useWatcher() {
+ super.set(FILESTORE_useWatcher, true);
+ return this;
+ }
+
+ /**
+ * Configuration property: Config file extension.
+ *
+ * <p>
+ * File extension identifier for config files.
+ *
+ * @param value
+ * The new value for this property.
+ * <br>The default is <js>"cfg"</js>.
+ * @return This object (for method chaining).
+ */
+ public FileStoreBuilder ext(String value) {
+ super.set(FILESTORE_ext, value);
+ return this;
+ }
+
+ @Override /* ContextBuilder */
+ public FileStore build() {
+ return new FileStore(getPropertyStore());
+ }
+}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/MemoryStore.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/MemoryStore.java
new file mode 100644
index 0000000..98a64a8
--- /dev/null
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/MemoryStore.java
@@ -0,0 +1,82 @@
+//
***************************************************************************************************************************
+// * 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.source;
+
+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.
+ *
+ * <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();
+ }
+
+ @Override /* Context */
+ public MemoryStoreBuilder builder() {
+ return new MemoryStoreBuilder(getPropertyStore());
+ }
+
+ 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);
+ }
+
+ @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
+ }
+}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/MemoryStoreBuilder.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/MemoryStoreBuilder.java
new file mode 100644
index 0000000..346e298
--- /dev/null
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/MemoryStoreBuilder.java
@@ -0,0 +1,42 @@
+//
***************************************************************************************************************************
+// * 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.source;
+
+import org.apache.juneau.*;
+
+/**
+ * Builder for {@link MemoryStore} objects.
+ */
+public class MemoryStoreBuilder extends StoreBuilder {
+
+ /**
+ * Constructor, default settings.
+ */
+ public MemoryStoreBuilder() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param ps The initial configuration settings for this builder.
+ */
+ public MemoryStoreBuilder(PropertyStore ps) {
+ super(ps);
+ }
+
+ @Override /* ContextBuilder */
+ public MemoryStore build() {
+ return new MemoryStore(getPropertyStore());
+ }
+}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSource.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/Store.java
similarity index 51%
rename from
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSource.java
rename to
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/Store.java
index 02c8038..f340ae5 100644
---
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigSource.java
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/Store.java
@@ -10,33 +10,34 @@
// * "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;
+package org.apache.juneau.config.source;
-/**
- * Represents a storage location of a configuration file.
- */
-public abstract class ConfigSource {
+import java.io.*;
+import java.util.*;
- /** The path of the config file. */
- private final ConfigSourceSettings settings;
+import org.apache.juneau.*;
+/**
+ * Represents a storage location for configuration files.
+ *
+ * <p>
+ * Content stores require two methods to be implemented:
+ * <ul>
+ * <li class='jm'>{@link #read(String)} - Retrieve a config file.
+ * <li class='jm'>{@link #write(String,String,String)} - Store a config
file.
+ * </ul>
+ */
+public abstract class Store extends Context implements Closeable {
+
+ private final List<StoreListener> listeners = new LinkedList<>();
+
/**
* Constructor.
*
- * @param settings
- * The settings for this config source.
- */
- protected ConfigSource(ConfigSourceSettings settings) {
- this.settings = settings;
- }
-
- /**
- * Returns the name of the config file.
- *
- * @return The name of the config file.
+ * @param ps The settings for this content store.
*/
- protected final ConfigSourceSettings getSettings() {
- return settings;
+ protected Store(PropertyStore ps) {
+ super(ps);
}
/**
@@ -46,29 +47,71 @@ public abstract class ConfigSource {
* @return The contents of the configuration file.
* @throws Exception
*/
- protected abstract String read(String name) throws Exception;
+ public abstract String read(String name) throws Exception;
/**
* Saves the contents of the configuration file if the underlying
storage hasn't been modified.
*
* @param name The config file name.
- * @param contents The new contents of the configuration file.
+ * @param oldContents The old contents.
+ * @param newContents The new contents.
* @return <jk>true</jk> if we successfully saved the new configuration
file contents, or <jk>false</jk> if the
* underlying storage changed since the last time the {@link
#read(String)} method was called.
* @throws Exception
*/
- protected abstract boolean write(String name, String contents) throws
Exception;
+ public abstract boolean write(String name, String oldContents, String
newContents) throws Exception;
+
+ /**
+ * Registers a new listener on this store.
+ *
+ * @param l The new listener.
+ * @return This object (for method chaining).
+ */
+ public Store register(StoreListener l) {
+ this.listeners.add(l);
+ return this;
+ }
+
+ /**
+ * Unregisters a listener from this store.
+ *
+ * @param l The listener to unregister.
+ * @return This object (for method chaining).
+ */
+ public Store unregister(StoreListener l) {
+ this.listeners.remove(l);
+ return this;
+ }
/**
- * Returns whether the underlying configuration contents have changed.
+ * Called when the physical contents of a config file have changed.
*
* <p>
- * For example, if the configuration source is a file, this method
would return <jk>true</jk> if the
- * file on the filesystem has been modified since the {@link
#read(String)} method was called.
+ * Triggers calls to {@link StoreListener#onChange(String, String)} on
all registered listeners.
*
- * @param name The config file name.
- * @return <jk>true</jk> if the persisted contents of the config file
have changed.
- * @throws Exception
+ * @param name The config name (e.g. the filename without the
extension).
+ * @param contents The new contents.
+ * @return This object (for method chaining).
+ */
+ protected Store onChange(String name, String contents) {
+ for (StoreListener l : listeners)
+ l.onChange(name, contents);
+ return this;
+ }
+
+ /**
+ * Unused.
*/
- protected abstract boolean hasBeenModified(String name) throws
Exception;
+ @Override /* Context */
+ public final Session createSession(SessionArgs args) {
+ throw new NoSuchMethodError();
+ }
+
+ /**
+ * Unused.
+ */
+ @Override /* Context */
+ public final SessionArgs createDefaultSessionArgs() {
+ throw new NoSuchMethodError();
+ }
}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/StoreBuilder.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/StoreBuilder.java
new file mode 100644
index 0000000..c3a7b6e
--- /dev/null
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/StoreBuilder.java
@@ -0,0 +1,40 @@
+//
***************************************************************************************************************************
+// * 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.source;
+
+import org.apache.juneau.*;
+
+/**
+ * Base builder class for {@link Store} objects.
+ */
+public abstract class StoreBuilder extends ContextBuilder {
+
+ /**
+ * Constructor, default settings.
+ */
+ public StoreBuilder() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param ps The initial configuration settings for this builder.
+ */
+ public StoreBuilder(PropertyStore ps) {
+ super(ps);
+ }
+
+ @Override
+ public abstract Store build();
+}
diff --git
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/StoreListener.java
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/StoreListener.java
new file mode 100644
index 0000000..0526e56
--- /dev/null
+++
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/source/StoreListener.java
@@ -0,0 +1,27 @@
+//
***************************************************************************************************************************
+// * 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.source;
+
+/**
+ * Listens for changes to stored config files.
+ */
+public interface StoreListener {
+
+ /**
+ * Called when the physical contents of a config file have changed.
+ *
+ * @param name The config name (e.g. the filename without the
extension).
+ * @param contents The new config contents;
+ */
+ void onChange(String name, String contents);
+}
--
To stop receiving notification emails like this one, please contact
[email protected].