Repository: incubator-juneau Updated Branches: refs/heads/master 39cecfc7a -> 0ab9c5261
Updates to config API. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/0ab9c526 Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/0ab9c526 Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/0ab9c526 Branch: refs/heads/master Commit: 0ab9c5261ea6251bd205389d22bcf84c87744c14 Parents: 39cecfc Author: JamesBognar <[email protected]> Authored: Fri Oct 6 10:43:58 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Fri Oct 6 10:43:58 2017 -0400 ---------------------------------------------------------------------- .../org/apache/juneau/ini/ConfigSource.java.off | 74 +++++++++++ .../apache/juneau/ini/ConfigSourceFile.java.off | 133 +++++++++++++++++++ .../juneau/ini/ConfigSourceMemory.java.off | 131 ++++++++++++++++++ .../juneau/ini/ConfigSourceSettings.java.off | 104 +++++++++++++++ 4 files changed, 442 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ab9c526/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSource.java.off ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSource.java.off b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSource.java.off new file mode 100644 index 0000000..a8a337a --- /dev/null +++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSource.java.off @@ -0,0 +1,74 @@ +// *************************************************************************************************************************** +// * 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.ini; + +/** + * Represents a storage location of a configuration file. + */ +public abstract class ConfigSource { + + /** The path of the config file. */ + private final ConfigSourceSettings settings; + + /** + * 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. + */ + protected final ConfigSourceSettings getSettings() { + return settings; + } + + /** + * Returns the contents of the configuration file. + * + * @param name The config file name. + * @return The contents of the configuration file. + * @throws Exception + */ + protected 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. + * @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; + + /** + * Returns whether the underlying configuration contents 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. + * + * @param name The config file name. + * @return <jk>true</jk> if the persisted contents of the config file have changed. + * @throws Exception + */ + protected abstract boolean hasBeenModified(String name) throws Exception; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ab9c526/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceFile.java.off ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceFile.java.off b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceFile.java.off new file mode 100644 index 0000000..723c03b --- /dev/null +++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceFile.java.off @@ -0,0 +1,133 @@ +// *************************************************************************************************************************** +// * 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.ini; + +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+"'"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ab9c526/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceMemory.java.off ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceMemory.java.off b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceMemory.java.off new file mode 100644 index 0000000..9f73675 --- /dev/null +++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceMemory.java.off @@ -0,0 +1,131 @@ +// *************************************************************************************************************************** +// * 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.ini; + +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 { + private String contents; + private long lastModified; + + private 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; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ab9c526/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceSettings.java.off ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceSettings.java.off b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceSettings.java.off new file mode 100644 index 0000000..7a72bae --- /dev/null +++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/ini/ConfigSourceSettings.java.off @@ -0,0 +1,104 @@ +// *************************************************************************************************************************** +// * 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.ini; + +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; + + @SuppressWarnings("hiding") + static class Builder { + private List<String> searchPaths = Arrays.asList(new String[]{"."}); + private Charset charset = Charset.defaultCharset(); + private 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; + } +} +
