content: /*
   * Copyright 1999-2021 Alibaba Group Holding Ltd.
   *
   * 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 com.alibaba.nacos.plugin.environment;
  
  import com.alibaba.nacos.common.spi.NacosServiceLoader;
  import com.alibaba.nacos.common.utils.StringUtils;
  import com.alibaba.nacos.plugin.environment.spi.CustomEnvironmentPluginService;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.Objects;
  import java.util.HashSet;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Set;
  import java.util.Collection;
  import java.util.Comparator;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.stream.Collectors;
  
  /**
   * CustomEnvironment Plugin Management.
   *
   * @author : huangtianhui
   */
  public class CustomEnvironmentPluginManager {
  
      private static final Logger LOGGER = LoggerFactory.getLogger(CustomEnvironmentPluginManager.class);
  
      private static final List<CustomEnvironmentPluginService> SERVICE_LIST = new LinkedList<>();
  
      private static final CustomEnvironmentPluginManager INSTANCE = new CustomEnvironmentPluginManager();
  
      public CustomEnvironmentPluginManager() {
          loadInitial();
      }
  
      private void loadInitial() {
          Collection<CustomEnvironmentPluginService> customEnvironmentPluginServices = NacosServiceLoader.load(
                  CustomEnvironmentPluginService.class);
          for (CustomEnvironmentPluginService customEnvironmentPluginService : customEnvironmentPluginServices) {
              if (StringUtils.isBlank(customEnvironmentPluginService.pluginName())) {
                  LOGGER.warn("[customEnvironmentPluginService] Load customEnvironmentPluginService({}) customEnvironmentPluginName(null/empty) fail."
                          + " Please Add customEnvironmentPluginName to resolve.", customEnvironmentPluginService.getClass());
                  continue;
              }
              LOGGER.info("[CustomEnvironmentPluginManager] Load customEnvironmentPluginService({}) customEnvironmentPluginName({}) successfully.",
                      customEnvironmentPluginService.getClass(), customEnvironmentPluginService.pluginName());
          }
          SERVICE_LIST.addAll(customEnvironmentPluginServices.stream()
                  .sorted(Comparator.comparingInt(CustomEnvironmentPluginService::order))
                  .collect(Collectors.toList()));
      }
  
      public static CustomEnvironmentPluginManager getInstance() {
          return INSTANCE;
      }
  
      public Set<String> getPropertyKeys() {
          Set<String> keys = new HashSet<>();
          for (CustomEnvironmentPluginService customEnvironmentPluginService : SERVICE_LIST) {
              keys.addAll(customEnvironmentPluginService.propertyKey());
          }
          return keys;
      }
  
      public Map<String, Object> getCustomValues(Map<String, Object> sourceProperty) {
          Map<String, Object> customValuesMap = new HashMap<>(1);
          for (CustomEnvironmentPluginService customEnvironmentPluginService : SERVICE_LIST) {
              Set<String> keys = customEnvironmentPluginService.propertyKey();
              Map<String, Object> propertyMap = new HashMap<>(keys.size());
              for (String key : keys) {
                  propertyMap.put(key, sourceProperty.get(key));
              }
              Map<String, Object> targetPropertyMap = customEnvironmentPluginService.customValue(propertyMap);
              //Only the current plugin key is allowed
              Set<String> targetKeys = new HashSet<>(targetPropertyMap.keySet());
              targetKeys.removeAll(keys);
              for (String key : targetKeys) {
                  targetPropertyMap.remove(key);
              }
              customValuesMap.putAll(targetPropertyMap);
          }
          // [issue 13367] fix ConcurrentModificationException
          customValuesMap.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
          return customValuesMap;
      }
  
      /**
       * Injection realization.
       *
       * @param customEnvironmentPluginService customEnvironmentPluginService implementation
       */
      public static synchronized void join(CustomEnvironmentPluginService customEnvironmentPluginService) {
          if (Objects.isNull(customEnvironmentPluginService)) {
              return;
          }
          SERVICE_LIST.add(customEnvironmentPluginService);
          LOGGER.info("[CustomEnvironmentPluginService] join successfully.");
      }
  }
  