org.apache.servicecomb.config.ConfigUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.servicecomb.config.ConfigUtil.java

Source

/*
 * 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.servicecomb.config;

import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_CSE_PREFIX;
import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_KEY_SPLITER;
import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_SERVICECOMB_PREFIX;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.EnvironmentConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.servicecomb.config.archaius.scheduler.NeverStartPollingScheduler;
import org.apache.servicecomb.config.archaius.sources.ConfigModel;
import org.apache.servicecomb.config.archaius.sources.MicroserviceConfigLoader;
import org.apache.servicecomb.config.archaius.sources.MicroserviceConfigurationSource;
import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource;
import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netflix.config.ConcurrentCompositeConfiguration;
import com.netflix.config.ConcurrentMapConfiguration;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicConfiguration;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicWatchedConfiguration;
import com.netflix.config.WatchedUpdateListener;
import com.netflix.config.WatchedUpdateResult;

public final class ConfigUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);

    private static final String MICROSERVICE_CONFIG_LOADER_KEY = "cse-microservice-config-loader";

    private static Map<String, Object> localConfig = new HashMap<>();

    /**
     * <p>The configurations not read by ServiceComb.</p>
     * <p>
     * For example, this map can store the configurations read by SpringBoot from application.properties,
     * If users write the configurations of ServiceComb into application.yml instead of microservice.yaml,
     * this can help {@link ConfigUtil} load config correctly.
     * </p>
     */
    private static final Map<String, Map<String, Object>> EXTRA_CONFIG_MAP = new LinkedHashMap<>();

    private ConfigUtil() {
    }

    public static void setConfigs(Map<String, Object> config) {
        localConfig = config;
    }

    public static void addConfig(String key, Object value) {
        localConfig.put(key, value);
    }

    public static Object getProperty(String key) {
        Object config = DynamicPropertyFactory.getBackingConfigurationSource();
        return getProperty(config, key);
    }

    public static Object getProperty(Object config, String key) {
        if (null != config && Configuration.class.isInstance(config)) {
            Configuration configuration = (Configuration) config;
            return configuration.getProperty(key);
        }
        return null;
    }

    private static void setMicroserviceConfigLoader(Configuration config, MicroserviceConfigLoader loader) {
        config.setProperty(MICROSERVICE_CONFIG_LOADER_KEY, loader);
    }

    public static MicroserviceConfigLoader getMicroserviceConfigLoader() {
        return (MicroserviceConfigLoader) getProperty(MICROSERVICE_CONFIG_LOADER_KEY);
    }

    public static MicroserviceConfigLoader getMicroserviceConfigLoader(Configuration config) {
        return (MicroserviceConfigLoader) getProperty(config, MICROSERVICE_CONFIG_LOADER_KEY);
    }

    public static ConcurrentCompositeConfiguration createLocalConfig() {
        MicroserviceConfigLoader loader = new MicroserviceConfigLoader();
        loader.loadAndSort();
        if (localConfig.size() > 0) {
            ConfigModel model = new ConfigModel();
            model.setConfig(localConfig);
            loader.getConfigModels().add(model);
        }

        LOGGER.info("create local config:");
        for (ConfigModel configModel : loader.getConfigModels()) {
            LOGGER.info(" {}.", configModel.getUrl());
        }

        ConcurrentCompositeConfiguration config = ConfigUtil.createLocalConfig(loader.getConfigModels());
        ConfigUtil.setMicroserviceConfigLoader(config, loader);
        return config;
    }

    public static ConcurrentCompositeConfiguration createLocalConfig(List<ConfigModel> configModelList) {
        ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();

        duplicateCseConfigToServicecomb(config, new ConcurrentMapConfiguration(new SystemConfiguration()),
                "configFromSystem");
        duplicateCseConfigToServicecomb(config,
                convertEnvVariable(new ConcurrentMapConfiguration(new EnvironmentConfiguration())),
                "configFromEnvironment");
        // If there is extra configurations, add it into config.
        EXTRA_CONFIG_MAP.entrySet().stream().filter(mapEntry -> !mapEntry.getValue().isEmpty())
                .forEachOrdered(configMapEntry -> duplicateCseConfigToServicecomb(config,
                        new ConcurrentMapConfiguration(configMapEntry.getValue()), configMapEntry.getKey()));
        // we have already copy the cse config to the serviceComb config when we load the config from local yaml files
        // hence, we do not need duplicate copy it.
        config.addConfiguration(new DynamicConfiguration(new MicroserviceConfigurationSource(configModelList),
                new NeverStartPollingScheduler()), "configFromYamlFile");
        duplicateCseConfigToServicecombAtFront(config,
                new ConcurrentMapConfiguration(ConfigMapping.getConvertedMap(config)), "configFromMapping");
        return config;
    }

    public static AbstractConfiguration convertEnvVariable(AbstractConfiguration source) {
        Iterator<String> keys = source.getKeys();
        while (keys.hasNext()) {
            String key = keys.next();
            String[] separatedKey = key.split(CONFIG_KEY_SPLITER);
            if (separatedKey.length == 1) {
                continue;
            }
            String newKey = String.join(".", separatedKey);
            source.addProperty(newKey, source.getProperty(key));
        }
        return source;
    }

    //inject a copy of servicecomb.xxx for cse.xxx
    private static void duplicateCseConfigToServicecomb(AbstractConfiguration source) {
        Iterator<String> keys = source.getKeys();
        while (keys.hasNext()) {
            String key = keys.next();
            if (!key.startsWith(CONFIG_CSE_PREFIX)) {
                continue;
            }

            String servicecombKey = CONFIG_SERVICECOMB_PREFIX + key.substring(key.indexOf(".") + 1);
            if (!source.containsKey(servicecombKey)) {
                source.addProperty(servicecombKey, source.getProperty(key));
            } else {
                LOGGER.warn("Key {} with an ambiguous item {} exists, it's recommended to use only one of them.",
                        key, servicecombKey);
            }
        }
    }

    private static void duplicateCseConfigToServicecomb(ConcurrentCompositeConfiguration compositeConfiguration,
            AbstractConfiguration source, String sourceName) {
        duplicateCseConfigToServicecomb(source);

        compositeConfiguration.addConfiguration(source, sourceName);
    }

    private static void duplicateCseConfigToServicecombAtFront(
            ConcurrentCompositeConfiguration compositeConfiguration, AbstractConfiguration source,
            String sourceName) {
        duplicateCseConfigToServicecomb(source);

        compositeConfiguration.addConfigurationAtFront(source, sourceName);
    }

    private static ConfigCenterConfigurationSource createConfigCenterConfigurationSource(
            Configuration localConfiguration) {
        ConfigCenterConfigurationSource configCenterConfigurationSource = SPIServiceUtils
                .getTargetService(ConfigCenterConfigurationSource.class);
        if (null == configCenterConfigurationSource) {
            LOGGER.info("config center SPI service can not find, skip to load configuration from config center");
            return null;
        }

        if (!configCenterConfigurationSource.isValidSource(localConfiguration)) {
            LOGGER.info("Config Source serverUri is not correctly configured.");
            return null;
        }
        return configCenterConfigurationSource;
    }

    private static void createDynamicWatchedConfiguration(ConcurrentCompositeConfiguration localConfiguration,
            ConfigCenterConfigurationSource configCenterConfigurationSource) {
        ConcurrentMapConfiguration injectConfig = new ConcurrentMapConfiguration();
        localConfiguration.addConfigurationAtFront(injectConfig, "extraInjectConfig");
        configCenterConfigurationSource.addUpdateListener(new ServiceCombPropertyUpdateListener(injectConfig));

        DynamicWatchedConfiguration configFromConfigCenter = new DynamicWatchedConfiguration(
                configCenterConfigurationSource);
        duplicateCseConfigToServicecomb(configFromConfigCenter);
        localConfiguration.addConfigurationAtFront(configFromConfigCenter, "configCenterConfig");
    }

    public static AbstractConfiguration createDynamicConfig() {
        ConcurrentCompositeConfiguration compositeConfig = ConfigUtil.createLocalConfig();
        ConfigCenterConfigurationSource configCenterConfigurationSource = createConfigCenterConfigurationSource(
                compositeConfig);
        if (configCenterConfigurationSource != null) {
            createDynamicWatchedConfiguration(compositeConfig, configCenterConfigurationSource);
        }
        return compositeConfig;
    }

    public static void installDynamicConfig() {
        if (ConfigurationManager.isConfigurationInstalled()) {
            LOGGER.warn("Configuration installed by others, will ignore this configuration.");
            return;
        }

        ConcurrentCompositeConfiguration compositeConfig = ConfigUtil.createLocalConfig();
        ConfigCenterConfigurationSource configCenterConfigurationSource = createConfigCenterConfigurationSource(
                compositeConfig);
        if (configCenterConfigurationSource != null) {
            createDynamicWatchedConfiguration(compositeConfig, configCenterConfigurationSource);
        }

        ConfigurationManager.install(compositeConfig);

        if (configCenterConfigurationSource != null) {
            configCenterConfigurationSource.init(compositeConfig);
        }
    }

    public static void destroyConfigCenterConfigurationSource() {
        SPIServiceUtils.getAllService(ConfigCenterConfigurationSource.class).forEach(source -> {
            try {
                source.destroy();
            } catch (Throwable e) {
                LOGGER.error("Failed to destroy {}", source.getClass().getName());
            }
        });
    }

    public static void addExtraConfig(String extraConfigName, Map<String, Object> extraConfig) {
        EXTRA_CONFIG_MAP.put(extraConfigName, extraConfig);
    }

    public static void clearExtraConfig() {
        EXTRA_CONFIG_MAP.clear();
    }

    private static class ServiceCombPropertyUpdateListener implements WatchedUpdateListener {

        private final ConcurrentMapConfiguration injectConfig;

        ServiceCombPropertyUpdateListener(ConcurrentMapConfiguration injectConfig) {
            this.injectConfig = injectConfig;
        }

        @Override
        public void updateConfiguration(WatchedUpdateResult watchedUpdateResult) {
            Map<String, Object> adds = watchedUpdateResult.getAdded();
            if (adds != null) {
                for (String add : adds.keySet()) {
                    if (add.startsWith(CONFIG_CSE_PREFIX)) {
                        String key = CONFIG_SERVICECOMB_PREFIX + add.substring(add.indexOf(".") + 1);
                        injectConfig.addProperty(key, adds.get(add));
                    }
                }
            }

            Map<String, Object> deletes = watchedUpdateResult.getDeleted();
            if (deletes != null) {
                for (String delete : deletes.keySet()) {
                    if (delete.startsWith(CONFIG_CSE_PREFIX)) {
                        injectConfig.clearProperty(
                                CONFIG_SERVICECOMB_PREFIX + delete.substring(delete.indexOf(".") + 1));
                    }
                }
            }

            Map<String, Object> changes = watchedUpdateResult.getChanged();
            if (changes != null) {
                for (String change : changes.keySet()) {
                    if (change.startsWith(CONFIG_CSE_PREFIX)) {
                        String key = CONFIG_SERVICECOMB_PREFIX + change.substring(change.indexOf(".") + 1);
                        injectConfig.setProperty(key, changes.get(change));
                    }
                }
            }
        }
    }
}