juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject [2/2] incubator-juneau git commit: Introduce ConfigFileBuilder class.
Date Thu, 30 Mar 2017 00:40:00 GMT
Introduce ConfigFileBuilder class.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/26155757
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/26155757
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/26155757

Branch: refs/heads/master
Commit: 2615575787b2b8e302068e6170e64e19f05b7e38
Parents: b37a47d
Author: JamesBognar <jamesbognar@apache.org>
Authored: Wed Mar 29 20:39:56 2017 -0400
Committer: JamesBognar <jamesbognar@apache.org>
Committed: Wed Mar 29 20:39:56 2017 -0400

----------------------------------------------------------------------
 .../juneau/ini/ConfigFileBuilderTest.java       | 196 ++++++++++++
 .../org/apache/juneau/ini/ConfigFileTest.java   | 138 ++++----
 .../org/apache/juneau/ini/ConfigMgrTest.java    | 204 ------------
 .../java/org/apache/juneau/ini/ConfigFile.java  |   1 -
 .../apache/juneau/ini/ConfigFileBuilder.java    | 311 ++++++++++++++++++
 .../java/org/apache/juneau/ini/ConfigMgr.java   | 314 -------------------
 .../java/org/apache/juneau/ini/package.html     |  33 +-
 .../apache/juneau/svl/vars/ConfigFileVar.java   |   2 +-
 juneau-core/src/main/javadoc/overview.html      |  21 +-
 .../juneau/microservice/Microservice.java       |   7 +-
 .../microservice/resources/ConfigResource.java  |   2 +-
 .../org/apache/juneau/rest/client/package.html  |   2 +-
 .../java/org/apache/juneau/rest/RestConfig.java |   6 +-
 .../java/org/apache/juneau/rest/package.html    |   2 +-
 14 files changed, 611 insertions(+), 628 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
