All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.config.Config Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * 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 static org.apache.juneau.common.internal.ArgUtils.*;
import static org.apache.juneau.common.internal.IOUtils.*;
import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.common.internal.ThrowableUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;

import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.config.event.*;
import org.apache.juneau.config.internal.*;
import org.apache.juneau.config.mod.*;
import org.apache.juneau.config.store.*;
import org.apache.juneau.config.vars.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.svl.*;
import org.apache.juneau.utils.*;

/**
 * Main configuration API class.
 *
 * 
Notes:
    *
  • This class is thread safe and reusable. *
* *
See Also:
*/ public final class Config extends Context implements ConfigEventListener { //----------------------------------------------------------------------------------------------------------------- // Static //----------------------------------------------------------------------------------------------------------------- private static boolean DISABLE_AUTO_SYSTEM_PROPS = Boolean.getBoolean("juneau.disableAutoSystemProps"); private static volatile Config SYSTEM_DEFAULT = findSystemDefault(); /** * Sets a system default configuration. * * @param systemDefault The new system default configuration. */ public synchronized static void setSystemDefault(Config systemDefault) { SYSTEM_DEFAULT = systemDefault; } /** * Returns the system default configuration. * * @return The system default configuration, or null if it doesn't exist. */ public synchronized static Config getSystemDefault() { return SYSTEM_DEFAULT; } private synchronized static Config findSystemDefault() { for (String n : getCandidateSystemDefaultConfigNames()) { Config config = find(n); if (config != null) { if (! DISABLE_AUTO_SYSTEM_PROPS) config.setSystemProperties(); return config; } } return null; } /** * Returns the list of candidate system default configuration file names. * *

* If the "juneau.configFile" system property is set, returns a singleton of that value. *
Otherwise, returns a list consisting of the following values: *

    *
  1. File with same name as jar file but with ".cfg" extension. (e.g. "myjar.cfg") *
  2. Any file ending in ".cfg" in the home directory (names ordered alphabetically). *
  3. "juneau.cfg" *
  4. "default.cfg" *
  5. "application.cfg" *
  6. "app.cfg" *
  7. "settings.cfg" *
  8. "application.properties" *
*

* * @return * A list of candidate file names. *
The returned list is modifiable. *
Each call constructs a new list. */ public synchronized static List getCandidateSystemDefaultConfigNames() { List l = list(); String s = System.getProperty("juneau.configFile"); if (s != null) { l.add(s); return l; } String cmd = System.getProperty("sun.java.command", "not_found").split("\\s+")[0]; if (cmd.endsWith(".jar") && ! cmd.contains("surefirebooter")) { cmd = cmd.replaceAll(".*?([^\\\\\\/]+)\\.jar$", "$1"); l.add(cmd + ".cfg"); cmd = cmd.replaceAll("[\\.\\_].*$", ""); // Try also without version in jar name. l.add(cmd + ".cfg"); } Set files = sortedSet(new File(".").listFiles()); for (File f : files) if (f.getName().endsWith(".cfg")) l.add(f.getName()); l.add("juneau.cfg"); l.add("default.cfg"); l.add("application.cfg"); l.add("app.cfg"); l.add("settings.cfg"); l.add("application.properties"); return l; } private synchronized static Config find(String name) { if (name == null) return null; if (FileStore.DEFAULT.exists(name)) return Config.create(name).store(FileStore.DEFAULT).build(); if (ClasspathStore.DEFAULT.exists(name)) return Config.create(name).store(ClasspathStore.DEFAULT).build(); return null; } /** * Creates a new builder for this object. * * @return A new builder. */ public static Builder create() { return new Builder(); } /** * Same as {@link #create()} but initializes the builder with the specified config name. * * @param name The configuration name. * @return A new builder. */ public static Builder create(String name) { return new Builder().name(name); } //------------------------------------------------------------------------------------------------------------------- // Builder //------------------------------------------------------------------------------------------------------------------- /** * Builder class. */ @FluentSetters public static class Builder extends Context.Builder { String name; ConfigStore store; WriterSerializer serializer; ReaderParser parser; Map mods; VarResolver varResolver; int binaryLineLength; BinaryFormat binaryFormat; boolean multiLineValuesOnSeparateLines, readOnly; /** * Constructor, default settings. */ protected Builder() { name = env("Config.name", "Configuration.cfg"); store = FileStore.DEFAULT; serializer = Json5Serializer.DEFAULT; parser = JsonParser.DEFAULT; mods = map(); mods(XorEncodeMod.INSTANCE); varResolver = VarResolver.DEFAULT; binaryLineLength = env("Config.binaryLineLength", -1); binaryFormat = env("Config.binaryFormat", BinaryFormat.BASE64); multiLineValuesOnSeparateLines = env("Config.multiLineValuesOnSeparateLines", false); readOnly = env("Config.readOnly", false); } /** * Copy constructor. * * @param copyFrom The bean to copy from. */ protected Builder(Config copyFrom) { super(copyFrom); name = copyFrom.name; store = copyFrom.store; serializer = copyFrom.serializer; parser = copyFrom.parser; mods = copyOf(copyFrom.mods); varResolver = copyFrom.varResolver; binaryLineLength = copyFrom.binaryLineLength; binaryFormat = copyFrom.binaryFormat; multiLineValuesOnSeparateLines = copyFrom.multiLineValuesOnSeparateLines; readOnly = copyFrom.readOnly; } /** * Copy constructor. * * @param copyFrom The builder to copy from. */ protected Builder(Builder copyFrom) { super(copyFrom); name = copyFrom.name; store = copyFrom.store; serializer = copyFrom.serializer; parser = copyFrom.parser; mods = copyOf(copyFrom.mods); varResolver = copyFrom.varResolver; binaryLineLength = copyFrom.binaryLineLength; binaryFormat = copyFrom.binaryFormat; multiLineValuesOnSeparateLines = copyFrom.multiLineValuesOnSeparateLines; readOnly = copyFrom.readOnly; } @Override /* Context.Builder */ public Builder copy() { return new Builder(this); } @Override /* Context.Builder */ public Config build() { return build(Config.class); } //----------------------------------------------------------------------------------------------------------------- // Properties //----------------------------------------------------------------------------------------------------------------- /** * Configuration name. * *

* Specifies the configuration name. *
This is typically the configuration file name, although * the name can be anything identifiable by the {@link ConfigStore} used for retrieving and storing the configuration. * * @param value * The new value for this property. *
The default is the first value found: *

    *
  • System property "Config.name" *
  • Environment variable "CONFIG_NAME" *
  • "Configuration.cfg" *
* @return This object. */ public Builder name(String value) { name = value; return this; } /** * Configuration store. * *

* The configuration store used for retrieving and storing configurations. * * @param value * The new value for this property. *
The default is {@link FileStore#DEFAULT}. * @return This object. */ public Builder store(ConfigStore value) { store = value; return this; } /** * Configuration store. * *

* Convenience method for calling store(ConfigMemoryStore.DEFAULT). * * @return This object. */ public Builder memStore() { store = MemoryStore.DEFAULT; return this; } /** * POJO serializer. * *

* The serializer to use for serializing POJO values. * * @param value * The new value for this property. *
The default is {@link Json5Serializer#DEFAULT} * @return This object. */ public Builder serializer(WriterSerializer value) { serializer = value; return this; } /** * POJO parser. * *

* The parser to use for parsing values to POJOs. * * @param value * The new value for this property. *
The default is {@link JsonParser#DEFAULT}. * @return This object. */ public Builder parser(ReaderParser value) { parser = value; return this; } /** * Adds a value modifier. * *

* Modifiers are used to modify entry value before being persisted. * * @param values * The mods to apply to this config. * @return This object. */ public Builder mods(Mod...values) { for (Mod value : values) mods.put(value.getId(), value); return this; } /** * SVL variable resolver. * *

* The resolver to use for resolving SVL variables. * * @param value * The new value for this property. *
The default is {@link VarResolver#DEFAULT}. * @return This object. */ public Builder varResolver(VarResolver value) { varResolver = value; return this; } /** * Binary value line length. * *

* When serializing binary values, lines will be split after this many characters. *
Use -1 to represent no line splitting. * * @param value * The new value for this property. *
The default is the first value found: *

    *
  • System property "Config.binaryLineLength" *
  • Environment variable "CONFIG_BINARYLINELENGTH" *
  • -1 *
* @return This object. */ public Builder binaryLineLength(int value) { binaryLineLength = value; return this; } /** * Binary value format. * *

* The format to use when persisting byte arrays. * *

    *
  • {@link BinaryFormat#BASE64} - BASE64-encoded string. *
  • {@link BinaryFormat#HEX} - Hexadecimal. *
  • {@link BinaryFormat#SPACED_HEX} - Hexadecimal with spaces between bytes. *
* * @param value * The new value for this property. *
The default is the first value found: *
    *
  • System property "Config.binaryFormat" *
  • Environment variable "CONFIG_BINARYFORMAT" *
  • {@link BinaryFormat#BASE64} *
* @return This object. */ public Builder binaryFormat(BinaryFormat value) { binaryFormat = value; return this; } /** * Multi-line values on separate lines. * *

* When enabled, multi-line values will always be placed on a separate line from the key. * *

* The default is the first value found: *

    *
  • System property "Config.multiLineValuesOnSeparateLine" *
  • Environment variable "CONFIG_MULTILINEVALUESONSEPARATELINE" *
  • false *
* * @return This object. */ public Builder multiLineValuesOnSeparateLines() { multiLineValuesOnSeparateLines = true; return this; } /** * Read-only mode. * *

* When enabled, attempts to call any setters on this object will throw an {@link UnsupportedOperationException}. * *

* The default is the first value found: *

    *
  • System property "Config.readOnly" *
  • Environment variable "CONFIG_READONLY" *
  • false *
* * @return This object. */ public Builder readOnly() { readOnly = true; return this; } // @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder annotations(Annotation...values) { super.annotations(values); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder apply(AnnotationWorkList work) { super.apply(work); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder applyAnnotations(java.lang.Class...fromClasses) { super.applyAnnotations(fromClasses); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder applyAnnotations(Method...fromMethods) { super.applyAnnotations(fromMethods); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder cache(Cache value) { super.cache(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder debug() { super.debug(); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder debug(boolean value) { super.debug(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder impl(Context value) { super.impl(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder type(Class value) { super.type(value); return this; } // } //------------------------------------------------------------------------------------------------------------------- // Instance //------------------------------------------------------------------------------------------------------------------- final String name; final ConfigStore store; final WriterSerializer serializer; final ReaderParser parser; final Map mods; final VarResolver varResolver; final int binaryLineLength; final BinaryFormat binaryFormat; final boolean multiLineValuesOnSeparateLines, readOnly; final BeanSession beanSession; final VarResolverSession varSession; private final ConfigMap configMap; private final List listeners = synced(linkedList()); @Override /* Context */ public Builder copy() { return new Builder(this); } /** * Constructor. * * @param builder The builder for this object. * @throws IOException Thrown by underlying stream. */ public Config(Builder builder) throws IOException { super(builder); name = builder.name; store = builder.store; configMap = store.getMap(name); configMap.register(this); serializer = builder.serializer; parser = builder.parser; beanSession = parser.getBeanContext().getSession(); mods = unmodifiable(copyOf(builder.mods)); varResolver = builder.varResolver; varSession = varResolver .copy() .vars(ConfigVar.class) .bean(Config.class, this) .build() .createSession(); binaryLineLength = builder.binaryLineLength; binaryFormat = builder.binaryFormat; multiLineValuesOnSeparateLines = builder.multiLineValuesOnSeparateLines; readOnly = builder.readOnly; } Config(Config copyFrom, VarResolverSession varSession) { super(copyFrom); name = copyFrom.name; store = copyFrom.store; configMap = copyFrom.configMap; configMap.register(this); serializer = copyFrom.serializer; parser = copyFrom.parser; mods = copyFrom.mods; varResolver = copyFrom.varResolver; this.varSession = varSession; binaryLineLength = copyFrom.binaryLineLength; binaryFormat = copyFrom.binaryFormat; multiLineValuesOnSeparateLines = copyFrom.multiLineValuesOnSeparateLines; readOnly = copyFrom.readOnly; beanSession = copyFrom.beanSession; } /** * Creates a copy of this config using the specified var session for resolving variables. * *

* This creates a shallow copy of the config but replacing the variable resolver. * * @param varSession The var session used for resolving string variables. * @return A new config object. */ public Config resolving(VarResolverSession varSession) { return new Config(this, varSession); } /** * Returns the name associated with this config (usually a file name). * * @return The name associated with this config, or null if it has no name. */ public String getName() { return name; } //----------------------------------------------------------------------------------------------------------------- // Workhorse getters //----------------------------------------------------------------------------------------------------------------- /** * Returns the specified value as a string from the config file. * *

* Unlike {@link #getString(String)}, this method doesn't replace SVL variables. * * @param key The key. * @return The value, or null if the section or value doesn't exist. */ private String getRaw(String key) { String sname = sname(key); String skey = skey(key); ConfigMapEntry ce = configMap.getEntry(sname, skey); if (ce == null) return null; return removeMods(ce.getModifiers(), ce.getValue()); } String applyMods(String mods, String x) { if (mods != null && x != null) for (int i = 0; i < mods.length(); i++) x = getMod(mods.charAt(i)).doApply(x); return x; } String removeMods(String mods, String x) { if (mods != null && x != null) for (int i = mods.length()-1; i > -1; i--) x = getMod(mods.charAt(i)).doRemove(x); return x; } Mod getMod(char id) { Mod x = mods.get(id); return x == null ? Mod.NO_OP : x; } //----------------------------------------------------------------------------------------------------------------- // Utility methods //----------------------------------------------------------------------------------------------------------------- /** * Takes the settings defined in this configuration and sets them as system properties. * * @return This object. */ public Config setSystemProperties() { for (String section : getSectionNames()) { for (String key : getKeys(section)) { String k = (section.isEmpty() ? key : section + '/' + key); System.setProperty(k, getRaw(k)); } } return this; } //----------------------------------------------------------------------------------------------------------------- // Workhorse setters //----------------------------------------------------------------------------------------------------------------- /** * Sets a value in this config. * * @param key The key. * @param value The value. * @return This object. * @throws UnsupportedOperationException If configuration is read only. */ public Config set(String key, String value) { checkWrite(); assertArgNotNull("key", key); String sname = sname(key); String skey = skey(key); ConfigMapEntry ce = configMap.getEntry(sname, skey); if (ce == null && value == null) return this; String s = applyMods(ce == null ? null : ce.getModifiers(), stringify(value)); configMap.setEntry(sname, skey, s, null, null, null); return this; } /** * Adds or replaces an entry with the specified key with a POJO serialized to a string using the registered * serializer. * *

* Equivalent to calling put(key, value, isEncoded(key)). * * @param key The key. * @param value The new value POJO. * @return The previous value, or null if the section or key did not previously exist. * @throws SerializeException * If serializer could not serialize the value or if a serializer is not registered with this config file. * @throws UnsupportedOperationException If configuration is read only. */ public Config set(String key, Object value) throws SerializeException { return set(key, value, null); } /** * Same as {@link #set(String, Object)} but allows you to specify the serializer to use to serialize the * value. * * @param key The key. * @param value The new value. * @param serializer * The serializer to use for serializing the object. * If null, then uses the predefined serializer on the config file. * @return The previous value, or null if the section or key did not previously exist. * @throws SerializeException * If serializer could not serialize the value or if a serializer is not registered with this config file. * @throws UnsupportedOperationException If configuration is read only. */ public Config set(String key, Object value, Serializer serializer) throws SerializeException { return set(key, serialize(value, serializer)); } /** * Same as {@link #set(String, Object)} but allows you to specify all aspects of a value. * * @param key The key. * @param value The new value. * @param serializer * The serializer to use for serializing the object. * If null, then uses the predefined serializer on the config file. * @param modifiers * Optional modifiers to apply to the value. *
If null, then previous value will not be replaced. * @param comment * Optional same-line comment to add to this value. *
If null, then previous value will not be replaced. * @param preLines * Optional comment or blank lines to add before this entry. *
If null, then previous value will not be replaced. * @return The previous value, or null if the section or key did not previously exist. * @throws SerializeException * If serializer could not serialize the value or if a serializer is not registered with this config file. * @throws UnsupportedOperationException If configuration is read only. */ public Config set(String key, Object value, Serializer serializer, String modifiers, String comment, List preLines) throws SerializeException { checkWrite(); assertArgNotNull("key", key); String sname = sname(key); String skey = skey(key); modifiers = nullIfEmpty(modifiers); String s = applyMods(modifiers, serialize(value, serializer)); configMap.setEntry(sname, skey, s, modifiers, comment, preLines); return this; } /** * Removes an entry with the specified key. * * @param key The key. * @return The previous value, or null if the section or key did not previously exist. * @throws UnsupportedOperationException If configuration is read only. */ public Config remove(String key) { checkWrite(); String sname = sname(key); String skey = skey(key); configMap.removeEntry(sname, skey); return this; } /** * Encodes and unencoded entries in this config. * *

* If any entries in the config are marked as encoded but not actually encoded, * this will encode them. * * @return This object. * @throws UnsupportedOperationException If configuration is read only. */ public Config applyMods() { checkWrite(); for (String section : configMap.getSections()) { for (String key : configMap.getKeys(section)) { ConfigMapEntry ce = configMap.getEntry(section, key); if (ce.getModifiers() != null) { String mods = ce.getModifiers(); String value = ce.getValue(); for (int i = 0; i < mods.length(); i++) { Mod mod = getMod(mods.charAt(i)); if (! mod.isApplied(value)) { configMap.setEntry(section, key, mod.apply(value), null, null, null); } } } } } return this; } //----------------------------------------------------------------------------------------------------------------- // API methods //----------------------------------------------------------------------------------------------------------------- /** * Gets the entry with the specified key. * *

* The key can be in one of the following formats... *

    *
  • * "key" - A value in the default section (i.e. defined above any [section] header). *
  • * "section/key" - A value from the specified section. *
* *

* If entry does not exist, returns an empty {@link Entry} object. * * @param key The key. * @return The entry bean, never null. */ public Entry get(String key) { return new Entry(this, configMap, sname(key), skey(key)); } /** * Gets the entry with the specified key. * *

* The key can be in one of the following formats... *

    *
  • * "key" - A value in the default section (i.e. defined above any [section] header). *
  • * "section/key" - A value from the specified section. *
* *

* If entry does not exist, returns null. * *

Notes:
    *
  • This method is equivalent to calling get(key).orElse(null);. *
* * @param key The key. * @return The entry value, or null if it doesn't exist. */ public String getString(String key) { return new Entry(this, configMap, sname(key), skey(key)).orElse(null); } /** * Gets the section with the specified name. * *

* If section does not exist, returns an empty {@link Section} object. * * @param name The section name. null and blank refer to the default section. * @return The section bean, never null. */ public Section getSection(String name) { return new Section(this, configMap, emptyIfNull(name)); } /** * Returns the keys of the entries in the specified section. * * @param section * The section name to write from. *
If empty, refers to the default section. *
Must not be null. * @return * An unmodifiable set of keys, or an empty set if the section doesn't exist. */ public Set getKeys(String section) { return configMap.getKeys(section(section)); } /** * Returns the section names defined in this config. * * @return The section names defined in this config. */ public Set getSectionNames() { return unmodifiable(configMap.getSections()); } /** * Returns true if this section contains the specified key and the key has a non-blank value. * * @param key The key. * @return true if this section contains the specified key and the key has a non-blank value. */ public boolean exists(String key) { return isNotEmpty(get(key).as(String.class).orElse(null)); } /** * Creates the specified section if it doesn't exist. * *

* Returns the existing section if it already exists. * * @param name * The section name. *
Must not be null. *
Use blank for the default section. * @param preLines * Optional comment and blank lines to add immediately before the section. *
If null, previous pre-lines will not be replaced. * @return The appended or existing section. * @throws UnsupportedOperationException If configuration is read only. */ public Config setSection(String name, List preLines) { try { return setSection(section(name), preLines, null); } catch (SerializeException e) { throw asRuntimeException(e); // Impossible. } } /** * Creates the specified section if it doesn't exist. * * @param name * The section name. *
Must not be null. *
Use blank for the default section. * @param preLines * Optional comment and blank lines to add immediately before the section. *
If null, previous pre-lines will not be replaced. * @param contents * Values to set in the new section. *
Can be null. * @return The appended or existing section. * @throws SerializeException Contents could not be serialized. * @throws UnsupportedOperationException If configuration is read only. */ public Config setSection(String name, List preLines, Map contents) throws SerializeException { checkWrite(); configMap.setSection(section(name), preLines); if (contents != null) for (Map.Entry e : contents.entrySet()) set(section(name) + '/' + e.getKey(), e.getValue()); return this; } /** * Removes the section with the specified name. * * @param name The name of the section to remove * @return This object. * @throws UnsupportedOperationException If configuration is read only. */ public Config removeSection(String name) { checkWrite(); configMap.removeSection(name); return this; } /** * Creates the specified import statement if it doesn't exist. * * @param sectionName * The section name where to place the import statement. *
Must not be null. *
Use blank for the default section. * @param importName * The import name. *
Must not be null. * @param preLines * Optional comment and blank lines to add immediately before the import statement. *
If null, previous pre-lines will not be replaced. * @return The appended or existing import statement. * @throws UnsupportedOperationException If configuration is read only. */ public Config setImport(String sectionName, String importName, List preLines) { checkWrite(); configMap.setImport(section(name), importName, preLines); return this; } /** * Removes the import statement with the specified name from the specified section. * * @param sectionName * The section name where to place the import statement. *
Must not be null. *
Use blank for the default section. * @param importName * The import name. *
Must not be null. * @return This object. * @throws UnsupportedOperationException If configuration is read only. */ public Config removeImport(String sectionName, String importName) { checkWrite(); configMap.removeImport(sectionName, importName); return this; } /** * Loads the contents of the specified map of maps into this config. * * @param m The maps to load. * @return This object. * @throws SerializeException Value could not be serialized. */ public Config load(Map> m) throws SerializeException { if (m != null) for (Map.Entry> e : m.entrySet()) { setSection(e.getKey(), null, e.getValue()); } return this; } /** * Commit the changes in this config to the store. * * @return This object. * @throws IOException Thrown by underlying stream. * @throws UnsupportedOperationException If configuration is read only. */ public Config commit() throws IOException { checkWrite(); configMap.commit(); return this; } /** * Saves this config file to the specified writer as an INI file. * *

* The writer will automatically be closed. * * @param w The writer to send the output to. * @return This object. * @throws IOException If a problem occurred trying to send contents to the writer. */ public Writer writeTo(Writer w) throws IOException { return configMap.writeTo(w); } /** * Add a listener to this config to react to modification events. * *

* Listeners should be removed using {@link #removeListener(ConfigEventListener)}. * * @param listener The new listener to add. * @return This object. */ public synchronized Config addListener(ConfigEventListener listener) { listeners.add(listener); return this; } /** * Removes a listener from this config. * * @param listener The listener to remove. * @return This object. */ public synchronized Config removeListener(ConfigEventListener listener) { listeners.remove(listener); return this; } /** * Closes this configuration object by unregistering it from the underlying config map. * * @throws IOException Thrown by underlying stream. */ public void close() throws IOException { configMap.unregister(this); } /** * Overwrites the contents of the config file. * * @param contents The new contents of the config file. * @param synchronous Wait until the change has been persisted before returning this map. * @return This object. * @throws IOException Thrown by underlying stream. * @throws InterruptedException Thread was interrupted. * @throws UnsupportedOperationException If configuration is read only. */ public Config load(Reader contents, boolean synchronous) throws IOException, InterruptedException { checkWrite(); configMap.load(read(contents), synchronous); return this; } /** * Overwrites the contents of the config file. * * @param contents The new contents of the config file. * @param synchronous Wait until the change has been persisted before returning this map. * @return This object. * @throws IOException Thrown by underlying stream. * @throws InterruptedException Thread was interrupted. * @throws UnsupportedOperationException If configuration is read only. */ public Config load(String contents, boolean synchronous) throws IOException, InterruptedException { checkWrite(); configMap.load(contents, synchronous); return this; } /** * Does a rollback of any changes on this config currently in memory. * * @return This object. * @throws UnsupportedOperationException If configuration is read only. */ public Config rollback() { checkWrite(); configMap.rollback(); return this; } /** * Returns the contents of this config as a simple map. * * @return The contents of this config as a simple map. */ public JsonMap toMap() { return configMap.asMap(); } //----------------------------------------------------------------------------------------------------------------- // Test methods //----------------------------------------------------------------------------------------------------------------- ConfigMap getConfigMap() { return configMap; } List getListeners() { return unmodifiable(listeners); } //----------------------------------------------------------------------------------------------------------------- // Interface methods //----------------------------------------------------------------------------------------------------------------- @Override /* ConfigEventListener */ public synchronized void onConfigChange(ConfigEvents events) { for (ConfigEventListener l : listeners) l.onConfigChange(events); } //----------------------------------------------------------------------------------------------------------------- // Private methods //----------------------------------------------------------------------------------------------------------------- private String serialize(Object value, Serializer serializer) throws SerializeException { if (value == null) return ""; if (serializer == null) serializer = this.serializer; Class c = value.getClass(); if (value instanceof CharSequence) return nlIfMl((CharSequence)value); if (isSimpleType(c)) return value.toString(); if (value instanceof byte[]) { String s = null; byte[] b = (byte[])value; if (binaryFormat == BinaryFormat.HEX) s = toHex(b); else if (binaryFormat == BinaryFormat.SPACED_HEX) s = toSpacedHex(b); else s = base64Encode(b); int l = binaryLineLength; if (l <= 0 || s.length() <= l) return s; StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i += l) sb.append(binaryLineLength > 0 ? "\n" : "").append(s.substring(i, Math.min(s.length(), i + l))); return sb.toString(); } String r = null; if (multiLineValuesOnSeparateLines) r = "\n" + (String)serializer.serialize(value); else r = (String)serializer.serialize(value); if (r.startsWith("'")) return r.substring(1, r.length()-1); return r; } private String nlIfMl(CharSequence cs) { String s = cs.toString(); if (s.indexOf('\n') != -1 && multiLineValuesOnSeparateLines) return "\n" + s; return s; } private boolean isSimpleType(Type t) { if (! (t instanceof Class)) return false; Class c = (Class)t; return (c == String.class || c.isPrimitive() || c.isAssignableFrom(Number.class) || c == Boolean.class || c.isEnum()); } private String sname(String key) { assertArgNotNull("key", key); int i = key.indexOf('/'); if (i == -1) return ""; return key.substring(0, i); } private String skey(String key) { int i = key.indexOf('/'); if (i == -1) return key; return key.substring(i+1); } private String section(String section) { assertArgNotNull("section", section); if (isEmpty(section)) return ""; return section; } void checkWrite() { if (readOnly) throw new UnsupportedOperationException("Cannot call this method on a read-only configuration."); } //----------------------------------------------------------------------------------------------------------------- // Other methods //----------------------------------------------------------------------------------------------------------------- @Override /* Object */ public String toString() { return configMap.toString(); } @Override /* Object */ protected void finalize() throws Throwable { close(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy