From 7179224d8b1094845a5651844bc512f86da966e2 Mon Sep 17 00:00:00 2001
From: Mark Struberg
Date: Thu, 26 Jan 2017 08:57:45 +0100
Subject: [PATCH 1/2] a purely CDI based Configuration System
As designed by Romain Mannu-Bucau and Mark Struberg
---
api/pom.xml | 8 +
.../java/io/microprofile/config/Config.java | 10 +-
.../microprofile/config/ConfigProvider.java | 137 +----------------
.../io/microprofile/config/ConfigValue.java | 139 ------------------
.../config/internal/ConfigBuilder.java | 51 +++++++
.../config/internal/ConfigExtension.java | 68 +++++++++
.../config/spi/ConfigSourceProvider.java | 46 ------
.../geronimo/config/DefaultConfigBuilder.java | 12 +-
.../config/DefaultConfigProvider.java | 5 +-
9 files changed, 141 insertions(+), 335 deletions(-)
delete mode 100644 api/src/main/java/io/microprofile/config/ConfigValue.java
create mode 100644 api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java
create mode 100644 api/src/main/java/io/microprofile/config/internal/ConfigExtension.java
delete mode 100644 api/src/main/java/io/microprofile/config/spi/ConfigSourceProvider.java
diff --git a/api/pom.xml b/api/pom.xml
index 3a68c25..3127ced 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -39,5 +39,13 @@
+
+
+ org.apache.geronimo.specs
+ geronimo-jcdi_1.1_spec
+ 1.0
+ provided
+
+
diff --git a/api/src/main/java/io/microprofile/config/Config.java b/api/src/main/java/io/microprofile/config/Config.java
index 76e2e55..97f2448 100644
--- a/api/src/main/java/io/microprofile/config/Config.java
+++ b/api/src/main/java/io/microprofile/config/Config.java
@@ -18,7 +18,6 @@
import java.util.Map;
-import io.microprofile.config.spi.ConfigSourceProvider;
import io.microprofile.config.spi.Converter;
import io.microprofile.config.spi.ConfigSource;
@@ -28,7 +27,7 @@
* If multiple {@link ConfigSource}s have the same ordinal, their order is undefined.
*
* You can provide your own lookup paths by implementing and registering additional
- * {@link ConfigSource}s and {@link ConfigSourceProvider} implementations.
+ * {@link ConfigSource}s implementations.
*
*
* @see ConfigProvider to resolve the current configuration.
@@ -37,13 +36,6 @@
*/
public interface Config {
- /**
- * Create a {@link ConfigValue} to access the underlying configuration.
- *
- * @param key the property key
- */
- ConfigValue access(String key);
-
/**
* Resolves the value configured for the given key.
*
diff --git a/api/src/main/java/io/microprofile/config/ConfigProvider.java b/api/src/main/java/io/microprofile/config/ConfigProvider.java
index ae0f690..c753d89 100644
--- a/api/src/main/java/io/microprofile/config/ConfigProvider.java
+++ b/api/src/main/java/io/microprofile/config/ConfigProvider.java
@@ -16,11 +16,11 @@
*/
package io.microprofile.config;
-import java.util.ServiceLoader;
-import java.util.logging.Logger;
+import javax.enterprise.inject.spi.CDI;
+import io.microprofile.config.internal.ConfigExtension;
import io.microprofile.config.spi.ConfigSource;
-import io.microprofile.config.spi.Converter;
+
/**
* This is the central class to access a {@link Config}.
@@ -56,8 +56,6 @@
*/
public class ConfigProvider {
- private static volatile SPI instance;
-
/**
* Provide a {@link Config} based on all {@link ConfigSource}s
* of the current Thread Context ClassLoader (TCCL)
@@ -65,134 +63,7 @@ public class ConfigProvider {
* There is exactly a single Config instance per Application
*/
public static Config getConfig() {
- return loadSpi().getConfig();
- }
-
- /**
- * Provide a {@link Config} based on all {@link ConfigSource}s
- * of the given ClassLoader.
- *
- * There is exactly a single Config instance per Application. The Application get's identified via a ClassLoader
- */
- public static Config getConfig(ClassLoader forClassLoader) {
- return instance.getConfig(forClassLoader);
- }
-
- /**
- * Create a {@link ConfigBuilder} for providing a fresh {@link Config} instance.
- *
- * The ConfigProvider will not manage the Config instance internally.
- * That means that {@link #getConfig()} does not pick up a Config created that way.
- */
- public static ConfigBuilder newConfig() {
- return instance.newConfig();
+ return CDI.current().getBeanManager().getExtension(ConfigExtension.class).getConfig();
}
- /**
- * Create a {@link ConfigBuilder} for register a {@link Config} instance to a certain Application.
- * Invoking {@link ConfigBuilder#build()} will effectively register the Config.
- * Use {@link ConfigBuilder#forClassLoader(ClassLoader)} to define the application the Config should be for,
- * otherwise the current ThreadContextClassLoader will be used.
- *
- * A {@link Config} registered that way will get picked up on any subsequent call to {@link ConfigProvider#getConfig()}.
- *
- * @throws IllegalStateException if a {@link Config} has already been associated for the Application.
- */
- public static ConfigBuilder registerConfig() {
- return instance.registerConfig();
- }
-
-
- /**
- * A {@link Config} normally gets released if the ClassLoader it represents gets destroyed.
- * Invoke this method if you like to destroy the Config prematurely.
- */
- public static void releaseConfig(Config config) {
- instance.releaseConfig(config);
- }
-
-
- /**
- * Builder for manually creating an instance of a {@code Config}.
- *
- * @see ConfigProvider#newConfig()
- */
- public interface ConfigBuilder {
- ConfigBuilder ignoreDefaultSources();
- ConfigBuilder forClassLoader(ClassLoader loader);
- ConfigBuilder withSources(ConfigSource... sources);
- ConfigBuilder withConverters(Converter>... filters);
- Config build();
- }
-
- /**
- * This interface gets implemented internally by the Config library.
- * The implementation registers itself via {@link java.util.ServiceLoader} mechanism.
- * In an OSGi environment
- */
- public interface SPI {
- Config getConfig();
- Config getConfig(ClassLoader forClassLoader);
- ConfigBuilder newConfig();
- ConfigBuilder registerConfig();
- void releaseConfig(Config config);
- }
-
- /**
- * Attention, handle with care!
- * This method is not intended to be used from a user.
- * It is for integration with e.g. OSGi from within a BundleActivator.
- * TODO probably remove this and add native OSGi support later.
- */
- public static synchronized void setSPI(SPI newInstance) {
- instance = newInstance;
- }
-
- private static SPI loadSpi() {
- if (instance == null) {
- synchronized (SPI.class) {
- if (instance != null) {
- return instance;
- }
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- if (cl == null) {
- cl = SPI.class.getClassLoader();
- }
-
- SPI newInstance = loadSpi(cl);
-
- if (newInstance == null) {
- throw new IllegalStateException("No ConfigResolver SPI implementation found!");
- }
-
- instance = newInstance;
- }
- }
-
- return instance;
- }
-
- private static SPI loadSpi(ClassLoader cl) {
- if (cl == null) {
- return null;
- }
-
- // start from the root CL and go back down to the TCCL
- SPI instance = loadSpi(cl.getParent());
-
- if (instance == null) {
- ServiceLoader sl = ServiceLoader.load(SPI.class, cl);
- for (SPI spi : sl) {
- if (instance != null) {
- Logger.getLogger(ConfigProvider.class.getName())
- .warning("Multiple ConfigResolver SPIs found. Ignoring " + spi.getClass().getName());
- } else {
- instance = spi;
- }
- }
- }
- return instance;
- }
-
-
}
diff --git a/api/src/main/java/io/microprofile/config/ConfigValue.java b/api/src/main/java/io/microprofile/config/ConfigValue.java
deleted file mode 100644
index d93b95b..0000000
--- a/api/src/main/java/io/microprofile/config/ConfigValue.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * See the NOTICE file distributed with this work for additional information
- * regarding copyright ownership.
- * The author 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 io.microprofile.config;
-
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Accessor to a configured value.
- * It follows a builder pattern.
- *
- * Accessing the configured value is finally done via {@link #getValue()}
- *
- * @author Mark Struberg
- * @author Gerhard Petracek
- * @author Ron Smeral
- */
-public interface ConfigValue {
-
- /**
- * Sets the type of the configuration entry to the given class and returns this builder.
- * The default type of a ConfigValue is {@code String}.
- *
- * @param clazz The target type
- * @param The target type
- * @return This builder as a typed ConfigValue
- */
- ConfigValue as(Class clazz);
-
- /**
- * Sets the default value to use in case the resolution returns null.
- * @param value the default value
- * @return This builder
- */
- ConfigValue withDefault(T value);
-
- /**
- * Sets the default value to use in case the resolution returns null. Converts the given String to the type of
- * this resolver using the same method as used for the configuration entries.
- * @param value string value to be converted and used as default
- * @return This builder
- */
- ConfigValue withStringDefault(String value);
-
- /**
- * Specify that a resolved value will get cached for a certain amount of time.
- * After the time expires the next {@link #getValue()} will again resolve the value
- * from the underlying {@link Config}.
- *
- * @param value the amount of the TimeUnit to wait
- * @param timeUnit the TimeUnit for the value
- *
- * @return This builder
- */
- ConfigValue cacheFor(long value, TimeUnit timeUnit);
-
- /**
- * Whether to evaluate variables in configured values.
- * A variable starts with '${' and ends with '}', e.g.
- *
- * mycompany.some.url=${myserver.host}/some/path
- * myserver.host=http://localhost:8081
- *
- * If 'evaluateVariables' is enabled, the result for the above key
- * {@code "mycompany.some.url"} would be:
- * {@code "http://localhost:8081/some/path"}
- * @param evaluateVariables whether to evaluate variables in values or not
- * @return This builder
- */
- ConfigValue evaluateVariables(boolean evaluateVariables);
-
- /**
- * Appends the resolved value of the given property to the key of this builder.
- * TODO further explain.
- * @return This builder
- */
- ConfigValue withLookupChain(String... postfixNames);
-
- /**
- * Whether to log picking up any value changes as INFO.
- *
- * @return This builder
- */
- ConfigValue logChanges(boolean logChanges);
-
- /**
- * Returns the converted resolved filtered value.
- * @return the resolved value
- */
- T getValue();
-
- /**
- * Resolves the value and split it on each comma (',') character.
- * If a comma is contained in the values it must get escaped with a preceding backslash ("\,").
- * Any backslash needs to get escaped via double-backslash ("\\").
- *
- * @return the list of configured comma separated values or an empty Iterable if no
- */
- Iterable getValueList();
-
- /**
- * Returns the key given in {@link Config#access(String)}.
- * @return the original key
- */
- String getKey();
-
- /**
- * Returns the actual key which led to successful resolution and corresponds to the resolved value. This applies
- * only when {@link #withLookupChain(String...)} is used.
- * Otherwise the resolved key should always be equal to the original key.
- * This method is provided for cases, when arameterized resolution is
- * requested but the value for such appended key is not found and some of the fallback keys is used.
- *
- * This should be called only after calling {@link #getValue()} otherwise the value is undefined (but likely
- * null).
- */
- String getResolvedKey();
-
- /**
- * Returns the default value provided by {@link #withDefault(Object)} or {@link #withStringDefault(String)}.
- * Returns null if no default was provided.
- * @return the default value or {@code null}
- */
- T getDefaultValue();
-}
diff --git a/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java b/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java
new file mode 100644
index 0000000..99009a2
--- /dev/null
+++ b/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * 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 io.microprofile.config.internal;
+
+import io.microprofile.config.Config;
+import io.microprofile.config.spi.ConfigSource;
+import io.microprofile.config.spi.Converter;
+
+/**
+ * Builder for manually creating an instance of a {@code Config}.
+ */
+public class ConfigBuilder {
+ public ConfigBuilder ignoreDefaultSources() {
+ //X TODO
+ return null;
+ }
+
+ public ConfigBuilder forClassLoader(ClassLoader loader) {
+ //X TODO
+ return null;
+ }
+
+ public ConfigBuilder withSources(ConfigSource... sources) {
+ //X TODO
+ return null;
+ }
+
+ public ConfigBuilder withConverters(Converter>[] filters) {
+ //X TODO
+ return null;
+ }
+
+ public Config build() {
+ //X TODO
+ return null;
+ }
+}
diff --git a/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java b/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java
new file mode 100644
index 0000000..be8c8b7
--- /dev/null
+++ b/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java
@@ -0,0 +1,68 @@
+/*
+ * 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 io.microprofile.config.internal;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.Extension;
+
+import io.microprofile.config.Config;
+
+/**
+ * This is the central part of the purely CDI based Configuration approach.
+ *
+ * It's quite messy as it has concrete classes in the API.
+ * Would need to think about finding a way to hide it.
+ *
+ * The idea has been born in a discussion between Romain and me.
+ *
+ * @author Mark Struberg
+ * @author Romain Manni-Bucau
+ */
+public class ConfigExtension implements Extension {
+
+ private Config config;
+
+ public Config getConfig() {
+ return config;
+ }
+
+ // trigger the creation of the Config subsystem early on
+ public void initConfig(@Observes BeforeBeanDiscovery bbd, BeanManager beanManager) {
+ ConfigBuilder configBuilder = newConfigBuilder();
+
+ // give other Extensions a way to tweak the config, add ConfigSources etc
+ // Attention those other observers must also be in Extensions!
+ // Usage:
+ // public void addMyConfigSource(@Observes ConfigBuilder configBuilder) { configBuilder.withConfigSource(...); }
+ beanManager.fireEvent(configBuilder);
+
+ // after that we got our ConfigBuilder filled up with all the info we need
+ // we now create the Config with it
+ this.config = configBuilder.build();
+
+ // and we now send another event to the other Extensions to allow using that Config even during boot
+ beanManager.fireEvent(config);
+ }
+
+ private ConfigBuilder newConfigBuilder() {
+ return null; //X TODO, needs a concrete class in the API -> ugly as hell
+ }
+
+
+}
diff --git a/api/src/main/java/io/microprofile/config/spi/ConfigSourceProvider.java b/api/src/main/java/io/microprofile/config/spi/ConfigSourceProvider.java
deleted file mode 100644
index ca4e04c..0000000
--- a/api/src/main/java/io/microprofile/config/spi/ConfigSourceProvider.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * See the NOTICE file distributed with this work for additional information
- * regarding copyright ownership.
- * The author 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 io.microprofile.config.spi;
-
-import java.util.List;
-
-/**
- * Implement this interfaces to provide a ConfigSource provider which
- * is able to maintain multiple ConfigSources. This is e.g. needed if
- * there are multiple property files of a given name.
- *
- * If a ConfigSource like JNDI only exists once, then there is no need
- * to implement it via the ConfigSourceProvider but should directly
- * expose a {@link ConfigSource}.
- *
- * A ConfigSourceProvider will get picked up via the
- * {@link java.util.ServiceLoader} mechanism and must get registered via
- * META-INF/services/javax.config.spi.ConfigSourceProvider
- *
- * @author Mark Struberg
- */
-public interface ConfigSourceProvider
-{
-
- /**
- * @param forClassLoader the classloader which should be used if any is needed
- *
- * @return For each e.g. property file, we return a single ConfigSource or an empty list if no ConfigSource exists.
- */
- List getConfigSources(ClassLoader forClassLoader);
-}
diff --git a/impl/src/main/java/org/apache/geronimo/config/DefaultConfigBuilder.java b/impl/src/main/java/org/apache/geronimo/config/DefaultConfigBuilder.java
index 2d504d3..25644e3 100644
--- a/impl/src/main/java/org/apache/geronimo/config/DefaultConfigBuilder.java
+++ b/impl/src/main/java/org/apache/geronimo/config/DefaultConfigBuilder.java
@@ -17,7 +17,7 @@
package org.apache.geronimo.config;
import io.microprofile.config.Config;
-import io.microprofile.config.ConfigProvider;
+import io.microprofile.config.internal.ConfigBuilder;
import io.microprofile.config.spi.ConfigSource;
import io.microprofile.config.spi.ConfigSourceProvider;
import io.microprofile.config.spi.Converter;
@@ -36,32 +36,32 @@
* @author Romain Manni-Bucau
* @author Mark Struberg
*/
-public class DefaultConfigBuilder implements ConfigProvider.ConfigBuilder {
+public class DefaultConfigBuilder extends ConfigBuilder {
protected ClassLoader forClassLoader;
private final List sources = new ArrayList<>();
private final List> converters = new ArrayList<>();
private boolean ignoreDefaultSources = false;
@Override
- public ConfigProvider.ConfigBuilder ignoreDefaultSources() {
+ public ConfigBuilder ignoreDefaultSources() {
this.ignoreDefaultSources = true;
return this;
}
@Override
- public ConfigProvider.ConfigBuilder forClassLoader(final ClassLoader loader) {
+ public ConfigBuilder forClassLoader(final ClassLoader loader) {
this.forClassLoader = loader;
return this;
}
@Override
- public ConfigProvider.ConfigBuilder withSources(final ConfigSource... sources) {
+ public ConfigBuilder withSources(final ConfigSource... sources) {
this.sources.addAll(asList(sources));
return this;
}
@Override
- public ConfigProvider.ConfigBuilder withConverters(Converter>... converters) {
+ public ConfigBuilder withConverters(Converter>... converters) {
this.converters.addAll(asList(converters));
return this;
}
diff --git a/impl/src/main/java/org/apache/geronimo/config/DefaultConfigProvider.java b/impl/src/main/java/org/apache/geronimo/config/DefaultConfigProvider.java
index 03adbff..c53aa7a 100644
--- a/impl/src/main/java/org/apache/geronimo/config/DefaultConfigProvider.java
+++ b/impl/src/main/java/org/apache/geronimo/config/DefaultConfigProvider.java
@@ -22,6 +22,7 @@
import java.util.Map;
import java.util.WeakHashMap;
import io.microprofile.config.Config;
+import io.microprofile.config.internal.ConfigBuilder;
import io.microprofile.config.ConfigProvider;
@@ -75,12 +76,12 @@ void registerConfig(Config config, ClassLoader forClassLoader) {
}
@Override
- public ConfigProvider.ConfigBuilder newConfig() {
+ public ConfigBuilder newConfig() {
return new DefaultConfigBuilder();
}
@Override
- public ConfigProvider.ConfigBuilder registerConfig() {
+ public ConfigBuilder registerConfig() {
return new ManualApplicationConfigBuilder(this);
}
From eda48ff5f3f6aa1647c67ba4221852498ae641fb Mon Sep 17 00:00:00 2001
From: Mark Struberg
Date: Thu, 26 Jan 2017 11:46:40 +0100
Subject: [PATCH 2/2] pick up ConfigBuilder via ServiceLoader
---
api/pom.xml | 6 +++
.../config/internal/ConfigBuilder.java | 33 ++++------------
.../config/internal/ConfigExtension.java | 38 ++++++++++++++++++-
.../javax.enterprise.inject.spi.Extension | 20 ++++++++++
4 files changed, 71 insertions(+), 26 deletions(-)
create mode 100644 api/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
diff --git a/api/pom.xml b/api/pom.xml
index 3127ced..7984ec7 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -46,6 +46,12 @@
1.0
provided
+
+ org.apache.geronimo.specs
+ geronimo-atinject_1.0_spec
+ 1.0
+ provided
+
diff --git a/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java b/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java
index 99009a2..f8c1296 100644
--- a/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java
+++ b/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java
@@ -22,30 +22,13 @@
/**
* Builder for manually creating an instance of a {@code Config}.
+ *
+ * @author Mark Struberg
*/
-public class ConfigBuilder {
- public ConfigBuilder ignoreDefaultSources() {
- //X TODO
- return null;
- }
-
- public ConfigBuilder forClassLoader(ClassLoader loader) {
- //X TODO
- return null;
- }
-
- public ConfigBuilder withSources(ConfigSource... sources) {
- //X TODO
- return null;
- }
-
- public ConfigBuilder withConverters(Converter>[] filters) {
- //X TODO
- return null;
- }
-
- public Config build() {
- //X TODO
- return null;
- }
+public abstract class ConfigBuilder {
+ public abstract ConfigBuilder ignoreDefaultSources();
+ public abstract ConfigBuilder forClassLoader(ClassLoader loader);
+ public abstract ConfigBuilder withSources(ConfigSource... sources);
+ public abstract ConfigBuilder withConverters(Converter>... filters);
+ public abstract Config build();
}
diff --git a/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java b/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java
index be8c8b7..fc7df4e 100644
--- a/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java
+++ b/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java
@@ -16,6 +16,9 @@
*/
package io.microprofile.config.internal;
+import java.util.ServiceLoader;
+import java.util.logging.Logger;
+
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
@@ -61,7 +64,40 @@ public void initConfig(@Observes BeforeBeanDiscovery bbd, BeanManager beanManage
}
private ConfigBuilder newConfigBuilder() {
- return null; //X TODO, needs a concrete class in the API -> ugly as hell
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null) {
+ cl = ConfigBuilder.class.getClassLoader();
+ }
+
+ ConfigBuilder instance = loadConfigBuilder(cl);
+
+ if (instance == null) {
+ throw new IllegalStateException("No ConfigResolver SPI implementation found!");
+ }
+
+ return instance;
+ }
+
+ private static ConfigBuilder loadConfigBuilder(ClassLoader cl) {
+ if (cl == null) {
+ return null;
+ }
+
+ // start from the root CL and go back down to the TCCL
+ ConfigBuilder instance = loadConfigBuilder(cl.getParent());
+
+ if (instance == null) {
+ ServiceLoader sl = ServiceLoader.load(ConfigBuilder.class, cl);
+ for (ConfigBuilder spi : sl) {
+ if (instance != null) {
+ Logger.getLogger(ConfigExtension.class.getName())
+ .warning("Multiple ConfigResolver SPIs found. Ignoring " + spi.getClass().getName());
+ } else {
+ instance = spi;
+ }
+ }
+ }
+ return instance;
}
diff --git a/api/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/api/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..bfb30eb
--- /dev/null
+++ b/api/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1,20 @@
+#
+# 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 current 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.
+#
+
+io.microprofile.config.internal.ConfigExtension
\ No newline at end of file