content: /*
   * Copyright 1999-2018 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.csp.sentinel.slots.block.flow;
  
  import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
  import com.alibaba.csp.sentinel.config.SentinelConfig;
  import com.alibaba.csp.sentinel.log.RecordLog;
  import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
  import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
  import com.alibaba.csp.sentinel.property.PropertyListener;
  import com.alibaba.csp.sentinel.property.SentinelProperty;
  import com.alibaba.csp.sentinel.slots.block.RuleManager;
  import com.alibaba.csp.sentinel.util.AssertUtil;
  import com.alibaba.csp.sentinel.util.StringUtil;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.Executors;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.TimeUnit;
  
  /**
   * <p>
   * One resources can have multiple rules. And these rules take effects in the following order:
   * <ol>
   * <li>requests from specified caller</li>
   * <li>no specified caller</li>
   * </ol>
   * </p>
   *
   * @author jialiang.linjl
   * @author Eric Zhao
   * @author Weihua
   */
  public class FlowRuleManager {
  
      private static volatile RuleManager<FlowRule> flowRules = new RuleManager<>();
  
      private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
      private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
  
      /** the corePool size of SCHEDULER must be set at 1, so the two task ({@link #startMetricTimerListener()} can run orderly by the SCHEDULER **/
      @SuppressWarnings("PMD.ThreadPoolCreationRule")
      private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
          new NamedThreadFactory("sentinel-metrics-record-task", true));
  
      static {
          currentProperty.addListener(LISTENER);
          startMetricTimerListener();
      }
  
      /**
       * <p> Start the MetricTimerListener
       * <ol>
       *     <li>If the flushInterval more than 0,
       * the timer will run with the flushInterval as the rate </li>.
       *      <li>If the flushInterval less than 0(include) or value is not valid,
       * then means the timer will not be started </li>
       * <ol></p>
       */
      private static void startMetricTimerListener() {
          long flushInterval = SentinelConfig.metricLogFlushIntervalSec();
          if (flushInterval <= 0) {
              RecordLog.info("[FlowRuleManager] The MetricTimerListener isn't started. If you want to start it, "
                      + "please change the value(current: {}) of config({}) more than 0 to start it.", flushInterval,
                  SentinelConfig.METRIC_FLUSH_INTERVAL);
              return;
          }
          SCHEDULER.scheduleAtFixedRate(new MetricTimerListener(), 0, flushInterval, TimeUnit.SECONDS);
      }
  
      /**
       * Listen to the {@link SentinelProperty} for {@link FlowRule}s. The property is the source of {@link FlowRule}s.
       * Flow rules can also be set by {@link #loadRules(List)} directly.
       *
       * @param property the property to listen.
       */
      public static void register2Property(SentinelProperty<List<FlowRule>> property) {
          AssertUtil.notNull(property, "property cannot be null");
          synchronized (LISTENER) {
              RecordLog.info("[FlowRuleManager] Registering new property to flow rule manager");
              currentProperty.removeListener(LISTENER);
              property.addListener(LISTENER);
              currentProperty = property;
          }
      }
  
      /**
       * Get a copy of the rules.
       *
       * @return a new copy of the rules.
       */
      public static List<FlowRule> getRules() {
          return flowRules.getRules();
      }
  
      /**
       * Load {@link FlowRule}s, former rules will be replaced.
       *
       * @param rules new rules to load.
       */
      public static void loadRules(List<FlowRule> rules) {
          currentProperty.updateValue(rules);
      }
  
      static List<FlowRule> getFlowRules(String resource) {
          return flowRules.getRules(resource);
      }
  
      public static boolean hasConfig(String resource) {
          return flowRules.hasConfig(resource);
      }
  
      public static boolean isOtherOrigin(String origin, String resourceName) {
          if (StringUtil.isEmpty(origin)) {
              return false;
          }
  
          List<FlowRule> rules = flowRules.getRules(resourceName);
  
          if (rules != null) {
              for (FlowRule rule : rules) {
                  if (origin.equals(rule.getLimitApp())) {
                      return false;
                  }
              }
          }
  
          return true;
      }
  
      private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {
  
          @Override
          public synchronized void configUpdate(List<FlowRule> value) {
              Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
              flowRules.updateRules(rules);
              RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
          }
  
          @Override
          public synchronized void configLoad(List<FlowRule> conf) {
              Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
              flowRules.updateRules(rules);
              RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);
          }
      }
  
  }
  