MEECROWAVE-203 adding configuration.complete support for meecrowave.properties
diff --git a/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java b/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
index 0028c60..748cd6f 100644
--- a/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
+++ b/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
@@ -2051,8 +2051,9 @@
 
         public Builder loadFrom(final String resource) {
             // load all of those files on the classpath, sorted by ordinal
-            Properties config = PropertyLoader.getProperties(resource);
-            if (config == null) {
+            Properties config = PropertyLoader.getProperties(resource,
+                    sortedProperties -> mergeProperties(resource, sortedProperties));
+            if (config == null || config.isEmpty()) {
                 final File file = new File(resource);
                 if (file.exists()) {
                     config = new Properties();
@@ -2369,6 +2370,27 @@
         public void setMeecrowaveProperties(final String meecrowaveProperties) {
             this.meecrowaveProperties = meecrowaveProperties;
         }
+
+        private Properties mergeProperties(final String resource, final List<Properties> sortedProperties) {
+            Properties mergedProperties = new Properties();
+            Properties master = null;
+            for (final Properties p : sortedProperties)
+            {
+                if (Boolean.parseBoolean(p.getProperty("configuration.complete", "false"))) {
+                    if (master != null) {
+                        throw new IllegalArgumentException("Ambiguous '" + resource + "', " +
+                                "multiple " + resource + " with configuration.complete=true");
+                    }
+                    master = p;
+                }
+                mergedProperties.putAll(p);
+            }
+
+            if (master != null) {
+                return master;
+            }
+            return mergedProperties;
+        }
     }
 
     public static class ValueTransformers implements Function<String, String> {
diff --git a/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java b/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java
index a652d76..94916dc 100644
--- a/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java
+++ b/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java
@@ -28,6 +28,7 @@
 import org.superbiz.app.RsApp;
 import org.superbiz.app.TestJsonEndpoint;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
@@ -35,10 +36,17 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Writer;
+import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
 import java.util.Properties;
 import java.util.stream.Stream;
 
+import static java.util.Arrays.asList;
+import static java.util.Collections.enumeration;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -46,6 +54,37 @@
 
 public class MeecrowaveTest {
     @Test
+    public void conflictingConfig() throws MalformedURLException {
+        withConfigClassLoader(() -> {
+            try {
+                new Meecrowave.Builder().loadFrom("test.config.properties");
+                fail("should have failed since it conflicts");
+            } catch (final IllegalArgumentException iae) {
+                // ok
+            }
+        }, configUrl("configuration.complete=true\nf=1"), configUrl("configuration.complete=true\nf=2"));
+    }
+
+    @Test
+    public void masterConfig() throws MalformedURLException {
+        withConfigClassLoader(() -> {
+            final Meecrowave.Builder builder = new Meecrowave.Builder();
+            builder.loadFrom("test.config.properties");
+            assertEquals(1, builder.getHttpPort());
+        }, configUrl("http=2"), configUrl("configuration.complete=true\nhttp=1"), configUrl("http=3"));
+    }
+
+    @Test
+    public void mergedConfig() throws MalformedURLException {
+        withConfigClassLoader(() -> {
+            final Meecrowave.Builder builder = new Meecrowave.Builder();
+            builder.loadFrom("test.config.properties");
+            assertEquals(2, builder.getHttpPort());
+            assertEquals(4, builder.getHttpsPort());
+        }, configUrl("http=2\nconfiguration.ordinal=2\nhttps=4"), configUrl("http=3\nconfiguration.ordinal=1"));
+    }
+
+    @Test
     public void configBinding() {
         final MyConfig config = new Meecrowave.Builder()
                 .property("my-prefix-port", "1234")
@@ -160,4 +199,51 @@
         @CliOption(name = "my-prefix-bool", description = "")
         private boolean bool;
     }
+
+    private static URL configUrl(final String content) throws MalformedURLException {
+        return new URL("memory", null, -1, "test.config.properties", new MemoryHandler(content));
+    }
+
+    private static void withConfigClassLoader(final Runnable test, final URL... urls) {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = thread.getContextClassLoader();
+        thread.setContextClassLoader(new ClassLoader(loader) {
+            @Override
+            public Enumeration<URL> getResources(final String name) throws IOException {
+                return "test.config.properties".equals(name) ? enumeration(asList(urls)) : super.getResources(name);
+            }
+        });
+        try {
+            test.run();
+        } finally {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    private static class MemoryHandler extends URLStreamHandler
+    {
+        private final String content;
+
+        private MemoryHandler(final String content)
+        {
+            this.content = content;
+        }
+
+        @Override
+        protected URLConnection openConnection(final URL u) {
+            return new URLConnection(u)
+            {
+                @Override
+                public void connect()
+                {
+                    // no-op
+                }
+
+                @Override
+                public InputStream getInputStream() {
+                    return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
+                }
+            };
+        }
+    }
 }
diff --git a/meecrowave-doc/src/main/jbake/content/meecrowave-core/configuration.adoc b/meecrowave-doc/src/main/jbake/content/meecrowave-core/configuration.adoc
index c49a74d..06e60e7 100755
--- a/meecrowave-doc/src/main/jbake/content/meecrowave-core/configuration.adoc
+++ b/meecrowave-doc/src/main/jbake/content/meecrowave-core/configuration.adoc
@@ -124,7 +124,11 @@
 - `configurationCustomizer.x=y` will set `x` to `y` for the customizer
 
 TIP: Out of the box, any `Builder` instance will read `meecrowave.properties`.
-`meecrowave.properties` uses CLI names (without the leading `--`).
+`meecrowave.properties` uses CLI names (without the leading `--`). It loads all available files from the classpath,
+they are merged using `configuration.ordinal` key (exactly like Apache OpenWebBeans does for its configuration).
+It also supports `configuration.complete=[true|false]` which enables a single file to host it with the `true` value
+and will consider this file as the merged result of all potential files found in the classpath. It is useful to
+avoid an implicit merging and can typically be used in `conf/meecrowave.properties` in bundle mode.
 See link:{context_rootpath}/meecrowave-core/cli.html[CLI] page for the list.
 
 === Valve configuration
diff --git a/pom.xml b/pom.xml
index 446a8aa..77efdc8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,7 @@
 
     <junit.version>4.13-beta-3</junit.version>
     <tomcat.version>9.0.22</tomcat.version>
-    <openwebbeans.version>2.0.11</openwebbeans.version>
+    <openwebbeans.version>2.0.12-SNAPSHOT</openwebbeans.version>
     <cxf.version>3.3.2</cxf.version>
     <johnzon.version>1.1.13-SNAPSHOT</johnzon.version>
     <log4j2.version>2.11.2</log4j2.version>