new file mode 100755
index 0000000..7a3501e
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
@@ -0,0 +1,196 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.ini;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.io.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.svl.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ConfigFileBuilderTest {
+
+	private static File tempDir;
+	private static String[] TEMP_DIR;
+
+	@BeforeClass
+	public static void setup() {
+		tempDir = new File(System.getProperty("java.io.tmpdir"), StringUtils.generateUUID(12));
+		FileUtils.mkdirs(tempDir, true);
+		TEMP_DIR = new String[]{tempDir.getAbsolutePath()};
+	}
+
+	@AfterClass
+	public static void teardown() {
+		FileUtils.delete(tempDir);
+	}
+
+	/**
+	 * 
+	 * @throws Exception
+	 */
+	@Test
+	public void testGet() throws Exception {
+		File f;
+		ConfigFileBuilder b1 = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists();
+
+		ConfigFile cf = b1.build("TestGet.cfg");
+		cf.put("Test/A", "a");
+
+		f = new File(tempDir, "TestGet.cfg");
+		assertTrue(f.exists());
+
+		cf.save();
+		assertTextEquals("[Test]|A = a|", IOUtils.read(f));
+
+		cf = b1.build("TestGet.cfg");
+		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
+
+		String NL = System.getProperty("line.separator");
+		cf = b1.build(new StringReader(("[Test]"+NL+"A = a"+NL)));
+		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
+
+		b1.charset(IOUtils.UTF8);
+		cf = b1.build("TestGet.cfg");
+		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
+	}
+	
+	/**
+	 * Retrieving config file should fail if the file doesn't exist and createIfNotExist == false.
+	 */
+	@Test
+	public void testFailOnNonExistentFiles() throws Exception {
+		ConfigFileBuilder b = new ConfigFileBuilder().paths(new String[]{tempDir.getAbsolutePath()});
+		try { b.build("TestGet2.cfg"); fail(); } catch (FileNotFoundException e) {}
+		try { b.build(tempDir.getAbsolutePath() + "TestGet2.cfg"); fail(); } catch (FileNotFoundException e) {}
+
+		b = new ConfigFileBuilder().paths().createIfNotExists();
+		try { b.build("TestGet.cfg"); fail(); } catch (FileNotFoundException e) {}
+	}	
+
+	
+	//====================================================================================================
+	// loadIfModified()
+	//====================================================================================================
+	@Test
+	public void testLoadIfModified() throws Exception {
+		ConfigFileBuilder b = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists();
+		File f;
+		ConfigFile cf = b.build("TestGet.cfg");
+		cf.put("Test/A", "a");
+
+		f = new File(tempDir, "TestGet.cfg");
+		String NL = System.getProperty("line.separator");
+		IOUtils.write(f, new StringReader("[Test]"+NL+"A = b"+NL));
+		FileUtils.modifyTimestamp(f);
+
+		cf.loadIfModified();
+		assertEquals("b", cf.getString("Test/A"));
+		cf.loadIfModified();
+		assertEquals("b", cf.getString("Test/A"));
+
+		// Config file with no backing file.
+		cf = b.build();
+		cf.put("Test/B", "b");
+		cf.loadIfModified();
+		cf.loadIfModified();
+		assertEquals("b", cf.getString("Test/B"));
+	}
+
+	//====================================================================================================
+	// read only
+	//====================================================================================================
+	@Test
+	public void testReadOnly() throws Exception {
+		ConfigFileBuilder cm = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists().readOnly();
+		ConfigFile cf = cm.build("TestGet.cfg");
+
+		// All these should fail.
+		try { cf.loadIfModified(); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.load(); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.load(new StringReader("")); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b",true); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b",true); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.removeString("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addLines("A","b=c"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addHeaderComments("A", "b=c"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.clearHeaderComments("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addSection("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.setSection("A",null); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.removeSection("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.save(); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.merge(cf); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addListener(new ConfigFileListener(){}); fail(); } catch (UnsupportedOperationException e) {}
+
+		// All these should succeed.
+		cf.getObject(String.class, "A");
+		cf.getObject(String.class, "A", "a");
+		cf.getString("A");
+		cf.getString("A","a");
+		cf.getObject(String.class, "A");
+		cf.getObject(String.class, "A", "a");
+		cf.getObject(String[].class, "A");
+		cf.getStringArray("A");
+		cf.getStringArray("A", null);
+		cf.getInt("A");
+		cf.getInt("A", 0);
+		cf.getBoolean("A");
+		cf.getBoolean("A", true);
+		cf.containsNonEmptyValue("A");
+		cf.getSectionMap("A");
+		cf.serializeTo(new StringWriter());
+		cf.serializeTo(new StringWriter(), ConfigFileFormat.INI);
+		cf.getResolving(VarResolver.DEFAULT);
+		cf.toWritable();
+	}
+
+	//====================================================================================================
+	// main(String[] args)
+	//====================================================================================================
+	@Test
+	public void testMain() throws Exception {
+		System.setProperty("exit.2", "0");
+		ConfigFileBuilder cm = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists();
+
+		ConfigFile cf = cm.build("Test.cfg")
+			.addLines(null, "# c1", "\t# c2", " c3 ", "  ", "x1=1", "x2=true", "x3=null")
+			.addLines("s1", "#c4", "k1=1", "#c5 foo=bar", "k2 = true", "k3  = \tnull");
+		cf.save();
+
+		File configFile = new File(tempDir, "Test.cfg");
+		File envFile = new File(tempDir, "Test.bat");
+
+		ConfigFileBuilder.main(new String[]{"createBatchEnvFile", "-configFile", configFile.getAbsolutePath(), "-envFile", envFile.getAbsolutePath()});
+		String expected = "rem c1|rem c2|rem c3||set x1 = 1|set x2 = true|set x3 = null|rem c4|set s1_k1 = 1|rem c5 foo=bar|set s1_k2 = true|set s1_k3 = null|";
+		String actual = IOUtils.read(envFile);
+		assertTextEquals(expected, actual);
+
+		ConfigFileBuilder.main(new String[]{"createShellEnvFile", "-configFile", configFile.getAbsolutePath(), "-envFile", envFile.getAbsolutePath()});
+		expected = "# c1|# c2|# c3||export x1=\"1\"|export x2=\"true\"|export x3=\"null\"|# c4|export s1_k1=\"1\"|# c5 foo=bar|export s1_k2=\"true\"|export s1_k3=\"null\"|";
+		actual = IOUtils.read(envFile);
+		assertTextEquals(expected, actual);
+
+		ConfigFileBuilder.main(new String[]{"setVals", "-configFile", configFile.getAbsolutePath(), "-vals", "x1=2", "s1/k1=2", "s2/k1=3"});
+		FileUtils.modifyTimestamp(configFile);
+		cf.loadIfModified();
+		assertObjectEquals("{'default':{x1:'2',x2:'true',x3:'null'},s1:{k1:'2',k2:'true',k3:'null'},s2:{k1:'3'}}", cf);
+
+		ConfigFileBuilder.main(new String[]{});
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
index 6e813a2..21c8287 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
@@ -32,6 +32,8 @@ import org.junit.*;
 @SuppressWarnings("javadoc")
 public class ConfigFileTest {
 
+	private ConfigFileBuilder configFileBuilder = new ConfigFileBuilder();
+	
 	private File getFreshFile() {
 		String tempDir = System.getProperty("java.io.tmpdir");
 		File f = new File(tempDir, "Test.cfg");
@@ -49,7 +51,7 @@ public class ConfigFileTest {
 
 		File f = getFreshFile();
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create(f)
+		ConfigFile cf = configFileBuilder.build(f)
 			.addLines(null, "# c1", "\t# c2", " c3 ");
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
 
@@ -58,7 +60,7 @@ public class ConfigFileTest {
 		assertTextEquals(expected, cfw);
 
 		cf.save();
-		cf = ConfigMgr.DEFAULT.create(f);
+		cf = configFileBuilder.build(f);
 		expected = "# c1|\t# c2| c3 |";
 		assertTextEquals(expected, cf);
 		assertTextEquals(expected, cfw);
@@ -73,7 +75,7 @@ public class ConfigFileTest {
 
 		File f = getFreshFile();
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create(f)
+		ConfigFile cf = configFileBuilder.build(f)
 			.addLines(null, "# c1", "\t# c2", " c3 ", "x1=1", "x2=true", "x3=null")
 			.addLines("s1", "#c4", "k1=1", "#c5 foo=bar", "k2 = true", "k3  = \tnull");
 		ConfigFile cfw = cf.getResolving().getResolving();
@@ -83,7 +85,7 @@ public class ConfigFileTest {
 		assertTextEquals(expected, cfw);
 
 		cf.save();
-		cf = ConfigMgr.DEFAULT.create(f);
+		cf = configFileBuilder.build(f);
 		cfw = cf.getResolving(VarResolver.DEFAULT);
 		assertEquals(1, cf.getInt("x1"));
 		assertEquals(true, cf.getBoolean("x2"));
@@ -138,7 +140,7 @@ public class ConfigFileTest {
 	@Test
 	public void testSerialization() throws Exception {
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create();
+		ConfigFile cf = configFileBuilder.build();
 		cf.put("x1", 1);
 
 		String expected = "{'default':{x1:'1'}}";
@@ -153,8 +155,8 @@ public class ConfigFileTest {
 	@Test
 	public void testHeaderComments() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create(getFreshFile()).addLines(null, "x").addLines("s1", "#c3", "#c4").addHeaderComments("s1", "#c1", "#c2"),
-			ConfigMgr.DEFAULT.create(getFreshFile()).addLines(null, "x").addLines("s1", "#c3", "#c4").addHeaderComments("s1", "#c1", "#c2").getResolving()
+			configFileBuilder.build(getFreshFile()).addLines(null, "x").addLines("s1", "#c3", "#c4").addHeaderComments("s1", "#c1", "#c2"),
+			configFileBuilder.build(getFreshFile()).addLines(null, "x").addLines("s1", "#c3", "#c4").addHeaderComments("s1", "#c1", "#c2").getResolving()
 		};
 
 		for (ConfigFile cf : cff) {
@@ -185,7 +187,7 @@ public class ConfigFileTest {
 
 		File f = getFreshFile();
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create(f)
+		ConfigFile cf = configFileBuilder.build(f)
 			.addLines(null, "x1=1")
 			.addLines("s1", "x2=2");
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
@@ -221,7 +223,7 @@ public class ConfigFileTest {
 	@Test
 	public void testPut() throws Exception {
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create();
+		ConfigFile cf = configFileBuilder.build();
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
 
 		cf.addSection(null);
@@ -257,7 +259,7 @@ public class ConfigFileTest {
 	@Test
 	public void testExampleInConfigFile() throws Exception {
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "# Default section", "key1 = 1", "key2 = true", "key3 = 1,2,3", "key4 = 'http://foo'", "")
 			.addHeaderComments("section1", "# Section 1")
 			.addLines("section1", "key1 = 2", "key2 = false", "key3 = 4,5,6", "key4 = 'http://bar'");
@@ -287,7 +289,7 @@ public class ConfigFileTest {
 		assertEquals(6, cfw.getObject(int[].class, "section1/key3")[2]);
 		assertEquals(new URL("http://bar").toString(), cfw.getObject(URL.class, "section1/key4").toString());
 
-		cf = ConfigMgr.DEFAULT.create(getFreshFile())
+		cf = configFileBuilder.build(getFreshFile())
 			.addLines(null, "# Default section")
 			.addHeaderComments("section1", "# Section 1");
 		cfw = cf.getResolving(VarResolver.DEFAULT);
@@ -362,7 +364,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testEnum() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create(getFreshFile())
+		ConfigFile cf = configFileBuilder.build(getFreshFile())
 			.addLines(null, "key1 = 'MINUTES'");
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
 
@@ -381,7 +383,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testBatchFileGeneration() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "# c1", "\t# c2", " c3 ", "", "  ", "x1=1", "x2=true", "x3=null")
 			.addHeaderComments(null, "header null", "", null)
 			.addLines("s1", "#c4", "k1=1", "#c5 foo=bar", "k2 = true", "k3  = \tnull")
@@ -404,7 +406,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testShellScriptGeneration() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "# c1", "\t# c2", " c3 ", "", " ", "x1=1", "x2=true", "x3=null")
 			.addHeaderComments(null, "header null", "", null)
 			.addLines("s1", "#c4", "k1=1", "#c5 foo=bar", "k2 = true", "k3  = \tnull")
@@ -429,7 +431,7 @@ public class ConfigFileTest {
 	public void testEncodedValues() throws Exception {
 		File f = getFreshFile();
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create(f)
+		ConfigFile cf = configFileBuilder.build(f)
 			.addLines("s1", "", "foo* = mypassword")
 			.getResolving(VarResolver.DEFAULT);
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
@@ -479,7 +481,7 @@ public class ConfigFileTest {
 	@Test
 	public void testVariables() throws Exception {
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines("s1",
 				"f1 = $S{foo}",
 				"f2 = $S{foo,bar}",
@@ -550,7 +552,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testMultiLines() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines("s1",
 				"f1 = x \ny \n  z"
 		);
@@ -574,7 +576,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testNumberShortcuts() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines("s1",
 				"f1 = 1M",
 				"f2 = 1K",
@@ -600,8 +602,8 @@ public class ConfigFileTest {
 	@Test
 	public void testListeners() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create(FileUtils.createTempFile("ConfigFileTest.cfg")).addLines(null, "a1=1").addLines("B", "b1=1"),
-			ConfigMgr.DEFAULT.create(FileUtils.createTempFile("ConfigFileTest.cfg")).addLines(null, "a1=1").addLines("B", "b1=1").getResolving(VarResolver.DEFAULT)
+			configFileBuilder.build(FileUtils.createTempFile("ConfigFileTest.cfg")).addLines(null, "a1=1").addLines("B", "b1=1"),
+			configFileBuilder.build(FileUtils.createTempFile("ConfigFileTest.cfg")).addLines(null, "a1=1").addLines("B", "b1=1").getResolving(VarResolver.DEFAULT)
 		};
 
 		for (ConfigFile cf : cff) {
@@ -732,7 +734,7 @@ public class ConfigFileTest {
 			assertEquals(1, count[0]);
 
 			// putAll(map)
-			ConfigFile cf2 = ConfigMgr.DEFAULT.create();
+			ConfigFile cf2 = configFileBuilder.build();
 			cf2.addLines("D", "d1=1","d2=1").addLines("E", "e1=1","e2=2");
 			changes.clear();
 			count[0] = 0;
@@ -916,8 +918,8 @@ public class ConfigFileTest {
 	@Test
 	public void testEntryListener() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("B", "b1=1"),
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("B", "b1=1").getResolving(VarResolver.DEFAULT)
+			configFileBuilder.build().addLines(null, "a1=1").addLines("B", "b1=1"),
+			configFileBuilder.build().addLines(null, "a1=1").addLines("B", "b1=1").getResolving(VarResolver.DEFAULT)
 		};
 
 		for (ConfigFile cf : cff) {
@@ -960,8 +962,8 @@ public class ConfigFileTest {
 	@Test
 	public void testSectionListener() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("B", "b1=1"),
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("B", "b1=1").getResolving(VarResolver.DEFAULT)
+			configFileBuilder.build().addLines(null, "a1=1").addLines("B", "b1=1"),
+			configFileBuilder.build().addLines(null, "a1=1").addLines("B", "b1=1").getResolving(VarResolver.DEFAULT)
 		};
 
 		for (ConfigFile cf : cff) {
@@ -1014,11 +1016,11 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testMerge() throws Exception {
-		ConfigFile cf1 = ConfigMgr.DEFAULT.create()
+		ConfigFile cf1 = configFileBuilder.build()
 			.addLines(null, "# comment a1", "a1=1")
 			.addLines("B", "# comment b1", "b1=1").addHeaderComments("B", "# comment B1")
 			.addLines("C", "# comment c1", "bc=1").addHeaderComments("C", "# comment C1");
-		ConfigFile cf2 = ConfigMgr.DEFAULT.create()
+		ConfigFile cf2 = configFileBuilder.build()
 			.addLines(null, "# comment a2", "a2=2")
 			.addLines("B", "# comment b2", "b2=2").addHeaderComments("B", "# comment B2")
 			.addLines("D", "# comment d2", "d2=2").addHeaderComments("D", "# comment D2");
@@ -1027,11 +1029,11 @@ public class ConfigFileTest {
 		String expected = "# comment a2|a2 = 2|# comment B2|[B]|# comment b2|b2 = 2|# comment D2|[D]|# comment d2|d2 = 2|";
 		assertTextEquals(expected, cf1);
 
-		cf1 = ConfigMgr.DEFAULT.create()
+		cf1 = configFileBuilder.build()
 			.addLines(null, "# comment a1", "a1=1")
 			.addLines("B", "# comment b1", "b1=1").addHeaderComments("B", "# comment B1")
 			.addLines("C", "# comment c1", "bc=1").addHeaderComments("C", "# comment C1").getResolving(VarResolver.DEFAULT);
-		cf2 = ConfigMgr.DEFAULT.create()
+		cf2 = configFileBuilder.build()
 			.addLines(null, "# comment a2", "a2=2")
 			.addLines("B", "# comment b2", "b2=2").addHeaderComments("B", "# comment B2")
 			.addLines("D", "# comment d2", "d2=2").addHeaderComments("D", "# comment D2").getResolving(VarResolver.DEFAULT);
@@ -1047,8 +1049,8 @@ public class ConfigFileTest {
 	@Test
 	public void testDefaultSection() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("", "a2=2").addLines("default", "a3=3"),
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("", "a2=2").addLines("default", "a3=3").getResolving(VarResolver.DEFAULT)
+			configFileBuilder.build().addLines(null, "a1=1").addLines("", "a2=2").addLines("default", "a3=3"),
+			configFileBuilder.build().addLines(null, "a1=1").addLines("", "a2=2").addLines("default", "a3=3").getResolving(VarResolver.DEFAULT)
 		};
 
 		for (ConfigFile cf : cff) {
@@ -1095,7 +1097,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testContains() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines(null, "a1=1").addLines("", "a2=2").addLines("default", "a3=3").addLines("A", "a4=4");
+		ConfigFile cf = configFileBuilder.build().addLines(null, "a1=1").addLines("", "a2=2").addLines("default", "a3=3").addLines("A", "a4=4");
 		ConfigFile cfw = cf.getResolving();
 
 		assertTrue(cf.containsKey(null));
@@ -1141,7 +1143,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetObjectArray() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines("A", "a1=1,2,3");
+		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=1,2,3");
 		ConfigFile cfw = cf.getResolving();
 		assertObjectEquals("[1,2,3]", cf.getObject(Integer[].class, "A/a1"));
 		assertObjectEquals("[1,2,3]", cfw.getObject(Integer[].class, "A/a1"));
@@ -1152,7 +1154,7 @@ public class ConfigFileTest {
 		assertObjectEquals("[]", cf.getObject(Integer[].class, "B/a1"));
 		assertObjectEquals("[]", cfw.getObject(Integer[].class, "B/a1"));
 
-		cf = ConfigMgr.DEFAULT.create().addLines("A", "a1 = 1 ,\n\t2 ,\n\t3 ");
+		cf = configFileBuilder.build().addLines("A", "a1 = 1 ,\n\t2 ,\n\t3 ");
 		assertObjectEquals("[1,2,3]", cf.getObject(Integer[].class, "A/a1"));
 		assertObjectEquals("[1,2,3]", cfw.getObject(Integer[].class, "A/a1"));
 
@@ -1172,7 +1174,7 @@ public class ConfigFileTest {
 		assertEquals("int", cf.getObject(int[].class, "A/a2", new int[]{4}).getClass().getComponentType().getSimpleName());
 
 		System.setProperty("X", "4,5,6");
-		cf = ConfigMgr.DEFAULT.create().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}").addLines("A", "a1=1,2,3").getResolving();
+		cf = configFileBuilder.build().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}").addLines("A", "a1=1,2,3").getResolving();
 		assertObjectEquals("[1,2,3]", cf.getObject(int[].class, "x1", new int[]{9}));
 		assertObjectEquals("[4,5,6]", cf.getObject(int[].class, "x2", new int[]{9}));
 		assertObjectEquals("[9]", cf.getObject(int[].class, "x3", new int[]{9}));
@@ -1185,7 +1187,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetStringArray() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines("A", "a1=1,2,3");
+		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=1,2,3");
 		ConfigFile cfw = cf.getResolving();
 		assertObjectEquals("['1','2','3']", cf.getStringArray("A/a1"));
 		assertObjectEquals("['1','2','3']", cfw.getStringArray("A/a1"));
@@ -1196,12 +1198,12 @@ public class ConfigFileTest {
 		assertObjectEquals("[]", cf.getStringArray("B/a1"));
 		assertObjectEquals("[]", cfw.getStringArray("B/a1"));
 
-		cf = ConfigMgr.DEFAULT.create().addLines("A", "a1 = 1 ,\n\t2 ,\n\t3 ");
+		cf = configFileBuilder.build().addLines("A", "a1 = 1 ,\n\t2 ,\n\t3 ");
 		assertObjectEquals("['1','2','3']", cf.getStringArray("A/a1"));
 		assertObjectEquals("['1','2','3']", cfw.getStringArray("A/a1"));
 
 		System.setProperty("X", "4,5,6");
-		cf = ConfigMgr.DEFAULT.create().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,$S{X}}").addLines("A", "a1=1,2,3").getResolving();
+		cf = configFileBuilder.build().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,$S{X}}").addLines("A", "a1=1,2,3").getResolving();
 		assertObjectEquals("['1','2','3']", cf.getStringArray("x1", new String[]{"9"}));
 		assertObjectEquals("['4','5','6']", cf.getStringArray("x2", new String[]{"9"}));
 		assertObjectEquals("['9']", cf.getStringArray("x3", new String[]{"9"}));
@@ -1216,7 +1218,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetSectionMap() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines("A", "a1=1", "").addLines("D", "d1=$C{A/a1}","d2=$S{X}");
+		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=1", "").addLines("D", "d1=$C{A/a1}","d2=$S{X}");
 
 		assertObjectEquals("{a1:'1'}", cf.getSectionMap("A"));
 		assertNull(cf.getSectionMap("B"));
@@ -1242,8 +1244,8 @@ public class ConfigFileTest {
 	@Test
 	public void testLoadFromReader() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1"),
-			ConfigMgr.DEFAULT.create().addLines(null, "a1=1").getResolving(VarResolver.DEFAULT)
+			configFileBuilder.build().addLines(null, "a1=1"),
+			configFileBuilder.build().addLines(null, "a1=1").getResolving(VarResolver.DEFAULT)
 		};
 
 		for (ConfigFile cf : cff) {
@@ -1258,7 +1260,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testToWritable() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "a=b");
 		ConfigFile cfw = cf.getResolving();
 
@@ -1279,7 +1281,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testContainsNonEmptyKey() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "a=b","c=");
 		ConfigFile cfw = cf.getResolving();
 
@@ -1306,7 +1308,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetSectionKeys() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1", "x2=")
 			.addLines("A", "a1=1", "a2=");
 
@@ -1330,7 +1332,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testAddLines() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1");
 
@@ -1342,7 +1344,7 @@ public class ConfigFileTest {
 		assertObjectEquals("{'default':{x1:'2',x2:'1'},A:{a1:'2',a2:'1'},B:{b1:'2',b2:'1'},C:{}}", cf);
 		assertTextEquals("# comment1|x1 = 2|x2 = 1|foobar|[A]|# comment2|a1 = 2|a2 = 1|foobar|[B]|# comment3|b1 = 2|b2 = 1|foobar|[C]||", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1")
 			.getResolving();
@@ -1355,7 +1357,7 @@ public class ConfigFileTest {
 		assertObjectEquals("{'default':{x1:'2',x2:'1'},A:{a1:'2',a2:'1'},B:{b1:'2',b2:'1'},C:{}}", cf);
 		assertTextEquals("# comment1|x1 = 2|x2 = 1|foobar|[A]|# comment2|a1 = 2|a2 = 1|foobar|[B]|# comment3|b1 = 2|b2 = 1|foobar|[C]||", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=$C{A/a2}")
 			.addLines("A", "a1=1")
 			.getResolving();
@@ -1376,7 +1378,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testAddHeaderComments() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1");
 
@@ -1390,7 +1392,7 @@ public class ConfigFileTest {
 
 		assertTextEquals("# h1|# h2|# h3|x1 = 1|# h4|[A]|a1 = 1|# h5|[B]|#|[C]|", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1");
 
@@ -1407,7 +1409,7 @@ public class ConfigFileTest {
 		cf.clearHeaderComments(null).clearHeaderComments("A").clearHeaderComments("B").clearHeaderComments("C");
 		assertTextEquals("x1 = 1|[A]|a1 = 1|[B]|[C]|", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1")
 			.getResolving();
@@ -1433,7 +1435,7 @@ public class ConfigFileTest {
 	@Test
 	public void testGetString() throws Exception {
 		System.setProperty("S1", "1");
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1", "x2=$C{A/a2}", "x3=$S{S1,2}", "x4=$S{S2,3}")
 			.addLines("A", "a1=1", "a2=$C{A/a1}", "a3=$S{S1,2}", "a4=$S{S2,3}");
 
@@ -1497,7 +1499,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testPutString() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1");
 
@@ -1510,7 +1512,7 @@ public class ConfigFileTest {
 		assertObjectEquals("{'default':{x1:'2',x2:'3'},A:{a1:'2',a2:'3'},B:{b1:'2'}}", cf);
 		assertTextEquals("x1 = 2|x2 = 3|[A]|a1 = 2|a2 = 3|[B]|b1 = 2|", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1")
 			.getResolving();
@@ -1545,7 +1547,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testPutStringEncoded() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1");
 
@@ -1558,7 +1560,7 @@ public class ConfigFileTest {
 		assertObjectEquals("{'default':{x1:'2',x2:'3'},A:{a1:'2',a2:'3'},B:{b1:'2'}}", cf);
 		assertTextEquals("x1* = {XA==}|x2* = {XQ==}|[A]|a1* = {XA==}|a2* = {XQ==}|[B]|b1* = {XA==}|", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1")
 			.getResolving();
@@ -1593,7 +1595,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testRemoveString() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create()
+		ConfigFile cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1");
 
@@ -1605,7 +1607,7 @@ public class ConfigFileTest {
 		assertObjectEquals("{'default':{},A:{}}", cf);
 		assertTextEquals("[A]|", cf);
 
-		cf = ConfigMgr.DEFAULT.create()
+		cf = configFileBuilder.build()
 			.addLines(null, "x1=1")
 			.addLines("A", "a1=1")
 			.getResolving();
@@ -1625,7 +1627,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetObject() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines("A", "a1=1,2,3", "a2=1", "a3=true", "a4=1.2", "a5=1.2,3.4");
+		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=1,2,3", "a2=1", "a3=true", "a4=1.2", "a5=1.2,3.4");
 		ConfigFile cfw = cf.getResolving();
 
 		assertObjectEquals("['1','2','3']", cf.getObject(String[].class, "A/a1"));
@@ -1662,7 +1664,7 @@ public class ConfigFileTest {
 	@Test
 	public void testGetInt() throws Exception {
 		System.setProperty("X", "1");
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,2}").addLines("A", "a1=1");
+		ConfigFile cf = configFileBuilder.build().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,2}").addLines("A", "a1=1");
 
 		try {
 			cf.getInt("x1");
@@ -1704,7 +1706,7 @@ public class ConfigFileTest {
 	@Test
 	public void testGetBoolean() throws Exception {
 		System.setProperty("X", "true");
-		ConfigFile cf = ConfigMgr.DEFAULT.create().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,true}").addLines("A", "a1=true");
+		ConfigFile cf = configFileBuilder.build().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,true}").addLines("A", "a1=true");
 
 		assertFalse(cf.getBoolean("x1"));
 		assertFalse(cf.getBoolean("x2"));
@@ -1732,7 +1734,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetSectionAsBean() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create();
+		ConfigFile cf = configFileBuilder.build();
 		cf.put("A/a", "1");
 		cf.put("A/b", "2");
 
@@ -1769,7 +1771,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testWriteProperties() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create();
+		ConfigFile cf = configFileBuilder.build();
 		cf.put("B/a", "1");
 
 		B b = new B();
@@ -1865,8 +1867,8 @@ public class ConfigFileTest {
 	@Test
 	public void testBadInput() throws Exception {
 		ConfigFile[] cff = {
-			ConfigMgr.DEFAULT.create().addLines("A", "a1=1", ""),
-			ConfigMgr.DEFAULT.create().addLines("A", "a1=1", "").getResolving()
+			configFileBuilder.build().addLines("A", "a1=1", ""),
+			configFileBuilder.build().addLines("A", "a1=1", "").getResolving()
 		};
 
 		for (ConfigFile cf : cff) {
@@ -2022,7 +2024,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testSerializedAsJson() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create();
+		ConfigFile cf = configFileBuilder.build();
 		cf.put("a", "1");
 		cf.put("B/a", "2");
 
@@ -2039,7 +2041,7 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testResolvingWithOverride() throws Exception {
-		ConfigFile cf = ConfigMgr.DEFAULT.create();
+		ConfigFile cf = configFileBuilder.build();
 		cf.put("a", "$A{X}");
 		cf.put("b", "$B{X}");
 		cf.put("c", "$A{$B{X}}");
@@ -2115,7 +2117,7 @@ public class ConfigFileTest {
 	public void testMultilineValues() throws Exception {
 		File f = getFreshFile();
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create(f);
+		ConfigFile cf = configFileBuilder.build(f);
 		cf.put("a", "a,\nb,\nc");
 		cf.put("A/a", "a,\nb,\nc");
 
@@ -2138,7 +2140,7 @@ public class ConfigFileTest {
 	public void testSpecialCharacterEncoding() throws Exception {
 		File f = getFreshFile();
 
-		ConfigFile cf = ConfigMgr.DEFAULT.create(f);
+		ConfigFile cf = configFileBuilder.build(f);
 		cf.put("a", "a,#b,=c");
 		cf.put("A/a", "a,#b,=c");
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigMgrTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigMgrTest.java b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigMgrTest.java
deleted file mode 100755
index 4039fcb..0000000
--- a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigMgrTest.java
+++ /dev/null
@@ -1,204 +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.ini;
-
-import static org.apache.juneau.TestUtils.*;
-import static org.junit.Assert.*;
-
-import java.io.*;
-import java.nio.charset.*;
-
-import org.apache.juneau.internal.*;
-import org.apache.juneau.json.*;
-import org.apache.juneau.svl.*;
-import org.junit.*;
-
-@SuppressWarnings("javadoc")
-public class ConfigMgrTest {
-
-	private static File tempDir;
-
-	@BeforeClass
-	public static void setup() {
-		tempDir = new File(System.getProperty("java.io.tmpdir"), StringUtils.generateUUID(12));
-		FileUtils.mkdirs(tempDir, true);
-	}
-
-	@AfterClass
-	public static void teardown() {
-		FileUtils.delete(tempDir);
-	}
-
-	//====================================================================================================
-	// get(String path)
-	// get(String path, boolean create)
-	//====================================================================================================
-	@Test
-	public void testGet() throws Exception {
-		File f;
-		ConfigMgr cm = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), new String[]{tempDir.getAbsolutePath()});
-
-		ConfigFile cf = cm.get("TestGet.cfg", true);
-		cf.put("Test/A", "a");
-
-		f = new File(tempDir, "TestGet.cfg");
-		assertTrue(f.exists());
-
-		cf.save();
-		assertTextEquals("[Test]|A = a|", IOUtils.read(f));
-
-		cf = cm.get("TestGet.cfg");
-		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
-
-		ConfigFile cf2 = cm.get(tempDir.getAbsolutePath() + "/TestGet.cfg");
-		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf2);
-		assertTrue(cf == cf2);  // Relative and absolute paths must resolve to same config file.
-
-		try { cm.get("TestGet2.cfg"); fail(); } catch (FileNotFoundException e) {}
-		try { cm.get(tempDir.getAbsolutePath() + "TestGet2.cfg"); fail(); } catch (FileNotFoundException e) {}
-
-		cm.get(tempDir.getAbsolutePath() + "TestGet2.cfg", true);
-
-		ConfigMgr cm2 = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), new String[]{tempDir.getAbsolutePath()});
-		cf = cm2.get("TestGet.cfg");
-		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
-
-		cm2 = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), null);
-		try { cf = cm2.get("TestGet.cfg"); fail(); } catch (FileNotFoundException e) {}
-
-		String NL = System.getProperty("line.separator");
-		cf = cm2.create(new StringReader(("[Test]"+NL+"A = a"+NL)));
-		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
-
-		ConfigMgr cm3 = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, IOUtils.UTF8, new String[]{tempDir.getAbsolutePath()});
-		cf = cm3.get("TestGet.cfg");
-		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
-
-		cm.deleteAll();
-		cm2.deleteAll();
-		cm3.deleteAll();
-	}
-
-	//====================================================================================================
-	// loadIfModified()
-	//====================================================================================================
-	@Test
-	public void testLoadIfModified() throws Exception {
-		ConfigMgr cm = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), new String[]{tempDir.getAbsolutePath()});
-		File f;
-		ConfigFile cf = cm.get("TestGet.cfg", true);
-		cf.put("Test/A", "a");
-
-		f = new File(tempDir, "TestGet.cfg");
-		String NL = System.getProperty("line.separator");
-		IOUtils.write(f, new StringReader("[Test]"+NL+"A = b"+NL));
-		FileUtils.modifyTimestamp(f);
-
-		cm.loadIfModified();
-		assertEquals("b", cf.getString("Test/A"));
-		cm.loadIfModified();
-		assertEquals("b", cf.getString("Test/A"));
-
-		// Config file with no backing file.
-		cf = cm.create();
-		cf.put("Test/B", "b");
-		cm.loadIfModified();
-		cf.loadIfModified();
-		assertEquals("b", cf.getString("Test/B"));
-
-		cm.deleteAll();
-	}
-
-	//====================================================================================================
-	// read only
-	//====================================================================================================
-	@Test
-	public void testReadOnly() throws Exception {
-		ConfigMgr cm = new ConfigMgr(true, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), new String[]{tempDir.getAbsolutePath()});
-		ConfigFile cf = cm.get("TestGet.cfg", true);
-
-		// All these should fail.
-		try { cf.loadIfModified(); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.load(); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.load(new StringReader("")); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.put("A","b"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.put("A","b",true); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.put("A","b"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.put("A","b",true); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.removeString("A"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.addLines("A","b=c"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.addHeaderComments("A", "b=c"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.clearHeaderComments("A"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.addSection("A"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.setSection("A",null); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.removeSection("A"); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.save(); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.merge(cf); fail(); } catch (UnsupportedOperationException e) {}
-		try { cf.addListener(new ConfigFileListener(){}); fail(); } catch (UnsupportedOperationException e) {}
-
-		// All these should succeed.
-		cf.getObject(String.class, "A");
-		cf.getObject(String.class, "A", "a");
-		cf.getString("A");
-		cf.getString("A","a");
-		cf.getObject(String.class, "A");
-		cf.getObject(String.class, "A", "a");
-		cf.getObject(String[].class, "A");
-		cf.getStringArray("A");
-		cf.getStringArray("A", null);
-		cf.getInt("A");
-		cf.getInt("A", 0);
-		cf.getBoolean("A");
-		cf.getBoolean("A", true);
-		cf.containsNonEmptyValue("A");
-		cf.getSectionMap("A");
-		cf.serializeTo(new StringWriter());
-		cf.serializeTo(new StringWriter(), ConfigFileFormat.INI);
-		cf.getResolving(VarResolver.DEFAULT);
-		cf.toWritable();
-	}
-
-	//====================================================================================================
-	// main(String[] args)
-	//====================================================================================================
-	@Test
-	public void testMain() throws Exception {
-		System.setProperty("exit.2", "0");
-		ConfigMgr cm = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), new String[]{tempDir.getAbsolutePath()});
-
-		ConfigFile cf = cm.get("Test.cfg", true)
-			.addLines(null, "# c1", "\t# c2", " c3 ", "  ", "x1=1", "x2=true", "x3=null")
-			.addLines("s1", "#c4", "k1=1", "#c5 foo=bar", "k2 = true", "k3  = \tnull");
-		cf.save();
-
-		File configFile = new File(tempDir, "Test.cfg");
-		File envFile = new File(tempDir, "Test.bat");
-
-		ConfigMgr.main(new String[]{"createBatchEnvFile", "-configFile", configFile.getAbsolutePath(), "-envFile", envFile.getAbsolutePath()});
-		String expected = "rem c1|rem c2|rem c3||set x1 = 1|set x2 = true|set x3 = null|rem c4|set s1_k1 = 1|rem c5 foo=bar|set s1_k2 = true|set s1_k3 = null|";
-		String actual = IOUtils.read(envFile);
-		assertTextEquals(expected, actual);
-
-		ConfigMgr.main(new String[]{"createShellEnvFile", "-configFile", configFile.getAbsolutePath(), "-envFile", envFile.getAbsolutePath()});
-		expected = "# c1|# c2|# c3||export x1=\"1\"|export x2=\"true\"|export x3=\"null\"|# c4|export s1_k1=\"1\"|# c5 foo=bar|export s1_k2=\"true\"|export s1_k3=\"null\"|";
-		actual = IOUtils.read(envFile);
-		assertTextEquals(expected, actual);
-
-		ConfigMgr.main(new String[]{"setVals", "-configFile", configFile.getAbsolutePath(), "-vals", "x1=2", "s1/k1=2", "s2/k1=3"});
-		FileUtils.modifyTimestamp(configFile);
-		cf.loadIfModified();
-		assertObjectEquals("{'default':{x1:'2',x2:'true',x3:'null'},s1:{k1:'2',k2:'true',k3:'null'},s2:{k1:'3'}}", cf);
-
-		ConfigMgr.main(new String[]{});
-	}
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
index 40a0f9e..d84242e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
@@ -744,7 +744,6 @@ public abstract class ConfigFile implements Map<String,Section> {
 		return null;
 	}
 
-
 	private static int parseIntWithSuffix(String s) {
 		assertFieldNotNull(s, "s");
 		int m = 1;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
new file mode 100644
index 0000000..a699b4f
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
@@ -0,0 +1,311 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.ini;
+
+import static org.apache.juneau.ini.ConfigFileFormat.*;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * Builder for creating instances of {@link ConfigFile ConfigFiles}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * 	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+ * 	String setting = cf.get(<js>"MySection/mysetting"</js>);
+ * </p>
+ */
+@SuppressWarnings("hiding")
+public class ConfigFileBuilder {
+
+	private WriterSerializer serializer = JsonSerializer.DEFAULT_LAX;
+	private ReaderParser parser = JsonParser.DEFAULT;
+	private Encoder encoder = new XorEncoder();
+	private boolean readOnly = false, createIfNotExists = false;
+	private Charset charset = Charset.defaultCharset();
+	private List<File> searchPaths = new AList<File>().append(new File("."));
+
+	/**
+	 * Specify the encoder to use for encoded config file entries (e.g. <js>"mySecret*={...}"</js>).
+	 * <p>
+	 * The default value for this setting is an instance of {@link XorEncoder}.
+	 *
+	 * @param encoder The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder encoder(Encoder encoder) {
+		this.encoder = encoder;
+		return this;
+	}
+
+	/**
+	 * Specify the serializer to use for serializing POJOs when using {@link ConfigFile#put(String, Object)}.
+	 * <p>
+	 * The default value for this setting is {@link JsonSerializer#DEFAULT_LAX}.
+	 *
+	 * @param serializer The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder serializer(WriterSerializer serializer) {
+		this.serializer = serializer;
+		return this;
+	}
+
+	/**
+	 * Specify the parser to use for parsing POJOs when using {@link ConfigFile#getObject(Class,String)}.
+	 * <p>
+	 * The default value for this setting is {@link JsonParser#DEFAULT}
+	 *
+	 * @param parser The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder parser(ReaderParser parser) {
+		this.parser = parser;
+		return this;
+	}
+
+	/**
+	 * Specify the config file character encoding.
+	 * <p>
+	 * The default value for this setting is {@link Charset#defaultCharset()}.
+	 *
+	 * @param charset The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder charset(Charset charset) {
+		this.charset = charset;
+		return this;
+	}
+
+	/**
+	 * Specify the search paths for config files.
+	 * <p>
+	 * Can contain relative or absolute paths.
+	 * <p>
+	 * The default value for this setting is <code>[<js>"."</js>]</code>.
+	 *
+	 * @param searchPaths The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder paths(String...searchPaths) {
+		this.searchPaths = new LinkedList<File>();
+		for (String p : searchPaths)
+			this.searchPaths.add(new File(p));
+		return this;
+	}
+
+	/**
+	 * Make {@link ConfigFile ConfigFiles} read-only.
+	 * <p>
+	 * The default value of this setting is <jk>false</jk>.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder readOnly() {
+		this.readOnly = true;
+		return this;
+	}
+
+	/**
+	 * Create config files if they cannot be found on the file system.
+	 * <p>
+	 * The default value for this setting is <jk>false</jk>.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public ConfigFileBuilder createIfNotExists() {
+		this.createIfNotExists = true;
+		return this;
+	}
+
+	/**
+	 * Returns the config file with the specified absolute or relative path.
+	 *
+	 * @param path The absolute or relative path of the config file.
+	 * @return The config file.
+	 * @throws IOException If config file could not be parsed.
+	 * @throws FileNotFoundException If config file could not be found.
+	 */
+	public ConfigFile build(String path) throws IOException {
+		return new ConfigFileImpl(resolve(path), readOnly, encoder, serializer, parser, charset);
+	}
+
+	/**
+	 * Create a new empty config file not backed by any file.
+	 *
+	 * @return A new config file.
+	 * @throws IOException
+	 */
+	public ConfigFile build() throws IOException {
+		return new ConfigFileImpl(null, false, encoder, serializer, parser, charset);
+	}
+
+	/**
+	 * Create a new config file backed by the specified file.
+	 * <p>
+	 * This method is provided primarily for testing purposes.
+	 *
+	 * @param f The file to create a config file from.
+	 * @return A new config file.
+	 * @throws IOException
+	 */
+	public ConfigFile build(File f) throws IOException {
+		return new ConfigFileImpl(f, false, encoder, serializer, parser, charset);
+	}
+
+	/**
+	 * Create a new config file not backed by a file.
+	 *
+	 * @param r The reader containing an INI-formatted file to initialize the config file from.
+	 * @return A new config file.
+	 * @throws IOException
+	 */
+	public ConfigFile build(Reader r) throws IOException {
+		return new ConfigFileImpl(null, false, encoder, serializer, parser, charset).load(r);
+	}
+
+	private File resolve(String path) throws IOException {
+
+		// Handle absolute file.
+		File f = new File(path);
+		if (f.isAbsolute()) {
+			if (createIfNotExists)
+				FileUtils.create(f);
+			if (f.exists())
+				return f;
+			throw new FileNotFoundException("Could not find config file '"+path+"'");
+		}
+
+		if (searchPaths.isEmpty())
+			throw new FileNotFoundException("No search paths specified in ConfigFileBuilder.");
+
+		// Handle paths relative to search paths.
+		for (File sf : searchPaths) {
+			f = new File(sf.getAbsolutePath() + "/" + path);
+			if (f.exists())
+				return f;
+		}
+
+		if (createIfNotExists) {
+			f = new File(searchPaths.get(0).getAbsolutePath() + "/" + path);
+			FileUtils.create(f);
+			return f;
+		}
+
+		throw new FileNotFoundException("Could not find config file '"+path+"'");
+	}
+
+	/**
+	 * Implements command-line features for working with INI configuration files.
+	 * <p>
+	 * Invoke as a normal Java program...
+	 * <p>
+	 * <p class='bcode'>
+	 * 	java org.apache.juneau.ini.ConfigFileBuilder [args]
+	 * </p>
+	 * <p>
+	 * Arguments can be any of the following...
+	 * <ul class='spaced-list'>
+	 * 	<li>No arguments<br>
+	 * 		Prints usage message.<br>
+	 * 	<li><code>createBatchEnvFile -configfile &lt;configFile&gt; -envfile &lt;batchFile&gt; [-verbose]</code><br>
+	 * 		Creates a batch file that will set each config file entry as an environment variable.<br>
+	 * 		Characters in the keys that are not valid as environment variable names (e.g. <js>'/'</js> and <js>'.'</js>)
+	 * 			will be converted to underscores.<br>
+	 * 	<li><code>createShellEnvFile -configFile &lt;configFile&gt; -envFile &lt;configFile&gt; [-verbose]</code>
+	 * 		Creates a shell script that will set each config file entry as an environment variable.<br>
+	 * 		Characters in the keys that are not valid as environment variable names (e.g. <js>'/'</js> and <js>'.'</js>)
+	 * 			will be converted to underscores.<br>
+	 * 	<li><code>setVals -configFile &lt;configFile&gt; -vals [var1=val1 [var2=val2...]] [-verbose]</code>
+	 * 		Sets values in config files.<br>
+	 * </ul>
+	 * <p>
+	 * For example, the following command will create the file <code>'MyConfig.bat'</code> from the contents of the file <code>'MyConfig.cfg'</code>.
+	 * <p class='bcode'>
+	 * 	java org.apache.juneau.ini.ConfigFileBuilder createBatchEnvFile -configfile C:\foo\MyConfig.cfg -batchfile C:\foo\MyConfig.bat
+	 * </p>
+	 *
+	 * @param args Command-line arguments
+	 */
+	public static void main(String[] args) {
+
+		Args a = new Args(args);
+		String command = a.getArg(0);
+		String configFile = a.getArg("configFile");
+		String envFile = a.getArg("envFile");
+		List<String> vals = a.getArgs("vals");
+
+		if (command == null || ! (command.equals("createBatchEnvFile") || command.equals("createShellEnvFile") || command.equals("setVals")))
+			printUsageAndExit();
+		else if (configFile.isEmpty())
+			printUsageAndExit();
+		else if (command.equals("setVals") && vals.isEmpty())
+			printUsageAndExit();
+		else if ((command.equals("createBatchEnvFile") || command.equals("createShellEnvFile")) && envFile.isEmpty())
+			printUsageAndExit();
+		else {
+			try {
+				ConfigFile cf = new ConfigFileBuilder().build(configFile);
+
+				if (command.equalsIgnoreCase("setVals")) {
+					for (String val : vals) {
+						String[] x = val.split("\\=");
+						if (x.length != 2)
+							throw new RuntimeException("Invalid format for value: '"+val+"'.  Must be in the format 'key=value'");
+						cf.put(x[0], x[1]);
+					}
+					cf.save();
+					return;
+
+				} else if (command.equalsIgnoreCase("createBatchEnvFile")) {
+					Writer fw = new OutputStreamWriter(new FileOutputStream(envFile), Charset.defaultCharset());
+					try {
+						cf.serializeTo(fw, BATCH);
+					} finally {
+						fw.close();
+					}
+					return;
+
+				} else if (command.equalsIgnoreCase("createShellEnvFile")) {
+					Writer fw = new OutputStreamWriter(new FileOutputStream(envFile), Charset.defaultCharset());
+					try {
+						cf.serializeTo(fw, SHELL);
+					} finally {
+						fw.close();
+					}
+					return;
+				}
+
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	private static void printUsageAndExit() {
+		System.err.println("---Usage---"); // NOT DEBUG
+		System.err.println("java -cp juneau.jar org.apache.juneau.ini.ConfigFile createBatchEnvFile -configFile <configFile> -envFile <envFile> [-verbose]"); // NOT DEBUG
+		System.err.println("java -cp juneau.jar org.apache.juneau.ini.ConfigFile createShellEnvFile -configFile <configFile> -envFile <envFile> [-verbose]"); // NOT DEBUG
+		System.err.println("java -cp juneau.jar org.apache.juneau.ini.ConfigFile setVals -configFile <configFile> -vals [var1 val1 [var2 val2...]] [-verbose]"); // NOT DEBUG
+		int rc = Integer.getInteger("exit.2", 2);
+		if (rc != 0)
+			System.exit(rc);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core/src/main/java/org/apache/juneau/ini/ConfigMgr.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigMgr.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigMgr.java
deleted file mode 100644
index 0ebf523..0000000
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigMgr.java
+++ /dev/null
@@ -1,314 +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.ini;
-
-import static org.apache.juneau.ini.ConfigFileFormat.*;
-
-import java.io.*;
-import java.nio.charset.*;
-import java.util.*;
-import java.util.concurrent.*;
-
-import org.apache.juneau.internal.*;
-import org.apache.juneau.json.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.utils.*;
-
-/**
- * Manager for retrieving shared instances of {@link ConfigFile ConfigFiles}.
- *
- * <h5 class='section'>Example:</h5>
- * <p class='bcode'>
- * 	ConfigFile cf = ConfigMgr.<jsf>DEFAULT</jsf>.get(<js>"MyConfig.cfg"</js>);
- * 	String setting = cf.get(<js>"MySection/mysetting"</js>);
- * </p>
- */
-public class ConfigMgr {
-
-	/**
-	 * Default reusable configuration manager.
-	 * <ul class='spaced-list'>
-	 * 	<li>Read-only: <jk>false</jk>.
-	 * 	<li>Encoder: {@link XorEncoder}.
-	 * 	<li>Serializer: {@link JsonSerializer#DEFAULT}.
-	 * 	<li>Parser: {@link JsonParser#DEFAULT}.
-	 * 	<li>Charset: {@link Charset#defaultCharset()}.
-	 * 	<li>Search paths: [<js>"."</js>].
-	 * </ul>
-	 */
-	public static final ConfigMgr DEFAULT = new ConfigMgr(false, new XorEncoder(), JsonSerializer.DEFAULT, JsonParser.DEFAULT, Charset.defaultCharset(), new String[]{"."});
-
-	private ConcurrentHashMap<String,File> files = new ConcurrentHashMap<String,File>();
-	private ConcurrentHashMap<File,ConfigFile> configs = new ConcurrentHashMap<File,ConfigFile>();
-	private final WriterSerializer serializer;
-	private final ReaderParser parser;
-	private final Encoder encoder;
-	private final boolean readOnly;
-	private final Charset charset;
-	private final List<File> searchPaths = new LinkedList<File>();
-
-	/**
-	 * Create a custom configuration manager.
-	 *
-	 * @param readOnly Make {@link ConfigFile ConfigFiles} read-only.
-	 * @param encoder Optional.  Specify the encoder to use for encoded config file entries (e.g. <js>"mySecret*={...}"</js>).
-	 * @param serializer Optional.  Specify the serializer to use for serializing POJOs when using {@link ConfigFile#put(String, Object)}.
-	 * @param parser Optional.  Specify the parser to use for parsing POJOs when using {@link ConfigFile#getObject(Class,String)}.
-	 * @param charset Optional.  Specify the config file character encoding.  If <jk>null</jk>, uses {@link Charset#defaultCharset()}.
-	 * @param searchPaths Specify the search paths for config files.  Can contain relative or absolute paths.
-	 */
-	public ConfigMgr(boolean readOnly, Encoder encoder, WriterSerializer serializer, ReaderParser parser, Charset charset, String[] searchPaths) {
-		this.readOnly = readOnly;
-		this.encoder = encoder;
-		this.serializer = serializer;
-		this.parser = parser;
-		this.charset = charset;
-		if (searchPaths != null)
-			for (String p : searchPaths)
-				this.searchPaths.add(new File(p));
-	}
-
-	/**
-	 * Returns the config file with the specified absolute or relative path.
-	 * <p>
-	 * Multiple calls to the same path return the same <code>ConfigFile</code> instance.
-	 *
-	 * @param path The absolute or relative path of the config file.
-	 * @return The config file.
-	 * @throws IOException If config file could not be parsed.
-	 * @throws FileNotFoundException If config file could not be found.
-	 */
-	public ConfigFile get(String path) throws IOException {
-		return get(path, false);
-	}
-
-	/**
-	 * Returns the config file with the specified absolute or relative path.
-	 * <p>
-	 * Multiple calls to the same path return the same <code>ConfigFile</code> instance.
-	 * <p>
-	 * If file doesn't exist and <code>create</code> is <jk>true</jk>, the configuration file will be
-	 * create in the location identified by the first entry in the search paths.
-	 *
-	 * @param path The absolute or relative path of the config file.
-	 * @param create Create the config file if it doesn't exist.
-	 * @return The config file.
-	 * @throws IOException If config file could not be parsed.
-	 * @throws FileNotFoundException If config file could not be found or could not be created.
-	 */
-	public ConfigFile get(String path, boolean create) throws IOException {
-
-		File f = resolve(path, create);
-
-		ConfigFile cf = configs.get(f);
-		if (cf != null)
-			return cf;
-
-		cf = new ConfigFileImpl(f, readOnly, encoder, serializer, parser, charset);
-		configs.putIfAbsent(f, cf);
-		return configs.get(f);
-	}
-
-	/**
-	 * Create a new empty config file not backed by any file.
-	 *
-	 * @return A new config file.
-	 * @throws IOException
-	 */
-	public ConfigFile create() throws IOException {
-		return new ConfigFileImpl(null, false, encoder, serializer, parser, charset);
-	}
-
-	/**
-	 * Create a new config file backed by the specified file.
-	 * Note that {@link #get(String)} is the preferred method for getting access to config files
-	 * 	since this method will create a new config file each time it is called.
-	 * This method is provided primarily for testing purposes.
-	 *
-	 * @param f The file to create a config file from.
-	 * @return A new config file.
-	 * @throws IOException
-	 */
-	public ConfigFile create(File f) throws IOException {
-		return new ConfigFileImpl(f, false, encoder, serializer, parser, charset);
-	}
-
-	/**
-	 * Create a new config file not backed by a file.
-	 *
-	 * @param r The reader containing an INI-formatted file to initialize the config file from.
-	 * @return A new config file.
-	 * @throws IOException
-	 */
-	public ConfigFile create(Reader r) throws IOException {
-		return new ConfigFileImpl(null, false, encoder, serializer, parser, charset).load(r);
-	}
-
-	/**
-	 * Reloads any config files that were modified.
-	 * @throws IOException
-	 */
-	public void loadIfModified() throws IOException {
-		for (ConfigFile cf : configs.values())
-			cf.loadIfModified();
-	}
-
-	/**
-	 * Delete all configuration files registered with this config manager.
-	 */
-	public void deleteAll() {
-		for (File f : Collections.list(configs.keys()))  // Don't use keySet(), otherwise fails on Java 6/7 if compiled using Java 8.
-			FileUtils.delete(f);
-		files.clear();
-		configs.clear();
-	}
-
-	private File resolve(String path, boolean create) throws IOException {
-
-		// See if it's cached.
-		File f = files.get(path);
-		if (f != null)
-			return f;
-
-		// Handle absolute file.
-		f = new File(path);
-		if (f.isAbsolute()) {
-			if (create)
-				FileUtils.create(f);
-			if (f.exists())
-				return addFile(path, f);
-			throw new FileNotFoundException("Could not find config file '"+path+"'");
-		}
-
-		if (searchPaths.isEmpty())
-			throw new FileNotFoundException("No search paths specified on ConfigMgr.");
-
-		// Handle paths relative to search paths.
-		for (File sf : searchPaths) {
-			f = new File(sf.getAbsolutePath() + "/" + path);
-			if (f.exists())
-				return addFile(path, f);
-		}
-
-		if (create) {
-			f = new File(searchPaths.get(0).getAbsolutePath() + "/" + path);
-			FileUtils.create(f);
-				return addFile(path, f);
-		}
-
-		throw new FileNotFoundException("Could not find config file '"+path+"'");
-	}
-
-	private File addFile(String path, File f) {
-		files.putIfAbsent(path, f);
-		return files.get(path);
-	}
-
-	/**
-	 * Implements command-line features for working with INI configuration files.
-	 * <p>
-	 * Invoke as a normal Java program...
-	 * <p>
-	 * <p class='bcode'>
-	 * 	java org.apache.juneau.ini.ConfigMgr [args]
-	 * </p>
-	 * <p>
-	 * Arguments can be any of the following...
-	 * <ul class='spaced-list'>
-	 * 	<li>No arguments<br>
-	 * 		Prints usage message.<br>
-	 * 	<li><code>createBatchEnvFile -configfile &lt;configFile&gt; -envfile &lt;batchFile&gt; [-verbose]</code><br>
-	 * 		Creates a batch file that will set each config file entry as an environment variable.<br>
-	 * 		Characters in the keys that are not valid as environment variable names (e.g. <js>'/'</js> and <js>'.'</js>)
-	 * 			will be converted to underscores.<br>
-	 * 	<li><code>createShellEnvFile -configFile &lt;configFile&gt; -envFile &lt;configFile&gt; [-verbose]</code>
-	 * 		Creates a shell script that will set each config file entry as an environment variable.<br>
-	 * 		Characters in the keys that are not valid as environment variable names (e.g. <js>'/'</js> and <js>'.'</js>)
-	 * 			will be converted to underscores.<br>
-	 * 	<li><code>setVals -configFile &lt;configFile&gt; -vals [var1=val1 [var2=val2...]] [-verbose]</code>
-	 * 		Sets values in config files.<br>
-	 * </ul>
-	 * <p>
-	 * For example, the following command will create the file <code>'MyConfig.bat'</code> from the contents of the file <code>'MyConfig.cfg'</code>.
-	 * <p class='bcode'>
-	 * 	java org.apache.juneau.ini.ConfigMgr createBatchEnvFile -configfile C:\foo\MyConfig.cfg -batchfile C:\foo\MyConfig.bat
-	 * </p>
-	 *
-	 * @param args Command-line arguments
-	 */
-	public static void main(String[] args) {
-
-		Args a = new Args(args);
-		String command = a.getArg(0);
-		String configFile = a.getArg("configFile");
-		String envFile = a.getArg("envFile");
-		List<String> vals = a.getArgs("vals");
-
-		if (command == null || ! (command.equals("createBatchEnvFile") || command.equals("createShellEnvFile") || command.equals("setVals")))
-			printUsageAndExit();
-		else if (configFile.isEmpty())
-			printUsageAndExit();
-		else if (command.equals("setVals") && vals.isEmpty())
-			printUsageAndExit();
-		else if ((command.equals("createBatchEnvFile") || command.equals("createShellEnvFile")) && envFile.isEmpty())
-			printUsageAndExit();
-		else {
-			try {
-				ConfigFile cf = ConfigMgr.DEFAULT.get(configFile);
-
-				if (command.equalsIgnoreCase("setVals")) {
-					for (String val : vals) {
-						String[] x = val.split("\\=");
-						if (x.length != 2)
-							throw new RuntimeException("Invalid format for value: '"+val+"'.  Must be in the format 'key=value'");
-						cf.put(x[0], x[1]);
-					}
-					cf.save();
-					return;
-
-				} else if (command.equalsIgnoreCase("createBatchEnvFile")) {
-					Writer fw = new OutputStreamWriter(new FileOutputStream(envFile), Charset.defaultCharset());
-					try {
-						cf.serializeTo(fw, BATCH);
-					} finally {
-						fw.close();
-					}
-					return;
-
-				} else if (command.equalsIgnoreCase("createShellEnvFile")) {
-					Writer fw = new OutputStreamWriter(new FileOutputStream(envFile), Charset.defaultCharset());
-					try {
-						cf.serializeTo(fw, SHELL);
-					} finally {
-						fw.close();
-					}
-					return;
-				}
-
-			} catch (Exception e) {
-				e.printStackTrace();
-			}
-		}
-	}
-
-	private static void printUsageAndExit() {
-		System.err.println("---Usage---"); // NOT DEBUG
-		System.err.println("java -cp juneau.jar org.apache.juneau.ini.ConfigFile createBatchEnvFile -configFile <configFile> -envFile <envFile> [-verbose]"); // NOT DEBUG
-		System.err.println("java -cp juneau.jar org.apache.juneau.ini.ConfigFile createShellEnvFile -configFile <configFile> -envFile <envFile> [-verbose]"); // NOT DEBUG
-		System.err.println("java -cp juneau.jar org.apache.juneau.ini.ConfigFile setVals -configFile <configFile> -vals [var1 val1 [var2 val2...]] [-verbose]"); // NOT DEBUG
-		int rc = Integer.getInteger("exit.2", 2);
-		if (rc != 0)
-			System.exit(rc);
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core/src/main/java/org/apache/juneau/ini/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/package.html b/juneau-core/src/main/java/org/apache/juneau/ini/package.html
index a35c338..fd1d7fd 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/package.html
@@ -72,7 +72,7 @@
 <h2 class='topic' onclick='toggle(this)'>1 - Overview</h2>
 <div class='topic'>
 	<p>
-		The {@link org.apache.juneau.ini.ConfigMgr} and {@link org.apache.juneau.ini.ConfigFile} classes 
+		The {@link org.apache.juneau.ini.ConfigFileBuilder} and {@link org.apache.juneau.ini.ConfigFile} classes 
 		implement an API for working with INI-style configuration files such as the following:
 	</p>
 	<p class='bcode'>
@@ -95,7 +95,7 @@
 	</p>
 	
 	<p>
-		The {@link org.apache.juneau.ini.ConfigMgr} class is used to instantiate instances of 
+		The {@link org.apache.juneau.ini.ConfigFileBuilder} class is used to instantiate instances of 
 		{@link org.apache.juneau.ini.ConfigFile} which can then be used to retrieve config file values through either <js>"key"</js> or <js>"Section/key"</js> identifiers.
 	</p>
 
@@ -106,7 +106,7 @@
 	URL key4;
 	
 	<jc>// Get our config file using the default config manager</jc>
-	ConfigFile f = ConfigMgr.<jsf>DEFAULT</jsf>.getConfig(<js>"C:/temp/MyConfig.cfg"</js>);
+	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"C:/temp/MyConfig.cfg"</js>);
 
 	<jc>// Read values from default section</jc>
 	key1 = f.getInt(<js>"key1"</js>);
@@ -127,7 +127,7 @@
 	
 	<p class='bcode'>
 	<jc>// Construct the sample INI file programmatically</jc>
-	ConfigFile f = ConfigMgr.<jsf>DEFAULT</jsf>.getConfig(<js>"C:/temp/MyConfig.cfg"</js>, <jk>true</jk>)
+	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"C:/temp/MyConfig.cfg"</js>, <jk>true</jk>)
 		.addLines(<jk>null</jk>,                     <jc>// The default 'null' section</jc>
 			<js>"# Default section"</js>,             <jc>// A regular comment</jc>
 			<js>"key1 = 1"</js>,                      <jc>// A numeric entry</jc>
@@ -150,7 +150,7 @@
 		Note how we're setting values as POJOs which will be automatically converted to strings when persisted to disk.
 	<p class='bcode'>
 	<jc>// Construct the sample INI file programmatically</jc>
-	ConfigFile f = ConfigMgr.<jsf>DEFAULT</jsf>.getConfig(<js>"C:/temp/MyConfig.cfg"</js>, <jk>true</jk>)
+	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"C:/temp/MyConfig.cfg"</js>, <jk>true</jk>)
 		.addLines(<jk>null</jk>,
 			<js>"# Default section"</js>)
 		.addHeaderComments(<js>"Section1"</js>,
@@ -250,11 +250,6 @@
 	<ck>customMessage</ck> = <cv>Java home is $C{MyProperties/javaHome} and the environment path is $C{MyProperties/path}.</cv>
 	</p>
 	<p>
-		Resolving config files (and any config files retrieved through the same <code>ConfigMgr</code> that point to the same physical file)
-		share the same underlying config files in memory.  
-		This allows changes in one instance of the config file to be reflected in all.
-	</p>
-	<p>
 		Support for variables is extensible.  You can add support for your own variables by implementing custom 
 		{@link org.apache.juneau.svl.VarResolver VarResolvers}.<br>
 		For example, the microservice <code>Resource</code> class provides access to config files that
@@ -305,8 +300,8 @@
 	<p>
 		The default encoder is {@link org.apache.juneau.ini.XorEncoder} which is a simple XOR+Base64 encoder.<br>
 		If desired, custom encoder can be used by implementing the {@link org.apache.juneau.ini.Encoder}
-		interface and creating your own <code>ConfigMgr</code> using the {@link org.apache.juneau.ini.ConfigMgr#ConfigMgr(boolean,Encoder,WriterSerializer,ReaderParser,Charset,String[])}
-		constructor.
+		interface and creating your own <code>ConfigFileBuilder</code> using the {@link org.apache.juneau.ini.ConfigFileBuilder#encoder(Encoder)}
+		method.
 	</p>
 </div>
 
@@ -331,7 +326,7 @@
 	<h6 class="topic">Example:</h6>
 	<p class='bcode'>
 	<jc>// Get our config file using the default config manager</jc>
-	ConfigFile f = ConfigMgr.<jsf>DEFAULT</jsf>.getConfig(<js>"C:/temp/MyConfig.cfg"</js>);
+	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"C:/temp/MyConfig.cfg"</js>);
 
 	<jc>// Add a listener for an entry</jc>
 	f.addListener(
@@ -343,10 +338,6 @@
 		}
 	);
 	</p>
-	<p>
-		Note that since {@link org.apache.juneau.ini.ConfigFile} instances for the same physical files are shared in {@link org.apache.juneau.ini.ConfigMgr}, a change made
-		in once instance of a config file will trigger all listeners defined on that physical file.
-	</p>
 </div>
 
 <!-- ======================================================================================================== -->
@@ -354,12 +345,12 @@
 <h2 class='topic' onclick='toggle(this)'>5 - Command Line API</h2>
 <div class='topic'>
 	<p>
-		The {@link org.apache.juneau.ini.ConfigMgr} class contains a {@link org.apache.juneau.ini.ConfigMgr#main(String[])}
+		The {@link org.apache.juneau.ini.ConfigFileBuilder} class contains a {@link org.apache.juneau.ini.ConfigFileBuilder#main(String[])}
 			method that can be used to work with config files through a command-line prompt.<br>
 		This is invoked as a normal Java command:
 	</p>
 	<p class='bcode'>
-	java -jar juneau.jar org.apache.juneau.ini.ConfigMgr [args]
+	java -jar juneau.jar org.apache.juneau.ini.ConfigFileBuilder [args]
 	</p>
 	<p>
 		Arguments can be any of the following...
@@ -382,7 +373,7 @@
 		For example, the following command will create the file <code>'MyConfig.bat'</code> from the contents of the file <code>'MyConfig.cfg'</code>.
 	</p>
 	<p class='bcode'>
-		java org.apache.juneau.ini.ConfigMgr createBatchEnvFile -configfile C:\foo\MyConfig.cfg -batchfile C:\foo\MyConfig.bat
+		java org.apache.juneau.ini.ConfigFileBuilder createBatchEnvFile -configfile C:\foo\MyConfig.cfg -batchfile C:\foo\MyConfig.bat
 	</p>
 </div>
 
@@ -637,7 +628,7 @@
 	<jk>public</jk> ConfigFile setConfigContents(<ja>@Body</ja> Reader contents) <jk>throws</jk> Exception {
 		
 		<jc>// Create a new in-memory config file based on the contents of the HTTP request.</jc>
-		ConfigFile cf2 = ConfigMgr.<jsf>DEFAULT</jsf>.create().load(contents);
+		ConfigFile cf2 = new ConfigFileBuilder.build().load(contents);
 		
 		<jc>// Merge the in-memory config file into the existing config file and save it.
 		// Then return the modified config file to be parsed as a POJO.</jc>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core/src/main/java/org/apache/juneau/svl/vars/ConfigFileVar.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/vars/ConfigFileVar.java b/juneau-core/src/main/java/org/apache/juneau/svl/vars/ConfigFileVar.java
index a6f96c9..77023dc 100644
--- a/juneau-core/src/main/java/org/apache/juneau/svl/vars/ConfigFileVar.java
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/vars/ConfigFileVar.java
@@ -27,7 +27,7 @@ import org.apache.juneau.svl.*;
  * <h5 class='section'>Example:</h5>
  * <p class='bcode'>
  * 	<jc>// Create a config file object.</jc>
- * 	ConfigFile configFile = ConfigMgr.<jsf>DEFAULT</jsf>.get(<js>"MyConfig.cfg"</js>);
+ * 	ConfigFile configFile = new ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
  *
  * 	<jc>// Create a variable resolver that resolves config file entries (e.g. "$C{MySection/myKey}")</jc>
  * 	VarResolver r = <jk>new</jk> VarResolver().addVars(ConfigVar.<js>class</js>).addContextObject(<jsf>SESSION_config</jsf>, configFile);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/26155757/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index 8ca14b2..3b99922 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -1389,7 +1389,7 @@
 	URL key4;
 	
 	<jc>// Load our config file</jc>
-	ConfigFile f = ConfigMgr.<jsf>DEFAULT</jsf>.get(<js>"MyIniFile.cfg"</js>);
+	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>);
 	
 	<jc>// Read values from default section</jc>
 	key1 = f.getInt(<js>"key1"</js>);
@@ -1408,7 +1408,7 @@
 		</p>
 		<p class='bcode'>
 	<jc>// Construct the sample INI file programmatically</jc>
-	ConfigFile cf = ConfigMgr.<jsf>DEFAULT</jsf>.create(<js>"MyIniFile.cfg"</js>)
+	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>)
 		.addLines(<jk>null</jk>,
 			<js>"# Default section"</js>,
 			<js>"key1 = 1"</js>,
@@ -1430,7 +1430,7 @@
 		</p>
 		<p class='bcode'>
 	<jc>// Construct the sample INI file programmatically</jc>
-	ConfigFile cf = ConfigMgr.<jsf>DEFAULT</jsf>.create(<js>"MyIniFile.cfg"</js>)
+	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>)
 		.addLines(<jk>null</jk>,
 			<js>"# Default section"</js>)
 		.addHeaderComments(<js>"section1"</js>,
@@ -5323,7 +5323,7 @@
 			} 
 		) 
 		<jk>public</jk> ConfigFile setConfigContents(<ja>@Body</ja> Reader contents) <jk>throws</jk> Exception { 
-			ConfigFile cf2 = ConfigMgr.<jsf>DEFAULT</jsf>.create().load(contents); 
+			ConfigFile cf2 = <jk>new</jk> ConfigFileBuilder().build().load(contents); 
 			<jk>return</jk> getConfig().merge(cf2).save(); 
 		} 
 		
@@ -5704,7 +5704,7 @@
 
 		<h6 class='topic'>org.apache.juneau</h6>
 		<ul class='spaced-list'>
-			<li>Revamped the serializer and parser classes to use builders for creation.
+			<li>Revamped the serializer, parser classes to use builders for creation.
 				Serializers and parsers are now unmodifiable objects once they are created.
 				This is a breaking code change that will require adoption.
 				<p class='bcode'>
@@ -5729,8 +5729,7 @@
 				<li>{@link org.apache.juneau.serializer.SerializerGroupBuilder}
 				<li>{@link org.apache.juneau.parser.ParserGroupBuilder}
 				<li>{@link org.apache.juneau.encoders.EncoderGroupBuilder}
-			</ul>
-				Also introduced 			
+			<li>Revamped the config file API to use a build:  {@link org.apache.juneau.ini.ConfigFileBuilder}.
 			<li>Removed the <code><del>Lockable</del></code> interface.
 			<li>New <code>addBeanTypeProperties</code> setting added to serializers to override the 
 				{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_addBeanTypeProperties} setting
@@ -6386,10 +6385,10 @@
 							<li>{@link org.apache.juneau.ini.ConfigFile#toWritable()} - 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>{@link org.apache.juneau.ini.ConfigFile#getInt(String)} - Now supports <js>"M"</js> and <js>"K"</js> to identify millions and thousands.
 						</ul>
-					<li>New methods in {@link org.apache.juneau.ini.ConfigMgr}:
+					<li>New methods in <code><del>ConfigMgr</del></code>:
 						<ul>
-							<li>{@link org.apache.juneau.ini.ConfigMgr#create()}, {@link org.apache.juneau.ini.ConfigMgr#create(Reader)}, {@link org.apache.juneau.ini.ConfigMgr#create(File)}
-							<li>{@link org.apache.juneau.ini.ConfigMgr#deleteAll()}	
+							<li><code><del>ConfigMgr.create()</del></code>, <code><del>ConfigMgr.create(Reader)</del></code>, <code><del>ConfigMgr.create(File)</del></code>
+							<li><code><del>ConfigMgr.deleteAll()</del></code>	
 						</ul>
 					<li>New methods in {@link org.apache.juneau.ini.Section}:
 						<ul>
@@ -6796,7 +6795,7 @@
 			<li>Significant API changes to <a class='doclink' href='org/apache/juneau/ini/package-summary.html#TOC'>org.apache.juneau.ini</a> API.
 				<ul>
 					<li>{@link org.apache.juneau.ini.ConfigFile} is now thread safe and can be shared across multiple threads.
-					<li>New {@link org.apache.juneau.ini.ConfigMgr} class for managing configuration files.
+					<li>New <code><del>ConfigMgr</del></code> class for managing configuration files.
 					<li>Serializers and parsers can be associated with config files for storing and retrieving POJOs.  
 						Default support provided for JSON.
 				</ul>


Mime
View raw message