package com.alibaba.csp.sentinel.datasource.apollo;

import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.log.RecordLog;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;

/**
 * A read-only {@code DataSource} with <a href="http://github.com/ctripcorp/apollo">Apollo</a> as its configuration
 * source.
 * <br />
 * When the rule is changed in Apollo, it will take effect in real time.
 *
 * @author Jason Song
 * @author Haojun Ren
 */
public class ApolloDataSource<T> extends AbstractDataSource<String, T> {

    private final Config config;
    private final String flowRulesKey;
    private final String defaultFlowRuleValue;

    private ConfigChangeListener configChangeListener;

    /**
     * Constructs the Apollo data source
     *
     * @param namespaceName        the namespace name in Apollo, should not be null or empty
     * @param flowRulesKey         the flow rules key in the namespace, should not be null or empty
     * @param defaultFlowRuleValue the default flow rules value when the flow rules key is not found or any error
     *                             occurred
     * @param parser               the parser to transform string configuration to actual flow rules
     */
    public ApolloDataSource(String namespaceName, String flowRulesKey, String defaultFlowRuleValue,
                            Converter<String, T> parser) {
        super(parser);

        Preconditions.checkArgument(!Strings.isNullOrEmpty(namespaceName), "Namespace name could not be null or empty");
        Preconditions.checkArgument(!Strings.isNullOrEmpty(flowRulesKey), "FlowRuleKey could not be null or empty!");

        this.flowRulesKey = flowRulesKey;
        this.defaultFlowRuleValue = defaultFlowRuleValue;

        this.config = ConfigService.getConfig(namespaceName);

        initialize();

        RecordLog.info(String.format("Initialized rule for namespace: %s, flow rules key: %s",
            namespaceName, flowRulesKey));
    }

    private void initialize() {
        initializeConfigChangeListener();
        loadAndUpdateRules();
    }

    private void loadAndUpdateRules() {
        try {
            T newValue = loadConfig();
            if (newValue == null) {
                RecordLog.warn("[ApolloDataSource] WARN: rule config is null, you may have to check your data source");
            }
            getProperty().updateValue(newValue);
        } catch (Throwable ex) {
            RecordLog.warn("[ApolloDataSource] Error when loading rule config", ex);
        }
    }

    private void initializeConfigChangeListener() {
        configChangeListener = new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                ConfigChange change = changeEvent.getChange(flowRulesKey);
                //change is never null because the listener will only notify for this key
                if (change != null) {
                    RecordLog.info("[ApolloDataSource] Received config changes: " + change.toString());
                }
                loadAndUpdateRules();
            }
        };
        config.addChangeListener(configChangeListener, Sets.newHashSet(flowRulesKey));
    }

    @Override
    public String readSource() throws Exception {
        return config.getProperty(flowRulesKey, defaultFlowRuleValue);
    }

    @Override
    public void close() throws Exception {
        config.removeChangeListener(configChangeListener);
    }
}

@@ -992,25 +992,20 @@
  String 
-flowR
+r
 ule
-s
 Key;%0A   
@@ -1025,36 +1025,32 @@
 l String default
-Flow
 RuleValue;%0A%0A    
@@ -1264,25 +1264,20 @@
  @param 
-flowR
+r
 ule
-s
 Key     
@@ -1280,30 +1280,29 @@
         
+  
+   
 the 
-flow 
 rule
-s
  key in 
@@ -1357,36 +1357,32 @@
 * @param default
-Flow
 RuleValue the de
@@ -1375,16 +1375,20 @@
 leValue 
+    
 the defa
@@ -1391,26 +1391,20 @@
 default 
-flow 
 rule
-s
  value w
@@ -1411,28 +1411,21 @@
 hen the 
-flow 
 rule
-s k
+K
 ey is no
@@ -1655,25 +1655,20 @@
  String 
-flowR
+r
 ule
-s
 Key, Str
@@ -1670,36 +1670,32 @@
 , String default
-Flow
 RuleValue,%0A     
@@ -1956,28 +1956,19 @@
 pty(
-flowR
+r
 ule
-s
 Key), %22
-Flow
 Rule
@@ -2020,32 +2020,22 @@
 his.
-flowR
+r
 ule
-s
 Key = 
-flowR
+r
 ule
-s
 Key;
@@ -2051,28 +2051,24 @@
 this.default
-Flow
 RuleValue = 
@@ -2066,36 +2066,32 @@
 eValue = default
-Flow
 RuleValue;%0A%0A    
@@ -2245,27 +2245,21 @@
 ace: %25s,
- flow
  rule
-s
  key: %25s
@@ -2260,28 +2260,16 @@
 ey: %25s%22,
-%0A           
  namespa
@@ -2276,25 +2276,20 @@
 ceName, 
-flowR
+r
 ule
-s
 Key));%0A 
@@ -3112,25 +3112,20 @@
 tChange(
-flowR
+r
 ule
-s
 Key);%0A  
@@ -3506,25 +3506,20 @@
 HashSet(
-flowR
+r
 ule
-s
 Key));%0A 
@@ -3626,17 +3626,12 @@
 rty(
-flowR
+r
 ule
-s
 Key,
@@ -3642,12 +3642,8 @@
 ault
-Flow
 Rule

Improve field naming in ApolloDataSource (#593)