content: package com.lememo.nacosconfigadapter;
  
  import com.alibaba.nacos.api.NacosFactory;
  import com.alibaba.nacos.api.config.ConfigService;
  import com.alibaba.nacos.api.config.listener.Listener;
  import com.alibaba.nacos.api.exception.NacosException;
  
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.util.*;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.Executor;
  
  /**
   * @author houyi
   **/
  public abstract class AbstractNacosConfigService implements NacosConfigService {
  
      /**
       * key和unit的映射关系
       */
      private Map<String, ConfigUnit> unitMap;
  
      /**
       * key和配置对象的映射关系
       */
      private Map<String, NacosConfig> configMap;
  
      /**
       * Nacos 配置项的 dataId
       */
      private String dataId;
  
      /**
       * Nacos 配置项的 group
       */
      private String group;
  
      /**
       * Nacos 配置服务 api
       */
      private ConfigService configService;
  
      public AbstractNacosConfigService(String serverAddress, String dataId, String group) {
          this.unitMap = new ConcurrentHashMap<>();
          this.configMap = new ConcurrentHashMap<>();
          // 刷新 unit
          this.refreshUnit();
          this.dataId = dataId;
          this.group = group;
          Properties properties = new Properties();
          properties.put("serverAddr", serverAddress);
          try {
              this.configService = NacosFactory.createConfigService(properties);
              // 增加监听器
              Listener listener = new ConfigListener();
              this.configService.addListener(dataId, group, listener);
          } catch (NacosException e) {
              e.printStackTrace();
          }
      }
  
      /**
       * 获取 ConfigUnit 的对象列表
       *
       * @return 对象列表
       */
      public abstract List<ConfigUnit> getConfigUnitList();
  
      /**
       * 接收到最新的配置信息时，是否对 ConfigUnit 进行刷新
       *
       * @return true：刷新 false：不刷新
       */
      public abstract boolean refreshUnitOnReceiveConfig();
  
      @Override
      public NacosConfig getConfig(String key) {
          return configMap.get(key);
      }
  
      @Override
      public Object getConfigValue(String key) {
          NacosConfig config = getConfig(key);
          return config != null ? config.getValue() : null;
      }
  
      @Override
      public List<NacosConfig> getConfigList(int grade) {
          if (configMap.isEmpty()) {
              return new ArrayList<>();
          }
          List<NacosConfig> configList = new ArrayList<>(configMap.size());
          for (Map.Entry<String, NacosConfig> entry : configMap.entrySet()) {
              NacosConfig config = entry.getValue();
              // 如果当前config的等级小于或等于指定的等级
              if (!config.higherGrade(grade)) {
                  configList.add(config);
              }
          }
          // 根据 grade 进行升序排序
          configList.sort(new Comparator<NacosConfig>() {
              @Override
              public int compare(NacosConfig o1, NacosConfig o2) {
                  return o1.getUnit().getGrade() - o2.getUnit().getGrade();
              }
          });
          return configList;
      }
  
      @Override
      public boolean updateConfig(String key, Object value, int grade) {
          if (key == null || key.trim().length() == 0) {
              throw new IllegalArgumentException("key can not be null");
          }
          if (value == null) {
              throw new IllegalArgumentException("value can not be null");
          }
          // 获取配置项
          NacosConfig config = getConfig(key);
          if (config == null) {
              throw new IllegalArgumentException("config does not exists with key=" + key);
          }
          // 如果配置项的 unit 不合法
          if (!config.validUnit()) {
              throw new IllegalArgumentException("config unit invalid with key=" + key);
          }
          // 如果当前配置项的等级更高，则不允许修改
          if (config.higherGrade(grade)) {
              throw new IllegalArgumentException("you can't update config with key=" + key + ", cause this config has a higher grade");
          }
          // 如果当前配置项的等级更高，则不允许修改
          if (config.readOnly()) {
              throw new IllegalArgumentException("you can't update config with key=" + key + ", cause this config is read only");
          }
  
          // 更新配置项前的一些操作，例如：
          // 1.记录更新操作的日志，更新者，更新时间等等，方便回溯
          // 2.甚至可以把操作前的内容记录下来，方便一键回滚到上一次的值
  
          // 更新配置项的值
          config.setValue(value);
          // 检查当前配置项的值类型是否合法
          if (!config.validValueType()) {
              throw new IllegalArgumentException("value type invalid with key=" + key + ", should be:" + config.getUnit().getType());
          }
          configMap.put(key, config);
  
          // 将更新后的配置内容推送到 nacos 服务端
          return publishConfig();
      }
  
      /**
       * 刷新key 和 unit 的对应关系
       */
      private void refreshUnit() {
          List<ConfigUnit> factorList = getConfigUnitList();
          if (factorList == null || factorList.isEmpty()) {
              return;
          }
          unitMap.clear();
          for (ConfigUnit unit : factorList) {
              unitMap.put(unit.getKey(), unit);
          }
      }
  
      /**
       * 重新处理配置信息
       * 将配置信息加载到 keyConfig 中去
       *
       * @param content 配置信息
       */
      private void reloadConfigs(String content) {
          Properties properties = new Properties();
          try {
              properties.load(new ByteArrayInputStream(content.getBytes()));
          } catch (IOException e) {
              e.printStackTrace();
          }
          // 清空内存中原来的值
          configMap.clear();
          // 遍历 properties
          for (Object k : properties.keySet()) {
              String key = k.toString();
              // 获取该 key 对应的 unit
              ConfigUnit unit = unitMap.get(key);
              if (unit == null) {
                  continue;
              }
              // 将配置项的值封装成 config 对象
              String val = properties.getProperty(key);
              Object value = parseValue(val, unit.getType());
              NacosConfig config = new NacosConfig(key, value, unit);
              configMap.put(key, config);
          }
      }
  
      private Object parseValue(String val, ConfigType type) {
          Object value;
          try {
              switch (type) {
                  case INT: {
                      value = Integer.parseInt(val);
                  }
                  break;
                  case DOUBLE: {
                      value = Double.parseDouble(val);
                  }
                  break;
                  case FLOAT: {
                      value = Float.parseFloat(val);
                  }
                  break;
                  case LONG: {
                      value = Long.parseLong(val);
                  }
                  break;
                  case BOOLEAN: {
                      value = Boolean.parseBoolean(val);
                  }
                  break;
                  case STRING: {
                      value = val;
                  }
                  break;
                  default: {
                      value = val;
                  }
                  break;
              }
          } catch (Exception e) {
              value = val;
          }
          return value;
      }
  
  
      private boolean publishConfig() {
          StringBuilder sb = new StringBuilder();
          int size = configMap.size();
          int index = 1;
          for (Map.Entry<String, NacosConfig> entry : configMap.entrySet()) {
              sb.append(entry.getKey())
                      .append("=")
                      .append(entry.getValue().getValue());
              if (index++ < size) {
                  sb.append("\n");
              }
          }
          String newContent = sb.toString();
          try {
              configService.publishConfig(dataId, group, newContent);
          } catch (NacosException e) {
              e.printStackTrace();
              return false;
          }
          return true;
      }
  
      private class ConfigListener implements Listener {
  
          @Override
          public Executor getExecutor() {
              return null;
          }
  
          @Override
          public void receiveConfigInfo(String content) {
              // 如果需要每次对 unit 进行刷新
              if (refreshUnitOnReceiveConfig()) {
                  refreshUnit();
              }
              reloadConfigs(content);
          }
      }
  
  
  }
  