diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/PluginData.java b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/PluginData.java
index a93e39e3f6b9..99f83720c550 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/PluginData.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/PluginData.java
@@ -180,24 +180,14 @@ public int hashCode() {
return Objects.hash(getId(), getName(), config, role, getEnabled(), getSort(), pluginJar, getNamespaceId());
}
- public static final class Builder {
-
- private String id;
-
- private String name;
+ public static final class Builder extends BaseData {
private String config;
private String role;
- private Boolean enabled;
-
- private Integer sort;
-
private String pluginJar;
- private String namespaceId;
-
/**
* no args constructor.
*/
@@ -220,7 +210,7 @@ public static Builder builder() {
* @return this
*/
public Builder id(final String id) {
- this.id = id;
+ setId(id);
return this;
}
@@ -231,7 +221,7 @@ public Builder id(final String id) {
* @return this
*/
public Builder name(final String name) {
- this.name = name;
+ setName(name);
return this;
}
@@ -264,7 +254,7 @@ public Builder role(final String role) {
* @return this
*/
public Builder enabled(final Boolean enabled) {
- this.enabled = enabled;
+ setEnabled(enabled);
return this;
}
@@ -275,7 +265,7 @@ public Builder enabled(final Boolean enabled) {
* @return this
*/
public Builder sort(final Integer sort) {
- this.sort = sort;
+ setSort(sort);
return this;
}
@@ -297,7 +287,7 @@ public Builder pluginJar(final String pluginJar) {
* @return this
*/
public Builder namespaceId(final String namespaceId) {
- this.namespaceId = namespaceId;
+ setNamespaceId(namespaceId);
return this;
}
@@ -308,14 +298,14 @@ public Builder namespaceId(final String namespaceId) {
*/
public PluginData build() {
PluginData pluginData = new PluginData();
- pluginData.setId(id);
- pluginData.setName(name);
+ pluginData.setId(getId());
+ pluginData.setName(getName());
pluginData.setConfig(config);
pluginData.setRole(role);
- pluginData.setEnabled(enabled);
- pluginData.setSort(sort);
+ pluginData.setEnabled(getEnabled());
+ pluginData.setSort(getSort());
pluginData.setPluginJar(pluginJar);
- pluginData.setNamespaceId(namespaceId);
+ pluginData.setNamespaceId(getNamespaceId());
return pluginData;
}
}
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/RuleData.java b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/RuleData.java
index ec3fef65f616..9a6c0b64d997 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/RuleData.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/RuleData.java
@@ -64,19 +64,19 @@ public RuleData() {
* @param builder builder
*/
private RuleData(final Builder builder) {
- this.setId(builder.id);
- this.setName(builder.name);
+ this.setId(builder.getId());
+ this.setName(builder.getName());
this.pluginName = builder.pluginName;
this.selectorId = builder.selectorId;
this.matchMode = builder.matchMode;
- this.setSort(builder.sort);
- this.setEnabled(builder.enabled);
+ this.setSort(builder.getSort());
+ this.setEnabled(builder.getEnabled());
this.loged = builder.loged;
this.handle = builder.handle;
this.conditionDataList = builder.conditionDataList;
this.beforeConditionDataList = builder.beforeConditionDataList;
this.matchRestful = builder.matchRestful;
- this.setNamespaceId(builder.namespaceId);
+ this.setNamespaceId(builder.getNamespaceId());
}
/**
@@ -298,7 +298,7 @@ public Boolean getMatchRestful() {
public void setMatchRestful(final Boolean matchRestful) {
this.matchRestful = matchRestful;
}
-
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -365,11 +365,7 @@ public String toString() {
/**
* class builder.
*/
- public static final class Builder {
-
- private String id;
-
- private String name;
+ public static final class Builder extends BaseData {
private String pluginName;
@@ -377,10 +373,6 @@ public static final class Builder {
private Integer matchMode;
- private Integer sort;
-
- private Boolean enabled;
-
private Boolean loged;
private String handle;
@@ -388,10 +380,8 @@ public static final class Builder {
private List conditionDataList;
private List beforeConditionDataList;
-
+
private Boolean matchRestful;
-
- private String namespaceId;
/**
* no args constructor.
@@ -415,7 +405,7 @@ public RuleData build() {
* @return this
*/
public Builder id(final String id) {
- this.id = id;
+ setId(id);
return this;
}
@@ -426,7 +416,7 @@ public Builder id(final String id) {
* @return this
*/
public Builder name(final String name) {
- this.name = name;
+ setName(name);
return this;
}
@@ -470,7 +460,7 @@ public Builder matchMode(final Integer matchMode) {
* @return this
*/
public Builder sort(final Integer sort) {
- this.sort = sort;
+ setSort(sort);
return this;
}
@@ -481,7 +471,7 @@ public Builder sort(final Integer sort) {
* @return this
*/
public Builder enabled(final Boolean enabled) {
- this.enabled = enabled;
+ setEnabled(enabled);
return this;
}
@@ -528,7 +518,7 @@ public Builder beforeConditionDataList(final List beforeCondition
this.beforeConditionDataList = beforeConditionDataList;
return this;
}
-
+
/**
* build match restful.
*
@@ -539,7 +529,7 @@ public Builder matchRestful(final Boolean matchRestful) {
this.matchRestful = matchRestful;
return this;
}
-
+
/**
* build namespaceId.
*
@@ -547,7 +537,7 @@ public Builder matchRestful(final Boolean matchRestful) {
* @return this
*/
public Builder namespaceId(final String namespaceId) {
- this.namespaceId = namespaceId;
+ setNamespaceId(namespaceId);
return this;
}
}
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/SelectorData.java b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/SelectorData.java
index c6aec37add82..71750e8afb8e 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/SelectorData.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/SelectorData.java
@@ -71,21 +71,21 @@ public SelectorData() {
* @param builder builder
*/
private SelectorData(final Builder builder) {
- this.setId(builder.id);
+ this.setId(builder.getId());
this.pluginId = builder.pluginId;
this.pluginName = builder.pluginName;
- this.setName(builder.name);
+ this.setName(builder.getName());
this.matchMode = builder.matchMode;
this.type = builder.type;
- this.setSort(builder.sort);
- this.setEnabled(builder.enabled);
+ this.setSort(builder.getSort());
+ this.setEnabled(builder.getEnabled());
this.logged = builder.logged;
this.continued = builder.continued;
this.handle = builder.handle;
this.conditionList = builder.conditionList;
this.matchRestful = builder.matchRestful;
this.beforeConditionList = builder.beforeConditionList;
- this.setNamespaceId(builder.namespaceId);
+ this.setNamespaceId(builder.getNamespaceId());
}
/**
@@ -345,11 +345,7 @@ public String toString() {
/**
* class builder.
*/
- public static final class Builder {
-
- private String id;
-
- private String name;
+ public static final class Builder extends BaseData {
private String pluginId;
@@ -359,10 +355,6 @@ public static final class Builder {
private Integer type;
- private Integer sort;
-
- private Boolean enabled;
-
private Boolean logged;
private Boolean continued;
@@ -375,8 +367,6 @@ public static final class Builder {
private List beforeConditionList;
- private String namespaceId;
-
/**
* no args constructor.
*/
@@ -399,7 +389,7 @@ public SelectorData build() {
* @return this
*/
public Builder id(final String id) {
- this.id = id;
+ setId(id);
return this;
}
@@ -432,7 +422,7 @@ public Builder pluginName(final String pluginName) {
* @return this
*/
public Builder name(final String name) {
- this.name = name;
+ setName(name);
return this;
}
@@ -465,7 +455,7 @@ public Builder type(final Integer type) {
* @return this
*/
public Builder sort(final Integer sort) {
- this.sort = sort;
+ setSort(sort);
return this;
}
@@ -476,7 +466,7 @@ public Builder sort(final Integer sort) {
* @return this
*/
public Builder enabled(final Boolean enabled) {
- this.enabled = enabled;
+ setEnabled(enabled);
return this;
}
@@ -553,7 +543,7 @@ public Builder beforeConditionList(final List beforeConditionList
* @return this
*/
public Builder namespaceId(final String namespaceId) {
- this.namespaceId = namespaceId;
+ setNamespaceId(namespaceId);
return this;
}
}
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
index 46d019790507..139d66349bc6 100644
--- a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
@@ -18,39 +18,22 @@
package org.apache.shenyu.plugin.base;
import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.shenyu.common.config.ShenyuConfig;
import org.apache.shenyu.common.constant.Constants;
-import org.apache.shenyu.common.dto.ConditionData;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
-import org.apache.shenyu.common.enums.MatchModeEnum;
import org.apache.shenyu.common.enums.SelectorTypeEnum;
-import org.apache.shenyu.common.enums.TrieCacheTypeEnum;
-import org.apache.shenyu.common.utils.ListUtil;
-import org.apache.shenyu.common.utils.LogUtils;
import org.apache.shenyu.plugin.api.ShenyuPlugin;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
-import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
-import org.apache.shenyu.plugin.base.cache.BaseDataCache;
-import org.apache.shenyu.plugin.base.cache.MatchDataCache;
-import org.apache.shenyu.plugin.base.condition.strategy.MatchStrategyFactory;
-import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
-import org.apache.shenyu.plugin.base.trie.ShenyuTrieNode;
+import org.apache.shenyu.plugin.base.maker.PluginDataDecisionMaker;
+import org.apache.shenyu.plugin.base.maker.RuleDataDecisionMaker;
+import org.apache.shenyu.plugin.base.maker.SelectorDataDecisionMaker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
-import java.util.stream.Collectors;
/**
* abstract shenyu plugin please extends.
@@ -59,15 +42,11 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
private static final Logger LOG = LoggerFactory.getLogger(AbstractShenyuPlugin.class);
- private static final String URI_CONDITION_TYPE = "uri";
-
- private ShenyuTrie selectorTrie;
-
- private ShenyuTrie ruleTrie;
-
- private ShenyuConfig.SelectorMatchCache selectorMatchConfig;
-
- private ShenyuConfig.RuleMatchCache ruleMatchConfig;
+ private SelectorDataDecisionMaker selectorDataDecisionMaker = new SelectorDataDecisionMaker();
+
+ private RuleDataDecisionMaker ruleDataDecisionMaker = new RuleDataDecisionMaker();
+
+ private PluginDataDecisionMaker pluginDataDecisionMaker = new PluginDataDecisionMaker();
/**
* this is Template Method child has implements your own logic.
@@ -90,133 +69,53 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
*/
@Override
public Mono execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {
- initCacheConfig();
final String pluginName = named();
- PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
- // early exit
- if (Objects.isNull(pluginData) || !pluginData.getEnabled()) {
- return chain.execute(exchange);
- }
final String path = getRawPath(exchange);
- List selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
- if (CollectionUtils.isEmpty(selectors)) {
- return handleSelectorIfNull(pluginName, exchange, chain);
+
+ List pluginDataList = pluginDataDecisionMaker.getData(pluginName);
+ if (CollectionUtils.isEmpty(pluginDataList) || !pluginDataDecisionMaker.shouldContinue(pluginDataList.get(0))) {
+ return pluginDataDecisionMaker.handleEmpty(pluginName, exchange, chain);
}
- SelectorData selectorData = obtainSelectorDataCacheIfEnabled(path);
- // handle Selector
- if (Objects.nonNull(selectorData) && StringUtils.isBlank(selectorData.getId())) {
- return handleSelectorIfNull(pluginName, exchange, chain);
+
+ List selectorDataList = selectorDataDecisionMaker.getData(pluginName);
+ if (CollectionUtils.isEmpty(selectorDataList)) {
+ return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain);
}
+
+ SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange, pluginName, selectorDataList, path, null);
if (Objects.isNull(selectorData)) {
- selectorData = trieMatchSelector(exchange, pluginName, path);
- if (Objects.isNull(selectorData)) {
- selectorData = defaultMatchSelector(exchange, selectors, path);
- if (Objects.isNull(selectorData)) {
- return handleSelectorIfNull(pluginName, exchange, chain);
- }
- }
+ return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain);
}
+
printLog(selectorData, pluginName);
- if (!selectorData.getContinued()) {
- // if continued, not match rules
+ if (!selectorDataDecisionMaker.shouldContinue(selectorData)) {
return doExecute(exchange, chain, selectorData, defaultRuleData(selectorData));
}
- List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
- if (CollectionUtils.isEmpty(rules)) {
- return handleRuleIfNull(pluginName, exchange, chain);
+
+ List ruleDataList = ruleDataDecisionMaker.getData(selectorData.getId());
+ if (CollectionUtils.isEmpty(ruleDataList)) {
+ return ruleDataDecisionMaker.handleEmpty(pluginName, exchange, chain);
}
+
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
- //get last
- RuleData rule = rules.get(rules.size() - 1);
+ RuleData rule = ruleDataList.get(ruleDataList.size() - 1);
printLog(rule, pluginName);
return doExecute(exchange, chain, selectorData, rule);
}
- // lru map as L1 cache,the cache is enabled by default.
- // if the L1 cache fails to hit, using L2 cache based on trie cache.
- // if the L2 cache fails to hit, execute default strategy.
- RuleData ruleData = obtainRuleDataCacheIfEnabled(path);
- if (Objects.nonNull(ruleData) && Objects.isNull(ruleData.getId())) {
- return handleRuleIfNull(pluginName, exchange, chain);
- }
+
+ RuleData ruleData = ruleDataDecisionMaker.matchData(exchange, named(), ruleDataList, path, selectorData);
if (Objects.isNull(ruleData)) {
- // L1 cache not exist data, try to get data through trie cache
- ruleData = trieMatchRule(exchange, selectorData, path);
- // trie cache fails to hit, execute default strategy
- if (Objects.isNull(ruleData)) {
- ruleData = defaultMatchRule(exchange, rules, path);
- if (Objects.isNull(ruleData)) {
- return handleRuleIfNull(pluginName, exchange, chain);
- }
- }
+ return ruleDataDecisionMaker.handleEmpty(pluginName, exchange, chain);
}
+
printLog(ruleData, pluginName);
return doExecute(exchange, chain, selectorData, ruleData);
}
-
+
protected String getRawPath(final ServerWebExchange exchange) {
return exchange.getRequest().getURI().getRawPath();
}
-
- private void initCacheConfig() {
- if (Objects.isNull(selectorMatchConfig) || Objects.isNull(ruleMatchConfig)) {
- ShenyuConfig shenyuConfig = SpringBeanUtils.getInstance().getBean(ShenyuConfig.class);
- selectorMatchConfig = shenyuConfig.getSelectorMatchCache();
- ruleMatchConfig = shenyuConfig.getRuleMatchCache();
- }
- if (Objects.isNull(selectorTrie) || Objects.isNull(ruleTrie)) {
- selectorTrie = SpringBeanUtils.getInstance().getBean(TrieCacheTypeEnum.SELECTOR.getTrieType());
- ruleTrie = SpringBeanUtils.getInstance().getBean(TrieCacheTypeEnum.RULE.getTrieType());
- }
- }
-
- private SelectorData obtainSelectorDataCacheIfEnabled(final String path) {
- return selectorMatchConfig.getCache().getEnabled() ? MatchDataCache.getInstance().obtainSelectorData(named(), path) : null;
- }
-
- private RuleData obtainRuleDataCacheIfEnabled(final String path) {
- return ruleMatchConfig.getCache().getEnabled() ? MatchDataCache.getInstance().obtainRuleData(named(), path) : null;
- }
- private void cacheSelectorData(final String path, final SelectorData selectorData) {
- if (Boolean.FALSE.equals(selectorMatchConfig.getCache().getEnabled()) || Objects.isNull(selectorData)
- || Boolean.TRUE.equals(selectorData.getMatchRestful())) {
- return;
- }
- int initialCapacity = selectorMatchConfig.getCache().getInitialCapacity();
- long maximumSize = selectorMatchConfig.getCache().getMaximumSize();
- if (StringUtils.isBlank(selectorData.getId())) {
- MatchDataCache.getInstance().cacheSelectorData(path, selectorData, initialCapacity, maximumSize);
- return;
- }
- List conditionList = selectorData.getConditionList();
- if (CollectionUtils.isNotEmpty(conditionList)) {
- boolean isUriCondition = conditionList.stream().allMatch(v -> URI_CONDITION_TYPE.equals(v.getParamType()));
- if (isUriCondition) {
- MatchDataCache.getInstance().cacheSelectorData(path, selectorData, initialCapacity, maximumSize);
- }
- }
- }
-
- private void cacheRuleData(final String path, final RuleData ruleData) {
- // if the ruleCache is disabled or rule data is null, not cache rule data.
- if (Boolean.FALSE.equals(ruleMatchConfig.getCache().getEnabled()) || Objects.isNull(ruleData)
- || Boolean.TRUE.equals(ruleData.getMatchRestful())) {
- return;
- }
- int initialCapacity = ruleMatchConfig.getCache().getInitialCapacity();
- long maximumSize = ruleMatchConfig.getCache().getMaximumSize();
- if (StringUtils.isBlank(ruleData.getId())) {
- MatchDataCache.getInstance().cacheRuleData(path, ruleData, initialCapacity, maximumSize);
- return;
- }
- List conditionList = ruleData.getConditionDataList();
- if (CollectionUtils.isNotEmpty(conditionList)) {
- boolean isUriCondition = conditionList.stream().allMatch(v -> URI_CONDITION_TYPE.equals(v.getParamType()));
- if (isUriCondition) {
- MatchDataCache.getInstance().cacheRuleData(path, ruleData, initialCapacity, maximumSize);
- }
- }
- }
private RuleData defaultRuleData(final SelectorData selectorData) {
RuleData ruleData = new RuleData();
@@ -225,7 +124,7 @@ private RuleData defaultRuleData(final SelectorData selectorData) {
ruleData.setId(Constants.DEFAULT_RULE);
return ruleData;
}
-
+
/**
* Handle selector if null mono.
*
@@ -237,7 +136,7 @@ private RuleData defaultRuleData(final SelectorData selectorData) {
protected Mono handleSelectorIfNull(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) {
return chain.execute(exchange);
}
-
+
/**
* Handle rule if null mono.
*
@@ -250,173 +149,6 @@ protected Mono handleRuleIfNull(final String pluginName, final ServerWebEx
return chain.execute(exchange);
}
- private Pair matchSelector(final ServerWebExchange exchange, final Collection selectors) {
- List filterCollectors = selectors.stream()
- .filter(selector -> selector.getEnabled() && filterSelector(selector, exchange))
- .distinct()
- .collect(Collectors.toList());
- if (filterCollectors.size() > 1) {
- return Pair.of(Boolean.FALSE, manyMatchSelector(filterCollectors));
- } else {
- return Pair.of(Boolean.TRUE, filterCollectors.stream().findFirst().orElse(null));
- }
- }
-
- private SelectorData manyMatchSelector(final List filterCollectors) {
- //What needs to be dealt with here is the and condition. If the number of and conditions is the same and is matched at the same time,
- // it will be sorted by the sort field.
- Map>> collect =
- filterCollectors.stream().map(selector -> {
- boolean match = MatchModeEnum.match(selector.getMatchMode(), MatchModeEnum.AND);
- int sort = 0;
- if (match) {
- sort = selector.getConditionList().size();
- }
- return Pair.of(sort, selector);
- }).collect(Collectors.groupingBy(Pair::getLeft));
- Integer max = Collections.max(collect.keySet());
- List> pairs = collect.get(max);
- return pairs.stream().map(Pair::getRight).min(Comparator.comparing(SelectorData::getSort)).orElse(null);
- }
-
- private Boolean filterSelector(final SelectorData selector, final ServerWebExchange exchange) {
- if (selector.getType() == SelectorTypeEnum.CUSTOM_FLOW.getCode()) {
- if (CollectionUtils.isEmpty(selector.getConditionList())) {
- return false;
- }
- return MatchStrategyFactory.match(selector.getMatchMode(), selector.getConditionList(), exchange);
- }
- return true;
- }
-
- private Pair matchRule(final ServerWebExchange exchange, final Collection rules) {
- List filterRuleData = rules.stream()
- .filter(rule -> filterRule(rule, exchange))
- .distinct()
- .collect(Collectors.toList());
- if (filterRuleData.size() > 1) {
- return Pair.of(Boolean.FALSE, manyMatchRule(filterRuleData));
- } else {
- return Pair.of(Boolean.TRUE, filterRuleData.stream().findFirst().orElse(null));
- }
- }
-
- private RuleData manyMatchRule(final List filterRuleData) {
- Map>> collect =
- filterRuleData.stream().map(rule -> {
- boolean match = MatchModeEnum.match(rule.getMatchMode(), MatchModeEnum.AND);
- int sort = 0;
- if (match) {
- sort = rule.getConditionDataList().size();
- }
- return Pair.of(sort, rule);
- }).collect(Collectors.groupingBy(Pair::getLeft));
- Integer max = Collections.max(collect.keySet());
- List> pairs = collect.get(max);
- return pairs.stream().map(Pair::getRight).min(Comparator.comparing(RuleData::getSort)).orElse(null);
- }
-
- private Boolean filterRule(final RuleData ruleData, final ServerWebExchange exchange) {
- return ruleData.getEnabled() && MatchStrategyFactory.match(ruleData.getMatchMode(), ruleData.getConditionDataList(), exchange);
- }
-
- private SelectorData trieMatchSelector(final ServerWebExchange exchange, final String pluginName, final String path) {
- if (!selectorMatchConfig.getTrie().getEnabled()) {
- return null;
- }
- SelectorData selectorData = null;
- ShenyuTrieNode shenyuTrieNode = selectorTrie.match(path, pluginName);
- if (Objects.nonNull(shenyuTrieNode)) {
- LogUtils.info(LOG, "{} selector match path from shenyu trie, path:{}", pluginName, path);
- List> collection = shenyuTrieNode.getPathCache().get(pluginName);
- if (CollectionUtils.isNotEmpty(collection)) {
- Pair selectorDataPair;
- if (collection.size() > 1) {
- selectorDataPair = matchSelector(exchange, ListUtil.castList(collection, SelectorData.class::cast));
- } else {
- Object selectorObj = collection.stream().findFirst().orElse(null);
- SelectorData selector = Objects.nonNull(selectorObj) ? (SelectorData) selectorObj : null;
- boolean cached = Objects.nonNull(selector) && selector.getConditionList().stream().allMatch(condition -> URI_CONDITION_TYPE.equals(condition.getParamType()));
- selectorDataPair = Pair.of(cached, selector);
- }
- selectorData = selectorDataPair.getRight();
- if (selectorDataPair.getLeft() && Objects.nonNull(selectorData)) {
- cacheSelectorData(path, selectorData);
- }
- }
- }
- return selectorData;
- }
-
- private RuleData trieMatchRule(final ServerWebExchange exchange, final SelectorData selectorData, final String path) {
- if (!ruleMatchConfig.getTrie().getEnabled()) {
- return null;
- }
- RuleData ruleData = null;
- ShenyuTrieNode shenyuTrieNode = ruleTrie.match(path, selectorData.getId());
- if (Objects.nonNull(shenyuTrieNode)) {
- LogUtils.info(LOG, "{} rule match path from shenyu trie", named());
- List> collection = shenyuTrieNode.getPathCache().get(selectorData.getId());
- if (CollectionUtils.isNotEmpty(collection)) {
- Pair ruleDataPair;
- if (collection.size() > 1) {
- ruleDataPair = matchRule(exchange, ListUtil.castList(collection, RuleData.class::cast));
- } else {
- Object ruleObj = collection.stream().findFirst().orElse(null);
- RuleData rule = Objects.nonNull(ruleObj) ? (RuleData) ruleObj : null;
- boolean cached = Objects.nonNull(rule) && rule.getConditionDataList().stream().allMatch(condition -> URI_CONDITION_TYPE.equals(condition.getParamType()));
- ruleDataPair = Pair.of(cached, rule);
- }
- ruleData = ruleDataPair.getRight();
- if (ruleDataPair.getLeft() && Objects.nonNull(ruleData)) {
- // exist only one rule data, cache rule
- cacheRuleData(path, ruleData);
- }
- }
- }
- return ruleData;
- }
-
- private SelectorData defaultMatchSelector(final ServerWebExchange exchange, final List selectors, final String path) {
- Pair matchSelectorPair = matchSelector(exchange, selectors);
- SelectorData selectorData = matchSelectorPair.getRight();
- if (Objects.nonNull(selectorData)) {
- LogUtils.info(LOG, "{} selector match success from default strategy", named());
- // cache selector data
- if (matchSelectorPair.getLeft()) {
- cacheSelectorData(path, selectorData);
- }
- return selectorData;
- } else {
- // if not match selector, cache empty selector data.
- if (matchSelectorPair.getLeft()) {
- SelectorData emptySelectorData = SelectorData.builder().pluginName(named()).build();
- cacheSelectorData(path, emptySelectorData);
- }
- return null;
- }
- }
-
- private RuleData defaultMatchRule(final ServerWebExchange exchange, final List rules, final String path) {
- Pair matchRulePair = matchRule(exchange, rules);
- RuleData ruleData = matchRulePair.getRight();
- if (Objects.nonNull(ruleData)) {
- LOG.info("{} rule match path from default strategy", named());
- // cache rule data
- if (matchRulePair.getLeft()) {
- cacheRuleData(path, ruleData);
- }
- return ruleData;
- } else {
- // if not match rule, cache empty rule data.
- if (matchRulePair.getLeft()) {
- RuleData emptyRuleData = RuleData.builder().pluginName(named()).build();
- cacheRuleData(path, emptyRuleData);
- }
- return null;
- }
- }
-
/**
* print selector log.
* please don't delete this method or refactor {@linkplain org.apache.shenyu.plugin.base.AbstractShenyuPlugin#printLog}
@@ -430,7 +162,7 @@ private void printLog(final SelectorData selectorData, final String pluginName)
LOG.info("{} selector success match , selector name :{}", pluginName, selectorData.getName());
}
}
-
+
/**
* print rule log.
*
@@ -442,5 +174,5 @@ private void printLog(final RuleData ruleData, final String pluginName) {
LOG.info("{} rule success match , rule name :{}", pluginName, ruleData.getName());
}
}
-
+
}
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/AbstractMatchDecisionMaker.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/AbstractMatchDecisionMaker.java
new file mode 100644
index 000000000000..95d6ce703554
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/AbstractMatchDecisionMaker.java
@@ -0,0 +1,97 @@
+/*
+ * 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.shenyu.plugin.base.maker;
+
+import org.apache.shenyu.common.dto.BaseData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.base.provider.DataProvider;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import java.util.List;
+
+/**
+ * Abstract base class for making match decisions in plugins using the Template Method pattern.
+ *
+ *
+ * This class provides a template for processing data matching logic in plugins. Subclasses are expected
+ * to implement the abstract methods to define specific behaviors for handling empty data, matching data,
+ * and determining whether processing should continue.
+ *
+ *
+ *
+ * The Template Method pattern is used here to allow subclasses to override certain steps of the algorithm
+ * without changing its structure.
+ *
+ *
+ * @param the type of data to be matched, extending {@link BaseData}
+ */
+public abstract class AbstractMatchDecisionMaker {
+
+ protected static final String URI_CONDITION_TYPE = "uri";
+
+ private final DataProvider dataProvider;
+
+
+ /**
+ * Constructs an AbstractMatchDecisionMaker with the specified data provider.
+ *
+ * @param dataProvider the data provider used to retrieve data for matching
+ */
+ protected AbstractMatchDecisionMaker(final DataProvider dataProvider) {
+ this.dataProvider = dataProvider;
+ }
+
+ /**
+ * Retrieves a list of data associated with the given key.
+ *
+ * @param key the key used to retrieve data
+ * @return a list of data objects associated with the key
+ */
+ public List getData(final String key) {
+ return dataProvider.getData(key);
+ }
+
+ /**
+ * Handles the scenario when no matching data is found for the given plugin.
+ *
+ * @param pluginName the name of the plugin
+ * @param exchange the current server web exchange
+ * @param chain the plugin chain to continue processing
+ * @return a {@link Mono} that completes when the handling is done
+ */
+ protected abstract Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain);
+
+ /**
+ * Matches the appropriate data from the provided list based on the exchange and path.
+ *
+ * @param exchange the current server web exchange
+ * @param dataList the list of data to match against
+ * @param path the request path to use for matching
+ * @return the matched data object, or {@code null} if no match is found
+ */
+ protected abstract T matchData(ServerWebExchange exchange, String dataName, List dataList, String path, SelectorData selectorData);
+
+ /**
+ * Determines whether processing should continue based on the matched data.
+ *
+ * @param data the matched data object
+ * @return {@code true} if processing should continue, {@code false} otherwise
+ */
+ protected abstract boolean shouldContinue(T data);
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/PluginDataDecisionMaker.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/PluginDataDecisionMaker.java
new file mode 100644
index 000000000000..b851770239d2
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/PluginDataDecisionMaker.java
@@ -0,0 +1,127 @@
+/*
+ * 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.shenyu.plugin.base.maker;
+
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.base.provider.PluginDataProvider;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+/**
+ * A specialized decision maker implementation responsible for plugin data matching and decision-making
+ * within the Apache ShenYu gateway ecosystem.
+ *
+ * This class extends {@link AbstractMatchDecisionMaker} to provide plugin-specific matching
+ * capabilities for {@link PluginData} objects. It serves as the foundational decision maker in
+ * ShenYu's three-tier matching system (Plugin → Selector → Rule), determining which plugins should
+ * process incoming requests based on their enabled status and configuration data.
+ *
+ * In the ShenYu gateway architecture, this component operates at the highest level of the
+ * decision hierarchy:
+ *
+ * - Plugin Level: Determines if a plugin is enabled and should process the request
+ * - Selector Level: Handles coarse-grained routing decisions
+ * - Rule Level: Applies fine-grained processing rules
+ *
+ * The PluginDataDecisionMaker ensures that only active and properly configured plugins participate
+ * in request processing, maintaining gateway efficiency and reliability.
+ *
+ * This decision maker integrates with ShenYu's plugin chain mechanism through the provided
+ * {@link PluginDataProvider}, which supplies the plugin configuration data needed for matching
+ * decisions. It follows the gateway's reactive programming model by returning {@link Mono} types
+ * for all asynchronous operations.
+ */
+public class PluginDataDecisionMaker extends AbstractMatchDecisionMaker {
+
+ /**
+ * Constructs a new PluginDataDecisionMaker with a PluginDataProvider.
+ *
+ * This constructor initializes the decision maker with a dedicated data provider
+ * that supplies plugin configuration information. The provider enables access to
+ * plugin metadata including enabled status, configuration parameters, and execution
+ * order settings.
+ */
+ public PluginDataDecisionMaker() {
+ super(new PluginDataProvider());
+ }
+
+ /**
+ * Handles the scenario when no plugin data is found for the specified plugin.
+ *
+ * When no matching plugin data is available, this method ensures the request
+ * continues through the plugin chain without interruption. This graceful degradation
+ * approach maintains system reliability by allowing requests to proceed even when
+ * specific plugin configurations are missing or unavailable.
+ *
+ * @param pluginName the name of the plugin being processed
+ * @param exchange the current server web exchange containing request and response data
+ * @param chain the plugin chain for continued request processing
+ * @return a Mono indicating completion of the empty handler operation
+ */
+ @Override
+ public Mono handleEmpty(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) {
+ return chain.execute(exchange);
+ }
+
+ /**
+ * Matches plugin data against the current request context.
+ *
+ * This method implements the core plugin matching logic, selecting the appropriate
+ * plugin data from the available candidates. It employs a straightforward selection
+ * strategy: when multiple plugin data entries are available, it returns the first one
+ * from the list, assuming the list is pre-sorted by priority or relevance.
+ *
+ * In typical ShenYu usage, plugin data lists contain configuration for a specific
+ * plugin name, so returning the first entry is semantically correct as there should
+ * be only one active configuration per plugin.
+ *
+ * @param exchange the current server web exchange containing request information
+ * @param dataName the name of the plugin data being matched
+ * @param dataList the list of candidate plugin data for matching
+ * @param path the request path (currently unused in base implementation)
+ * @param selectorData the selector data context (currently unused in base implementation)
+ * @return the matched PluginData, or null if the data list is empty
+ */
+ @Override
+ public PluginData matchData(final ServerWebExchange exchange, final String dataName, final List dataList, final String path, final SelectorData selectorData) {
+ return dataList.isEmpty() ? null : dataList.get(0);
+ }
+
+ /**
+ * Determines whether the plugin chain should continue processing after the current plugin.
+ *
+ * This decision is based solely on the plugin's enabled status. Only plugins that are
+ * explicitly enabled will allow continuation of the plugin chain. This provides a simple
+ * but effective mechanism for controlling plugin execution flow within the gateway.
+ *
+ * The continuation mechanism enables complex processing scenarios where certain plugins
+ * might terminate request processing (e.g., authentication failures) while others allow
+ * progression to subsequent processing stages.
+ *
+ * @param data the plugin data to evaluate for continuation
+ * @return true if the plugin is enabled and should continue processing, false otherwise
+ */
+ @Override
+ public boolean shouldContinue(final PluginData data) {
+ return data.getEnabled();
+ }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/RuleDataDecisionMaker.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/RuleDataDecisionMaker.java
new file mode 100644
index 000000000000..ba69ee888d39
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/RuleDataDecisionMaker.java
@@ -0,0 +1,329 @@
+/*
+ * 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.shenyu.plugin.base.maker;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.MatchModeEnum;
+import org.apache.shenyu.common.enums.TrieCacheTypeEnum;
+import org.apache.shenyu.common.utils.ListUtil;
+import org.apache.shenyu.common.utils.LogUtils;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.base.cache.MatchDataCache;
+import org.apache.shenyu.plugin.base.condition.strategy.MatchStrategyFactory;
+import org.apache.shenyu.plugin.base.provider.RuleDataProvider;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrieNode;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.apache.shenyu.plugin.api.ShenyuPlugin.LOG;
+
+/**
+ * A specialized decision maker implementation that handles rule matching logic within the Apache ShenYu gateway.
+ *
+ * This class extends {@link AbstractMatchDecisionMaker} to provide sophisticated rule matching
+ * capabilities for {@link RuleData} objects. It implements the core decision-making logic for determining
+ * which specific rules should be applied to incoming requests based on path matching, conditions, and
+ * configurable matching strategies.
+ *
+ * The RuleDataDecisionMaker operates as the second level in ShenYu's two-tier routing system:
+ *
+ * - Selector Level: Coarse-grained routing using {@link SelectorData}
+ * - Rule Level: Fine-grained processing logic using {@link RuleData}
+ *
+ * After a selector matches a request, this component determines the specific rule to apply within that selector.
+ *
+ *
+ * The decision maker employs multiple matching strategies:
+ *
+ * - Trie-based path matching: Efficient URI path lookup using {@link ShenyuTrie}
+ * - Condition evaluation: Rule condition validation using {@link MatchStrategyFactory}
+ * - Priority resolution: Conflict resolution when multiple rules match the same request
+ * - Configurable caching: Performance optimization through {@link MatchDataCache}
+ *
+ *
+ *
+ * This component integrates with ShenYu's plugin architecture to provide precise rule-based
+ * request processing, supporting complex routing scenarios with conditional logic and performance
+ * optimization through intelligent caching mechanisms.
+ */
+public class RuleDataDecisionMaker extends AbstractMatchDecisionMaker {
+
+ private ShenyuConfig.RuleMatchCache ruleMatchConfig;
+
+ private ShenyuTrie ruleTrie;
+
+ /**
+ * Constructs a new RuleDataDecisionMaker with a RuleDataProvider.
+ *
+ * Initializes the cache configuration and trie data structure specifically optimized
+ * for rule matching operations. The constructor sets up the necessary infrastructure
+ * for efficient rule lookup and matching performance.
+ */
+ public RuleDataDecisionMaker() {
+ super(new RuleDataProvider());
+ initCacheConfig();
+ }
+
+ /**
+ * Handles the scenario when no rule data is found for the given rule name.
+ *
+ * When no matching rules are found, this method allows the request to continue
+ * through the plugin chain without rule-specific processing. This ensures that
+ * the gateway can handle requests even when no specific rules are configured,
+ * maintaining system reliability and avoiding request processing interruptions.
+ *
+ * @param pluginName the name of the plugin being executed
+ * @param exchange the current server web exchange containing request and response data
+ * @param chain the plugin chain for continued request processing
+ * @return a Mono indicating completion of the empty handler operation
+ */
+ @Override
+ public Mono handleEmpty(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) {
+ return chain.execute(exchange);
+ }
+
+ /**
+ * Matches rule data against the current request context.
+ *
+ * This method implements the core rule matching logic, providing two matching strategies:
+ *
+ * - Direct matching: Uses pre-loaded rule data from the provided list when available
+ * - Trie-based matching: Falls back to sophisticated trie-based path matching
+ * when the data list is empty
+ *
+ * The method ensures optimal performance by prioritizing cached data while maintaining
+ * comprehensive matching capabilities through the trie structure.
+ *
+ *
+ * @param exchange the current server web exchange containing request information
+ * @param ruleName the name of the rule requesting matching
+ * @param dataList the list of candidate rule data for matching (may be empty)
+ * @param path the request path to match against rules
+ * @param selectorData the parent selector data context for rule matching
+ * @return the matched RuleData, or null if no suitable rule is found
+ */
+ @Override
+ public RuleData matchData(final ServerWebExchange exchange, final String ruleName, final List dataList, final String path, final SelectorData selectorData) {
+ return dataList.isEmpty() ? trieMatchRule(exchange, ruleName, selectorData, path) : dataList.get(0);
+ }
+
+ /**
+ * Determines whether the plugin chain should continue processing after the current rule.
+ *
+ * Rule continuation is based on the rule's enabled status. Only enabled rules
+ * permit further processing in the plugin chain. This allows for complex rule
+ * sequences where specific rules can terminate processing while others allow
+ * continuation to subsequent rules or plugins.
+ *
+ * @param data the rule data to evaluate for continuation
+ * @return true if the rule is enabled and should continue processing, false otherwise
+ */
+
+ @Override
+ public boolean shouldContinue(final RuleData data) {
+ return data.getEnabled();
+ }
+
+ /**
+ * Initializes the cache configuration and trie data structure for rule matching.
+ *
+ * This method performs lazy initialization of the rule matching infrastructure,
+ * retrieving configuration from ShenYu's global configuration and obtaining the
+ * appropriate trie implementation based on the rule cache type enumeration.
+ */
+ private void initCacheConfig() {
+ if (Objects.isNull(ruleMatchConfig)) {
+ ShenyuConfig shenyuConfig = SpringBeanUtils.getInstance().getBean(ShenyuConfig.class);
+ ruleMatchConfig = shenyuConfig.getRuleMatchCache();
+ }
+
+ if (Objects.isNull(ruleTrie)) {
+ ruleTrie = SpringBeanUtils.getInstance().getBean(TrieCacheTypeEnum.RULE.getTrieType());
+ }
+ }
+
+ /**
+ * Performs trie-based matching for rules when no pre-loaded data is available.
+ *
+ * This method implements sophisticated trie-based matching that:
+ *
+ * - Validates that trie matching is enabled in the configuration
+ * - Uses the ShenyuTrie to find path matches based on selector context
+ * - Handles both single and multiple rule matches with appropriate resolution
+ * - Applies caching for performance optimization when conditions permit
+ *
+ *
+ *
+ * @param exchange the current server web exchange
+ * @param ruleName the name of the rule being processed
+ * @param selectorData the parent selector data providing context for rule matching
+ * @param path the request path to match
+ * @return the matched RuleData, or null if no match is found or trie matching is disabled
+ */
+ private RuleData trieMatchRule(final ServerWebExchange exchange, final String ruleName, final SelectorData selectorData, final String path) {
+ if (!ruleMatchConfig.getTrie().getEnabled()) {
+ return null;
+ }
+ RuleData ruleData = null;
+ ShenyuTrieNode shenyuTrieNode = ruleTrie.match(path, selectorData.getId());
+ if (Objects.nonNull(shenyuTrieNode)) {
+ LogUtils.info(LOG, "{} rule match path from shenyu trie", ruleName);
+ List> collection = shenyuTrieNode.getPathCache().get(selectorData.getId());
+ if (CollectionUtils.isNotEmpty(collection)) {
+ Pair ruleDataPair;
+ if (collection.size() > 1) {
+ ruleDataPair = matchRule(exchange, ListUtil.castList(collection, RuleData.class::cast));
+ } else {
+ Object ruleObj = collection.stream().findFirst().orElse(null);
+ RuleData rule = Objects.nonNull(ruleObj) ? (RuleData) ruleObj : null;
+ boolean cached = Objects.nonNull(rule) && rule.getConditionDataList().stream().allMatch(condition -> URI_CONDITION_TYPE.equals(condition.getParamType()));
+ ruleDataPair = Pair.of(cached, rule);
+ }
+ ruleData = ruleDataPair.getRight();
+ if (ruleDataPair.getLeft() && Objects.nonNull(ruleData)) {
+ // exist only one rule data, cache rule
+ cacheRuleData(path, ruleData);
+ }
+ }
+ }
+ return ruleData;
+ }
+
+ /**
+ * Filters and matches a collection of rules against the current exchange.
+ *
+ * This method applies rule conditions and matching strategies to determine
+ * which rules are appropriate for the current request. It handles conflict
+ * resolution when multiple rules match and provides caching recommendations
+ * based on the matching results.
+ *
+ * @param exchange the current server web exchange
+ * @param rules the collection of rules to filter and match
+ * @return a Pair where the left value indicates cacheability and the right value contains the matched RuleData
+ */
+ private Pair matchRule(final ServerWebExchange exchange, final Collection rules) {
+ List filterRuleData = rules.stream()
+ .filter(rule -> filterRule(rule, exchange))
+ .distinct()
+ .collect(Collectors.toList());
+ if (filterRuleData.size() > 1) {
+ return Pair.of(Boolean.FALSE, manyMatchRule(filterRuleData));
+ } else {
+ return Pair.of(Boolean.TRUE, filterRuleData.stream().findFirst().orElse(null));
+ }
+ }
+
+ /**
+ * Filters a single rule based on its enabled status and conditions.
+ *
+ * This method validates that a rule is enabled and applies the appropriate
+ * match strategy using the MatchStrategyFactory. It serves as the core
+ * condition evaluation mechanism for rule matching.
+ *
+ * @param ruleData the rule to filter
+ * @param exchange the current server web exchange for condition evaluation
+ * @return true if the rule is enabled and matches the current request conditions, false otherwise
+ */
+ private Boolean filterRule(final RuleData ruleData, final ServerWebExchange exchange) {
+ return ruleData.getEnabled() && MatchStrategyFactory.match(ruleData.getMatchMode(), ruleData.getConditionDataList(), exchange);
+ }
+
+ /**
+ * Resolves conflicts when multiple rules match the same request.
+ *
+ * This method implements sophisticated priority resolution logic that:
+ *
+ * - Groups rules by the number of AND conditions matched
+ * - Selects the group with the highest number of AND conditions
+ * - Within that group, chooses the rule with the lowest sort value
+ *
+ * This ensures that more specific rules (with more conditions) take precedence
+ * over general ones, providing predictable and configurable rule prioritization.
+ *
+ *
+ * @param filterRuleData the list of matching rules to resolve
+ * @return the highest priority rule according to the resolution rules
+ */
+ private RuleData manyMatchRule(final List filterRuleData) {
+ Map>> collect =
+ filterRuleData.stream().map(rule -> {
+ boolean match = MatchModeEnum.match(rule.getMatchMode(), MatchModeEnum.AND);
+ int sort = 0;
+ if (match) {
+ sort = rule.getConditionDataList().size();
+ }
+ return Pair.of(sort, rule);
+ }).collect(Collectors.groupingBy(Pair::getLeft));
+ Integer max = Collections.max(collect.keySet());
+ List> pairs = collect.get(max);
+ return pairs.stream().map(Pair::getRight).min(Comparator.comparing(RuleData::getSort)).orElse(null);
+ }
+
+ /**
+ * Caches matched rule data for improved performance on subsequent requests.
+ *
+ * This method implements configurable caching that stores rule data when:
+ *
+ * - Caching is enabled in the configuration
+ * - The rule data is not null
+ * - The rule doesn't represent a RESTful match
+ * - All conditions are URI-based (ensuring cache validity)
+ *
+ * The caching mechanism respects the configured initial capacity and maximum
+ * size parameters to optimize memory usage while maintaining performance.
+ *
+ *
+ * @param path the request path used as cache key
+ * @param ruleData the rule data to cache
+ */
+ private void cacheRuleData(final String path, final RuleData ruleData) {
+ // if the ruleCache is disabled or rule data is null, not cache rule data.
+ if (Boolean.FALSE.equals(ruleMatchConfig.getCache().getEnabled()) || Objects.isNull(ruleData)
+ || Boolean.TRUE.equals(ruleData.getMatchRestful())) {
+ return;
+ }
+ int initialCapacity = ruleMatchConfig.getCache().getInitialCapacity();
+ long maximumSize = ruleMatchConfig.getCache().getMaximumSize();
+ if (StringUtils.isBlank(ruleData.getId())) {
+ MatchDataCache.getInstance().cacheRuleData(path, ruleData, initialCapacity, maximumSize);
+ return;
+ }
+ List conditionList = ruleData.getConditionDataList();
+ if (CollectionUtils.isNotEmpty(conditionList)) {
+ boolean isUriCondition = conditionList.stream().allMatch(v -> URI_CONDITION_TYPE.equals(v.getParamType()));
+ if (isUriCondition) {
+ MatchDataCache.getInstance().cacheRuleData(path, ruleData, initialCapacity, maximumSize);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/SelectorDataDecisionMaker.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/SelectorDataDecisionMaker.java
new file mode 100644
index 000000000000..8c1a8b0ac051
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/SelectorDataDecisionMaker.java
@@ -0,0 +1,327 @@
+/*
+ * 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.shenyu.plugin.base.maker;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.MatchModeEnum;
+import org.apache.shenyu.common.enums.SelectorTypeEnum;
+import org.apache.shenyu.common.enums.TrieCacheTypeEnum;
+import org.apache.shenyu.common.utils.ListUtil;
+import org.apache.shenyu.common.utils.LogUtils;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.base.cache.MatchDataCache;
+import org.apache.shenyu.plugin.base.condition.strategy.MatchStrategyFactory;
+import org.apache.shenyu.plugin.base.provider.SelectorDataProvider;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrieNode;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.apache.shenyu.plugin.api.ShenyuPlugin.LOG;
+
+/**
+ * The core decision-making engine for selector matching in Apache ShenYu gateway.
+ *
+ * This class implements the matching logic that determines which selector should handle
+ * incoming requests based on request attributes, path patterns, and configured conditions.
+ * It extends the abstract matching framework and specializes in selector-level routing decisions.
+ *
+ * In the ShenYu architecture, selectors represent the first level of request routing where
+ * coarse-grained filtering occurs before more specific rule matching. This decision maker
+ * evaluates various matching strategies including trie-based path matching, condition-based
+ * filtering, and custom flow selectors.
+ *
+ * The class employs multiple matching techniques:
+ *
+ * - Trie-based matching: For efficient path pattern matching using prefix trees
+ * - Condition-based filtering: Using match strategies from {@link MatchStrategyFactory}
+ * - Custom flow selectors: For advanced, user-defined routing logic
+ *
+ *
+ *
+ * Caching is extensively used to optimize performance, with configurable cache settings
+ * for both trie structures and match results. The caching behavior can be tuned through
+ * {@link ShenyuConfig.SelectorMatchCache} configuration.
+ */
+public class SelectorDataDecisionMaker extends AbstractMatchDecisionMaker {
+
+ private ShenyuConfig.SelectorMatchCache selectorMatchConfig;
+
+ private ShenyuTrie selectorTrie;
+
+ /**
+ * Constructs a new SelectorDataDecisionMaker with a SelectorDataProvider.
+ * Initializes cache configuration and trie structures for selector matching.
+ */
+ public SelectorDataDecisionMaker() {
+ super(new SelectorDataProvider());
+ initCacheConfig();
+ }
+
+ /**
+ * Handles the case when no selector data is found for the given plugin.
+ *
+ * When no matching selectors are found, this method allows the request to continue
+ * through the plugin chain without selector-specific processing. This ensures that
+ * requests can still be processed by subsequent plugins even when selector matching
+ * doesn't yield results.
+ *
+ * @param pluginName the name of the plugin being executed
+ * @param exchange the current server web exchange
+ * @param chain the plugin chain for continued processing
+ * @return a Mono indicating completion of the empty handling operation
+ */
+ @Override
+ public Mono handleEmpty(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) {
+ return chain.execute(exchange);
+ }
+
+ /**
+ * Performs the core selector matching logic for incoming requests.
+ *
+ * This method implements the selector matching algorithm that determines which
+ * selector should process the current request. It supports both direct list-based
+ * matching and advanced trie-based matching for path patterns.
+ *
+ * The matching process follows this hierarchy:
+ *
+ * - If pre-filtered data is available in the dataList, use the first match
+ * - Otherwise, perform trie-based matching on the request path
+ * - Apply condition filtering for custom flow selectors
+ * - Handle multiple matches using priority-based selection
+ *
+ *
+ *
+ * @param exchange the current server web exchange containing request information
+ * @param pluginName the name of the plugin requesting selector matching
+ * @param dataList pre-filtered list of selector data (may be empty)
+ * @param path the request path used for matching
+ * @param selectorData additional selector context (currently unused in base implementation)
+ * @return the matched SelectorData, or null if no match found
+ *
+ */
+ @Override
+ public SelectorData matchData(final ServerWebExchange exchange, final String pluginName, final List dataList, final String path, final SelectorData selectorData) {
+ return dataList.isEmpty() ? trieMatchSelector(exchange, pluginName, path) : dataList.get(0);
+ }
+
+ /**
+ * Determines whether the plugin chain should continue processing after selector matching.
+ *
+ * A selector can control the flow of the plugin chain by specifying continuation
+ * behavior. This method checks the selector's enabled status and continuation flag
+ * to determine if processing should proceed to the next plugin.
+ *
+ * @param data the selector data to evaluate for continuation
+ * @return true if the selector is enabled and configured to continue processing,
+ * false otherwise
+ */
+ @Override
+ public boolean shouldContinue(final SelectorData data) {
+ return data.getEnabled() && data.getContinued();
+ }
+
+ /**
+ * Initializes the cache configuration and trie structures for selector matching.
+ *
+ * This method lazily initializes the caching infrastructure by retrieving
+ * configuration from Spring context. It ensures that cache settings and trie
+ * instances are properly configured before matching operations begin.
+ */
+ private void initCacheConfig() {
+ if (Objects.isNull(selectorMatchConfig)) {
+ ShenyuConfig shenyuConfig = SpringBeanUtils.getInstance().getBean(ShenyuConfig.class);
+ selectorMatchConfig = shenyuConfig.getSelectorMatchCache();
+ }
+
+ if (Objects.isNull(selectorTrie)) {
+ selectorTrie = SpringBeanUtils.getInstance().getBean(TrieCacheTypeEnum.SELECTOR.getTrieType());
+ }
+ }
+
+ /**
+ * Performs trie-based matching for selectors when no pre-filtered data is available.
+ *
+ * This method implements efficient path matching using a trie data structure,
+ * which provides O(m) time complexity where m is the path length. It's particularly
+ * effective for routing scenarios with large numbers of path-based selectors.
+ *
+ * The matching process includes:
+ *
+ * - Checking if trie matching is enabled in configuration
+ * - Finding the matching trie node for the given path
+ * - Handling single and multiple matches appropriately
+ * - Caching results for future requests when conditions allow
+ *
+ *
+ *
+ * @param exchange the current server web exchange
+ * @param pluginName the plugin name for context-specific matching
+ * @param path the request path to match against selector patterns
+ * @return the matched SelectorData, or null if no match found
+ */
+ private SelectorData trieMatchSelector(final ServerWebExchange exchange, final String pluginName, final String path) {
+ if (!selectorMatchConfig.getTrie().getEnabled()) {
+ return null;
+ }
+ SelectorData selectorData = null;
+ ShenyuTrieNode shenyuTrieNode = selectorTrie.match(path, pluginName);
+ if (Objects.nonNull(shenyuTrieNode)) {
+ LogUtils.info(LOG, "{} selector match path from shenyu trie, path:{}", pluginName, path);
+ List> collection = shenyuTrieNode.getPathCache().get(pluginName);
+ if (CollectionUtils.isNotEmpty(collection)) {
+ Pair selectorDataPair;
+ if (collection.size() > 1) {
+ selectorDataPair = matchSelector(exchange, ListUtil.castList(collection, SelectorData.class::cast));
+ } else {
+ Object selectorObj = collection.stream().findFirst().orElse(null);
+ SelectorData selector = Objects.nonNull(selectorObj) ? (SelectorData) selectorObj : null;
+ boolean cached = Objects.nonNull(selector) && selector.getConditionList().stream().allMatch(condition -> URI_CONDITION_TYPE.equals(condition.getParamType()));
+ selectorDataPair = Pair.of(cached, selector);
+ }
+ selectorData = selectorDataPair.getRight();
+ if (selectorDataPair.getLeft() && Objects.nonNull(selectorData)) {
+ cacheSelectorData(path, selectorData, selectorMatchConfig);
+ }
+ }
+ }
+ return selectorData;
+ }
+
+ /**
+ * Filters and matches a collection of selectors against the current exchange.
+ *
+ * This method applies condition-based filtering to determine which selectors
+ * are appropriate for the current request. It handles both single and multiple
+ * matches, with special logic for resolving conflicts when multiple selectors
+ * match the same request.
+ *
+ * @param exchange the server web exchange to match against
+ * @param selectors the collection of selectors to filter
+ * @return a Pair containing a Boolean indicating if the result can be cached,
+ * and the matched SelectorData
+ */
+ private Pair matchSelector(final ServerWebExchange exchange, final Collection selectors) {
+ List filterCollectors = selectors.stream()
+ .filter(selector -> selector.getEnabled() && filterSelector(selector, exchange))
+ .distinct()
+ .collect(Collectors.toList());
+ if (filterCollectors.size() > 1) {
+ return Pair.of(Boolean.FALSE, manyMatchSelector(filterCollectors));
+ } else {
+ return Pair.of(Boolean.TRUE, filterCollectors.stream().findFirst().orElse(null));
+ }
+ }
+
+ /**
+ * Filters individual selectors based on their type and conditions.
+ *
+ * Custom flow selectors undergo comprehensive condition matching using the
+ * configured match strategy, while other selector types are accepted by default
+ * assuming they've passed earlier filtering stages.
+ *
+ * @param selector the selector to evaluate
+ * @param exchange the server web exchange for condition evaluation
+ * @return true if the selector should be considered for matching, false otherwise
+ */
+ private Boolean filterSelector(final SelectorData selector, final ServerWebExchange exchange) {
+ if (selector.getType() == SelectorTypeEnum.CUSTOM_FLOW.getCode()) {
+ if (CollectionUtils.isEmpty(selector.getConditionList())) {
+ return false;
+ }
+ return MatchStrategyFactory.match(selector.getMatchMode(), selector.getConditionList(), exchange);
+ }
+ return true;
+ }
+
+ /**
+ * Resolves conflicts when multiple selectors match the same request.
+ *
+ * This method implements a priority-based selection algorithm that considers:
+ *
+ * - Match mode (AND conditions are prioritized)
+ * - Number of matching conditions
+ * - Selector sort order for tie-breaking
+ *
+ *
+ *
+ * @param filterCollectors the list of matching selectors
+ * @return the highest priority selector according to the conflict resolution rules
+ */
+ private SelectorData manyMatchSelector(final List filterCollectors) {
+ // What needs to be dealt with here is the and condition. If the number of and conditions is the same and is matched at the same time,
+ // it will be sorted by the sort field.
+ Map>> collect =
+ filterCollectors.stream().map(selector -> {
+ boolean match = MatchModeEnum.match(selector.getMatchMode(), MatchModeEnum.AND);
+ int sort = 0;
+ if (match) {
+ sort = selector.getConditionList().size();
+ }
+ return Pair.of(sort, selector);
+ }).collect(Collectors.groupingBy(Pair::getLeft));
+ Integer max = Collections.max(collect.keySet());
+ List> pairs = collect.get(max);
+ return pairs.stream().map(Pair::getRight).min(Comparator.comparing(SelectorData::getSort)).orElse(null);
+ }
+
+ /**
+ * Caches selector matching results for performance optimization.
+ *
+ * This method implements the caching strategy for selector matches, with
+ * configurable cache size and eligibility criteria. Only selectors with
+ * URI-based conditions are cached to ensure cache consistency.
+ *
+ * @param path the request path used as cache key
+ * @param selectorData the selector data to cache
+ * @param selectorMatchConfig the cache configuration parameters
+ */
+ private void cacheSelectorData(final String path, final SelectorData selectorData, final ShenyuConfig.SelectorMatchCache selectorMatchConfig) {
+ if (Boolean.FALSE.equals(selectorMatchConfig.getCache().getEnabled()) || Objects.isNull(selectorData)
+ || Boolean.TRUE.equals(selectorData.getMatchRestful())) {
+ return;
+ }
+ int initialCapacity = selectorMatchConfig.getCache().getInitialCapacity();
+ long maximumSize = selectorMatchConfig.getCache().getMaximumSize();
+ if (StringUtils.isBlank(selectorData.getId())) {
+ MatchDataCache.getInstance().cacheSelectorData(path, selectorData, initialCapacity, maximumSize);
+ return;
+ }
+ List conditionList = selectorData.getConditionList();
+ if (CollectionUtils.isNotEmpty(conditionList)) {
+ boolean isUriCondition = conditionList.stream().allMatch(v -> URI_CONDITION_TYPE.equals(v.getParamType()));
+ if (isUriCondition) {
+ MatchDataCache.getInstance().cacheSelectorData(path, selectorData, initialCapacity, maximumSize);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/DataProvider.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/DataProvider.java
new file mode 100644
index 000000000000..0a23ee1b8bfa
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/DataProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.shenyu.plugin.base.provider;
+
+import java.util.List;
+
+/**
+ * A generic interface for providing data based on a given key.
+ *
+ * @param the type of data provided by this provider
+ */
+public interface DataProvider {
+ /**
+ * Retrieves a list of data items associated with the specified key.
+ *
+ * @param key the key used to look up the data; its meaning is defined by the implementation
+ * @return a list of data items of type {@code T} associated with the given key,
+ * or an empty list if no data is found
+ */
+ /**
+ * Retrieves a list of data items associated with the specified key.
+ *
+ * @param key the key used to look up the data; its meaning is defined by the implementation
+ * @return a list of data items of type {@code T} associated with the given key,
+ * or an empty list if no data is found
+ */
+ List getData(String key);
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/PluginDataProvider.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/PluginDataProvider.java
new file mode 100644
index 000000000000..2b5d1d2b2e83
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/PluginDataProvider.java
@@ -0,0 +1,47 @@
+/*
+ * 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.shenyu.plugin.base.provider;
+
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.plugin.base.cache.BaseDataCache;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A concrete implementation of the data provider pattern for retrieving plugin data
+ * in the ShenYu gateway system.
+ */
+public class PluginDataProvider implements DataProvider {
+
+ /**
+ * Retrieves plugin data from the base data cache for the specified plugin name.
+ *
+ * @param pluginName the name of the plugin to retrieve. This parameter should not be null,
+ * though the current implementation may return an empty list rather than
+ * throwing an exception for null values.
+ * @return an immutable list containing a single {@link PluginData} object if the plugin
+ * is found, otherwise an empty immutable list. The return value will never be null.
+ */
+ @Override
+ public List getData(final String pluginName) {
+ PluginData data = BaseDataCache.getInstance().obtainPluginData(pluginName);
+ return Objects.nonNull(data) ? Collections.singletonList(data) : Collections.emptyList();
+ }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/RuleDataProvider.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/RuleDataProvider.java
new file mode 100644
index 000000000000..80fad15bc714
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/RuleDataProvider.java
@@ -0,0 +1,45 @@
+/*
+ * 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.shenyu.plugin.base.provider;
+
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.plugin.base.cache.BaseDataCache;
+
+import java.util.List;
+
+/**
+ * A data provider implementation for retrieving rule data from the base data cache.
+ * This class follows the Data Provider pattern to abstract data access logic and provide
+ * a consistent interface for obtaining rule configuration data throughout the Shenyu plugin system.
+ *
+ * The RuleDataProvider serves as a bridge between the plugin infrastructure and the cached
+ * rule data, ensuring efficient data retrieval while maintaining separation of concerns.
+ */
+public class RuleDataProvider implements DataProvider {
+
+ /**
+ * Retrieves rule data from the base data cache based on the specified rule name.
+ * This method implements the data provider pattern by delegating to the singleton
+ *
+ */
+ @Override
+ public List getData(final String ruleName) {
+ List data = BaseDataCache.getInstance().obtainRuleData(ruleName);
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/SelectorDataProvider.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/SelectorDataProvider.java
new file mode 100644
index 000000000000..b9448349c2da
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/SelectorDataProvider.java
@@ -0,0 +1,77 @@
+/*
+ * 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.shenyu.plugin.base.provider;
+
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.plugin.base.cache.BaseDataCache;
+
+import java.util.List;
+
+/**
+ * A concrete implementation of the {@code DataProvider} interface that provides SelectorData
+ * retrieval functionality within the Apache ShenYu gateway ecosystem.
+ *
+ * This class implements the Data Provider pattern by serving as a specialized data source
+ * for selector information, which is crucial for request routing decisions in the gateway.
+ * Selectors define the coarse-grained routing rules that determine how incoming requests
+ * are matched to appropriate backend services.
+ *
+ * In the ShenYu architecture, this provider works within the plugin system to deliver
+ * selector configuration data that has been synchronized from the admin dashboard to the
+ * gateway's local cache. This enables efficient, in-memory lookup of routing rules during
+ * request processing without requiring database queries.
+ *
+ * The provider leverages the {@link BaseDataCache} singleton instance to access
+ * selector information that is maintained current through ShenYu's data synchronization
+ * mechanisms (WebSocket, ZooKeeper, Nacos, etc.).
+ *
+ */
+public class SelectorDataProvider implements DataProvider {
+
+ /**
+ * Retrieves a list of SelectorData objects associated with the specified selector name.
+ *
+ * This method implements the core provider contract by querying the gateway's
+ * cache infrastructure for selector configuration data. Selectors contain matching
+ * rules and conditions that determine which plugin and backend service should handle
+ * incoming requests.
+ *
+ * In the ShenYu request processing pipeline, selectors work together with rules
+ * to form a two-level routing system:
+ *
+ * - Selectors perform coarse-grained routing based on request attributes
+ * - Rules apply fine-grained processing logic within matched selectors
+ *
+ *
+ *
+ * If no selectors are found for the given name, an empty list is returned rather
+ * than null, ensuring null-safe operation for consumers of this provider.
+ *
+ * @param selectorName the name of the selector to retrieve data for; this typically
+ * corresponds to a plugin name or specific selector identifier
+ * configured in the ShenYu admin dashboard
+ * @return a list of SelectorData objects associated with the specified name, or an
+ * empty list if no selectors are found for the given name (never null)
+ *
+ */
+ @Override
+ public List getData(final String selectorName) {
+ List data = BaseDataCache.getInstance().obtainSelectorData(selectorName);
+ return data;
+ }
+}
\ No newline at end of file