Repository: incubator-juneau
Updated Branches:
  refs/heads/master c68cc34d5 -> ca59d8a4e


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/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 d84242e..da2e09f 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
@@ -57,9 +57,24 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @param value The new value.
         * @param encoded
         * @return The previous value, or <jk>null</jk> if the section or key 
did not previously exist.
+        * @throws SerializeException If the object value could not be 
converted to a JSON string for some reason.
         * @throws UnsupportedOperationException If config file is read only.
         */
-       public abstract String put(String sectionName, String sectionKey, 
Object value, boolean encoded);
+       public abstract String put(String sectionName, String sectionKey, 
Object value, boolean encoded) throws SerializeException;
+
+       /**
+        * Identical to {@link #put(String, String, Object, boolean)} except 
used when the value is a simple string
+        * to avoid having to catch a {@link SerializeException}.
+        *
+        * @param sectionName The section name.  Must not be <jk>null</jk>.
+        * @param sectionKey The section key.  Must not be <jk>null</jk>.
+        * @param value The new value.
+        * @param encoded
+        * @return The previous value, or <jk>null</jk> if the section or key 
did not previously exist.
+        * @throws UnsupportedOperationException If config file is read only.
+        */
+       public abstract String put(String sectionName, String sectionKey, 
String value, boolean encoded);
+
 
        /**
         * Removes an antry from this config file.
@@ -158,20 +173,38 @@ public abstract class ConfigFile implements 
Map<String,Section> {
        public abstract ConfigFile clearHeaderComments(String section);
 
        /**
-        * Returns the serializer in use for this config file.
+        * Returns the reusable bean session associated with this config file.
+        * <p>
+        * Used for performing simple datatype conversions.
         *
-        * @return This object (for method chaining).
-        * @throws SerializeException If no serializer is defined on this 
config file.
+        * @return The reusable bean session associated with this config file.
         */
-       protected abstract WriterSerializer getSerializer() throws 
SerializeException;
+       protected abstract BeanSession getBeanSession();
 
        /**
-        * Returns the parser in use for this config file.
+        * Converts the specified object to a string.
+        * <p>
+        * The serialized output is identical to LAX JSON (JSON with unquoted 
attributes) except for the following exceptions:
+        * <ul>
+        *      <li>Top level strings are not quoted.
+        * </ul>
         *
-        * @return This object (for method chaining).
-        * @throws ParseException If no parser is defined on this config file.
+        * @param o The object to serialize.
+        * @return The serialized object.
+        * @throws SerializeException
         */
-       protected abstract ReaderParser getParser() throws ParseException;
+       protected abstract String serialize(Object o) throws SerializeException;
+
+       /**
+        * Converts the specified string to an object of the specified type.
+        *
+        * @param s The string to parse.
+        * @param type The data type to create.
+        * @param args The generic type arguments if the type is a {@link 
Collection} or {@link Map}
+        * @return The parsed object.
+        * @throws ParseException
+        */
+       protected abstract <T> T parse(String s, Type type, Type...args) throws 
ParseException;
 
        /**
         * Places a read lock on this config file.
@@ -222,81 +255,173 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         *      <li><js>"section/key"</js> - A value from the specified section.
         * </ul>
         * <p>
-        * If the class type is an array, the value is split on commas and 
converted individually.
+        * The type can be a simple type (e.g. beans, strings, numbers) or 
parameterized type (collections/maps).
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+        *
+        *      <jc>// Parse into a linked-list of strings.</jc>
+        *      List l = cf.getObject(<js>"MySection/myListOfStrings"</js>, 
LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of beans.</jc>
+        *      List l = cf.getObject(<js>"MySection/myListOfBeans"</js>, 
LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of linked-lists of strings.</jc>
+        *      List l = cf.getObject(<js>"MySection/my2dListOfStrings"</js>, 
LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map of string keys/values.</jc>
+        *      Map m = cf.getObject(<js>"MySection/myMap"</js>, 
TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map containing string keys and values of 
lists containing beans.</jc>
+        *      Map m = cf.getObject(<js>"MySection/myMapOfListsOfBeans"</js>, 
TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, 
MyBean.<jk>class</jk>);
+        * </p>
+        * <p>
+        * <code>Collection</code> classes are assumed to be followed by zero 
or one objects indicating the element type.
+        * <p>
+        * <code>Map</code> classes are assumed to be followed by zero or two 
meta objects indicating the key and value types.
         * <p>
-        * If you specify a primitive element type using this method (e.g. 
<code><jk>int</jk>.<jk>class</jk></code>,
-        *      you will get an array of wrapped objects (e.g. 
<code>Integer[].<jk>class</jk></code>.
+        * The array can be arbitrarily long to indicate arbitrarily complex 
data structures.
+        * <p>
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>Use the {@link #getObject(String, Class)} method instead if 
you don't need a parameterized map/collection.
+        * </ul>
         *
-        * @param c The class to convert the value to.
         * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param type The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args The type arguments of the class if it's a collection or 
map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        *
         * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
         * @return The value, or <jk>null</jk> if the section or key does not 
exist.
         */
