diff --git a/api/pom.xml b/api/pom.xml index 3a68c25..7984ec7 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -39,5 +39,19 @@ + + + org.apache.geronimo.specs + geronimo-jcdi_1.1_spec + 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/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..f8c1296 --- /dev/null +++ b/api/src/main/java/io/microprofile/config/internal/ConfigBuilder.java @@ -0,0 +1,34 @@ +/* + * 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}. + * + * @author Mark Struberg + */ +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 new file mode 100644 index 0000000..fc7df4e --- /dev/null +++ b/api/src/main/java/io/microprofile/config/internal/ConfigExtension.java @@ -0,0 +1,104 @@ +/* + * 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 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; +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() { + 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/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/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 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); }