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 1df74a4  Config API refactoring.
1df74a4 is described below

commit 1df74a4602b80083a08c8533322e92a038f5acf6
Author: JamesBognar <[email protected]>
AuthorDate: Wed Feb 28 17:22:43 2018 -0500

    Config API refactoring.
---
 .../main/java/org/apache/juneau/config/Config.java |  35 +-
 .../apache/juneau/config/encode/ConfigEncoder.java |   2 +-
 .../juneau/config/encode/ConfigXorEncoder.java     |   2 +-
 .../config/{store => internal}/ConfigEntry.java    |   2 +-
 .../config/{store => internal}/ConfigMap.java      |   7 +-
 .../juneau/config/store/ConfigMemoryStore.java     |   3 +-
 .../apache/juneau/config/store/ConfigStore.java    |   1 +
 .../apache/juneau/config/ConfigBuilderTest.java    |   4 +-
 .../juneau/config/ConfigMapListenerTest.java       |  29 +-
 .../org/apache/juneau/config/ConfigMapTest.java    |  15 +-
 .../java/org/apache/juneau/config/ConfigTest.java  |  40 +-
 .../java/org/apache/juneau/svl/VarResolver.java    |   1 -
 .../org/apache/juneau/svl/VarResolverBuilder.java  |   3 +-
 .../juneau/svl/vars/CoalesceAndRecurseVar.java     |  49 --
 .../org/apache/juneau/svl/vars/CoalesceVar.java    |  14 -
 juneau-doc/src/main/javadoc/overview.html          | 666 +++++++++++++++++++--
 .../juneau/microservice/RestMicroservice.java      |   2 +-
 .../microservice/resources/ConfigResource.java     |   2 +-
 .../org/apache/juneau/rest/RestContextBuilder.java |   6 +-
 .../juneau/rest/annotation/ResourceSwagger.java    |   6 -
 20 files changed, 710 insertions(+), 179 deletions(-)

diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
index 0fbdfd4..d56a3cf 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
@@ -25,6 +25,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.config.encode.*;
 import org.apache.juneau.config.encode.ConfigEncoder;
 import org.apache.juneau.config.event.*;
+import org.apache.juneau.config.internal.*;
 import org.apache.juneau.config.store.*;
 import org.apache.juneau.config.vars.*;
 import org.apache.juneau.http.*;
@@ -35,7 +36,12 @@ import org.apache.juneau.serializer.*;
 import org.apache.juneau.svl.*;
 
 /**
- * TODO
+ * Main configuration API class.
+ * 
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ *     <li class='link'><a class='doclink' 
href='../../../../overview-summary.html#juneau-config'>Overview &gt; 
juneau-config</a>
+ * </ul>
  */
 public final class Config extends Context implements ConfigEventListener, 
Writable {
 
@@ -290,6 +296,19 @@ public final class Config extends Context implements 
ConfigEventListener, Writab
                return new ConfigBuilder();
        }
        
+       /**
+        * Same as {@link #create()} but initializes the builder with the 
specified config name.
+        * 
+        * <p>
+        * This is equivalent to simply calling <code><jk>new</jk> 
ConfigBuilder().name(name)</code>.
+        * 
+        * @param name The configuration name.
+        * @return A new {@link ConfigBuilder} object.
+        */
+       public static ConfigBuilder create(String name) {
+               return new ConfigBuilder().name(name);
+       }
+
        @Override /* Context */
        public ConfigBuilder builder() {
                return new ConfigBuilder(getPropertyStore());
@@ -1435,15 +1454,15 @@ public final class Config extends Context implements 
ConfigEventListener, Writab
        }
 
        /**
-        * Saves this config to the store.
+        * Commit the changes in this config to the store.
         * 
         * @return This object (for method chaining).
         * @throws IOException 
         * @throws UnsupportedOperationException If configuration is read only.
         */
