This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 94239f1  Config API modernization.
94239f1 is described below

commit 94239f137ba059d64cb8e72f0500905c4a8b3973
Author: JamesBognar <[email protected]>
AuthorDate: Sun Dec 26 14:43:30 2021 -0500

    Config API modernization.
---
 .../main/java/org/apache/juneau/config/Config.java | 214 +------------
 .../java/org/apache/juneau/config/Section.java     | 353 +++++++++++++++++++++
 .../microservice/resources/ConfigResource.java     |  28 +-
 .../org/apache/juneau/rest/test/ConfigTest.java    |   2 +-
 .../apache/juneau/config/ConfigInterfaceTest.java  |   2 +-
 .../java/org/apache/juneau/config/ConfigTest.java  |  65 ++--
 6 files changed, 409 insertions(+), 255 deletions(-)

diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
index 33ce5f4..af9175e 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
@@ -19,7 +19,6 @@ import static org.apache.juneau.internal.SystemEnv.*;
 import static org.apache.juneau.internal.ThrowableUtils.*;
 import static org.apache.juneau.internal.IOUtils.*;
 
-import java.beans.*;
 import java.io.*;
 import java.lang.annotation.*;
 import java.lang.reflect.*;
@@ -671,7 +670,7 @@ public final class Config extends Context implements 
ConfigEventListener {
         * @return This object.
         */
        public Config setSystemProperties() {
-               for (String section : getSections()) {
+               for (String section : getSectionNames()) {
                        for (String key : getKeys(section)) {
                                String k = (section.isEmpty() ? key : section + 
'/' + key);
                                System.setProperty(k, getRaw(k));
@@ -877,13 +876,26 @@ public final class Config extends Context implements 
ConfigEventListener {
         * If entry does not exist, returns an empty {@link Entry} object.
         *
         * @param key The key.
-        * @return The value, never <jk>null</jk>.
+        * @return The entry bean, never <jk>null</jk>.
         */
        public Entry get(String key) {
                return new Entry(this, configMap, sname(key), skey(key));
        }
 
        /**
+        * Gets the section with the specified name.
+        *
+        * <p>
+        * If section does not exist, returns an empty {@link Section} object.
+        *
+        * @param name The section name.  <jk>null</jk> and blank refer to the 
default section.
+        * @return The section bean, never <jk>null</jk>.
+        */
+       public Section getSection(String name) {
+               return new Section(this, configMap, emptyIfNull(name));
+       }
+
+       /**
         * Returns the keys of the entries in the specified section.
         *
         * @param section
@@ -936,209 +948,15 @@ public final class Config extends Context implements 
ConfigEventListener {
        }
 
        /**
-        * Shortcut for calling <code>getSectionAsBean(sectionName, c, 
<jk>false</jk>)</code>.
-        *
-        * @param section
-        *      The section name to write from.
-        *      <br>If empty, refers to the default section.
-        *      <br>Must not be <jk>null</jk>.
-        * @param c The bean class to create.
-        * @return A new bean instance.
-        * @throws ParseException Malformed input encountered.
-        */
-       public <T> T getSectionAsBean(String section, Class<T> c) throws 
ParseException {
-               return getSectionAsBean(section, c, false);
-       }
-
-       /**
-        * 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.
-        *
-        * <h5 class='figure'>Example config file</h5>
-        * <p class='bcode w800'>
-        *      <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>
-        *
-        * <h5 class='figure'>Example bean</h5>
-        * <p class='bcode w800'>
-        *      <jk>public class</jk> Address {
-        *              public String name, street, city;
-        *              public StateEnum state;
-        *              public int zip;
-        *      }
-        * </p>
-        *
-        * <h5 class='figure'>Example usage</h5>
-        * <p class='bcode w800'>
-        *      Config cf = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
-        *      Address myAddress = cf.getSectionAsBean(<js>"MySection"</js>, 
Address.<jk>class</jk>);
-        * </p>
-        *
-        * @param section
-        *      The section name to write from.
-        *      <br>If empty, refers to the default section.
-        *      <br>Must not be <jk>null</jk>.
-        * @param c The bean class to create.
-        * @param ignoreUnknownProperties
-        *      If <jk>false</jk>, throws a {@link ParseException} if the 
section contains an entry that isn't a bean property
-        *      name.
-        * @return A new bean instance, or <jk>null</jk> if the section doesn't 
exist.
-        * @throws ParseException Unknown property was encountered in section.
-        */
-       public <T> T getSectionAsBean(String section, Class<T> c, boolean 
ignoreUnknownProperties) throws ParseException {
-               assertArgNotNull("c", c);
-               section = section(section);
-
-               if (! configMap.hasSection(section))
-                       return null;
-
-               Set<String> keys = configMap.getKeys(section);
-
-               BeanMap<T> bm = beanSession.newBeanMap(c);
-               for (String k : keys) {
-                       BeanPropertyMeta bpm = bm.getPropertyMeta(k);
-                       if (bpm == null) {
-                               if (! ignoreUnknownProperties)
-                                       throw new ParseException("Unknown 
property ''{0}'' encountered in configuration section ''{1}''.", k, section);
-                       } else {
-                               bm.put(k, get(section + '/' + 
k).as(bpm.getClassMeta().getInnerClass()).orElse(null));
-                       }
-               }
-
-               return bm.getBean();
-       }
-
-       /**
-        * Returns a section of this config copied into an {@link OMap}.
-        *
-        * @param section
-        *      The section name to write from.
-        *      <br>If empty, refers to the default section.
-        *      <br>Must not be <jk>null</jk>.
-        * @return A new {@link OMap}, or <jk>null</jk> if the section doesn't 
exist.
-        * @throws ParseException Malformed input encountered.
-        */
-       public OMap getSectionAsMap(String section) throws ParseException {
-               section = section(section);
-
-               if (! configMap.hasSection(section))
-                       return null;
-
-               Set<String> keys = configMap.getKeys(section);
-
-               OMap om = new OMap();
-               for (String k : keys)
-                       om.put(k, get(section + '/' + 
k).as(Object.class).orElse(null));
-               return om;
-       }
-
-       /**
         * Returns the section names defined in this config.
         *
         * @return The section names defined in this config.
         */
-       public Set<String> getSections() {
+       public Set<String> getSectionNames() {
                return Collections.unmodifiableSet(configMap.getSections());
        }
 
        /**
-        * Wraps a config file section inside a Java interface so that values 
in the section can be read and
-        * write using getters and setters.
-        *
-        * <h5 class='figure'>Example config file</h5>
-        * <p class='bcode w800'>
-        *      <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>
-        *
-        * <h5 class='figure'>Example interface</h5>
-        * <p class='bcode w800'>
-        *      <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>
-        *
-        * <h5 class='figure'>Example usage</h5>
-        * <p class='bcode w800'>
-        *      Config cf = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
-        *
-        *      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>
-        *
-        * <ul class='notes'>
-        *      <li>Calls to setters when the configuration is read-only will 
cause {@link UnsupportedOperationException} to be thrown.
-        * </ul>
-        *
-        * @param section
-        *      The section name to write from.
-        *      <br>If empty, refers to the default section.
-        *      <br>Must not be <jk>null</jk>.
-        * @param c The proxy interface class.
-        * @return The proxy interface.
-        */
-       @SuppressWarnings("unchecked")
-       public <T> T getSectionAsInterface(String section, final Class<T> c) {
-               assertArgNotNull("c", c);
-               final String section2 = section(section);
-
-               if (! c.isInterface())
-                       throw illegalArgumentException("Class ''{0}'' passed to 
getSectionAsInterface() is not an interface.", c.getName());
-
-               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 Config.this.get(section2 
+ '/' + pd.getName()).to(rm.getGenericReturnType());
-                                       if (method.equals(wm))
-                                               return Config.this.set(section2 
+ '/' + pd.getName(), args[0]);
-                               }
-                               throw 
unsupportedOperationException("Unsupported interface method.  method=''{0}''", 
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.
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java
new file mode 100644
index 0000000..cfa98e0
--- /dev/null
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java
@@ -0,0 +1,353 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.config;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.apache.juneau.internal.ThrowableUtils.*;
+import static java.util.Optional.*;
+
+import java.beans.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.config.internal.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * A single section in a config file.
+ */
+public class Section {
+
+       final Config config;
+       private final ConfigMap configMap;
+       final String name;
+
+       /**
+        * Constructor.
+        *
+        * @param config The config that this entry belongs to.
+        * @param configMap The map that this belongs to.
+        * @param name The section name of this entry.
+        */
+       protected Section(Config config, ConfigMap configMap, String name) {
+               this.config = config;
+               this.configMap = configMap;
+               this.name = name;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this section exists.
+        *
+        * @return <jk>true</jk> if this section exists.
+        */
+       public boolean isPresent() {
+               return configMap.hasSection(name);
+       }
+
+       /**
+        * Shortcut for calling <code>toBean(sectionName, c, 
<jk>false</jk>)</code>.
+        *
+        * @param c The bean class to create.
+        * @return A new bean instance, or <jk>null</jk> if this section does 
not exist.
+        * @throws ParseException Malformed input encountered.
+        */
+       public <T> T toBean(Class<T> c) throws ParseException {
+               return toBean(c, false);
+       }
+
+       /**
+        * Shortcut for calling <code>asBean(sectionName, c, 
<jk>false</jk>)</code>.
+        *
+        * @param c The bean class to create.
+        * @return A new bean instance, or {@link Optional#empty()} if this 
section does not exist.
+        * @throws ParseException Malformed input encountered.
+        */
+       public <T> Optional<T> asBean(Class<T> c) throws ParseException {
+               return ofNullable(toBean(c));
+       }
+
+       /**
+        * 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.
+        *
+        * <h5 class='figure'>Example config file</h5>
+        * <p class='bcode w800'>
+        *      <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>
+        *
+        * <h5 class='figure'>Example bean</h5>
+        * <p class='bcode w800'>
+        *      <jk>public class</jk> Address {
+        *              public String name, street, city;
+        *              public StateEnum state;
+        *              public int zip;
+        *      }
+        * </p>
+        *
+        * <h5 class='figure'>Example usage</h5>
+        * <p class='bcode w800'>
+        *      Config cf = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
+        *      Address myAddress = 
cf.getSection(<js>"MySection"</js>).toBean(Address.<jk>class</jk>);
+        * </p>
+        *
+        * @param c The bean class to create.
+        * @param ignoreUnknownProperties
+        *      If <jk>false</jk>, throws a {@link ParseException} if the 
section contains an entry that isn't a bean property
+        *      name.
+        * @return A new bean instance, or <jk>null</jk> if this section 
doesn't exist.
+        * @throws ParseException Unknown property was encountered in section.
+        */
+       public <T> T toBean(Class<T> c, boolean ignoreUnknownProperties) throws 
ParseException {
+               assertArgNotNull("c", c);
+               if (! isPresent()) return null;
+
+               Set<String> keys = configMap.getKeys(name);
+
+               BeanMap<T> bm = config.beanSession.newBeanMap(c);
+               for (String k : keys) {
+                       BeanPropertyMeta bpm = bm.getPropertyMeta(k);
+                       if (bpm == null) {
+                               if (! ignoreUnknownProperties)
+                                       throw new ParseException("Unknown 
property ''{0}'' encountered in configuration section ''{1}''.", k, name);
+                       } else {
+                               bm.put(k, config.get(name + '/' + 
k).as(bpm.getClassMeta().getInnerClass()).orElse(null));
+                       }
+               }
+
+               return bm.getBean();
+       }
+
+       /**
+        * 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.
+        *
+        * <h5 class='figure'>Example config file</h5>
+        * <p class='bcode w800'>
+        *      <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>
+        *
+        * <h5 class='figure'>Example bean</h5>
+        * <p class='bcode w800'>
+        *      <jk>public class</jk> Address {
+        *              public String name, street, city;
+        *              public StateEnum state;
+        *              public int zip;
+        *      }
+        * </p>
+        *
+        * <h5 class='figure'>Example usage</h5>
+        * <p class='bcode w800'>
+        *      Config cf = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
+        *      Address myAddress = 
cf.getSection(<js>"MySection"</js>).asBean(Address.<jk>class</jk>).orElse(<jk>null</jk>);
+        * </p>
+        *
+        * @param c The bean class to create.
+        * @param ignoreUnknownProperties
+        *      If <jk>false</jk>, throws a {@link ParseException} if the 
section contains an entry that isn't a bean property
+        *      name.
+        * @return A new bean instance, or <jk>null</jk> if this section 
doesn't exist.
+        * @throws ParseException Unknown property was encountered in section.
+        */
+       public <T> Optional<T> asBean(Class<T> c, boolean 
ignoreUnknownProperties) throws ParseException {
+               return ofNullable(toBean(c, ignoreUnknownProperties));
+       }
+
+       /**
+        * Returns this section as a map.
+        *
+        * @return A new {@link OMap}, or <jk>null</jk> if this section doesn't 
exist.
+        */
+       public OMap toMap() {
+               if (! isPresent()) return null;
+
+               Set<String> keys = configMap.getKeys(name);
+
+               OMap om = new OMap();
+               for (String k : keys)
+                       om.put(k, config.get(name + '/' + 
k).as(Object.class).orElse(null));
+               return om;
+       }
+
+       /**
+        * Returns this section as a map.
+        *
+        * @return A new {@link OMap}, or {@link Optional#empty()} if this 
section doesn't exist.
+        */
+       public Optional<OMap> asMap() {
+               return ofNullable(toMap());
+       }
+
+       /**
+        * Wraps this section inside a Java interface so that values in the 
section can be read and
+        * write using getters and setters.
+        *
+        * <h5 class='figure'>Example config file</h5>
+        * <p class='bcode w800'>
+        *      <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>
+        *
+        * <h5 class='figure'>Example interface</h5>
+        * <p class='bcode w800'>
+        *      <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>
+        *
+        * <h5 class='figure'>Example usage</h5>
+        * <p class='bcode w800'>
+        *      Config cf = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
+        *
+        *      MyConfigInterface ci = 
cf.get(<js>"MySection"</js>).toInterface(MyConfigInterface.<jk>class</jk>);
+        *
+        *      <jk>int</jk> myInt = ci.getInt();
+        *
+        *      ci.setBean(<jk>new</jk> MyBean());
+        *
+        *      cf.save();
+        * </p>
+        *
+        * <ul class='notes'>
+        *      <li>Calls to setters when the configuration is read-only will 
cause {@link UnsupportedOperationException} to be thrown.
+        * </ul>
+        *
+        * @param c The proxy interface class.
+        * @return The proxy interface.
+        */
+       @SuppressWarnings("unchecked")
+       public <T> T toInterface(final Class<T> c) {
+               assertArgNotNull("c", c);
+
+               if (! c.isInterface())
+                       throw illegalArgumentException("Class ''{0}'' passed to 
toInterface() is not an interface.", c.getName());
+
+               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 config.get(name + '/' + 
pd.getName()).to(rm.getGenericReturnType());
+                                       if (method.equals(wm))
+                                               return config.set(name + '/' + 
pd.getName(), args[0]);
+                               }
+                               throw 
unsupportedOperationException("Unsupported interface method.  method=''{0}''", 
method);
+                       }
+               };
+
+               return (T)Proxy.newProxyInstance(c.getClassLoader(), new 
Class[] { c }, h);
+       }
+
+       /**
+        * Wraps this section inside a Java interface so that values in the 
section can be read and
+        * write using getters and setters.
+        *
+        * <h5 class='figure'>Example config file</h5>
+        * <p class='bcode w800'>
+        *      <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>
+        *
+        * <h5 class='figure'>Example interface</h5>
+        * <p class='bcode w800'>
+        *      <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>
+        *
+        * <h5 class='figure'>Example usage</h5>
+        * <p class='bcode w800'>
+        *      Config cf = 
Config.<jsm>create</jsm>().name(<js>"MyConfig.cfg"</js>).build();
+        *
+        *      MyConfigInterface ci = 
cf.get(<js>"MySection"</js>).asInterface(MyConfigInterface.<jk>class</jk>).orElse(<jk>null</jk>);
+        *
+        *      <jk>int</jk> myInt = ci.getInt();
+        *
+        *      ci.setBean(<jk>new</jk> MyBean());
+        *
+        *      cf.save();
+        * </p>
+        *
+        * <ul class='notes'>
+        *      <li>Calls to setters when the configuration is read-only will 
cause {@link UnsupportedOperationException} to be thrown.
+        * </ul>
+        *
+        * @param c The proxy interface class.
+        * @return The proxy interface.
+        */
+       public <T> Optional<T> asInterface(final Class<T> c) {
+               return ofNullable(toInterface(c));
+       }
+}
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
index 1cab30c..54da3c4 100755
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
@@ -25,7 +25,6 @@ import org.apache.juneau.http.annotation.FormData;
 import org.apache.juneau.http.annotation.Path;
 import org.apache.juneau.http.annotation.Response;
 import org.apache.juneau.http.annotation.Schema;
-import org.apache.juneau.parser.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.http.response.*;
@@ -103,7 +102,7 @@ public class ConfigResource extends BasicRestServlet {
        )
        public OMap getConfigSection(
                        @Path("section") @Schema(d="Section name in config 
file.") String section
-               ) throws SectionNotFound, BadConfig {
+               ) throws SectionNotFound {
 
                return getSection(section);
        }
@@ -121,7 +120,7 @@ public class ConfigResource extends BasicRestServlet {
        public String getConfigEntry(
                        @Path("section") @Schema(d="Section name in config 
file.") String section,
                        @Path("key") @Schema(d="Key name in section.") String 
key
-               ) throws SectionNotFound, BadConfig {
+               ) throws SectionNotFound {
 
                return getSection(section).getString(key);
        }
@@ -194,7 +193,7 @@ public class ConfigResource extends BasicRestServlet {
                        @Path("section") @Schema(d="Section name in config 
file.") String section,
                        @Path("key") @Schema(d="Key name in section.") String 
key,
                        @Body @Schema(d="New value for entry.") String value
-               ) throws SectionNotFound, BadConfig {
+               ) throws SectionNotFound {
 
                getContext().getConfig().set(section + '/' + key, value);
                return getSection(section).getString(key);
@@ -213,28 +212,11 @@ public class ConfigResource extends BasicRestServlet {
                }
        }
 
-       @Response @Schema(description="The configuration file contained syntax 
errors and could not be parsed.")
-       private class BadConfig extends InternalServerError {
-               private static final long serialVersionUID = 1L;
-
-               BadConfig(Exception e) {
-                       super(e, "The configuration file contained syntax 
errors and could not be parsed.");
-               }
-       }
-
        
//-----------------------------------------------------------------------------------------------------------------
        // Helper methods
        
//-----------------------------------------------------------------------------------------------------------------
 
-       private OMap getSection(String name) throws SectionNotFound, BadConfig {
-               OMap m;
-               try {
-                       m = getContext().getConfig().getSectionAsMap(name);
-               } catch (ParseException e) {
-                       throw new BadConfig(e);
-               }
-               if (m == null)
-                       throw new SectionNotFound();
-               return m;
+       private OMap getSection(String name) throws SectionNotFound {
+               return 
getContext().getConfig().getSection(name).asMap().orElseThrow(SectionNotFound::new);
        }
 }
diff --git 
a/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
 
b/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
index 2eaf940..691235d 100644
--- 
a/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
+++ 
b/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
@@ -40,7 +40,7 @@ public class ConfigTest extends RestTestcase {
 
                Config cf = Config.create().memStore().build().load(m);
 
-               
assertObject(cf.getSectionAsMap("Test")).asJson().is("{int1:'1',int2:'[1,2,3]',int3:'1',int4:'1',int5:'-1',boolean1:'true',boolean2:'[true,true]',testManifestEntry:'test-value'}");
+               
assertObject(cf.getSection("Test").toMap()).asJson().is("{int1:'1',int2:'[1,2,3]',int3:'1',int4:'1',int5:'-1',boolean1:'true',boolean2:'[true,true]',testManifestEntry:'test-value'}");
 
                assertEquals("'1'", c.get(URL + "/Test%2Fint1/" + 
getName(String.class)).run().getBody().asString());
                assertEquals("'[1,2,3]'", c.get(URL + "/Test%2Fint2/" + 
getName(String.class)).run().getBody().asString());
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigInterfaceTest.java 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigInterfaceTest.java
index f7ad992..c691c69 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigInterfaceTest.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigInterfaceTest.java
@@ -31,7 +31,7 @@ public class ConfigInterfaceTest {
 
        public ConfigInterfaceTest() throws Exception {
                cf = 
Config.create().serializer(SimpleJsonSerializer.DEFAULT.copy().addBeanTypes().addRootType().build()).build();
-               proxy = cf.getSectionAsInterface("A", ConfigInterface.class);
+               proxy = cf.getSection("A").toInterface(ConfigInterface.class);
        }
 
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
index f96bc98..24e5c8f 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
@@ -839,24 +839,24 @@ public class ConfigTest {
                ABean a = null;
                BBean b = null;
 
-               a = c.getSectionAsBean("", ABean.class);
+               a = c.getSection("").toBean(ABean.class);
                assertObject(a).asJson().is("{foo:'qux'}");
-               a = c.getSectionAsBean("", ABean.class);
+               a = c.getSection("").toBean(ABean.class);
                assertObject(a).asJson().is("{foo:'qux'}");
-               a = c.getSectionAsBean("S", ABean.class);
+               a = c.getSection(null).toBean(ABean.class);
+               assertObject(a).asJson().is("{foo:'qux'}");
+               a = c.getSection("S").toBean(ABean.class);
                assertObject(a).asJson().is("{foo:'baz'}");
 
-               b = c.getSectionAsBean("", BBean.class);
+               b = c.getSection("").toBean(BBean.class);
                assertObject(b).asJson().is("{foo:'qux'}");
-               b = c.getSectionAsBean("", BBean.class);
+               b = c.getSection("").toBean(BBean.class);
                assertObject(b).asJson().is("{foo:'qux'}");
-               b = c.getSectionAsBean("S", BBean.class);
+               b = c.getSection("S").toBean(BBean.class);
                assertObject(b).asJson().is("{foo:'baz'}");
 
-               assertThrown(()->c.getSectionAsBean("T", 
ABean.class)).message().is("Unknown property 'bar' encountered in configuration 
section 'T'.");
-               assertThrown(()->c.getSectionAsBean("T", 
BBean.class)).message().is("Unknown property 'bar' encountered in configuration 
section 'T'.");
-               assertThrown(()->c.getSectionAsBean(null, 
ABean.class)).message().is("Argument 'section' cannot be null.");
-               assertThrown(()->c.getSectionAsBean(null, 
BBean.class)).message().is("Argument 'section' cannot be null.");
+               
assertThrown(()->c.getSection("T").toBean(ABean.class)).message().is("Unknown 
property 'bar' encountered in configuration section 'T'.");
+               
assertThrown(()->c.getSection("T").toBean(BBean.class)).message().is("Unknown 
property 'bar' encountered in configuration section 'T'.");
        }
 
        
//====================================================================================================
@@ -869,13 +869,13 @@ public class ConfigTest {
                ABean a = null;
                BBean b = null;
 
-               a = c.getSectionAsBean("T", ABean.class, true);
+               a = c.getSection("T").toBean(ABean.class, true);
                assertObject(a).asJson().is("{foo:'qux'}");
-               b = c.getSectionAsBean("T", BBean.class, true);
+               b = c.getSection("T").toBean(BBean.class, true);
                assertObject(b).asJson().is("{foo:'qux'}");
 
-               assertThrown(()->c.getSectionAsBean("T", ABean.class, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'T'.");
-               assertThrown(()->c.getSectionAsBean("T", BBean.class, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'T'.");
+               assertThrown(()->c.getSection("T").toBean(ABean.class, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'T'.");
+               assertThrown(()->c.getSection("T").toBean(BBean.class, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'T'.");
        }
 
        
//====================================================================================================
@@ -885,13 +885,12 @@ public class ConfigTest {
        public void getSectionAsMap() throws Exception {
                Config c = init("a=1", "[S]", "b=2", "[T]");
 
-               assertObject(c.getSectionAsMap("")).asJson().is("{a:'1'}");
-               assertObject(c.getSectionAsMap("")).asJson().is("{a:'1'}");
-               assertObject(c.getSectionAsMap("S")).asJson().is("{b:'2'}");
-               assertObject(c.getSectionAsMap("T")).asJson().is("{}");
-               assertNull(c.getSectionAsMap("U"));
-
-               
assertThrown(()->c.getSectionAsMap(null)).message().is("Argument 'section' 
cannot be null.");
+               assertObject(c.getSection("").toMap()).asJson().is("{a:'1'}");
+               assertObject(c.getSection("").toMap()).asJson().is("{a:'1'}");
+               assertObject(c.getSection(null).toMap()).asJson().is("{a:'1'}");
+               assertObject(c.getSection("S").toMap()).asJson().is("{b:'2'}");
+               assertObject(c.getSection("T").toMap()).asJson().is("{}");
+               assertNull(c.getSection("U").toMap());
        }
 
        
//====================================================================================================
@@ -902,20 +901,22 @@ public class ConfigTest {
                Config c = init("foo=qux", "[S]", "foo=baz", "[T]", "foo=qux", 
"bar=qux");
                AInterface a = null;
 
-               a = c.getSectionAsInterface("", AInterface.class);
+               a = c.getSection("").toInterface(AInterface.class);
+               assertEquals("qux", a.getFoo());
+
+               a = c.getSection("").toInterface(AInterface.class);
                assertEquals("qux", a.getFoo());
 
-               a = c.getSectionAsInterface("", AInterface.class);
+               a = c.getSection(null).toInterface(AInterface.class);
                assertEquals("qux", a.getFoo());
 
-               a = c.getSectionAsInterface("S", AInterface.class);
+               a = c.getSection("S").toInterface(AInterface.class);
                assertEquals("baz", a.getFoo());
 
-               a = c.getSectionAsInterface("T", AInterface.class);
+               a = c.getSection("T").toInterface(AInterface.class);
                assertEquals("qux", a.getFoo());
 
-               assertThrown(()->c.getSectionAsInterface("T", 
ABean.class)).message().is("Class 'org.apache.juneau.config.ConfigTest$ABean' 
passed to getSectionAsInterface() is not an interface.");
-               assertThrown(()->c.getSectionAsInterface(null, 
AInterface.class)).message().is("Argument 'section' cannot be null.");
+               
assertThrown(()->c.getSection("T").toInterface(ABean.class)).message().is("Class
 'org.apache.juneau.config.ConfigTest$ABean' passed to toInterface() is not an 
interface.");
        }
 
        public static interface AInterface {
@@ -1424,15 +1425,15 @@ public class ConfigTest {
        public void testGetSectionMap() throws Exception {
                Config cf = init("[A]", "a1=1", "", "[D]", 
"d1=$C{A/a1}","d2=$S{X}");
 
-               assertObject(cf.getSectionAsMap("A")).asJson().is("{a1:'1'}");
-               assertNull(cf.getSectionAsMap("B"));
-               assertObject(cf.getSectionAsMap("C")).asJson().is("null");
+               
assertObject(cf.getSection("A").toMap()).asJson().is("{a1:'1'}");
+               assertNull(cf.getSection("B").toMap());
+               assertObject(cf.getSection("C").toMap()).asJson().is("null");
 
-               OMap m = cf.getSectionAsMap("A");
+               OMap m = cf.getSection("A").toMap();
                assertObject(m).asJson().is("{a1:'1'}");
 
                System.setProperty("X", "x");
-               m = cf.getSectionAsMap("D");
+               m = cf.getSection("D").toMap();
                assertObject(m).asJson().is("{d1:'1',d2:'x'}");
                System.clearProperty("X");
        }

Reply via email to