-       @SuppressWarnings("unchecked")
-       public final <T> T getObject(Class<T> c, String key) throws 
ParseException {
-               assertFieldNotNull(c, "c");
-               return getObject(c, key, c.isArray() ? 
(T)Array.newInstance(c.getComponentType(), 0) : null);
+       public final <T> T getObject(String key, Type type, Type...args) throws 
ParseException {
+               assertFieldNotNull(key, "key");
+               assertFieldNotNull(type, "type");
+               return parse(getString(key), type, args);
        }
 
        /**
-        * Gets the entry with the specified key and converts it to the 
specified value..
+        * Same as {@link #getObject(String, Type, Type...)} except optimized 
for a non-parameterized class.
         * <p>
-        * The key can be in one of the following formats...
-        * <ul class='spaced-list'>
-        *      <li><js>"key"</js> - A value in the default section (i.e. 
defined above any <code>[section]</code> header).
-        *      <li><js>"section/key"</js> - A value from the specified section.
-        * </ul>
+        * This is the preferred parse method for simple types since you don't 
need to cast the results.
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+        *
+        *      <jc>// Parse into a string.</jc>
+        *      String s = cf.getObject(<js>"MySection/mySimpleString"</js>, 
String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a bean.</jc>
+        *      MyBean b = cf.getObject(<js>"MySection/myBean"</js>, 
MyBean.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a bean array.</jc>
+        *      MyBean[] ba = cf.getObject(<js>"MySection/myBeanArray"</js>, 
MyBean[].<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of objects.</jc>
+        *      List l = cf.getObject(<js>"MySection/myList"</js>, 
LinkedList.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map of object keys/values.</jc>
+        *      Map m = cf.getObject(<js>"MySection/myMap"</js>, 
TreeMap.<jk>class</jk>);
+        * </p>
+        *
+        * @param <T> The class type of the object being created.
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param type The object type to create.
+        * @return The parsed object.
+        * @throws ParseException If the input contains a syntax error or is 
malformed, or is not valid for the specified type.
+        * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for 
maps and collections.
+        */
+       public final <T> T getObject(String key, Class<T> type) throws 
ParseException {
+               assertFieldNotNull(key, "key");
+               assertFieldNotNull(type, "c");
+               return parse(getString(key), type);
+       }
+
+
+       /**
+        * Gets the entry with the specified key and converts it to the 
specified value.
+        * <p>
+        * Same as {@link #getObject(String, Class)}, but with a default value.
         *
-        * @param c The class to convert the value to.
         * @param key The key.  See {@link #getString(String)} for a 
description of the key.
         * @param def The default value if section or key does not exist.
+        * @param type The class to convert the value to.
+        *
         * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
         * @return The value, or <jk>null</jk> if the section or key does not 
exist.
         */
-       public final <T> T getObject(Class<T> c, String key, T def) throws 
ParseException {
-               assertFieldNotNull(c, "c");
+       public final <T> T getObjectWithDefault(String key, T def, Class<T> 
type) throws ParseException {
                assertFieldNotNull(key, "key");
-               return getObject(c, getSectionName(key), getSectionKey(key), 
def);
+               assertFieldNotNull(type, "c");
+               T t = parse(getString(key), type);
+               return (t == null ? def : t);
        }
 
        /**
-        * Same as {@link #getObject(Class, String, Object)}, but value is 
referenced through section name and key instead of full key.
+        * Gets the entry with the specified key and converts it to the 
specified value.
+        * <p>
+        * Same as {@link #getObject(String, Type, Type...)}, but with a 
default value.
+        *
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param def The default value if section or key does not exist.
+        * @param type The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args The type arguments of the class if it's a collection or 
map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or <jk>null</jk> if the section or key does not 
exist.
+        */
+       public final <T> T getObjectWithDefault(String key, T def, Type type, 
Type...args) throws ParseException {
+               assertFieldNotNull(key, "key");
+               assertFieldNotNull(type, "type");
+               T t = parse(getString(key), type, args);
+               return (t == null ? def : t);
+       }
+
+       /**
+        * Gets the entry with the specified key and converts it to the 
specified value.
+        * <p>
+        * Same as {@link #getObject(String, Class)}, but used when key is 
already broken into section/key.
         *
-        * @param c The class to convert the value to.
         * @param sectionName The section name.  Must not be <jk>null</jk>.
         * @param sectionKey The section key.  Must not be <jk>null</jk>.
-        * @param def The default value if section or key does not exist.
+        * @param c The class to convert the value to.
+        *
         * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
         * @return The value, or the default value if the section or value 
doesn't exist.
         */
-       @SuppressWarnings("unchecked")
-       public <T> T getObject(Class<T> c, String sectionName, String 
sectionKey, T def) throws ParseException {
-               String s = get(sectionName, sectionKey);
-               if (s == null)
-                       return def;
-               if (c == String.class)
-                       return (T)s;
-               if (c == Integer.class || c == int.class)
-                       return (T)(StringUtils.isEmpty(s) ? def : 
Integer.valueOf(parseIntWithSuffix(s)));
-               if (c == Boolean.class || c == boolean.class)
-                       return (T)(StringUtils.isEmpty(s) ? def : 
Boolean.valueOf(Boolean.parseBoolean(s)));
-               if (c == String[].class) {
-                       String[] r = StringUtils.isEmpty(s) ? new String[0] : 
StringUtils.split(s, ',');
-                       return (T)(r.length == 0 ? def : r);
-               }
-               if (c.isArray()) {
-                       Class<?> ce = c.getComponentType();
-                       if (StringUtils.isEmpty(s))
-                               return def;
-                       String[] r = StringUtils.split(s, ',');
-                       Object o = Array.newInstance(ce, r.length);
-                       for (int i = 0; i < r.length; i++)
-                               Array.set(o, i, getParser().parse(r[i], ce));
-                       return (T)o;
-               }
-               if (StringUtils.isEmpty(s))
-                       return def;
-               return getParser().parse(s, c);
+       public final <T> T getObject(String sectionName, String sectionKey, 
Class<T> c) throws ParseException {
+               assertFieldNotNull(sectionName, "sectionName");
+               assertFieldNotNull(sectionKey, "sectionKey");
+               return parse(get(sectionName, sectionKey), c);
+       }
+
+       /**
+        * Gets the entry with the specified key and converts it to the 
specified value.
+        * <p>
+        * Same as {@link #getObject(String, Type, Type...)}, but used when key 
is already broken into section/key.
+        *
+        * @param sectionName The section name.  Must not be <jk>null</jk>.
+        * @param sectionKey The section key.  Must not be <jk>null</jk>.
+        * @param type The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args The type arguments of the class if it's a collection or 
map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or <jk>null</jk> if the section or key does not 
exist.
+        */
+       public final <T> T getObject(String sectionName, String sectionKey, 
Type type, Type...args) throws ParseException {
+               assertFieldNotNull(sectionName, "sectionName");
+               assertFieldNotNull(sectionKey, "sectionKey");
+               return parse(get(sectionName, sectionKey), type, args);
        }
 
        /**
@@ -430,33 +555,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         */
        public final String put(String key, Object value, boolean encoded) 
throws SerializeException {
                assertFieldNotNull(key, "key");
-               if (value == null)
-                       value = "";
-               Class<?> c = value.getClass();
-               if (isSimpleType(c))
-                       return put(getSectionName(key), getSectionKey(key), 
value.toString(), encoded);
-               if (c.isAssignableFrom(Collection.class)) {
-                       Collection<?> c2 = (Collection<?>)value;
-                       String[] r = new String[c2.size()];
-                       int i = 0;
-                       for (Object o2 : c2) {
-                               boolean isSimpleType = o2 == null ? true : 
isSimpleType(o2.getClass());
-                               r[i++] = (isSimpleType ? Array.get(value, 
i).toString() : getSerializer().toString(Array.get(value, i)));
-                       }
-                       return put(getSectionName(key), getSectionKey(key), 
StringUtils.join(r, ','), encoded);
-               } else if (c.isArray()) {
-                       boolean isSimpleType = 
isSimpleType(c.getComponentType());
-                       String[] r = new String[Array.getLength(value)];
-                       for (int i = 0; i < r.length; i++) {
-                               r[i] = (isSimpleType ? Array.get(value, 
i).toString() : getSerializer().toString(Array.get(value, i)));
-                       }
-                       return put(getSectionName(key), getSectionKey(key), 
StringUtils.join(r, ','), encoded);
-               }
-               return put(getSectionName(key), getSectionKey(key), 
getSerializer().toString(value), encoded);
-       }
-
-       private static boolean isSimpleType(Class<?> c) {
-               return (c == String.class || c.isPrimitive() || 
c.isAssignableFrom(Number.class) || c == Boolean.class);
+               return put(getSectionName(key), getSectionKey(key), 
serialize(value), encoded);
        }
 
        /**
@@ -510,7 +609,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
                                        Class<?> pt = m.getParameterTypes()[0];
                                        if (permittedPropertyTypes == null || 
permittedPropertyTypes.length == 0 || ArrayUtils.contains(pt, 
permittedPropertyTypes)) {
                                                String propName = 
Introspector.decapitalize(m.getName().substring(3));
-                                               Object value = getObject(pt, 
sectionName, propName, null);
+                                               Object value = 
getObject(sectionName, propName, pt);
                                                if (value != null) {
                                                        m.invoke(bean, value);
                                                        om.put(propName, value);
@@ -528,7 +627,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
        }
 
        /**
-        * Shortcut for calling <code>asBean(sectionName, c, 
<jk>false</jk>)</code>.
+        * Shortcut for calling <code>getSectionAsBean(sectionName, c, 
<jk>false</jk>)</code>.
         *
         * @param sectionName The section name to write from.
         * @param c The bean class to create.
@@ -541,6 +640,33 @@ public abstract class ConfigFile implements 
Map<String,Section> {
 
        /**
         * Converts this config file section to the specified bean instance.
+        * <p>
+        * Key/value pairs in the config file section get copied as bean 
property values to the specified bean class.
+        * <p>
+        * <h6 class='figure'>Example config file</h6>
+        * <p class='bcode'>
+        *      <cs>[MyAddress]</cs>
+        *      <ck>name</ck> = <cv>John Smith</cv>
+        *      <ck>street</ck> = <cv>123 Main Street</cv>
+        *      <ck>city</ck> = <cv>Anywhere</cv>
+        *      <ck>state</ck> = <cv>NY</cv>
+        *      <ck>zip</ck> = <cv>12345</cv>
+        * </p>
+        *
+        * <h6 class='figure'>Example bean</h6>
+        * <p class='bcode'>
+        *      <jk>public class</jk> Address {
+        *              public String name, street, city;
+        *              public StateEnum state;
+        *              public int zip;
+        *      }
+        * </p>
+        *
+        * <h6 class='figure'>Example usage</h6>
+        * <p class='bcode'>
+        *      ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+        *      Address myAddress = cf.getSectionAsBean(<js>"MySection"</js>, 
Address.<jk>class</jk>);
+        * </p>
         *
         * @param sectionName The section name to write from.
         * @param c The bean class to create.
@@ -553,14 +679,14 @@ public abstract class ConfigFile implements 
Map<String,Section> {
                assertFieldNotNull(c, "c");
                readLock();
                try {
-                       BeanMap<T> bm = 
getParser().getBeanContext().createSession().newBeanMap(c);
+                       BeanMap<T> bm = getBeanSession().newBeanMap(c);
                        for (String k : getSectionKeys(sectionName)) {
                                BeanPropertyMeta bpm = bm.getPropertyMeta(k);
                                if (bpm == null) {
                                        if (! ignoreUnknownProperties)
                                                throw new 
ParseException("Unknown property {0} encountered", k);
                                } else {
-                                       bm.put(k, 
getObject(bpm.getClassMeta().getInnerClass(), sectionName + '/' + k));
+                                       bm.put(k, getObject(sectionName + '/' + 
k, bpm.getClassMeta().getInnerClass()));
                                }
                        }
                        return bm.getBean();
@@ -570,6 +696,88 @@ public abstract class ConfigFile implements 
Map<String,Section> {
        }
 
        /**
+        * Wraps a config file section inside a Java interface so that values 
in the section can be read and
+        * write using getters and setters.
+        * <p>
+        * <h6 class='figure'>Example config file</h6>
+        * <p class='bcode'>
+        *      <cs>[MySection]</cs>
+        *      <ck>string</ck> = <cv>foo</cv>
+        *      <ck>int</ck> = <cv>123</cv>
+        *      <ck>enum</ck> = <cv>ONE</cv>
+        *      <ck>bean</ck> = <cv>{foo:'bar',baz:123}</cv>
+        *      <ck>int3dArray</ck> = <cv>[[[123,null],null],null]</cv>
+        *      <ck>bean1d3dListMap</ck> = 
<cv>{key:[[[[{foo:'bar',baz:123}]]]]}</cv>
+        * </p>
+        *
+        * <h6 class='figure'>Example interface</h6>
+        * <p class='bcode'>
+        *      <jk>public interface</jk> MyConfigInterface {
+        *
+        *              String getString();
+        *              <jk>void</jk> setString(String x);
+        *
+        *              <jk>int</jk> getInt();
+        *              <jk>void</jk> setInt(<jk>int</jk> x);
+        *
+        *              MyEnum getEnum();
+        *              <jk>void</jk> setEnum(MyEnum x);
+        *
+        *              MyBean getBean();
+        *              <jk>void</jk> setBean(MyBean x);
+        *
+        *              <jk>int</jk>[][][] getInt3dArray();
+        *              <jk>void</jk> setInt3dArray(<jk>int</jk>[][][] x);
+        *
+        *              Map&lt;String,List&lt;MyBean[][][]&gt;&gt; 
getBean1d3dListMap();
+        *              <jk>void</jk> 
setBean1d3dListMap(Map&lt;String,List&lt;MyBean[][][]&gt;&gt; x);
+        *      }
+        * </p>
+        *
+        * <h6 class='figure'>Example usage</h6>
+        * <p class='bcode'>
+        *      ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+        *
+        *      MyConfigInterface ci = 
cf.getSectionAsInterface(<js>"MySection"</js>, 
MyConfigInterface.<jk>class</jk>);
+        *
+        *      <jk>int</jk> myInt = ci.getInt();
+        *
+        *      ci.setBean(<jk>new</jk> MyBean());
+        *
+        *      cf.save();
+        * </p>
+        *
+        * @param sectionName The section name to retrieve as an interface 
proxy.
+        * @param c The proxy interface class.
+        * @return The proxy interface.
+        */
+       @SuppressWarnings("unchecked")
+       public final <T> T getSectionAsInterface(final String sectionName, 
final Class<T> c) {
+               assertFieldNotNull(c, "c");
+
+               if (! c.isInterface())
+                       throw new UnsupportedOperationException("Class passed 
to getSectionAsInterface is not an interface.");
+
+               InvocationHandler h = new InvocationHandler() {
+
+                       @Override
+                       public Object invoke(Object proxy, Method method, 
Object[] args) throws Throwable {
+                               BeanInfo bi = Introspector.getBeanInfo(c, null);
+                               for (PropertyDescriptor pd : 
bi.getPropertyDescriptors()) {
+                                       Method rm = pd.getReadMethod(), wm = 
pd.getWriteMethod();
+                                       if (method.equals(rm))
+                                               return 
ConfigFile.this.getObject(sectionName, pd.getName(), rm.getGenericReturnType());
+                                       if (method.equals(wm))
+                                               return 
ConfigFile.this.put(sectionName, pd.getName(), args[0], false);
+                               }
+                               throw new 
UnsupportedOperationException("Unsupported interface method.  method=[ " + 
method + " ]");
+                       }
+               };
+
+               return (T)Proxy.newProxyInstance(c.getClassLoader(), new 
Class[] { c }, h);
+       }
+
+       /**
         * Returns <jk>true</jk> if this section contains the specified key and 
the key has a non-blank value.
         *
         * @param key The key.  See {@link #getString(String)} for a 
description of the key.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/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
index a699b4f..5d44f10 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
@@ -69,7 +69,7 @@ public class ConfigFileBuilder {
        }
 
        /**
-        * Specify the parser to use for parsing POJOs when using {@link 
ConfigFile#getObject(Class,String)}.
+        * Specify the parser to use for parsing POJOs when using {@link 
ConfigFile#getObject(String,Class)}.
         * <p>
         * The default value for this setting is {@link JsonParser#DEFAULT}
         *

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
index f1ef0eb..dc784b5 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
@@ -16,6 +16,7 @@ import static org.apache.juneau.ini.ConfigUtils.*;
 import static org.apache.juneau.internal.ThrowableUtils.*;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.nio.charset.*;
 import java.util.*;
 import java.util.concurrent.locks.*;
@@ -37,6 +38,7 @@ public final class ConfigFileImpl extends ConfigFile {
        private final Encoder encoder;
        private final WriterSerializer serializer;
        private final ReaderParser parser;
+       private final BeanSession pBeanSession;
        private final Charset charset;
        final List<ConfigFileListener> listeners = 
Collections.synchronizedList(new ArrayList<ConfigFileListener>());
 
@@ -66,7 +68,7 @@ public final class ConfigFileImpl extends ConfigFile {
         * If <jk>null</jk>, defaults to {@link XorEncoder#INSTANCE}.
         * @param serializer The serializer to use for serializing POJOs in the 
{@link #put(String, Object)} method.
         * If <jk>null</jk>, defaults to {@link JsonSerializer#DEFAULT}.
-        * @param parser The parser to use for parsing POJOs in the {@link 
#getObject(Class,String)} method.
+        * @param parser The parser to use for parsing POJOs in the {@link 
#getObject(String,Class)} method.
         * If <jk>null</jk>, defaults to {@link JsonParser#DEFAULT}.
         * @param charset The charset on the files.
         * If <jk>null</jk>, defaults to {@link Charset#defaultCharset()}.
@@ -85,6 +87,7 @@ public final class ConfigFileImpl extends ConfigFile {
                        for (Section s : sections.values())
                                s.setReadOnly();
                }
+               this.pBeanSession = 
this.parser.getBeanContext().createSession();
        }
 
        /**
@@ -193,6 +196,43 @@ public final class ConfigFileImpl extends ConfigFile {
                return this;
        }
 
+       @Override /* ConfigFile */
+       protected String serialize(Object value) throws SerializeException {
+               if (value == null)
+                       return "";
+               Class<?> c = value.getClass();
+               if (isSimpleType(c))
+                       return value.toString();
+               String s = serializer.toString(value);
+               if (s.startsWith("'"))
+                       return s.substring(1, s.length()-1);
+               return s;
+       }
+
+       @Override /* ConfigFile */
+       @SuppressWarnings({ "unchecked" })
+       protected <T> T parse(String s, Type type, Type...args) throws 
ParseException {
+
+               if (StringUtils.isEmpty(s))
+                       return null;
+
+               if (isSimpleType(type))
+                       return (T)pBeanSession.convertToType(s, (Class<?>)type);
+
+               char s1 = StringUtils.charAt(s, 0);
+               if (s1 != '[' && s1 != '{' && ! "null".equals(s))
+                       s = '\'' + s + '\'';
+
+               return parser.parse(s, type, args);
+       }
+
+       private static boolean isSimpleType(Type t) {
+               if (! (t instanceof Class))
+                       return false;
+               Class<?> c = (Class<?>)t;
+               return (c == String.class || c.isPrimitive() || 
c.isAssignableFrom(Number.class) || c == Boolean.class || c.isEnum());
+       }
+
 
        
//--------------------------------------------------------------------------------
        // Map methods
@@ -429,10 +469,17 @@ public final class ConfigFileImpl extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       public String put(String sectionName, String sectionKey, Object value, 
boolean encoded) {
+       public String put(String sectionName, String sectionKey, Object value, 
boolean encoded) throws SerializeException {
                assertFieldNotNull(sectionKey, "sectionKey");
                Section s = getSection(sectionName, true);
-               return s.put(sectionKey, value.toString(), encoded);
+               return s.put(sectionKey, serialize(value), encoded);
+       }
+
+       @Override /* ConfigFile */
+       public String put(String sectionName, String sectionKey, String value, 
boolean encoded) {
+               assertFieldNotNull(sectionKey, "sectionKey");
+               Section s = getSection(sectionName, true);
+               return s.put(sectionKey, value, encoded);
        }
 
        @Override /* ConfigFile */
@@ -657,17 +704,8 @@ public final class ConfigFileImpl extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       protected WriterSerializer getSerializer() throws SerializeException {
-               if (serializer == null)
-                       throw new SerializeException("Serializer not defined on 
config file.");
-               return serializer;
-       }
-
-       @Override /* ConfigFile */
-       protected ReaderParser getParser() throws ParseException {
-               if (parser == null)
-                       throw new ParseException("Parser not defined on config 
file.");
-               return parser;
+       protected BeanSession getBeanSession() {
+               return pBeanSession;
        }
 
        @Override /* ConfigFile */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
index 62f3899..5407c0f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
@@ -15,6 +15,7 @@ package org.apache.juneau.ini;
 import static org.apache.juneau.internal.ThrowableUtils.*;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
@@ -233,13 +234,8 @@ public final class ConfigFileWrapped extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       protected WriterSerializer getSerializer() throws SerializeException {
-               return cf.getSerializer();
-       }
-
-       @Override /* ConfigFile */
-       protected ReaderParser getParser() throws ParseException {
-               return cf.getParser();
+       protected BeanSession getBeanSession() {
+               return cf.getBeanSession();
        }
 
        @Override /* ConfigFile */
@@ -251,7 +247,12 @@ public final class ConfigFileWrapped extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       public String put(String sectionName, String sectionKey, Object value, 
boolean encoded) {
+       public String put(String sectionName, String sectionKey, String value, 
boolean encoded) {
+               return cf.put(sectionName, sectionKey, value, encoded);
+       }
+
+       @Override /* ConfigFile */
+       public String put(String sectionName, String sectionKey, Object value, 
boolean encoded) throws SerializeException {
                return cf.put(sectionName, sectionKey, value, encoded);
        }
 
@@ -274,4 +275,14 @@ public final class ConfigFileWrapped extends ConfigFile {
        protected void readUnlock() {
                cf.readUnlock();
        }
+
+       @Override /* ConfigFile */
+       protected String serialize(Object o) throws SerializeException {
+               return cf.serialize(o);
+       }
+
+       @Override /* ConfigFile */
+       protected <T> T parse(String s, Type type, Type... args) throws 
ParseException {
+               return cf.parse(s, type, args);
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
index 92ab945..490eed1 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
@@ -590,7 +590,7 @@ public class JsonParser extends ReaderParser {
                                                break;
                                        }
                                } else {
-                                       if (c == ',' || c == '}' || 
session.isWhitespace(c)) {
+                                       if (c == ',' || c == '}' || c == ']' || 
session.isWhitespace(c)) {
                                                s = r.getMarked(0, -1);
                                                r.unread();
                                                break;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/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 62ed843..980b84a 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -1373,14 +1373,14 @@
        <cc># Default section</cc>
        <ck>key1</ck> = <cv>1</cv>
        <ck>key2</ck> = <cv>true</cv>
-       <ck>key3</ck> = <cv>1,2,3</cv>
+       <ck>key3</ck> = <cv>[1,2,3]</cv>
        <ck>key4</ck> = <cv>http://foo</cv>
        
        <cc># Section 1</cc>
        <cs>[Section1]</cs>
        <ck>key1</ck> = <cv>2</cv>
        <ck>key2</ck> = <cv>false</cv>
-       <ck>key3</ck> = <cv>4,5,6</cv>
+       <ck>key3</ck> = <cv>[4,5,6]</cv>
        <ck>key4</ck> = <cv>http://bar</cv>
                </p>
                <p>
@@ -1393,7 +1393,7 @@
        URL key4;
        
        <jc>// Load our config file</jc>
-       ConfigFile f = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>);
+       ConfigFile f = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
        
        <jc>// Read values from default section</jc>
        key1 = f.getInt(<js>"key1"</js>);
@@ -1412,12 +1412,12 @@
                </p>
                <p class='bcode'>
        <jc>// Construct the sample INI file programmatically</jc>
-       ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>)
+       ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>)
                .addLines(<jk>null</jk>,
                        <js>"# Default section"</js>,
                        <js>"key1 = 1"</js>,
                        <js>"key2 = true"</js>,
-                       <js>"key3 = 1,2,3"</js>,
+                       <js>"key3 = [1,2,3]"</js>,
                        <js>"key4 = http://foo";</js>,
                        <js>""</js>)
                .addHeaderComments(<js>"Section1"</js>,
@@ -1425,7 +1425,7 @@
                .addLines(<js>"Section1"</js>,
                        <js>"key1 = 2"</js>,
                        <js>"key2 = false"</js>,
-                       <js>"key3 = 4,5,6"</js>,
+                       <js>"key3 = [4,5,6]"</js>,
                        <js>"key4 = http://bar";</js>)
                .save();
                </p>
@@ -1434,7 +1434,7 @@
                </p>
                <p class='bcode'>
        <jc>// Construct the sample INI file programmatically</jc>
-       ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>)
+       ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>)
                .addLines(<jk>null</jk>,
                        <js>"# Default section"</js>)
                .addHeaderComments(<js>"section1"</js>,
@@ -1450,11 +1450,14 @@
        cf.save();
                </p>
                <p>
+                       Values are LAX JSON (i.e. unquoted attributes, single 
quotes) except for top-level strings which are left unquoted.  
+                       Any parsable object types are supported as values (e.g. 
arrays, collections, beans, swappable objects, enums, etc...).
+               </p>
+               <p>
                        The config file looks deceptively simple, the config 
file API is a very powerful feature with many capabilities, including:
                </p>
                <ul class='spaced-list'>
                        <li>The ability to use variables to reference 
environment variables, system properties, other config file entries, and a host 
of other types.
-                       <li>The ability to store and retrieve POJOs as JSON.
                        <li>APIs for updating, modifying, and saving 
configuration files without losing comments or formatting.
                        <li>Extensive listener APIs.
                </ul>
@@ -1472,7 +1475,7 @@
        <ck>aBoolean</ck> = <cv>true</cv>
        
        <cc># An int array</cc>
-       <ck>anIntArray</ck> = <cv>1,2,3</cv>
+       <ck>anIntArray</ck> = <cv>[1,2,3]</cv>
        
        <cc># A POJO that can be converted from a String</cc>
        <ck>aURL</ck> = <cv>http://foo </cv>
@@ -1521,6 +1524,75 @@
        String myArg = cf.getString(<js>"MySection/myArg"</js>); 
        String firstArg = cf.getString(<js>"MySection/firstArg"</js>); 
                </p>
+               <p>
+                       Config files can also be used to directly populate 
beans using the {@link 
org.apache.juneau.ini.ConfigFile#getSectionAsBean(String,Class,boolean)}:
+               </p>
+               <p class='bcode'>
+       <jc>// Example config file</jc>
+       <cs>[MyAddress]</cs>
+       <ck>name</ck> = <cv>John Smith</cv>
+       <ck>street</ck> = <cv>123 Main Street</cv>
+       <ck>city</ck> = <cv>Anywhere</cv>
+       <ck>state</ck> = <cv>NY</cv>
+       <ck>zip</ck> = <cv>12345</cv>
+
+       <jc>// Example bean</jc>
+       <jk>public class</jk> Address {
+               public String name, street, city;
+               public StateEnum state;
+               public int zip;
+       }
+
+       <jc>// Example usage</jc>
+       ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+       Address myAddress = cf.getSectionAsBean(<js>"MySection"</js>, 
Address.<jk>class</jk>);
+               </p>
+               <p>
+                       Config file sections can also be accessed via interface 
proxies using {@link 
org.apache.juneau.ini.ConfigFile#getSectionAsInterface(String,Class)}:
+               </p>
+               <p class='bcode'>
+       <jc>// Example config file</jc>
+       <cs>[MySection]</cs>
+       <ck>string</ck> = <cv>foo</cv>
+       <ck>int</ck> = <cv>123</cv>
+       <ck>enum</ck> = <cv>ONE</cv>
+       <ck>bean</ck> = <cv>{foo:'bar',baz:123}</cv>
+       <ck>int3dArray</ck> = <cv>[[[123,null],null],null]</cv>
+       <ck>bean1d3dListMap</ck> = <cv>{key:[[[[{foo:'bar',baz:123}]]]]}</cv>
+
+       <jc>// Example interface</jc>
+       <jk>public interface</jk> MyConfigInterface {
+
+               String getString();
+               <jk>void</jk> setString(String x);
+
+               <jk>int</jk> getInt();
+               <jk>void</jk> setInt(<jk>int</jk> x);
+
+               MyEnum getEnum();
+               <jk>void</jk> setEnum(MyEnum x);
+
+               MyBean getBean();
+               <jk>void</jk> setBean(MyBean x);
+
+               <jk>int</jk>[][][] getInt3dArray();
+               <jk>void</jk> setInt3dArray(<jk>int</jk>[][][] x);
+               
+               Map&lt;String,List&lt;MyBean[][][]&gt;&gt; getBean1d3dListMap();
+               <jk>void</jk> 
setBean1d3dListMap(Map&lt;String,List&lt;MyBean[][][]&gt;&gt; x);
+       }
+       
+       <jc>// Example usage</jc>
+       ConfigFile cf = <jk>new</jk> 
ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+       MyConfigInterface ci = cf.getSectionAsInterface(<js>"MySection"</js>, 
MyConfigInterface.<jk>class</jk>);
+       <jk>int</jk> myInt = ci.getInt();
+       ci.setBean(<jk>new</jk> MyBean());
+       cf.save();
+               </p>
+                       
+               
+               
+               
                
                <h6 class='topic'>Additional Information</h6>
                <ul class='javahierarchy'>
@@ -5973,6 +6045,23 @@
                        <li>New package:  {@link org.apache.juneau.http}.
                        <li>Support for dynamic beans.  See {@link 
org.apache.juneau.annotation.BeanProperty#name() @BeanProperty.name()}.
                        <li>New doc: <a class='doclink' 
href='#Core.JacksonComparison'>2.12 - Comparison with Jackson</a>
+                       <li>All parsers now allow for numeric types with 
<js>'K'</js>/<js>'M'</js>/<js>'G'</js> suffixes to represent
+                               kilobytes, megabytes, and gigabytes.
+                               <p class='bcode'>
+       <jc>// Example</jc>
+       <jk>int</jk> i = JsonParser.<jsf>DEFAULT</jsf>.parse(<js>"123M"</js>);  
<jc>// 123MB</jc>
+                               </p>
+                       <li>New/modified methods on {@link 
org.apache.juneau.ini.ConfigFile}:
+                               <ul>
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#put(String,String,Object,boolean) 
put(String,String,Object,boolean)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,Type,Type...) 
getObject(String,Type,Type...)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,Class) 
getObject(String,Class)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,String,Type,Type...) 
getObject(String,String,Type,Type...)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,String,Class) 
getObject(String,String,Class)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Type,Type...)
 getObjectWithDefault(String,Object,Type,Type)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Class) 
getObjectWithDefault(String,Object,Class)} 
+                               </ul>
+                       <li>New ability to interact with config file sections 
with proxy interfaces with new method {@link 
org.apache.juneau.ini.ConfigFile#getSectionAsInterface(String,Class)}.
                </ul>
 
                <h6 class='topic'>org.apache.juneau.rest</h6>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-examples-rest/examples.cfg
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/examples.cfg 
b/juneau-examples-rest/examples.cfg
index e41894e..352533f 100755
--- a/juneau-examples-rest/examples.cfg
+++ b/juneau-examples-rest/examples.cfg
@@ -23,10 +23,10 @@
 
 resources = org.apache.juneau.examples.rest.RootResources
 
-# Ports to try.
+# Array of ports to try.
 # 0 means try a random port.
 # 3 0's means try 3 random ports.
-port = 10000, 0, 0, 0
+port = [10000, 0, 0, 0]
 
 # Authentication:  NONE, BASIC.
 authType = NONE

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-microservice-template/microservice.cfg
----------------------------------------------------------------------
diff --git a/juneau-microservice-template/microservice.cfg 
b/juneau-microservice-template/microservice.cfg
index 82ac437..c3eb1d0 100755
--- a/juneau-microservice-template/microservice.cfg
+++ b/juneau-microservice-template/microservice.cfg
@@ -27,9 +27,10 @@ REST = org.apache.juneau.microservice.rest.RestApplication
 
#================================================================================
 [REST]
 
-# The HTTP port number to use.
-# Default is Rest-Port setting in manifest file, or 8000.
-port = 10000
+# Array of ports to try for hosting the REST interface.
+# 0 means try a random port.
+# 3 0's means try 3 random ports.
+port = [10000, 0, 0, 0]
 
 # A JSON map of servlet paths to servlet classes.
 # Example:  

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
 
b/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
index 050ec1a..9c05035 100755
--- 
a/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
+++ 
b/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
@@ -269,7 +269,7 @@ public class RestMicroservice extends Microservice {
                        ch.setFormatter(new LogEntryFormatter(format, 
dateFormat, false));
                        logger.addHandler(ch);
                }
-               ObjectMap loggerLevels = cf.getObject(ObjectMap.class, 
"Logging/levels");
+               ObjectMap loggerLevels = cf.getObject("Logging/levels", 
ObjectMap.class);
                if (loggerLevels != null)
                for (String l : loggerLevels.keySet())
                        
Logger.getLogger(l).setLevel(loggerLevels.get(Level.class, l));
@@ -339,7 +339,7 @@ public class RestMicroservice extends Microservice {
                ConfigFile cf = getConfig();
                ObjectMap mf = getManifest();
                
-               int[] ports = cf.getObject(int[].class, "REST/port", 
mf.get(int[].class, "Rest-Port", new int[]{8000}));
+               int[] ports = cf.getObjectWithDefault("REST/port", 
mf.get(int[].class, "Rest-Port", new int[]{8000}), int[].class);
 
                port = findOpenPort(ports);
                if (port == 0) {
@@ -474,7 +474,7 @@ public class RestMicroservice extends Microservice {
                ObjectMap mf = getManifest();
                Map<String,Class<? extends Servlet>> rm = new 
LinkedHashMap<String,Class<? extends Servlet>>();
 
-               ObjectMap resourceMap = cf.getObject(ObjectMap.class, 
"REST/resourceMap");
+               ObjectMap resourceMap = cf.getObject("REST/resourceMap", 
ObjectMap.class);
                String[] resources = cf.getStringArray("REST/resources", 
mf.getStringArray("Rest-Resources"));
 
                if (resourceMap != null && ! resourceMap.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/juneau-rest-test.cfg
----------------------------------------------------------------------
diff --git a/juneau-rest-test/juneau-rest-test.cfg 
b/juneau-rest-test/juneau-rest-test.cfg
index e6dd8f3..ec9628a 100644
--- a/juneau-rest-test/juneau-rest-test.cfg
+++ b/juneau-rest-test/juneau-rest-test.cfg
@@ -26,7 +26,7 @@ resources = org.apache.juneau.rest.test.Root
 # Ports to try.
 # 0 means try a random port.
 # 3 0's means try 3 random ports.
-port = 10001, 0, 0, 0
+port = [10001, 0, 0, 0]
 
 # Authentication:  NONE, BASIC.
 authType = NONE
@@ -104,11 +104,11 @@ consoleLevel = WARNING
 
 [Test]
 int1 = 1
-int2 = 1,2,3
+int2 = [1,2,3]
 int3 = $C{Test/int1, -1}
 int4 = $C{Test/int3, -1}
 int5 = $C{XXX, -1}
 boolean1 = true
-boolean2 = true,true
+boolean2 = [true,true]
 path = $E{PATH}
 testManifestEntry = $MF{Test-Entry}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
index e8f84cc..d766290 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
@@ -33,6 +33,6 @@ public class ConfigResource extends Resource {
 
        @RestMethod(name="GET", path="/{key}/{class}")
        public Object test2(RestRequest req, @Path("key") String key, 
@Path("class") Class<?> c) throws Exception {
-               return req.getConfigFile().getObject(c, key);
+               return req.getConfigFile().getObject(key, c);
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
index 22ebaff..d52425d 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
@@ -33,17 +33,16 @@ public class ConfigTest extends RestTestcase {
 
                ConfigFile cf = c.doGet(URL).getResponse(ConfigFileImpl.class);
 
-               assertObjectEquals("{int1:'1',int2:'1,2,3',int3:'$C{Test/int1, 
-1}',int4:'$C{Test/int3, -1}',int5:'$C{XXX, 
-1}',boolean1:'true',boolean2:'true,true',path:'$E{PATH}',testManifestEntry:'$MF{Test-Entry}'}",
 cf.get("Test"));
+               
assertObjectEquals("{int1:'1',int2:'[1,2,3]',int3:'$C{Test/int1, 
-1}',int4:'$C{Test/int3, -1}',int5:'$C{XXX, 
-1}',boolean1:'true',boolean2:'[true,true]',path:'$E{PATH}',testManifestEntry:'$MF{Test-Entry}'}",
 cf.get("Test"));
 
                assertEquals("'1'", c.doGet(URL + "/Test%2Fint1/" + 
getName(String.class)).getResponseAsString());
-               assertEquals("['1']", c.doGet(URL + "/Test%2Fint1/" + 
getName(String[].class)).getResponseAsString());
-               assertEquals("'1,2,3'", c.doGet(URL + "/Test%2Fint2/" + 
getName(String.class)).getResponseAsString());
+               assertEquals("'[1,2,3]'", c.doGet(URL + "/Test%2Fint2/" + 
getName(String.class)).getResponseAsString());
                assertEquals("['1','2','3']", c.doGet(URL + "/Test%2Fint2/" + 
getName(String[].class)).getResponseAsString());
                assertEquals("[1,2,3]", c.doGet(URL + "/Test%2Fint2/" + 
getName(int[].class)).getResponseAsString());
                assertEquals("[1,2,3]", c.doGet(URL + "/Test%2Fint2/" + 
getName(Integer[].class)).getResponseAsString());
-               assertEquals("[1]", c.doGet(URL + "/Test%2Fint3/" + 
getName(int[].class)).getResponseAsString());
-               assertEquals("[1]", c.doGet(URL + "/Test%2Fint4/" + 
getName(int[].class)).getResponseAsString());
-               assertEquals("[-1]", c.doGet(URL + "/Test%2Fint5/" + 
getName(int[].class)).getResponseAsString());
+               assertEquals("1", c.doGet(URL + "/Test%2Fint3/" + 
getName(Integer.class)).getResponseAsString());
+               assertEquals("1", c.doGet(URL + "/Test%2Fint4/" + 
getName(Integer.class)).getResponseAsString());
+               assertEquals("-1", c.doGet(URL + "/Test%2Fint5/" + 
getName(Integer.class)).getResponseAsString());
                assertEquals("true", c.doGet(URL + "/Test%2Fboolean1/" + 
getName(Boolean.class)).getResponseAsString());
                assertEquals("[true,true]", c.doGet(URL + "/Test%2Fboolean2/" + 
getName(Boolean[].class)).getResponseAsString());
                assertTrue(c.doGet(URL + "/Test%2Fpath/" + 
getName(String.class)).getResponseAsString().length() > 10);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
index d8a0b1b..eb2f247 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
@@ -38,6 +38,7 @@ import org.junit.runners.Suite.*;
        GroupsTest.class,
        GzipTest.class,
        HeadersTest.class,
+       HtmlPropertiesTest.class,
        InheritanceTest.class,
        InterfaceProxyTest.class,
        JacocoDummyTest.class,
@@ -54,6 +55,7 @@ import org.junit.runners.Suite.*;
        ParsersTest.class,
        PathsTest.class,
        PathTest.class,
+       PathVariableTest.class,
        PropertiesTest.class,
        QueryTest.class,
        RestClientTest.class,


Reply via email to