-       public Config save() throws IOException {
+       public Config commit() throws IOException {
                checkWrite();
-               configMap.save();
+               configMap.commit();
                return this;
        }
 
@@ -1506,9 +1525,9 @@ public final class Config extends Context implements 
ConfigEventListener, Writab
         * @throws InterruptedException
         * @throws UnsupportedOperationException If configuration is read only.
         */
-       public Config write(Reader contents, boolean synchronous) throws 
IOException, InterruptedException {
+       public Config load(Reader contents, boolean synchronous) throws 
IOException, InterruptedException {
                checkWrite();
-               configMap.write(IOUtils.read(contents), synchronous);
+               configMap.load(IOUtils.read(contents), synchronous);
                return this;
        }
 
@@ -1522,9 +1541,9 @@ public final class Config extends Context implements 
ConfigEventListener, Writab
         * @throws InterruptedException
         * @throws UnsupportedOperationException If configuration is read only.
         */
-       public Config write(String contents, boolean synchronous) throws 
IOException, InterruptedException {
+       public Config load(String contents, boolean synchronous) throws 
IOException, InterruptedException {
                checkWrite();
-               configMap.write(contents, synchronous);
+               configMap.load(contents, synchronous);
                return this;
        }
 
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
index 4ddf74d..4dacb98 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
@@ -19,7 +19,7 @@ import org.apache.juneau.config.*;
  * 
  * <h5 class='section'>See Also:</h5>
  * <ul class='doctree'>
- *     <li class='link'><a class='doclink' 
href='../../../../overview-summary.html#juneau-config.EncodedEntries'>Overview 
&gt; juneau-config &gt; Encoded Entries</a>
+ *     <li class='link'><a class='doclink' 
href='../../../../../overview-summary.html#juneau-config.EncodedEntries'>Overview
 &gt; juneau-config &gt; Encoded Entries</a>
  * </ul>
  */
 public interface ConfigEncoder {
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
index 3a32ea6..dd97a29 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
@@ -24,7 +24,7 @@ import static org.apache.juneau.internal.IOUtils.*;
  * 
  * <h5 class='section'>See Also:</h5>
  * <ul class='doctree'>
- *     <li class='link'><a class='doclink' 
href='../../../../overview-summary.html#juneau-config.EncodedEntries'>Overview 
&gt; juneau-config &gt; Encoded Entries</a>
+ *     <li class='link'><a class='doclink' 
href='../../../../../overview-summary.html#juneau-config.EncodedEntries'>Overview
 &gt; juneau-config &gt; Encoded Entries</a>
  * </ul>
  */
 public final class ConfigXorEncoder implements ConfigEncoder {
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigEntry.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
similarity index 99%
rename from 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigEntry.java
rename to 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
index 4513f96..bee83cb 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigEntry.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
@@ -10,7 +10,7 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the 
License.                                              *
 // 
***************************************************************************************************************************
-package org.apache.juneau.config.store;
+package org.apache.juneau.config.internal;
 
 import static org.apache.juneau.internal.CollectionUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMap.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
similarity index 99%
rename from 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMap.java
rename to 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
index 89f0747..dd7c69c 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMap.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
@@ -10,7 +10,7 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the 
License.                                              *
 // 
***************************************************************************************************************************
-package org.apache.juneau.config.store;
+package org.apache.juneau.config.internal;
 
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.config.event.ConfigEventType.*;
@@ -22,6 +22,7 @@ import java.util.concurrent.locks.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.config.event.*;
+import org.apache.juneau.config.store.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -404,7 +405,7 @@ public class ConfigMap implements ConfigStoreListener {
         * @throws IOException
         * @throws InterruptedException
         */
-       public ConfigMap write(String contents, boolean synchronous) throws 
IOException, InterruptedException {
+       public ConfigMap load(String contents, boolean synchronous) throws 
IOException, InterruptedException {
                
                if (synchronous) {
                        final CountDownLatch latch = new CountDownLatch(1);
@@ -445,7 +446,7 @@ public class ConfigMap implements ConfigStoreListener {
         * @return This object (for method chaining).
         * @throws IOException
         */
-       public ConfigMap save() throws IOException {
+       public ConfigMap commit() throws IOException {
                writeLock();
                try {
                        String newContents = asString();
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMemoryStore.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMemoryStore.java
index 5bbbd2d..8c522cd 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMemoryStore.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigMemoryStore.java
@@ -81,7 +81,6 @@ public class ConfigMemoryStore extends ConfigStore {
                if (expectedContents != null && ! isEquals(currentContents, 
expectedContents)) 
                        return currentContents;
                
-               cache.put(name, newContents);
                update(name, newContents);
                
                return null;
@@ -91,7 +90,7 @@ public class ConfigMemoryStore extends ConfigStore {
        @Override /* ConfigStore */
        public synchronized ConfigMemoryStore update(String name, String 
newContents) {
                cache.put(name, newContents);
-               super.update(name, newContents);
+               super.update(name, newContents);  // Trigger any listeners.
                return this;
        }
 
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigStore.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigStore.java
index f805670..7012c19 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigStore.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigStore.java
@@ -17,6 +17,7 @@ import java.util.*;
 import java.util.concurrent.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.config.internal.*;
 
 /**
  * Represents a storage location for configuration files.
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigBuilderTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigBuilderTest.java
index 1e2aee8..75737d4 100755
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigBuilderTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigBuilderTest.java
@@ -50,11 +50,11 @@ public class ConfigBuilderTest {
                f = new File(tempDir, "TestGet.cfg");
                assertFalse(f.exists());
 
-               cf.save();
+               cf.commit();
                assertObjectEquals("{'':{},Test:{A:'a'}}", cf.asMap());
 
                String NL = System.getProperty("line.separator");
-               cf = cf.write("[Test]"+NL+"A = b"+NL, true);
+               cf = cf.load("[Test]"+NL+"A = b"+NL, true);
                assertObjectEquals("{'':{},Test:{A:'b'}}", cf.asMap());
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
index 52b0e50..9d7e602 100644
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
@@ -20,6 +20,7 @@ import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.config.event.*;
+import org.apache.juneau.config.internal.*;
 import org.apache.juneau.config.store.*;
 import org.junit.*;
 
@@ -47,7 +48,7 @@ public class ConfigMapListenerTest {
                ConfigMap cm = s.getMap("Foo.cfg");
                cm.register(l);
                cm.setEntry("", "foo", "baz", null, null, null);
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -74,7 +75,7 @@ public class ConfigMapListenerTest {
                ConfigMap cm = s.getMap("Foo.cfg");
                cm.register(l);
                cm.setEntry("S1", "foo", "baz", null, null, null);
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -104,7 +105,7 @@ public class ConfigMapListenerTest {
                cm.register(l);
                cm.setEntry("", "k", "vb", null, null, null);
                cm.setEntry("S1", "k1", "v1b", null, null, null);
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -130,7 +131,7 @@ public class ConfigMapListenerTest {
                cm.register(l);
                cm.setEntry("", "k", "kb", "^*", "C", Arrays.asList("#k"));
                cm.setEntry("S1", "k1", "k1b", "^*", "C1", 
Arrays.asList("#k1"));
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -162,7 +163,7 @@ public class ConfigMapListenerTest {
                cm.register(l);
                cm.setEntry("", "k", "kb", "^*", "Cb", Arrays.asList("#kb"));
                cm.setEntry("S1", "k1", "k1b", "^*", "Cb1", 
Arrays.asList("#k1b"));
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -195,7 +196,7 @@ public class ConfigMapListenerTest {
                cm.register(l);
                cm.removeEntry("", "k");
                cm.removeEntry("S1", "k1");
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -227,7 +228,7 @@ public class ConfigMapListenerTest {
                cm.register(l);
                cm.removeEntry("", "k");
                cm.removeEntry("S1", "k1");
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -260,7 +261,7 @@ public class ConfigMapListenerTest {
                cm.setSection("S2", null);
                cm.setSection("S3", Collections.<String>emptyList());
                cm.setEntry("S3", "k3", "v3", null, null, null);
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -295,7 +296,7 @@ public class ConfigMapListenerTest {
                cm.setSection("S2", null);
                cm.setSection("S3", Collections.<String>emptyList());
                cm.setEntry("S3", "k3", "v3", null, null, null);
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -339,7 +340,7 @@ public class ConfigMapListenerTest {
                cm.removeSection("S1");
                cm.removeSection("S2");
                cm.removeSection("S3");
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -417,7 +418,7 @@ public class ConfigMapListenerTest {
                        "[S1]",
                        "k1 = v1b"
                );
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -455,7 +456,7 @@ public class ConfigMapListenerTest {
                        "[S1]",
                        "k1 = v1b"
                );
-               cm.save();
+               cm.commit();
                wait(latch);
                assertNull(l.error);
                cm.unregister(l);
@@ -497,7 +498,7 @@ public class ConfigMapListenerTest {
                        ConfigMap cm = s.getMap("Foo.cfg");
                        cm.register(l);
                        cm.setEntry("S1", "k1", "v1c", null, null, null);
-                       cm.save();
+                       cm.commit();
                        wait(latch);
                        assertNull(l.error);
                        cm.unregister(l);
@@ -543,7 +544,7 @@ public class ConfigMapListenerTest {
                        cm.register(l);
                        cm.setEntry("S1", "k1", "v1c", null, null, null);
                        try {
-                               cm.save();
+                               cm.commit();
                                fail("Exception expected.");
                        } catch (ConfigException e) {
                                assertEquals("Unable to store contents of 
config to store.", e.getMessage());
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapTest.java
index f68b778..9fc752d 100644
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigMapTest.java
@@ -20,6 +20,7 @@ import static org.apache.juneau.TestUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.config.internal.*;
 import org.apache.juneau.config.store.*;
 import org.junit.*;
 
@@ -503,7 +504,7 @@ public class ConfigMapTest {
                
                assertTextEquals("[S1]|k1 = v1b|k2 = v2a|k3 = v3b|", cm);
                
-               cm.save();
+               cm.commit();
                assertTextEquals("[S1]|k1 = v1b|k2 = v2a|k3 = v3b|", 
s.read("Foo.cfg"));
                
                // Round trip.
@@ -536,7 +537,7 @@ public class ConfigMapTest {
                
                assertTextEquals("|#S1||[S1]||#k1||k1 = v1b||#k2||k2 = v2a|k3 = 
v3b||#k4||k4 = v4b|", cm);
                
-               cm.save();
+               cm.commit();
                assertTextEquals("|#S1||[S1]||#k1||k1 = v1b||#k2||k2 = v2a|k3 = 
v3b||#k4||k4 = v4b|", s.read("Foo.cfg"));
                
                // Round trip.
@@ -556,7 +557,7 @@ public class ConfigMapTest {
                
                assertEquals("v1\nv2\nv3", cm.getEntry("", "k").getValue());
                assertEquals("v1\nv2\nv3", cm.getEntry("S1", "k1").getValue());
-               cm.save();
+               cm.commit();
                assertTextEquals("k = v1|       v2|     v3|[S1]|k1 = v1|        
v2|     v3|", cm);
                
                // Round trip.
@@ -576,7 +577,7 @@ public class ConfigMapTest {
                
                assertEquals("v1 \n v2 \n v3", cm.getEntry("", "k").getValue());
                assertEquals("v1\t\n\tv2\t\n\tv3", cm.getEntry("S1", 
"k1").getValue());
-               cm.save();
+               cm.commit();
                assertTextEquals("k = v1 |       v2 |    v3|[S1]|k1 = v1        
|               v2      |               v3|", cm);
                
                // Round trip.
@@ -668,12 +669,12 @@ public class ConfigMapTest {
                for (char c : validChars.toCharArray()) {
                        String test = ""+c;
                        cm.setSection(test, Arrays.asList("test"));
-                       cm.save();
+                       cm.commit();
                        assertEquals("test", cm.getPreLines(test).get(0));
                        
                        test = "foo"+c+"bar";
                        cm.setSection(test, Arrays.asList("test"));
-                       cm.save();
+                       cm.commit();
                        assertEquals("test", cm.getPreLines(test).get(0));
                }
        }
@@ -953,7 +954,7 @@ public class ConfigMapTest {
 
                cm.setEntry("S1", "k1", null, null, "", null);
                assertTextEquals("[S1]|k1 = v1|", cm);
-               cm.save();
+               cm.commit();
                assertTextEquals("[S1]|k1 = v1|", cm);
                
                cm.setEntry("S1", "k1", null, null, null, null);
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
index 82082ec..64e6db7 100755
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
@@ -85,7 +85,7 @@ public class ConfigTest {
                assertEquals("6", c.get("S/b2"));
                assertEquals("7", c.get("T/c1"));
                
-               c.save();
+               c.commit();
                
                assertEquals("2", c.get("a1"));
                assertEquals("3", c.get("a2"));
@@ -127,7 +127,7 @@ public class ConfigTest {
                assertEquals("6", c.get("S/b2"));
                assertEquals("7", c.get("T/c1"));
                
-               c.save();
+               c.commit();
                
                assertEquals("2", c.get("a1"));
                assertEquals("3", c.get("a2"));
@@ -187,7 +187,7 @@ public class ConfigTest {
                c.set("T/c1", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
                
                assertTextEquals("#c1|#c2|a1* = {RhMWWFIFVksf} # 
comment|#c1|#c2|a2* = {RhMWWFIFVksf} # comment|#c1|#c2|a3* = {RhMWWFIFVksf} # 
comment|[S]|#c1|#c2|b1* = {RhMWWFIFVksf} # comment|#c1|#c2|b2* = {RhMWWFIFVksf} 
# comment|[T]|#c1|#c2|c1* = {RhMWWFIFVksf} # comment|", c.toString());
-               c.save();
+               c.commit();
                assertTextEquals("#c1|#c2|a1* = {RhMWWFIFVksf} # 
comment|#c1|#c2|a2* = {RhMWWFIFVksf} # comment|#c1|#c2|a3* = {RhMWWFIFVksf} # 
comment|[S]|#c1|#c2|b1* = {RhMWWFIFVksf} # comment|#c1|#c2|b2* = {RhMWWFIFVksf} 
# comment|[T]|#c1|#c2|c1* = {RhMWWFIFVksf} # comment|", c.toString());
                c = cb.build();
                assertTextEquals("#c1|#c2|a1* = {RhMWWFIFVksf} # 
comment|#c1|#c2|a2* = {RhMWWFIFVksf} # comment|#c1|#c2|a3* = {RhMWWFIFVksf} # 
comment|[S]|#c1|#c2|b1* = {RhMWWFIFVksf} # comment|#c1|#c2|b2* = {RhMWWFIFVksf} 
# comment|[T]|#c1|#c2|c1* = {RhMWWFIFVksf} # comment|", c.toString());
@@ -214,7 +214,7 @@ public class ConfigTest {
                c.remove("T/c1");
                
                assertTextEquals("[S]|", c.toString());
-               c.save();
+               c.commit();
                assertTextEquals("[S]|", c.toString());
                c = cb.build();
                assertTextEquals("[S]|", c.toString());
@@ -1216,7 +1216,7 @@ public class ConfigTest {
                cf.set("section1/key3", new int[]{4,5,6});
                cf.set("section1/key4", new URL("http://bar";));
 
-               cf.save();
+               cf.commit();
 
                assertEquals(1, cf.getInt("key1"));
                assertEquals(true, cf.getBoolean("key2"));
@@ -1239,7 +1239,7 @@ public class ConfigTest {
                );
                assertEquals(TimeUnit.MINUTES, cf.getObject("key1", 
TimeUnit.class));
 
-               cf.save();
+               cf.commit();
 
                assertEquals(TimeUnit.MINUTES, cf.getObject("key1", 
TimeUnit.class));
        }
@@ -1257,14 +1257,14 @@ public class ConfigTest {
                
                assertEquals("mypassword", cf.getString("s1/foo"));
 
-               cf.save();
+               cf.commit();
                String expected = "[s1]||foo* = {AwwJVhwUQFZEMg==}|";
 
                assertTextEquals(expected, cf);
 
                assertEquals("mypassword", cf.getString("s1/foo"));
 
-               cf.write(new StringReader("[s1]\nfoo* = mypassword2\n"), true);
+               cf.load(new StringReader("[s1]\nfoo* = mypassword2\n"), true);
                
                assertEquals("mypassword2", cf.getString("s1/foo"));
 
@@ -1287,7 +1287,7 @@ public class ConfigTest {
                );
 
                cf.encodeEntries();
-               cf.save();
+               cf.commit();
                String expected = "[s1]||foo* = {AwwJVhwUQFZEMg==}|";
                assertTextEquals(expected, 
ConfigMemoryStore.DEFAULT.read("Test.cfg"));
        }
@@ -1448,7 +1448,7 @@ public class ConfigTest {
                cf.set("B/b1", 3);
                cf.set("B/b3", 3);
                assertObjectEquals("[]", changes);
-               cf.save();
+               cf.commit();
                assertObjectEquals("['a1=3','a3=3','B/b1=3','B/b3=3']", 
changes);
 
                // Rollback.
@@ -1459,7 +1459,7 @@ public class ConfigTest {
                cf.set("B/b3", 3);
                assertObjectEquals("[]", changes);
                cf.rollback();
-               cf.save();
+               cf.commit();
                assertObjectEquals("[]", changes);
 
                // Overwrite
@@ -1470,21 +1470,21 @@ public class ConfigTest {
                cf.set("B/b2", "2");
                cf.set("C/c1", "2");
                cf.set("C/c2", "2");
-               cf.save();
+               cf.commit();
                
assertObjectEquals("['a1=2','a2=2','B/b1=2','B/b2=2','C/c1=2','C/c2=2']", 
changes);
 
                // Encoded
                changes.clear();
                cf.set("a4", "4", null, ConfigMod.ENCODED, null, null);
                cf.set("B/b4", "4", null, ConfigMod.ENCODED, null, null);
-               cf.save();
+               cf.commit();
                assertObjectEquals("['a4={Wg==}','B/b4={Wg==}']", changes);
                
                // Encoded overwrite
                changes.clear();
                cf.set("a4", "5");
                cf.set("B/b4", "5");
-               cf.save();
+               cf.commit();
                assertObjectEquals("['a4={Ww==}','B/b4={Ww==}']", changes);
 
                // Remove entries
@@ -1494,32 +1494,32 @@ public class ConfigTest {
                cf.remove("B/b4");
                cf.remove("B/bx");
                cf.remove("X/bx");
-               cf.save();
+               cf.commit();
                assertObjectEquals("['REMOVE_ENTRY(a4)','REMOVE_ENTRY(B/b4)']", 
changes);
 
                // Add section
                // Shouldn't trigger listener.
                changes.clear();
                cf.setSection("D", Arrays.asList("#comment"));
-               cf.save();
+               cf.commit();
                assertObjectEquals("[]", changes);
 
                // Add section with contents
                changes.clear();
                cf.setSection("E", null, new AMap<String,Object>().append("e1", 
"1").append("e2", "2"));
-               cf.save();
+               cf.commit();
                assertObjectEquals("['E/e1=1','E/e2=2']", changes);
 
                // Remove section
                changes.clear();
                cf.removeSection("E");
-               cf.save();
+               cf.commit();
                
assertObjectEquals("['REMOVE_ENTRY(E/e1)','REMOVE_ENTRY(E/e2)']", changes);
 
                // Remove non-existent section
                changes.clear();
                cf.removeSection("E");
-               cf.save();
+               cf.commit();
                assertObjectEquals("[]", changes);
        }
 
@@ -1705,7 +1705,7 @@ public class ConfigTest {
                cf.set("A/a", "a,#b,=c");
 
                assertTextEquals("a = a,\\u0023b,=c|[A]|a = a,\\u0023b,=c|", 
cf);
-               cf.save();
+               cf.commit();
                assertTextEquals("a = a,\\u0023b,=c|[A]|a = a,\\u0023b,=c|", 
cf);
 
                assertEquals("a,#b,=c", cf.getString("a"));
diff --git 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolver.java 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolver.java
index dfabaa6..6bc5d27 100644
--- 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolver.java
+++ 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolver.java
@@ -68,7 +68,6 @@ public class VarResolver {
         *      <li><code>$IF{booleanValue,thenValue[,elseValue]}</code> - 
{@link IfVar}
         *      
<li><code>$SW{test,matchPattern,thenValue[,matchPattern,thenValue][,elseValue]}</code>
 - {@link SwitchVar}
         *      <li><code>$CO{arg1[,arg2...]}</code> - {@link CoalesceVar}
-        *      <li><code>$CR{arg1[,arg2...]}</code> - {@link 
CoalesceAndRecurseVar}
         * </ul>
         * 
         * @see SystemPropertiesVar
diff --git 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverBuilder.java
 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverBuilder.java
index 3f2ccf5..617c5ab 100644
--- 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverBuilder.java
+++ 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverBuilder.java
@@ -68,13 +68,12 @@ public class VarResolverBuilder {
         *      <li>{@link SwitchVar}
         *      <li>{@link IfVar}
         *      <li>{@link CoalesceVar}
-        *      <li>{@link CoalesceAndRecurseVar}
         * </ul>
         * 
         * @return This object (for method chaining).
         */
        public VarResolverBuilder defaultVars() {
-               return vars(SystemPropertiesVar.class, EnvVariablesVar.class, 
SwitchVar.class, IfVar.class, CoalesceAndRecurseVar.class, CoalesceVar.class);
+               return vars(SystemPropertiesVar.class, EnvVariablesVar.class, 
SwitchVar.class, IfVar.class, CoalesceVar.class);
        }
 
        /**
diff --git 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceAndRecurseVar.java
 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceAndRecurseVar.java
deleted file mode 100644
index 98bc245..0000000
--- 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceAndRecurseVar.java
+++ /dev/null
@@ -1,49 +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.svl.vars;
-
-import org.apache.juneau.svl.*;
-
-/**
- * A basic variable resolver that returns the first non-null value.
- * 
- * <p>
- * The format for this var is <js>"$CR{arg1[,arg2...]}"</js>.
- * 
- * <p>
- * The difference between {@link CoalesceVar} and {@link 
CoalesceAndRecurseVar} is that the first will not resolve
- * inner variables nor recursively resolve variables, and the second will.
- * <br>Use {@link CoalesceVar} when resolving user-input.
- * 
- * <h5 class='section'>See Also:</h5>
- * <ul>
- *     <li class='link'><a class="doclink" 
href="../../../../../overview-summary.html#juneau-svl.SvlVariables">Overview 
&gt; juneau-svl &gt; SVL Variables</a>
- * </ul>
- */
-public class CoalesceAndRecurseVar extends MultipartResolvingVar {
-
-       /** The name of this variable. */
-       public static final String NAME = "CR";
-
-       /**
-        * Constructor.
-        */
-       public CoalesceAndRecurseVar() {
-               super(NAME);
-       }
-
-       @Override
-       public String resolve(VarResolverSession session, String arg) throws 
Exception {
-               return arg;
-       }
-}
diff --git 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceVar.java
 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceVar.java
index 4e7af41..fa02ad9 100644
--- 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceVar.java
+++ 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/CoalesceVar.java
@@ -20,10 +20,6 @@ import org.apache.juneau.svl.*;
  * <p>
  * The format for this var is <js>"$CO{arg1[,arg2...]}"</js>.
  * 
- * <p>
- * The difference between {@link CoalesceVar} and {@link 
CoalesceAndRecurseVar} is that the first will not resolve
- * inner variables nor recursively resolve variables, and the second will.
- * <br>Use {@link CoalesceVar} when resolving user-input.
  * 
  * <h5 class='section'>See Also:</h5>
  * <ul>
@@ -46,14 +42,4 @@ public class CoalesceVar extends MultipartResolvingVar {
        public String resolve(VarResolverSession session, String arg) throws 
Exception {
                return arg;
        }
-
-       @Override /* Var */
-       protected boolean allowNested() {
-               return false;
-       }
-       
-       @Override /* Var */
-       protected boolean allowRecurse() {
-               return false;
-       }
 }
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 482e95d..348fbbb 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -157,12 +157,16 @@
                        <li><p><a class='doclink' 
href='#juneau-config.BinaryData'>Binary Data</a></p>
                </ol>
                <li><p><a class='doclink' 
href='#juneau-config.Variables'>Variables</a></p>
+               <ol>
+                       <li><p><a class='doclink' 
href='#juneau-config.LogicVariables'>Logic Variables</a></p>
+               </ol>
                <li><p><a class='doclink' 
href='#juneau-config.EncodedEntries'>Encoded Entries</a></p>
                <li><p><a class='doclink' 
href='#juneau-config.SectionMaps'>Section Maps</a></p>
                <li><p><a class='doclink' 
href='#juneau-config.SectionBeans'>Section Beans</a></p>
                <li><p><a class='doclink' 
href='#juneau-config.SectionInterfaces'>Section Interfaces</a></p>
                <li><p><a class='doclink' 
href='#juneau-config.SettingValues'>Setting Values</a></p>
                <ol>
+                       <li><p><a class='doclink' 
href='#juneau-config.FileSystemChanges'>File System Changes</a></p>
                        <li><p><a class='doclink' 
href='#juneau-config.CustomEntrySerialization'>Custom Entry 
Serialization</a></p>
                        <li><p><a class='doclink' 
href='#juneau-config.BulkSettingValues'>Setting Values in Bulk</a></p>
                </ol>           
@@ -172,6 +176,7 @@
                <ol>
                        <li><p><a class='doclink' 
href='#juneau-config.ConfigMemoryStore'>ConfigMemoryStore</a></p>
                        <li><p><a class='doclink' 
href='#juneau-config.ConfigFileStore'>ConfigFileStore</a></p>
+                       <li><p><a class='doclink' 
href='#juneau-config.CustomConfigStores"'>Custom ConfigStores</a></p>
                </ol>
                <li><p><a class='doclink' 
href='#juneau-config.ReadOnlyConfigs'>Read-only Configs</a></p>
                <li><p><a class='doclink' 
href='#juneau-config.ClosingConfigs'>Closing Configs</a></p>
@@ -4381,10 +4386,14 @@
                <ul>
                        <li><code>$S{key}</code>,<code>$S{key,default}</code> - 
System properties.
                        <li><code>$E{key}</code>,<code>$E{key,default}</code> - 
Environment variables.
+               </ul>
+               <p>
+                       The following logic variables are also provided:
+               </p>
+               <ul>
                        
<li><code>$IF{booleanValue,thenValue[,elseValue]}</code> - If-else patterns.
-                       
<li><code>$SW{test,matchPattern,thenValue[,matchPattern,thenValue][,elseValue]}</code>
 - Switch patterns.
+                       
<li><code>$SW{test,matchPattern:thenValue[,matchPattern:thenValue...][,elseValue]}</code>
 - Switch patterns.
                        <li><code>$CO{arg1[,arg2...]}</code> - Coalesce 
variable.
-                       <li><code>$CR{arg1[,arg2...]}</code> - 
Coalesce-and-recurse variable.
                </ul>
                <h5 class='figure'>Example:</h5>
                <p class='bcode'>
@@ -4478,16 +4487,12 @@
                                <td class='code'>$CO{arg1[,arg2...]}</td>
                        </tr>
                        <tr class='dark'>
-                               <td>{@link 
org.apache.juneau.svl.vars.CoalesceAndRecurseVar}</td>
-                               <td class='code'>$CR{arg1[,arg2...]}</td>
-                       </tr>
-                       <tr class='dark'>
                                <td>{@link 
org.apache.juneau.svl.vars.IfVar}</td>
-                               <td 
class='code'>$IF{booleanArg,thenValue[,elseValue]}</td>
+                               <td class='code'>$IF{booleanArg ? 
thenValue}<br>$IF{booleanArg ? thenValue : elseValue}</td>
                        </tr>
                        <tr class='dark dd'>
                                <td>{@link 
org.apache.juneau.svl.vars.SwitchVar}</td>
-                               <td 
class='code'>$SW{stringArg(,pattern,thenValue)+[,elseValue]}</td>
+                               <td class='code'>$SW{stringArg ? pattern: 
value, pattern: value, ...}</td>
                        </tr>
                        <tr class='light dd'>
                                <td rowspan="1" 
style='text-align:center;font-weight:bold;padding:20px;' 
class='code'>juneau-config</td>
@@ -4698,12 +4703,21 @@
        <ck>key4</ck> = <cv>http://bar</cv>
                </p>
                <p>
-                       This code shows how the file can be accessed:
+                       Config files are access through the {@link 
org.apache.juneau.config.Config} class which
+                       are created through the {@link 
org.apache.juneau.config.ConfigBuilder} class.
+                       <br><code>create()</code> methods are provided on the 
<code>Config</code> class for creating builders:
                </p>
                <p class='bcode'>
-       <jc>// Load our config file</jc>
+       <jc>// Create a Config object</jc>
        Config c = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
        
+       <jc>// Shortcut</jc>
+       Config c = Config.<jsm>create</jsm>(<js>"MyConfig.cfg"</js>).build();
+               </p>
+               <p>
+                       Once instantiated, reading values from the config are 
simple:
+               </p>
+               <p class='bcode'>
        <jc>// Read values from section #1</jc>
        <jk>int</jk> key1 = c.getInt(<js>"Section1/key1"</js>);
        <jk>boolean</jk> key2 = c.getBoolean(<js>"Section1/key2"</js>);
@@ -4724,22 +4738,25 @@
                                        <li>Binary data
                                </ul>
                        <li>
-                               A listener API that allows you to, for example, 
reinitialize your REST resource if the config file 
-                               changes, or listen for changes to particular 
sections or values.
+                               Transactional modifications with 
commit/rollback capabilities.
+                       <li>
+                               A listener API.
                        <li>
-                               Filesystem watcher integration allows configs 
to reflect changes on the file system in real-time.
+                               Filesystem watcher integration allowing changes 
on the file system to be reflected in real-time.
                        <li>
                                Config files can be modified through the Config 
class (e.g. add/remove/modify sections and keys, add/remove comments and 
whitespace, etc...).
                                <br>When using these APIs, you <b>DO NOT</b> 
lose formatting in your existing configuration file.
                                All existing whitespace and comments are 
preserved for you!
                        <li>
-                               Support for encoding of values for added 
security.
+                               Support for value encoding for added security.
                        <li>
-                               Config sections can be used to directly 
populate beans.
+                               Support for SVL variables.
                        <li>
-                               Config sections can be accessed and manipulated 
through Java interface proxies.
+                               Directly populate beans from config sections.
                        <li>
-                               An extensible storage API allows you to write 
your own config storage location for files such as databases or the cloud.
+                               Accessing config sections through Java 
interface proxies.
+                       <li>
+                               An extensible storage API allows you to write 
your own config storage (e.g. storage in databases or the cloud).
                </ul>
                
                <!-- 
========================================================================================================
 -->
@@ -5240,15 +5257,12 @@
                </p>
                <p>
                        By default, <code>Configs</code> use the {@link 
org.apache.juneau.svl.VarResolver#DEFAULT} variable resolver
-                       which provides support for the following variables and 
constructs (in addition to the <code>$C</code> variable):
+                       which provides support for the following variables and 
constructs:
                </p>
                <ul class='spaced-list'>
+                       <li><code>$C{key}</code>,<code>$C{key,default}</code> - 
{@link org.apache.juneau.config.vars.ConfigVar}
                        <li><code>$S{key}</code>,<code>$S{key,default}</code> - 
{@link org.apache.juneau.svl.vars.SystemPropertiesVar}
                        <li><code>$E{key}</code>,<code>$E{key,default}</code> - 
{@link org.apache.juneau.svl.vars.EnvVariablesVar}
-                       
<li><code>$IF{booleanValue,thenValue[,elseValue]}</code> - {@link 
org.apache.juneau.svl.vars.IfVar}
-                       
<li><code>$SW{test,matchPattern,thenValue[,matchPattern,thenValue][,elseValue]}</code>
 - {@link org.apache.juneau.svl.vars.SwitchVar}
-                       <li><code>$CO{arg1[,arg2...]}</code> - {@link 
org.apache.juneau.svl.vars.CoalesceVar}
-                       <li><code>$CR{arg1[,arg2...]}</code> - {@link 
org.apache.juneau.svl.vars.CoalesceAndRecurseVar}
                </ul>
                <p>
                        The variable resolver can be overridden via the 
following:
@@ -5264,6 +5278,72 @@
                <ul class='doctree'>
                        <li class='jf'>{@link 
org.apache.juneau.config.Config#resolving(VarResolverSession)}
                </ul>
+
+               <!-- 
========================================================================================================
 -->
+               <a id="juneau-config.LogicVariables"></a>
+               <h4 class='topic' onclick='toggle(this)'>6.3.1 - Logic 
Variables</h4>
+               <div class='topic'>
+                       <p>
+                               The default variable resolver also provides the 
following logic variables for performing simple logical operations:
+                       </p>
+                       <ul class='spaced-list'>
+                               
<li><code>$IF{booleanValue,thenValue[,elseValue]}</code> - {@link 
org.apache.juneau.svl.vars.IfVar}
+                               
<li><code>$SW{test,matchPattern:thenValue[,matchPattern:thenValue...]}</code> - 
{@link org.apache.juneau.svl.vars.SwitchVar}
+                               <li><code>$CO{arg1[,arg2...]}</code> - {@link 
org.apache.juneau.svl.vars.CoalesceVar}
+                       </ul>
+                       <p>
+                               The <code>$IF</code> variable can be used for 
simple if/else logic:
+                       </p>
+                       <p class='bcode'>
+       <cc># Value set to 'foo' if myBooleanProperty is true</cc>
+       <ck>key1</ck> = 
+               <cv>$IF{                
+                       $S{myBooleanProperty},
+                       foo
+               }</cv>          
+                       
+       <cc># Value set to 'foo' if myBooleanProperty is true, 'bar' if 
false.</cc>
+       <ck>key2</ck> = 
+               <cv>$IF{                
+                       $S{myBooleanProperty},
+                       foo,
+                       bar
+               }</cv>          
+
+       <cc># Value set to key1 value if myBooleanProperty is true, key2 value 
if false.</cc>
+       <ck>key3</ck> = 
+               <cv>$IF{                
+                       $S{myBooleanProperty},
+                       $C{key1},
+                       $C{key2}
+               }</cv>          
+                       </p>
+                       <p>
+                               The <code>$SW</code> variable can be used for 
switch blocks based on pattern matching:
+                       </p>
+                       <p class='bcode'>
+       <cc># Shell command depends on the OS</cc>
+       <ck>shellCommand</ck> = 
+               <cv>$SW{                
+                       $S{os.name},
+                       *win*: bat,
+                       linux: bash,
+                       *: sh
+               }</cv>          
+                       </p>
+                       <p>
+                               The <code>$CO</code> variable can be used for 
coalescing of values (finding the first non-null/empty match):
+                       </p>
+                       <p class='bcode'>
+       <cc># Debug flag can be enabled by system property or environment 
variable.</cc>
+       <ck>debug</ck> = 
+               <cv>$CO{                
+                       $S{debug},
+                       $E{DEBUG},
+                       false
+               }</cv>          
+                       </p>
+               </div>
        </div>
 
        <!-- 
========================================================================================================
 -->
@@ -5304,7 +5384,7 @@
                        <br>The curly brackets around the value identify 
whether the value has been encoded or not. 
                </p>
                <p>
-                       Unencoded values are encoded when the file is saved 
using the {@link org.apache.juneau.config.Config#save()} method.
+                       Unencoded values are encoded when the file is saved 
using the {@link org.apache.juneau.config.Config#commit()} method.
                        <br>They can also be encoded by calling {@link 
org.apache.juneau.config.Config#encodeEntries()}.
                </p>
        </div>
@@ -5424,8 +5504,8 @@
        <jc>// Write a value.</jc>
        ci.setBean(<jk>new</jk> MyBean());
 
-       <jc>// Save your changes.</jc>
-       c.save();
+       <jc>// Commit your changes to the store.</jc>
+       c.commit();
                </p>
        </div>
 
@@ -5461,52 +5541,544 @@
                .set(<js>"Section1/key2"</js>, <jk>false</jk>)
                .set(<js>"Section1/key3"</js>, <jk>new int</jk>[]{4,5,6})
                .set(<js>"Section1/key4"</js>, <jk>new</jk> 
URI(<js>"http://bar";</js>))
-               .save();
+               .commit();
+               </p>
+               <p>
+                       The method {@link 
org.apache.juneau.config.Config#set(String,Object,Serializer,ConfigMod[],String,List)}
 can be used
+                       for adding comments and pre-lines to entries, and 
specifying encoded values.
+               </p>
+               <p class='bcode'>
+       <jc>// Set an encoded value with some comments.</jc>
+       c.set(<js>"key1"</js>, 1, <jk>null</jk>, ConfigMod.<jsf>ENCODED</jsf>, 
<js>"Same-line comment"</js>, 
+               Arrays.asList(
+                       <js>"# Comment 1"</js>, <js>""</js>, <js>"# Comment 
2"</js>
+               )
+       )
+               </p>
+               <p class='bcode'>
+       <cc># Comment 1</cc>
+       
+       <cc># Comment 2</cc>
+       <ck>key1</ck> = <cv>1</cv> <cc># Same-line comment</cc>
+               </p>    
+               <p>
+                       The last 4 arguments in {@link 
org.apache.juneau.config.Config#set(String,Object,Serializer,ConfigMod[],String,List)}
 
+                       are optional in that if you pass <jk>null</jk>, the 
current value will not be overwritten.
+                       <br>To unset the same-line comment, you should pass in 
a blank string.
+                       <br>To remove pre-lines, you should pass in an empty 
list.
+               </p>    
+               <p>
+                       Sections can be added with optional pre-lines using the 
<code>setSection</code> methods:
+               </p>
+               <p class='bcode'>
+       <jc>// Set an encoded value with some comments.</jc>
+       c.setSection(<js>"NewSection"</js>, 
+               Arrays.asList(
+                       <js>"# Section comment 1"</js>, <js>""</js>, <js>"# 
Section comment 2"</js>
+               )
+       );
+               </p>
+               <p class='bcode'>
+       <cc># Section comment 1</cc>
+       
+       <cc># Section comment 2</cc>
+       <cs>[NewSection]</cs>
+               </p>            
+               <p>
+                       Changes made to the configuration are transactional in 
nature.  
+                       <br>They are kept in memory until you call the {@link 
org.apache.juneau.config.Config#commit()} method.
+                       <br>Until then, you still see the modified values when 
you call any of the getters, but the modified values
+                       exist only in memory.
+               </p>
+               <p>
+                       Changes can be rolled back using the {@link 
org.apache.juneau.config.Config#rollback()} method.
                </p>
 
                <!-- 
========================================================================================================
 -->
+               <a id="juneau-config.FileSystemChanges"></a>
+               <h4 class='topic' onclick='toggle(this)'>6.8.1 - File System 
Changes</h4>
+               <div class='topic'>
+                       <p>
+                               In general, changes made to configuration files 
will be updated immediately in the <code>Config</code>
+                               object (although this is configurable and 
described in detail later).
+                       </p>
+                       <p>
+                               The <code>Config</code> object maintains an 
in-memory record of all changes that have been applied to it
+                               through getters and setters.
+                               <br>When the underlying file changes, the new 
contents are loaded and the in-memory changes are then 
+                               applied to the new configuration.
+                               <br>This provides the benefits of real-time 
updates of configurations while not losing any changes made in the JVM.
+                       </p>
+                       <p>
+                               If the <code>commit()</code> method is called 
on the <code>Config</code> objects after the file system
+                               contents have been modified, we will then 
reload the configuration from the file system, apply the
+                               changes, and then try to save to the file 
system again (up to 10 times).
+                       </p>
+               </div>
+               
+               <!-- 
========================================================================================================
 -->
                <a id="juneau-config.CustomEntrySerialization"></a>
-               <h4 class='topic' onclick='toggle(this)'>6.8.1 - Custom Entry 
Serialization</h4>
+               <h4 class='topic' onclick='toggle(this)'>6.8.2 - Custom Entry 
Serialization</h4>
                <div class='topic'>
+                       <p>
+                               Setter methods that take in a 
<code>Serializer</code> can be used to provide custom serialization of entries
+                               instead of using the predefined serializer.
+                       </p>
+                       <p class='bcode'>
+       <jc>// Set an XML value instead of a.</jc>
+       c.set(<js>"key1"</js>, myAddress, 
XmlSerializer.<jsf>DEFAULT_SQ_READABLE</jsf>);
+                       </p>
+                       <p class='bcode'>
+       <ck>key1</ck> = 
+               <cv>&lt;address&gt;     
+                       &lt;street&gt;123 Main Street&lt;/street&gt;
+                       &lt;city&gt;Anywhere&lt;/city&gt;
+                       &lt;state&gt;NY&lt;/state&gt;
+                       &lt;zip&gt;12345&lt;/zip&gt;
+               &lt;/address&gt;</cv>
+                       </p>            
+                       <p>
+                               The value can then be retrieved using the 
equivalent parser:
+                       </p>
+                       <p class='bcode'>
+       Address a = c.getObject(<js>"key1"</js>, XmlParser.<jsf>DEFAULT</jsf>, 
Address.<jk>class</jk>);                 
+                       </p>
                </div>
 
                <!-- 
========================================================================================================
 -->
                <a id="juneau-config.BulkSettingValues"></a>
-               <h4 class='topic' onclick='toggle(this)'>6.8.2 - Setting Values 
in Bulk</h4>
+               <h4 class='topic' onclick='toggle(this)'>6.8.3 - Setting Values 
in Bulk</h4>
                <div class='topic'>
+                       <p>
+                               The following methods can be used to bulk-load 
configuration values:
+                       </p>
+                       <ul class='doctree'>
+                               <li class='jc'>{@link 
org.apache.juneau.config.Config}
+                               <ul>
+                                       <li class='jm'>{@link 
org.apache.juneau.config.Config#setSection(String,List,Map) 
setSection(String,List,Map)}
+                                       <li class='jm'>{@link 
org.apache.juneau.config.Config#load(Map) load(Map)}
+                               </ul>
+                       </ul>
+                       <p>
+                               Changes can then be committed using the {@link 
org.apache.juneau.config.Config#commit()} method.
+                       </p>
                </div>
-
        </div>
 
        <!-- 
========================================================================================================
 -->
        <a id="juneau-config.Listeners"></a>
        <h3 class='topic' onclick='toggle(this)'>6.9 - Listeners</h3>
        <div class='topic'>
+               <p>
+                       Configuration change events can be listened for using 
the following methods:
+               </p>
+               <ul class='doctree'>
+                       <li class='jc'>{@link org.apache.juneau.config.Config}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.Config#addListener(ConfigEventListener) 
addListener(ConfigEventListener)}
+                               <li class='jm'>{@link 
org.apache.juneau.config.Config#removeListener(ConfigEventListener) 
removeListener(ConfigEventListener)}
+                       </ul>
+               </ul>
+               <p>
+                       The event listener interface consists of the following 
method:
+               </p>            
+               <ul class='doctree'>
+                       <li class='jic'>{@link 
org.apache.juneau.config.event.ConfigEventListener}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEventListener#onConfigChange(List) 
onConfigChange(List&lt;ConfigEvent&gt;)}
+                       </ul>
+               </ul>
+               <ul class='doctree'>
+                       <li class='jc'>{@link 
org.apache.juneau.config.event.ConfigEvent}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getType() getType()}
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getSection() getSection()}
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getKey() getKey()}
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getValue() getValue()}
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getModifiers() getModifiers()}
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getComment() getComment()}
+                               <li class='jm'>{@link 
org.apache.juneau.config.event.ConfigEvent#getPreLines() getPreLines}
+                       </ul>
+               </ul>
+               <p>
+                       This method is triggered:
+               </p>
+               <ul>
+                       <li>After {@link 
org.apache.juneau.config.Config#commit()} is called.
+                       <li>When the file changes on the file system. 
+               </ul>
+               <p>
+                       In both cases, the listener is triggered after the 
changes have been committed.
+               </p>
+               <p class='bcode'>
+       <jk>final</jk> Config c = 
Config.<jsm>create</jsm>(<js>"MyConfig.cfg"</js>).build();
+       
+       <jc>// Add a listener for changes to MySection/key1</jc>
+       c.addListener(
+               <jk>new</jk> ConfigEventListener() {
+                       <ja>@Override</ja>
+                       <jk>public void</jk> 
onConfigChange(List&lt;ConfigEvent&gt; events) {
+                               <jk>for</jk> (ConfigEvent event : events) {
+                                       <jk>if</jk> (event.getType() == 
<jsf>SET_ENTRY</jsf>) {
+                                               <jk>if</jk> 
(event.getSection().equals(<js>"MySection"</js>) &amp;&amp; 
event.getKey().equals(<js>"key1"</js>)) {
+               
+                                                       <jc>// Get the new 
value from the event.</jc>
+                                                       String newVal = 
event.getValue();
+                                                       
+                                                       <jc>// Or get the new 
value from the config (since the change has already been committed).</jc>
+                                                       newVal = 
c.getString(<js>"MySection/key1"</js>);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       )
+               </p>
        </div>
 
        <!-- 
========================================================================================================
 -->
        <a id="juneau-config.Serializing"></a>
        <h3 class='topic' onclick='toggle(this)'>6.10 - Serializing</h3>
        <div class='topic'>
+               <p>
+                       The following methods are used for serializing 
<code>Config</code> objects back into INI files:
+               </p>
+               <ul class='doctree'>
+                       <li class='jc'>{@link org.apache.juneau.config.Config}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.Config#writeTo(Writer)}
+                               <li class='jm'>{@link 
org.apache.juneau.config.Config#toString()}
+                       </ul>
+               </ul>
+               <p>
+                       Both methods are thread safe.
+               </p>
        </div>
 
        <!-- 
========================================================================================================
 -->
        <a id="juneau-config.ConfigStores"></a>
        <h3 class='topic' onclick='toggle(this)'>6.11 - Config Stores</h3>
        <div class='topic'>
+               <p>
+                       Configuration files are stored in entities called 
Stores.
+               </p>
+               <p>
+                       The API for implementing a store is simple:
+               </p>
+               <ul class='doctree'>
+                       <li class='jac'>{@link 
org.apache.juneau.config.store.ConfigStore}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigStore#read(String) read(String)} - Read a 
config file.
+                               <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigStore#write(String,String,String) 
write(String,String,String)} - Write a config file.
+                               <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigStore#update(String,String) 
update(String,String)} - Update the contents of a config file.
+                       </ul>
+               </ul>
+               <p>
+                       Read is self-explanatory:
+                       <br><code>String read(String name)</code>
+                       <br>Simple return the string contents of the specified 
configuration name:
+               </p>
+               <p>
+                       Write is slightly trickier:
+                       <br><code>String write(String name, String oldContents, 
String newContents)</code>
+                       <br>With this method, you pass in the old and new 
contents.
+                       <br>There are two possible outcomes to this call:
+               </p>
+               <ul>
+                       <li>The old contents match the current stored contents, 
the new contents will get stored, and the method returns <jk>null</jk>.
+                       <li>The old contents DO NOT match the current stored 
contents (i.e. it was modified in some way), the new contents are NOT
+                       stored, and the method returns the current stored 
contents.
+               </ul>
+               <p>
+                       The update method is called whenever the stored file 
gets modified:
+                       <br><code><jk>void</jk> update(String name, String 
newContents)</code>
+               </p>            
+               <p>
+                       The <code>ConfigStore</code> class also has the 
following listener methods:
+               </p>
+               <ul class='doctree'>
+                       <li class='jac'>{@link 
org.apache.juneau.config.store.ConfigStore}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigStore#register(String,ConfigStoreListener) 
register(String,ConfigStoreListener)} - Register a listener on the specified 
config name.
+                               <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigStore#unregister(String,ConfigStoreListener)
 unregister(String,ConfigStoreListener)} - Unregister a listener on the 
specified config name.
+                       </ul>
+               </ul>
+               <p>
+                       Note that this is a different listener than {@link 
org.apache.juneau.config.event.ConfigEventListener}.
+                       <br>In this case, we're just listening for changed 
files:
+               </p>            
+               <ul class='doctree'>
+                       <li class='jic'>{@link 
org.apache.juneau.config.store.ConfigStoreListener}
+                       <ul>
+                               <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigStoreListener#onChange(String)} - Called 
when file changes.  New contents passed in.
+                       </ul>
+               </ul>
+               <p>
+                       This listener is used by the <code>Config</code> class 
to listen for changes on the file system so that it can be
+                       updated in real-time.
+               </p>
+               <p>
+                       Two configuration stores are provided by default:
+               </p>
+               <ul>
+                       <li class='jc'>{@link 
org.apache.juneau.config.store.ConfigFileStore} - File-system storage.
+                       <li class='jc'>{@link 
org.apache.juneau.config.store.ConfigMemoryStore} - In-memory storage.
+               </ul>
+               <p>
+                       The store is defined on the <code>Config</code> object 
via the following:
+               </p>
+               <ul>
+                       <li class='jf'>{@link 
org.apache.juneau.config.Config#CONFIG_store}
+                       <li class='jm'>{@link 
org.apache.juneau.config.ConfigBuilder#store(ConfigStore)}
+               </ul>
+               
+               <h5 class='figure'>Example:</h5>
+               <p class='bcode'>
+       <jc>// Create a config with in-memory storage.</jc>
+       Config c = 
Config.<jsm>create</jsm>(<js>"MyConfig.cfg"</js>).store(ConfigMemoryStore.<jsf>DEFAULT</jsf>).build();
+               <p>
+                       The default store used is {@link 
org.apache.juneau.config.store.ConfigFileStore#DEFAULT} which defines
+                       the execution directory as the file system directory to 
store and retrieve files. 
+               </p>
+
                
                <!-- 
========================================================================================================
 -->
                <a id="juneau-config.ConfigMemoryStore"></a>
                <h4 class='topic' onclick='toggle(this)'>6.11.1 - 
ConfigMemoryStore</h4>
                <div class='topic'>
+                       <p>
+                               The {@link 
org.apache.juneau.config.store.ConfigMemoryStore} class is simply an in-memory 
storage
+                               location for configuration files.
+                               <br>There is no hard persistence and is used 
primarily for testing purposes.
+                       </p>
+                       <p>
+                               However, the implementation provides a good 
idea on how stores work, especially the write method:
+                       </p>
+                       <p>
+       <jk>public class</jk> ConfigMemoryStore <jk>extends</jk> ConfigStore {
+       
+               <jc>// Some methods ommitted.</jc>
+               
+               <jk>private final</jk> ConcurrentHashMap&lt;String,String&gt; 
<jf>cache</jf> = <jk>new</jk> ConcurrentHashMap&lt;&gt;();
+               
+               <ja>@Override</ja> <jc>/* ConfigStore */</jc>
+               <jk>public synchronized</jk> String read(String name) {
+                       <jk>return</jk> 
<jsm>emptyIfNull</jsm>(<jf>cache</jf>.get(name));
+               }
+       
+               <ja>@Override</ja> <jc>/* ConfigStore */</jc>
+               <jk>public synchronized</jk> String write(String name, String 
expectedContents, String newContents) {
+       
+                       <jc>// This is a no-op.</jc>
+                       <jk>if</jk> (isEquals(expectedContents, newContents))
+                               <jk>return null</jk>;
+                       
+                       String currentContents = read(name);
+                       
+                       <jk>if</jk> (expectedContents != <jk>null</jk> 
&amp;&amp; ! <jsm>isEquals</jsm>(currentContents, expectedContents)) 
+                               <jk>return</jk> currentContents;
+                       
+                       update(name, newContents);
+                       
+                       <jc>// Success!</jc>
+                       <jk>return null</jk>;
+               }
+       
+               
+               <ja>@Override</ja> <jc>/* ConfigStore */</jc>
+               <jk>public synchronized</jk> ConfigMemoryStore update(String 
name, String newContents) {
+                       <jf>cache</jf>.put(name, newContents);
+                       <jk>super</jk>.update(name, newContents);  <jc>// 
Trigger any listeners.</jc>
+                       <jk>return this</jk>;
+               }
+       }
+                       </p>
+                       
                </div>
                
                <!-- 
========================================================================================================
 -->
                <a id="juneau-config.ConfigFileStore"></a>
                <h4 class='topic' onclick='toggle(this)'>6.11.2 - 
ConfigFileStore</h4>
                <div class='topic'>
+                       <p>
+                               The {@link 
org.apache.juneau.config.store.ConfigFileStore} is the typical store used for 
configuration files.
+                               It provides the following configurable settings:
+                       </p>
+                       <ul class='doctree'>
+                               <li class='jic'>{@link 
org.apache.juneau.config.store.ConfigFileStore}
+                               <ul>
+                                       <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigFileStore#FILESTORE_charset}
+                                       <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigFileStore#FILESTORE_directory}
+                                       <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigFileStore#FILESTORE_updateOnWrite}
+                                       <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigFileStore#FILESTORE_useWatcher}
+                                       <li class='jm'>{@link 
org.apache.juneau.config.store.ConfigFileStore#FILESTORE_watcherSensitivity}
+                               </ul>
+                       </ul>
+                       
+                       <h5 class='figure'>Example:</h5>
+                       <p class='bcode'>
+       <jc>// Create a config store with a watcher thread and high 
sensitivity.</jc>
+       ConfigFileStore fs = 
ConfigFileStore.<jsm>create</jsm>().directory(<js>"configs"</js>).useWatcher().watcherSensitivity(<jsf>HIGH</jsf>).build();
+       
+       <jc>// Create a config using the store defined above.</jc>
+       Config c = 
Config.<jsm>create</jsm>(<js>"MyConfig.cfg"</js>).store(fs).build();
+                       </p>
+                       
                </div>
 
+               <!-- 
========================================================================================================
 -->
+               <a id="juneau-config.CustomConfigStores"></a>
+               <h4 class='topic' onclick='toggle(this)'>6.11.3 - Custom 
ConfigStores</h4>
+               <div class='topic'>
+                       <p>
+                               The <code>ConfigStore</code> API has been 
written to allow easy development of custom configuration storage classes.
+                       </p>
+                       <p>
+                               The example belows shows a starting point for 
an implementation based on a relational database.
+                               <br>Completing it is left as an exercise:
+                       </p>
+                       <h5 class='figure'>Store Class</h5>
+                       <p class='bcode'>
+       <jk>public class</jk> ConfigSqlStore <jk>extends</jk> ConfigStore {
+
+               <jc>// Configurable properties</jc>
+               <jk>static final</jk> String
+                       <jsf>CONFIGSQLSTORE_jdbcUrl</jsf> = 
<js>"ConfigSqlStore.jdbcUrl.s"</js>,
+                       <jsf>CONFIGSQLSTORE_tableName</jsf> = 
<js>"ConfigSqlStore.tableName.s"</js>,
+                       <jsf>CONFIGSQLSTORE_nameColumn</jsf> = 
<js>"ConfigSqlStore.nameColumn.s"</js>;
+                       <jsf>CONFIGSQLSTORE_valueColumn</jsf> = 
<js>"ConfigSqlStore.valueColumn.s"</js>;
+
+               <jc>// Instance fields</jc>
+               <jk>private final</jk> String <jf>jdbcUrl</jf>;
+               <jk>private final</jk> String <jf>tableName</jf>, 
<jf>nameColumn</jf>, <jf>valueColumn</jf>;
+               <jk>private final</jk> Thread <jf>watcherThread</jf>;
+               <jk>private final</jk> ConcurrentHashMap&lt;String,String&gt; 
<jf>cache</jf> = <jk>new</jk> ConcurrentHashMap&lt;&gt;();
+
+       
+               <jc>// Constructor called from builder.</jc>
+               <jk>protected</jk> ConfigSqlStore(PropertyStore ps) {
+                       <jk>super</jk>(ps);
+                       
+                       <jk>this</jk>.<jf>jdbcUrl</jf> = 
getStringProperty(<jsf>CONFIGSQLSTORE_jdbcUrl</jsf>, 
<js>"jdbc:derby:mydb"</js>);
+                       <jk>this</jk>.<jf>tableName</jf> = 
getStringProperty(<jsf>CONFIGSQLSTORE_tableName</jsf>);
+                       <jk>this</jk>.<jf>nameColumn</jf> = 
getStringProperty(<jsf>CONFIGSQLSTORE_nameColumn</jsf>);
+                       <jk>this</jk>.<jf>valueColumn</jf> = 
getStringProperty(<jsf>CONFIGSQLSTORE_valueColumn</jsf>);          
+                       
+                       <jk>this</jk>.watcherThread = <jk>new</jk> Thread() {
+                               <ja>@Override</ja> <jc>/* Thread */</jc>
+                               <jk>public void</jk> run() {
+                                       
ConfigSqlStore.<jk>this</jk>.checkForUpdates();
+                               }
+                       };      
+                       watcherThread.start();                                  
+               }
+               
+               <jk>private synchronized void</jk> checkForUpdates() {
+               
+                       <jc>// Loop through all our entries and find the latest 
values.</jc>
+                       <jk>for</jk> (Map.Entry&lt;String,String&gt; e : 
cache.entrySet()) {
+                               String name = e.getKey();
+                               String cacheContents = e.getValue();
+                               String newContents = getDatabaseValue(name);
+                               
+                               <jc>// Change detected!</jc>
+                               <jk>if</jk> (! 
cacheContents.equals(newContents)) 
+                                       update(name, newContents);
+                       }
+                       
+               }
+               
+               <jc>// Reads the value from the database.</jc>
+               <jk>protected</jk> String getDatabaseValue(String name) {
+                       <jc>// Implement me!</jc>
+               }
+       
+               <ja>@Override</ja> <jc>/* ConfigStore */</jc>
+               <jk>public synchronized</jk> String read(String name) {
+                       String contents = <jf>cache</jf>.get(name);
+                       <jk>if</jk> (contents == <jk>null</jk>) {
+                               contents = readDatabaseValue();
+                               update(name, contents);
+                       }
+                       <jk>return</jk> contents;
+               }
+       
+               <ja>@Override</ja> <jc>/* ConfigStore */</jc>
+               <jk>public synchronized</jk> String write(String name, String 
expectedContents, String newContents) {
+       
+                       <jc>// This is a no-op.</jc>
+                       <jk>if</jk> (isEquals(expectedContents, newContents))
+                               <jk>return null</jk>;
+                       
+                       String currentContents = read(name);
+                       
+                       <jk>if</jk> (expectedContents != <jk>null</jk> 
&amp;&amp; ! <jsm>isEquals</jsm>(currentContents, expectedContents)) 
+                               <jk>return</jk> currentContents;
+                       
+                       update(name, newContents);
+                       
+                       <jc>// Success!</jc>
+                       <jk>return null</jk>;
+               }
+       
+               
+               <ja>@Override</ja> <jc>/* ConfigStore */</jc>
+               <jk>public synchronized</jk> ConfigSqlStore update(String name, 
String newContents) {
+                       <jf>cache</jf>.put(name, newContents);
+                       <jk>super</jk>.update(name, newContents);  <jc>// 
Trigger any listeners.</jc>
+                       <jk>return this</jk>;
+               }
+               
+               <ja>@Override</ja> <jc>/* Closeable */</jc>
+               <jk>public synchronized void</jk> close() {
+                       <jk>if</jk> (watcher != <jk>null</jk>)
+                               watcher.interrupt();
+               }
+       }
+                       </p>
+                       <p>
+                               The purpose of the builder class is to simply 
set values in the {@link org.apache.juneau.PropertyStore}
+                               that's passed to the <code>ConfigStore</code>:
+                       </p>
+                       <h5 class='figure'>Builder Class</h5>
+                       <p class='bcode'>
+       <jk>public class</jk> ConfigSqlStoreBuilder <jk>extends</jk> 
ConfigStoreBuilder {
+
+               <jk>public</jk> ConfigSqlStoreBuilder() {
+                       <jk>super</jk>();
+               }
+
+               <jk>publicv ConfigSqlStoreBuilder(PropertyStore ps) {
+                       <jk>super</jk>(ps);
+               }
+
+               <jk>public</jk> ConfigSqlStoreBuilder jdbcUrl(String value) {
+                       <jk>super</jk>.set(<jsf>CONFIGSQLSTORE_jdbcUrl</jsf>, 
value);
+                       <jk>return this</jk>;
+               }
+
+               <jk>public</jk> ConfigSqlStoreBuilder tableName(String value) {
+                       <jk>super</jk>.set(<jsf>CONFIGSQLSTORE_tableName</jsf>, 
value);
+                       <jk>return this</jk>;
+               }
+
+               <jk>public</jk> ConfigSqlStoreBuilder nameColumn(String value) {
+                       
<jk>super</jk>.set(<jsf>CONFIGSQLSTORE_nameColumn</jsf>, value);
+                       <jk>return this</jk>;
+               }
+
+               <jk>public</jk> ConfigSqlStoreBuilder valueColumn(String value) 
{
+                       
<jk>super</jk>.set(<jsf>CONFIGSQLSTORE_valueColumn</jsf>, value);
+                       <jk>return this</jk>;
+               }
+
+               <ja>@Override</ja> <jc>/* ContextBuilder */</jc>
+               <jk>public</jk> ConfigFileStore build() {
+                       <jk>return new</jk> ConfigFileStore(getPropertyStore());
+               }
+       }
+               </div>
+       
        </div>
 
        <!-- 
========================================================================================================
 -->
@@ -5523,7 +6095,7 @@
                <h6 class='figure'>Example:</h6>
                <p class='bcode'>
        <jc>// Create a read-only config</jc>
-       Config c = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).readOnly().build();
+       Config c = 
Config.<jsm>create</jsm>(<js>"MyConfig.cfg"</js>).readOnly().build();
                </p>
                <p>
                        This causes all methods that make modifications to 
throw {@link java.lang.UnsupportedOperationException}.
@@ -5531,7 +6103,7 @@
        </div>
 
        <!-- 
========================================================================================================
 -->
-       <a id="#juneau-config.ClosingConfigs"></a>
+       <a id="juneau-config.ClosingConfigs"></a>
        <h3 class='topic' onclick='toggle(this)'>6.13 - Closing Configs</h3>
        <div class='topic'>
                <p>
@@ -5540,7 +6112,7 @@
                </p>
                <p class='bcode'>
        <jc>// Create a transient config.</jc>
-       Config c = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
+       Config c = Config.<jsm>create</jsm>(<js>"MyConfig.cfg"</js>).build();
        
        <jc>// Do stuff with it.</jc>
        
@@ -8164,12 +8736,6 @@
                                <td 
style='text-align:center;font-weight:bold'>yes</td>
                        </tr>
                        <tr class='dark'>
-                               <td>{@link 
org.apache.juneau.svl.vars.CoalesceAndRecurseVar}</td>
-                               <td class='code'>$CR{arg1[,arg2...]}</td>
-                               <td 
style='text-align:center;font-weight:bold'>yes</td>
-                               <td 
style='text-align:center;font-weight:bold'>yes</td>
-                       </tr>
-                       <tr class='dark'>
                                <td>{@link 
org.apache.juneau.svl.vars.IfVar}</td>
                                <td 
class='code'>$IF{booleanArg,thenValue[,elseValue]}</td>
                                <td 
style='text-align:center;font-weight:bold'>yes</td>
@@ -15820,6 +16386,7 @@
        
        <h5 class='toc'>What's new in each release</h5>
        <ul class='toc'>
+               <li><p><a class='doclink' href='#7.1.1'>7.1.1 (TBD)</a></p>
                <li><p><a class='doclink' href='#7.1.0'>7.1.0 (TBD)</a></p>
                <li><p><a class='doclink' href='#7.0.1'>7.0.1 (Dec 24, 
2017)</a></p>
                <li><p><a class='doclink' href='#7.0.0'>7.0.0 (Oct 25, 
2017)</a></p>
@@ -15893,10 +16460,28 @@
        </ul>
 
        <!-- 
===========================================================================================================
 -->
+       <a id="7.1.1"></a>
+       <h3 class='topic' onclick='toggle(this)'>7.1.1 (TBD)</h3>
+       <div class='topic'>
+               <p>
+                       TBD
+               </p>
+
+               <h5 class='topic'>juneau-config</h5>
+               <ul class='spaced-list'>
+                       <li>
+                               <code>Config.save()</code> method renamed to 
{@link org.apache.juneau.config.Config#commit()}.
+                       <li>
+                               <code>Config.write(Reader,boolean)</code> 
method renamed to {@link org.apache.juneau.config.Config#load(Reader,boolean)}.
+               </ul>
+       </div>
+       
+       <!-- 
===========================================================================================================
 -->
        <a id="7.1.0"></a>
        <h3 class='topic' onclick='toggle(this)'>7.1.0 (TBD)</h3>
        <div class='topic'>
                <p>
+                       Version 7.1.0 is a major update with major 
implementation refactoring across all aspects of the product.
                </p>
        
                <h5 class='topic'>juneau-marshall</h5>
@@ -15978,7 +16563,6 @@
                                New SVL variables:
                                <ul>
                                        <li>{@link 
org.apache.juneau.svl.vars.CoalesceVar}
-                                       <li>{@link 
org.apache.juneau.svl.vars.CoalesceAndRecurseVar}
                                </ul>
                        <li>
                                Improvements to the HTTP-Part APIs.
@@ -18446,8 +19030,8 @@
                                                        
<li><code><del>ConfigFile.containsNonEmptyValue(String)</del></code> 
                                                        
<li><code><del>ConfigFile.isEncoded(String)</del></code> 
                                                        
<li><code><del>ConfigFile.addListener(ConfigFileListener)</del></code> - Listen 
for modification events on the config file.
-                                                       
<li><code><del>ConfigFile.merge(ConfigFile)} - Merge the contents of another 
config file into this config file.
-                                                       
<li><code><del>ConfigFile.getResolving()}, 
<del><code>ConfigFile.getResolving(StringVarResolver)</code></del> - Return an 
instance of the config file that resolves string variables.
+                                                       
<li><code><del>ConfigFile.merge(ConfigFile)</del></code> - Merge the contents 
of another config file into this config file.
+                                                       
<li><code><del>ConfigFile.getResolving()</del></code>, 
<del><code>ConfigFile.getResolving(StringVarResolver)</code></del> - Return an 
instance of the config file that resolves string variables.
                                                                Much more 
efficient than the previous design since the same underlying config file object 
is shared.
                                                        
<li><code><del>ConfigFile.toWritable()</del></code> - Wraps the config file in 
a {@link org.apache.juneau.Writable} interface so that it can be serialized by 
the REST interface as a plain-text INI file instead of as a serialized POJO.
                                                        
<li><code><del>ConfigFile.getInt(String)</del></code> - Now supports 
<js>"M"</js> and <js>"K"</js> to identify millions and thousands.
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
index 3f704f4..75ce57f 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
@@ -403,7 +403,7 @@ public class RestMicroservice extends Microservice {
        }
 
        /**
-        * Called when {@link Config#save()} is called on the config file.
+        * Called when {@link Config#commit()} is called on the config file.
         * 
         * <p>
         * The default behavior is configured by the following value in the 
config file:
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
index 9de2e66..1027513 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
@@ -153,7 +153,7 @@ public class ConfigResource extends Resource {
                )
        )
        public ObjectMap setConfigContents(@Body Reader contents) throws 
Exception {
-               return getServletConfig().getConfig().write(contents, 
true).asMap();
+               return getServletConfig().getConfig().load(contents, 
true).asMap();
        }
 
        /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index c06afeb..fd4dbd1 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -140,10 +140,7 @@ public class RestContextBuilder extends BeanContextBuilder 
implements ServletCon
 
                try {
 
-//                     ConfigBuilder cfb = Config.create();
-
                        htmlDocBuilder = new HtmlDocBuilder(properties);
-//                     this.config = cfb.build();
                        varResolverBuilder = new VarResolverBuilder()
                                .vars(
                                        SystemPropertiesVar.class,
@@ -151,8 +148,7 @@ public class RestContextBuilder extends BeanContextBuilder 
implements ServletCon
                                        ConfigVar.class,
                                        IfVar.class,
                                        SwitchVar.class,
-                                       CoalesceVar.class,
-                                       CoalesceAndRecurseVar.class
+                                       CoalesceVar.class
                                );
 
                        VarResolver vr = varResolverBuilder.build();
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
index b96fc32..31b5624 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
@@ -60,7 +60,6 @@ public @interface ResourceSwagger {
         * Value can contain any of the following variables:  
         * {@link ConfigVar $C} 
         * {@link CoalesceVar $CO}
-        * {@link CoalesceAndRecurseVar $CR}
         * {@link EnvVariablesVar $E} 
         * {@link FileVar $F} 
         * {@link ServletInitParamVar $I},
@@ -117,7 +116,6 @@ public @interface ResourceSwagger {
         * Value can contain any of the following variables:  
         * {@link ConfigVar $C} 
         * {@link CoalesceVar $CO}
-        * {@link CoalesceAndRecurseVar $CR}
         * {@link EnvVariablesVar $E} 
         * {@link FileVar $F} 
         * {@link ServletInitParamVar $I},
@@ -174,7 +172,6 @@ public @interface ResourceSwagger {
         * Value can contain any of the following variables:  
         * {@link ConfigVar $C} 
         * {@link CoalesceVar $CO}
-        * {@link CoalesceAndRecurseVar $CR}
         * {@link EnvVariablesVar $E} 
         * {@link FileVar $F} 
         * {@link ServletInitParamVar $I},
@@ -237,7 +234,6 @@ public @interface ResourceSwagger {
         * Value can contain any of the following variables:  
         * {@link ConfigVar $C} 
         * {@link CoalesceVar $CO}
-        * {@link CoalesceAndRecurseVar $CR}
         * {@link EnvVariablesVar $E} 
         * {@link FileVar $F} 
         * {@link ServletInitParamVar $I},
@@ -275,7 +271,6 @@ public @interface ResourceSwagger {
         * Value can contain any of the following variables:  
         * {@link ConfigVar $C} 
         * {@link CoalesceVar $CO}
-        * {@link CoalesceAndRecurseVar $CR}
         * {@link EnvVariablesVar $E} 
         * {@link FileVar $F} 
         * {@link ServletInitParamVar $I},
@@ -313,7 +308,6 @@ public @interface ResourceSwagger {
         * Value can contain any of the following variables:  
         * {@link ConfigVar $C} 
         * {@link CoalesceVar $CO}
-        * {@link CoalesceAndRecurseVar $CR}
         * {@link EnvVariablesVar $E} 
         * {@link FileVar $F} 
         * {@link ServletInitParamVar $I},

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to