juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject [juneau] branch master updated: Config API refactoring.
Date Wed, 28 Feb 2018 22:22:54 GMT
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 <jamesbognar@apache.org>
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
jamesbognar@apache.org.

Mime
View raw message