All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.config.MpConfigProviderResolver Maven / Gradle / Ivy

There is a newer version: 4.1.1
Show newest version
/*
 * Copyright (c) 2019, 2020 Oracle and/or its affiliates.
 *
 * Licensed 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.helidon.config;

import java.time.Instant;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import io.helidon.common.GenericType;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.eclipse.microprofile.config.spi.ConfigSource;

/**
 * Integration with microprofile config.
 * This class is an implementation of a java service obtained through ServiceLoader.
 */
public class MpConfigProviderResolver extends ConfigProviderResolver {
    private static final Map CONFIGS = new IdentityHashMap<>();
    private static final ReadWriteLock RW_LOCK = new ReentrantReadWriteLock();
    // specific for native image - we want to replace config provided during build with runtime configuration
    private static final List BUILD_CONFIG = new LinkedList<>();

    @Override
    public Config getConfig() {
        return getConfig(Thread.currentThread().getContextClassLoader());
    }

    @Override
    public Config getConfig(ClassLoader loader) {
        if (null == loader) {
            loader = ClassLoader.getSystemClassLoader();
        }
        Lock lock = RW_LOCK.readLock();
        try {
            lock.lock();
            Config config = CONFIGS.get(loader);

            if (null == config) {
                lock.unlock();
                lock = RW_LOCK.writeLock();
                lock.lock();
                Config c = buildConfig(loader);
                return doRegisterConfig(c, loader);
            } else {
                return config;
            }
        } finally {
            lock.unlock();
        }
    }

    private Config buildConfig(ClassLoader loader) {
        MpConfigBuilder builder = getBuilder();

        Optional meta = MetaConfig.metaConfig();

        meta.ifPresent(builder::metaConfig);

        builder.forClassLoader(loader);

        if (meta.isEmpty()) {
            builder.addDefaultSources();
        }

        return builder.addDiscoveredSources()
                .addDiscoveredConverters()
                .build();
    }

    @Override
    public MpConfigBuilder getBuilder() {
        return new MpConfigBuilder();
    }

    @Override
    public void registerConfig(Config config, ClassLoader classLoader) {
        Lock lock = RW_LOCK.writeLock();
        try {
            lock.lock();
            doRegisterConfig(config, classLoader);
        } finally {
            lock.unlock();
        }
    }

    /**
     * This method should only be called when running within native image, as soon as runtime configuration
     * is available.
     *
     * @param config configuration to use
     */
    public static void runtimeStart(io.helidon.config.Config config) {
        if (BUILD_CONFIG.isEmpty()) {
            return;
        }
        BUILD_CONFIG.forEach(it -> it.set(config));
        BUILD_CONFIG.clear();
    }

    /**
     * This method should only be called when generating native image, as late in the process as possible.
     */
    public static void buildTimeEnd() {
        Lock lock = RW_LOCK.writeLock();
        try {
            lock.lock();
            CONFIGS.forEach((key, value) -> BUILD_CONFIG.add(value));
            CONFIGS.clear();
        } finally {
            lock.unlock();
        }
    }

    private ConfigDelegate doRegisterConfig(Config config, ClassLoader classLoader) {
        ConfigDelegate currentConfig = CONFIGS.remove(classLoader);

        if (config instanceof ConfigDelegate) {
            config = ((ConfigDelegate) config).delegate();
        }

        io.helidon.config.Config helidonConfig = (io.helidon.config.Config) config;

        if (null != currentConfig) {
            currentConfig.set(helidonConfig);
        }

        ConfigDelegate newConfig = new ConfigDelegate(helidonConfig);
        CONFIGS.put(classLoader, newConfig);

        return newConfig;
    }

    @Override
    public void releaseConfig(Config config) {
        // first attempt to find it
        Lock lock = RW_LOCK.readLock();
        AtomicReference cl = new AtomicReference<>();

        try {
            lock.lock();
            for (Map.Entry entry : CONFIGS.entrySet()) {
                Config configFromRef = entry.getValue().delegate();
                if (config == configFromRef) {
                    cl.set(entry.getKey());
                    break;
                }
            }
        } finally {
            lock.unlock();
        }

        // if found, remove it
        if (cl.get() != null) {
            lock = RW_LOCK.writeLock();
            try {
                lock.lock();
                CONFIGS.remove(cl.get());
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * A delegate used to allow replacing configuration at runtime for components
     * that hold a reference to configuration obtained at build time.
     */
    public static final class ConfigDelegate implements io.helidon.config.Config, Config {
        private AtomicReference delegate;

        private ConfigDelegate(io.helidon.config.Config delegate) {
            this.delegate = new AtomicReference<>(delegate);
        }

        private void set(io.helidon.config.Config newDelegate) {
            this.delegate.set(newDelegate);
        }

        private io.helidon.config.Config getCurrent() {
            return delegate.get().context().last();
        }

        @Override
        public Instant timestamp() {
            return getCurrent().timestamp();
        }

        @Override
        public Key key() {
            return getCurrent().key();
        }

        @Override
        public io.helidon.config.Config get(Key key) {
            return getCurrent().get(key);
        }

        @Override
        public io.helidon.config.Config detach() {
            return getCurrent().detach();
        }

        @Override
        public Type type() {
            return getCurrent().type();
        }

        @Override
        public boolean hasValue() {
            return getCurrent().hasValue();
        }

        @Override
        public Stream traverse(Predicate predicate) {
            return getCurrent().traverse();
        }

        @Override
        public  T convert(Class type, String value) throws ConfigMappingException {
            return getCurrent().convert(type, value);
        }

        @Override
        public  ConfigValue as(GenericType genericType) {
            return getCurrent().as(genericType);
        }

        @Override
        public  ConfigValue as(Class type) {
            return getCurrent().as(type);
        }

        @Override
        public  ConfigValue as(Function mapper) {
            return getCurrent().as(mapper);
        }

        @Override
        public  ConfigValue> asList(Class type) throws ConfigMappingException {
            return getCurrent().asList(type);
        }

        @Override
        public  ConfigValue> asList(Function mapper) throws ConfigMappingException {
            return getCurrent().asList(mapper);
        }

        @Override
        public ConfigValue> asNodeList() throws ConfigMappingException {
            return getCurrent().asNodeList();
        }

        @Override
        public ConfigValue> asMap() throws MissingValueException {
            return getCurrent().asMap();
        }

        @Override
        public  T getValue(String propertyName, Class propertyType) {
            return ((Config) getCurrent()).getValue(propertyName, propertyType);
        }

        @Override
        public  Optional getOptionalValue(String propertyName, Class propertyType) {
            return ((Config) getCurrent()).getOptionalValue(propertyName, propertyType);
        }

        @Override
        public Iterable getPropertyNames() {
            return ((Config) getCurrent()).getPropertyNames();
        }

        @Override
        public Iterable getConfigSources() {
            return ((Config) getCurrent()).getConfigSources();
        }

        /**
         * Get the underlying instance of this delegate pattern.
         *
         * @return the instance backing this config delegate
         */
        public Config delegate() {
            return (Config) getCurrent();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy