From 3b8668ef9b047fe1991268a4a65f737a559cf63f Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Fri, 14 Nov 2025 14:31:39 +0800 Subject: [PATCH 1/9] =?UTF-8?q?fixed:=20#6081=201.=E6=8A=BD=E5=8F=96Plugin?= =?UTF-8?q?Data=E3=80=81SelectorData=20=E5=92=8C=20RuleData=20=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E9=83=A8=E5=88=86=E4=B8=BA=20BaseData=202.=20?= =?UTF-8?q?=E8=A7=84=E5=88=99=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91=E9=87=8D?= =?UTF-8?q?=E6=9E=84=20=20=E5=AE=9A=E4=B9=89=E6=95=B0=E6=8D=AE=E5=86=B3?= =?UTF-8?q?=E7=AD=96=E5=A4=84=E7=90=86=E7=B1=BB=EF=BC=8C=E6=8F=90=E5=8F=96?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E9=83=A8=E5=88=86=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=B9=B6=E5=81=9A=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/shenyu/common/dto/BaseData.java | 107 +++++++++ .../apache/shenyu/common/dto/PluginData.java | 118 +--------- .../apache/shenyu/common/dto/RuleData.java | 220 ++++-------------- .../shenyu/common/dto/SelectorData.java | 194 ++++----------- .../plugin/base/AbstractShenyuPlugin.java | 161 ++++++------- .../maker/AbstractMatchDecisionMaker.java | 27 +++ .../base/maker/PluginDataDecisionMaker.java | 30 +++ .../base/maker/RuleDataDecisionMaker.java | 30 +++ .../base/maker/SelectorDataDecisionMaker.java | 30 +++ .../plugin/base/provider/DataProvider.java | 7 + .../base/provider/PluginDataProvider.java | 15 ++ .../base/provider/RuleDataProvider.java | 15 ++ .../base/provider/SelectorDataProvider.java | 18 ++ 13 files changed, 450 insertions(+), 522 deletions(-) create mode 100644 shenyu-common/src/main/java/org/apache/shenyu/common/dto/BaseData.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/AbstractMatchDecisionMaker.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/PluginDataDecisionMaker.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/RuleDataDecisionMaker.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/SelectorDataDecisionMaker.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/DataProvider.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/PluginDataProvider.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/RuleDataProvider.java create mode 100644 shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/SelectorDataProvider.java diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/BaseData.java b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/BaseData.java new file mode 100644 index 000000000000..f9a95bbead05 --- /dev/null +++ b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/BaseData.java @@ -0,0 +1,107 @@ +package org.apache.shenyu.common.dto; + +public class BaseData { + + protected String id; + + protected String name; + + protected Boolean enabled; + + protected Integer sort; + + protected String namespaceId; + + + /** + * get id. + * + * @return id + */ + public String getId() { + return id; + } + + /** + * set id. + * + * @param id id + */ + public void setId(final String id) { + this.id = id; + } + + /** + * get name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * set name. + * + * @param name name + */ + public void setName(final String name) { + this.name = name; + } + + /** + * get enabled. + * + * @return enabled + */ + public Boolean getEnabled() { + return enabled; + } + + + /** + * set enabled. + * + * @param enabled enabled + */ + public void setEnabled(final Boolean enabled) { + this.enabled = enabled; + } + + /** + * get sort. + * + * @return enabled + */ + public Integer getSort() { + return sort; + } + + + /** + * set sort. + * + * @param sort sort value + */ + public void setSort(final Integer sort) { + this.sort = sort; + } + + /** + * get namespaceId. + * + * @return namespaceId + */ + public String getNamespaceId() { + return namespaceId; + } + + /** + * set namespaceId. + * + * @param namespaceId namespaceId + */ + public void setNamespaceId(final String namespaceId) { + this.namespaceId = namespaceId; + } +} 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 1ed11aa14acd..7a6c462dd7d0 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 @@ -24,24 +24,14 @@ * * @since 2.0.0 */ -public class PluginData { - - private String id; - - private String name; +public class PluginData extends BaseData { private String config; private String role; - private Boolean enabled; - - private Integer sort; - private String pluginJar; - private String namespaceId; - /** * no args constructor. */ @@ -132,41 +122,6 @@ public static Builder builder() { return new Builder(); } - /** - * get id. - * - * @return id - */ - public String getId() { - return id; - } - - /** - * set id. - * - * @param id id - */ - public void setId(final String id) { - this.id = id; - } - - /** - * get name. - * - * @return name - */ - public String getName() { - return name; - } - - /** - * set name. - * - * @param name name - */ - public void setName(final String name) { - this.name = name; - } /** * get config. @@ -204,14 +159,6 @@ public void setRole(final String role) { this.role = role; } - /** - * get enabled. - * - * @return enabled - */ - public Boolean getEnabled() { - return enabled; - } /** * get pluginJar. @@ -231,52 +178,6 @@ public void setPluginJar(final String pluginJar) { this.pluginJar = pluginJar; } - /** - * get sort. - * - * @return enabled - */ - public Integer getSort() { - return sort; - } - - - /** - * set sort. - * - * @param sort sort value - */ - public void setSort(final Integer sort) { - this.sort = sort; - } - - /** - * set enabled. - * - * @param enabled enabled - */ - public void setEnabled(final Boolean enabled) { - this.enabled = enabled; - } - - /** - * get namespaceId. - * - * @return namespaceId - */ - public String getNamespaceId() { - return namespaceId; - } - - /** - * set namespaceId. - * - * @param namespaceId namespaceId - */ - public void setNamespaceId(final String namespaceId) { - this.namespaceId = namespaceId; - } - @Override public boolean equals(final Object o) { if (this == o) { @@ -287,8 +188,8 @@ public boolean equals(final Object o) { } PluginData that = (PluginData) o; return Objects.equals(id, that.id) && Objects.equals(name, that.name) && Objects.equals(config, that.config) - && Objects.equals(role, that.role) && Objects.equals(enabled, that.enabled) && Objects.equals(sort, that.sort) - && Objects.equals(pluginJar, that.pluginJar) && Objects.equals(namespaceId, that.namespaceId); + && Objects.equals(role, that.role) && Objects.equals(enabled, that.enabled) && Objects.equals(sort, that.sort) + && Objects.equals(pluginJar, that.pluginJar) && Objects.equals(namespaceId, that.namespaceId); } @Override @@ -296,24 +197,13 @@ public int hashCode() { return Objects.hash(id, name, config, role, enabled, sort, pluginJar, namespaceId); } - 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. */ 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 2e2efa05d4f6..d0d2abc736aa 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 @@ -25,11 +25,7 @@ * * @since 2.0.0 */ -public class RuleData { - - private String id; - - private String name; +public class RuleData extends BaseData{ private String pluginName; @@ -40,10 +36,6 @@ public class RuleData { */ private Integer matchMode; - private Integer sort; - - private Boolean enabled; - private Boolean loged; /** @@ -54,16 +46,11 @@ public class RuleData { private List conditionDataList; private List beforeConditionDataList; - + /** * match restful. */ private Boolean matchRestful; - - /** - * namespaceId. - */ - private String namespaceId; /** * no args constructor. @@ -101,46 +88,6 @@ public static Builder builder() { return new Builder(); } - /** - * get id. - * - * @return id - */ - public String getId() { - return id; - } - - /** - * set id. - * - * @param id id - * @return this - */ - public RuleData setId(final String id) { - this.id = id; - return this; - } - - /** - * get name. - * - * @return name - */ - public String getName() { - return name; - } - - /** - * set name. - * - * @param name name - * @return this - */ - public RuleData setName(final String name) { - this.name = name; - return this; - } - /** * get pluginName. * @@ -201,46 +148,6 @@ public RuleData setMatchMode(final Integer matchMode) { return this; } - /** - * get sort. - * - * @return sort - */ - public Integer getSort() { - return sort; - } - - /** - * set sort. - * - * @param sort sort - * @return this - */ - public RuleData setSort(final Integer sort) { - this.sort = sort; - return this; - } - - /** - * get enabled. - * - * @return enabled - */ - public Boolean getEnabled() { - return enabled; - } - - /** - * set enabled. - * - * @param enabled enabled - * @return this - */ - public RuleData setEnabled(final Boolean enabled) { - this.enabled = enabled; - return this; - } - /** * get loged. * @@ -318,7 +225,7 @@ public List getBeforeConditionDataList() { public void setBeforeConditionDataList(final List beforeConditionDataList) { this.beforeConditionDataList = beforeConditionDataList; } - + /** * get match restful. * @@ -327,7 +234,7 @@ public void setBeforeConditionDataList(final List beforeCondition public Boolean getMatchRestful() { return matchRestful; } - + /** * set match restful. * @@ -336,23 +243,8 @@ public Boolean getMatchRestful() { public void setMatchRestful(final Boolean matchRestful) { this.matchRestful = matchRestful; } - - /** - * get namespaceId. - * @return namespaceId - */ - public String getNamespaceId() { - return namespaceId; - } - - /** - * set namespaceId. - * @param namespaceId namespaceId - */ - public void setNamespaceId(final String namespaceId) { - this.namespaceId = namespaceId; - } - + + @Override public boolean equals(final Object o) { if (this == o) { @@ -363,67 +255,63 @@ public boolean equals(final Object o) { } final RuleData ruleData = (RuleData) o; return Objects.equals(id, ruleData.id) - && Objects.equals(name, ruleData.name) - && Objects.equals(pluginName, ruleData.pluginName) - && Objects.equals(selectorId, ruleData.selectorId) - && Objects.equals(matchMode, ruleData.matchMode) - && Objects.equals(sort, ruleData.sort) - && Objects.equals(enabled, ruleData.enabled) - && Objects.equals(loged, ruleData.loged) - && Objects.equals(handle, ruleData.handle) - && Objects.equals(conditionDataList, ruleData.conditionDataList) - && Objects.equals(beforeConditionDataList, ruleData.beforeConditionDataList) - && Objects.equals(matchRestful, ruleData.matchRestful) - && Objects.equals(namespaceId, ruleData.namespaceId); + && Objects.equals(name, ruleData.name) + && Objects.equals(pluginName, ruleData.pluginName) + && Objects.equals(selectorId, ruleData.selectorId) + && Objects.equals(matchMode, ruleData.matchMode) + && Objects.equals(sort, ruleData.sort) + && Objects.equals(enabled, ruleData.enabled) + && Objects.equals(loged, ruleData.loged) + && Objects.equals(handle, ruleData.handle) + && Objects.equals(conditionDataList, ruleData.conditionDataList) + && Objects.equals(beforeConditionDataList, ruleData.beforeConditionDataList) + && Objects.equals(matchRestful, ruleData.matchRestful) + && Objects.equals(namespaceId, ruleData.namespaceId); } @Override public int hashCode() { return Objects.hash(id, name, pluginName, selectorId, matchMode, sort, enabled, loged, handle, conditionDataList, - beforeConditionDataList, matchRestful, namespaceId); + beforeConditionDataList, matchRestful, namespaceId); } @Override public String toString() { return "RuleData{" - + "id='" - + id - + '\'' - + ", name='" - + name - + '\'' - + ", pluginName='" - + pluginName - + '\'' - + ", selectorId='" - + selectorId - + '\'' - + ", matchMode=" - + matchMode - + ", sort=" - + sort - + ", enabled=" - + enabled - + ", loged=" - + loged - + ", handle='" - + handle - + '\'' - + ", conditionDataList=" - + conditionDataList - + ", matchRestful=" - + matchRestful - + '}'; + + "id='" + + id + + '\'' + + ", name='" + + name + + '\'' + + ", pluginName='" + + pluginName + + '\'' + + ", selectorId='" + + selectorId + + '\'' + + ", matchMode=" + + matchMode + + ", sort=" + + sort + + ", enabled=" + + enabled + + ", loged=" + + loged + + ", handle='" + + handle + + '\'' + + ", conditionDataList=" + + conditionDataList + + ", matchRestful=" + + matchRestful + + '}'; } /** * class builder. */ - public static final class Builder { - - private String id; - - private String name; + public static final class Builder extends BaseData { private String pluginName; @@ -431,10 +319,6 @@ public static final class Builder { private Integer matchMode; - private Integer sort; - - private Boolean enabled; - private Boolean loged; private String handle; @@ -442,10 +326,8 @@ public static final class Builder { private List conditionDataList; private List beforeConditionDataList; - + private Boolean matchRestful; - - private String namespaceId; /** * no args constructor. @@ -582,7 +464,7 @@ public Builder beforeConditionDataList(final List beforeCondition this.beforeConditionDataList = beforeConditionDataList; return this; } - + /** * build match restful. * @@ -593,7 +475,7 @@ public Builder matchRestful(final Boolean matchRestful) { this.matchRestful = matchRestful; return this; } - + /** * build namespaceId. * 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 616154c1a1c6..0749031a60a4 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 @@ -25,9 +25,7 @@ * * @since 2.0.0 */ -public class SelectorData { - - private String id; +public class SelectorData extends BaseData { private String pluginId; @@ -36,8 +34,6 @@ public class SelectorData { */ private String pluginName; - private String name; - /** * matchMode(0 and 1 or). */ @@ -48,10 +44,6 @@ public class SelectorData { */ private Integer type; - private Integer sort; - - private Boolean enabled; - private Boolean logged; private Boolean continued = Boolean.TRUE; @@ -67,11 +59,6 @@ public class SelectorData { */ private Boolean matchRestful; - /** - * namespaceId. - */ - private String namespaceId; - /** * no args constructor. */ @@ -110,23 +97,6 @@ public static Builder builder() { return new Builder(); } - /** - * get id. - * - * @return id - */ - public String getId() { - return id; - } - - /** - * set id. - * - * @param id id - */ - public void setId(final String id) { - this.id = id; - } /** * get pluginId. @@ -164,23 +134,6 @@ public void setPluginName(final String pluginName) { this.pluginName = pluginName; } - /** - * get name. - * - * @return name - */ - public String getName() { - return name; - } - - /** - * set name. - * - * @param name name - */ - public void setName(final String name) { - this.name = name; - } /** * get matchMode. @@ -218,42 +171,6 @@ public void setType(final Integer type) { this.type = type; } - /** - * get sort. - * - * @return sort - */ - public Integer getSort() { - return sort; - } - - /** - * set sort. - * - * @param sort sort - */ - public void setSort(final Integer sort) { - this.sort = sort; - } - - /** - * get enabled. - * - * @return enabled - */ - public Boolean getEnabled() { - return enabled; - } - - /** - * set enabled. - * - * @param enabled enabled - */ - public void setEnabled(final Boolean enabled) { - this.enabled = enabled; - } - /** * get logged. * @@ -365,24 +282,6 @@ public void setBeforeConditionList(final List beforeConditionList this.beforeConditionList = beforeConditionList; } - /** - * get namespaceId. - * - * @return namespaceId - */ - public String getNamespaceId() { - return namespaceId; - } - - /** - * set namespaceId. - * - * @param namespaceId namespaceId - */ - public void setNamespaceId(final String namespaceId) { - this.namespaceId = namespaceId; - } - @Override public boolean equals(final Object o) { if (this == o) { @@ -393,11 +292,11 @@ public boolean equals(final Object o) { } SelectorData that = (SelectorData) o; return Objects.equals(id, that.id) && Objects.equals(pluginId, that.pluginId) && Objects.equals(pluginName, that.pluginName) - && Objects.equals(name, that.name) && Objects.equals(matchMode, that.matchMode) && Objects.equals(type, that.type) - && Objects.equals(sort, that.sort) && Objects.equals(enabled, that.enabled) && Objects.equals(logged, that.logged) - && Objects.equals(continued, that.continued) && Objects.equals(handle, that.handle) && Objects.equals(conditionList, that.conditionList) - && Objects.equals(beforeConditionList, that.beforeConditionList) && Objects.equals(matchRestful, that.matchRestful) - && Objects.equals(namespaceId, that.namespaceId); + && Objects.equals(name, that.name) && Objects.equals(matchMode, that.matchMode) && Objects.equals(type, that.type) + && Objects.equals(sort, that.sort) && Objects.equals(enabled, that.enabled) && Objects.equals(logged, that.logged) + && Objects.equals(continued, that.continued) && Objects.equals(handle, that.handle) && Objects.equals(conditionList, that.conditionList) + && Objects.equals(beforeConditionList, that.beforeConditionList) && Objects.equals(matchRestful, that.matchRestful) + && Objects.equals(namespaceId, that.namespaceId); } @Override @@ -408,63 +307,55 @@ public int hashCode() { @Override public String toString() { return "SelectorData{" - + "id='" - + id - + '\'' - + ", pluginId='" - + pluginId - + '\'' - + ", pluginName='" - + pluginName - + '\'' - + ", name='" - + name - + '\'' - + ", matchMode=" - + matchMode - + ", type=" - + type - + ", sort=" - + sort - + ", enabled=" - + enabled - + ", logged=" - + logged - + ", continued=" - + continued - + ", handle='" - + handle - + '\'' - + ", conditionList=" - + conditionList - + ", matchRestful=" - + matchRestful - + ", namespaceId=" - + namespaceId - + '}'; + + "id='" + + id + + '\'' + + ", pluginId='" + + pluginId + + '\'' + + ", pluginName='" + + pluginName + + '\'' + + ", name='" + + name + + '\'' + + ", matchMode=" + + matchMode + + ", type=" + + type + + ", sort=" + + sort + + ", enabled=" + + enabled + + ", logged=" + + logged + + ", continued=" + + continued + + ", handle='" + + handle + + '\'' + + ", conditionList=" + + conditionList + + ", matchRestful=" + + matchRestful + + ", namespaceId=" + + namespaceId + + '}'; } /** * class builder. */ - public static final class Builder { - - private String id; + public static final class Builder extends BaseData { private String pluginId; private String pluginName; - private String name; - private Integer matchMode; private Integer type; - private Integer sort; - - private Boolean enabled; - private Boolean logged; private Boolean continued; @@ -476,9 +367,6 @@ public static final class Builder { private Boolean matchRestful; private List beforeConditionList; - - private String namespaceId; - /** * no args constructor. */ 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..38dbaf1ee605 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 @@ -37,6 +37,10 @@ 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.handler.PluginDataHandler; +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.apache.shenyu.plugin.base.trie.ShenyuTrie; import org.apache.shenyu.plugin.base.trie.ShenyuTrieNode; import org.slf4j.Logger; @@ -60,13 +64,13 @@ 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; /** @@ -92,71 +96,56 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin { 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); + + PluginDataDecisionMaker pluginDataDecisionMaker = new PluginDataDecisionMaker(); + 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); + + SelectorDataDecisionMaker selectorDataDecisionMaker = new SelectorDataDecisionMaker(); + List selectorDataList = selectorDataDecisionMaker.getData(pluginName); + if (CollectionUtils.isEmpty(selectorDataList)) { + return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } - 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); - } - } + + SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange, selectorDataList, path); + if (selectorData == null) { + 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); + + + RuleDataDecisionMaker ruleDataDecisionMaker = new RuleDataDecisionMaker(); + 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); - } - 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); - } - } + + RuleData ruleData = ruleDataDecisionMaker.matchData(exchange, ruleDataList, path); + if (ruleData == null) { + 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); @@ -168,18 +157,18 @@ private void initCacheConfig() { 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())) { + || Boolean.TRUE.equals(selectorData.getMatchRestful())) { return; } int initialCapacity = selectorMatchConfig.getCache().getInitialCapacity(); @@ -196,11 +185,11 @@ private void cacheSelectorData(final String path, final SelectorData selectorDat } } } - + 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())) { + || Boolean.TRUE.equals(ruleData.getMatchRestful())) { return; } int initialCapacity = ruleMatchConfig.getCache().getInitialCapacity(); @@ -225,7 +214,7 @@ private RuleData defaultRuleData(final SelectorData selectorData) { ruleData.setId(Constants.DEFAULT_RULE); return ruleData; } - + /** * Handle selector if null mono. * @@ -237,7 +226,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. * @@ -252,9 +241,9 @@ protected Mono handleRuleIfNull(final String pluginName, final ServerWebEx private Pair matchSelector(final ServerWebExchange exchange, final Collection selectors) { List filterCollectors = selectors.stream() - .filter(selector -> selector.getEnabled() && filterSelector(selector, exchange)) - .distinct() - .collect(Collectors.toList()); + .filter(selector -> selector.getEnabled() && filterSelector(selector, exchange)) + .distinct() + .collect(Collectors.toList()); if (filterCollectors.size() > 1) { return Pair.of(Boolean.FALSE, manyMatchSelector(filterCollectors)); } else { @@ -266,14 +255,14 @@ 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)); + 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); @@ -291,9 +280,9 @@ private Boolean filterSelector(final SelectorData selector, final ServerWebExcha private Pair matchRule(final ServerWebExchange exchange, final Collection rules) { List filterRuleData = rules.stream() - .filter(rule -> filterRule(rule, exchange)) - .distinct() - .collect(Collectors.toList()); + .filter(rule -> filterRule(rule, exchange)) + .distinct() + .collect(Collectors.toList()); if (filterRuleData.size() > 1) { return Pair.of(Boolean.FALSE, manyMatchRule(filterRuleData)); } else { @@ -303,14 +292,14 @@ private Pair matchRule(final ServerWebExchange exchange, fina 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)); + 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); @@ -319,7 +308,7 @@ private RuleData manyMatchRule(final List filterRuleData) { 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; @@ -347,7 +336,7 @@ private SelectorData trieMatchSelector(final ServerWebExchange exchange, final S } return selectorData; } - + private RuleData trieMatchRule(final ServerWebExchange exchange, final SelectorData selectorData, final String path) { if (!ruleMatchConfig.getTrie().getEnabled()) { return null; @@ -376,7 +365,7 @@ private RuleData trieMatchRule(final ServerWebExchange exchange, final SelectorD } return ruleData; } - + private SelectorData defaultMatchSelector(final ServerWebExchange exchange, final List selectors, final String path) { Pair matchSelectorPair = matchSelector(exchange, selectors); SelectorData selectorData = matchSelectorPair.getRight(); @@ -396,7 +385,7 @@ private SelectorData defaultMatchSelector(final ServerWebExchange exchange, fina return null; } } - + private RuleData defaultMatchRule(final ServerWebExchange exchange, final List rules, final String path) { Pair matchRulePair = matchRule(exchange, rules); RuleData ruleData = matchRulePair.getRight(); @@ -416,7 +405,7 @@ private RuleData defaultMatchRule(final ServerWebExchange exchange, final List { + private final DataProvider dataProvider; + + protected AbstractMatchDecisionMaker(DataProvider dataProvider) { + this.dataProvider = dataProvider; + } + + public List getData(String key) { + return dataProvider.getData(key); + } + + protected abstract Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain); + + protected abstract T matchData(ServerWebExchange exchange, List dataList, String path); + + 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..7513086ac087 --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/PluginDataDecisionMaker.java @@ -0,0 +1,30 @@ +package org.apache.shenyu.plugin.base.maker; + +import org.apache.shenyu.common.dto.PluginData; +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; + +public class PluginDataDecisionMaker extends AbstractMatchDecisionMaker { + public PluginDataDecisionMaker() { + super(new PluginDataProvider()); + } + + @Override + public Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain) { + return chain.execute(exchange); + } + + @Override + protected PluginData matchData(ServerWebExchange exchange, List dataList, String path) { + return dataList.isEmpty() ? null : dataList.get(0); + } + + @Override + public boolean shouldContinue(PluginData data) { + return data != null && 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..d75a11c8282d --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/RuleDataDecisionMaker.java @@ -0,0 +1,30 @@ +package org.apache.shenyu.plugin.base.maker; + +import org.apache.shenyu.common.dto.RuleData; +import org.apache.shenyu.plugin.api.ShenyuPluginChain; +import org.apache.shenyu.plugin.base.provider.RuleDataProvider; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.List; + +public class RuleDataDecisionMaker extends AbstractMatchDecisionMaker { + public RuleDataDecisionMaker() { + super(new RuleDataProvider()); + } + + @Override + public Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain) { + return chain.execute(exchange); + } + + @Override + public RuleData matchData(ServerWebExchange exchange, List dataList, String path) { + return dataList.isEmpty() ? null : dataList.get(0); + } + + @Override + protected boolean shouldContinue(RuleData data) { + return data != null && 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/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..ff373d026f8c --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/SelectorDataDecisionMaker.java @@ -0,0 +1,30 @@ +package org.apache.shenyu.plugin.base.maker; + +import org.apache.shenyu.common.dto.SelectorData; +import org.apache.shenyu.plugin.api.ShenyuPluginChain; +import org.apache.shenyu.plugin.base.provider.SelectorDataProvider; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.List; + +public class SelectorDataDecisionMaker extends AbstractMatchDecisionMaker { + public SelectorDataDecisionMaker() { + super(new SelectorDataProvider()); + } + + @Override + public Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain) { + return chain.execute(exchange); + } + + @Override + public SelectorData matchData(ServerWebExchange exchange, List dataList, String path) { + return dataList.isEmpty() ? null : dataList.get(0); + } + + @Override + public boolean shouldContinue(SelectorData data) { + return data != null && data.getEnabled(); + } +} \ 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..7bed0ebf1195 --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/DataProvider.java @@ -0,0 +1,7 @@ +package org.apache.shenyu.plugin.base.provider; + +import java.util.List; + +public interface DataProvider { + 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..b5e6697ad3d0 --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/PluginDataProvider.java @@ -0,0 +1,15 @@ +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; + +public class PluginDataProvider implements DataProvider { + @Override + public List getData(String pluginName) { + PluginData data = BaseDataCache.getInstance().obtainPluginData(pluginName); + return data != null ? 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..f6b73c8b9b88 --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/RuleDataProvider.java @@ -0,0 +1,15 @@ +package org.apache.shenyu.plugin.base.provider; +import org.apache.shenyu.common.dto.RuleData; +import org.apache.shenyu.common.dto.SelectorData; +import org.apache.shenyu.plugin.base.cache.BaseDataCache; + +import java.util.Collections; +import java.util.List; + +public class RuleDataProvider implements DataProvider { + @Override + public List getData(String ruleName) { + List data = BaseDataCache.getInstance().obtainRuleData(ruleName); + return data != null ? 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/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..d50dc4f80122 --- /dev/null +++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/SelectorDataProvider.java @@ -0,0 +1,18 @@ +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.Collections; +import java.util.List; + +public class SelectorDataProvider implements DataProvider { + @Override + public List getData(String selectorName) { + List data = BaseDataCache.getInstance().obtainSelectorData(selectorName); + return data != null ? data : Collections.emptyList(); + } +} \ No newline at end of file From c9a42172ef1d2185e18bfb5a2be8bc334e50835a Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Fri, 14 Nov 2025 15:06:17 +0800 Subject: [PATCH 2/9] =?UTF-8?q?fixed:=20=E8=A7=A3=E5=86=B3=E4=B8=8E?= =?UTF-8?q?=E4=B8=BB=E5=88=86=E6=94=AF=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/shenyu/common/dto/PluginData.java | 32 +++++++------------ .../apache/shenyu/common/dto/RuleData.java | 20 ++++++------ .../shenyu/common/dto/SelectorData.java | 20 ++++++------ 3 files changed, 31 insertions(+), 41 deletions(-) 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 b431c51954e6..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()); } /** @@ -405,7 +405,7 @@ public RuleData build() { * @return this */ public Builder id(final String id) { - this.id = id; + setId(id); return this; } @@ -416,7 +416,7 @@ public Builder id(final String id) { * @return this */ public Builder name(final String name) { - this.name = name; + setName(name); return this; } @@ -460,7 +460,7 @@ public Builder matchMode(final Integer matchMode) { * @return this */ public Builder sort(final Integer sort) { - this.sort = sort; + setSort(sort); return this; } @@ -471,7 +471,7 @@ public Builder sort(final Integer sort) { * @return this */ public Builder enabled(final Boolean enabled) { - this.enabled = enabled; + setEnabled(enabled); return this; } @@ -537,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 ae2d7b92421d..c688b72b1aaf 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()); } /** @@ -388,7 +388,7 @@ public SelectorData build() { * @return this */ public Builder id(final String id) { - this.id = id; + setId(id); return this; } @@ -421,7 +421,7 @@ public Builder pluginName(final String pluginName) { * @return this */ public Builder name(final String name) { - this.name = name; + setName(name); return this; } @@ -454,7 +454,7 @@ public Builder type(final Integer type) { * @return this */ public Builder sort(final Integer sort) { - this.sort = sort; + setSort(sort); return this; } @@ -465,7 +465,7 @@ public Builder sort(final Integer sort) { * @return this */ public Builder enabled(final Boolean enabled) { - this.enabled = enabled; + setEnabled(enabled); return this; } @@ -542,7 +542,7 @@ public Builder beforeConditionList(final List beforeConditionList * @return this */ public Builder namespaceId(final String namespaceId) { - this.namespaceId = namespaceId; + setNamespaceId(namespaceId); return this; } } From 837f02a8a4a7d9a811651201880e3214973f1adc Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Thu, 27 Nov 2025 16:49:34 +0800 Subject: [PATCH 3/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DAI=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/base/AbstractShenyuPlugin.java | 7 ++-- .../base/maker/PluginDataDecisionMaker.java | 2 +- .../base/maker/RuleDataDecisionMaker.java | 2 +- .../base/maker/SelectorDataDecisionMaker.java | 2 +- .../plugin/base/provider/DataProvider.java | 16 +++++++++ .../base/provider/PluginDataProvider.java | 35 +++++++++++++++++++ .../base/provider/SelectorDataProvider.java | 3 -- 7 files changed, 59 insertions(+), 8 deletions(-) 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 38dbaf1ee605..9eea4dab173d 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 @@ -73,6 +73,10 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin { private ShenyuConfig.RuleMatchCache ruleMatchConfig; + private SelectorDataDecisionMaker selectorDataDecisionMaker = new SelectorDataDecisionMaker(); + + private RuleDataDecisionMaker ruleDataDecisionMaker = new RuleDataDecisionMaker(); + /** * this is Template Method child has implements your own logic. * @@ -104,7 +108,6 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return pluginDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } - SelectorDataDecisionMaker selectorDataDecisionMaker = new SelectorDataDecisionMaker(); List selectorDataList = selectorDataDecisionMaker.getData(pluginName); if (CollectionUtils.isEmpty(selectorDataList)) { return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); @@ -121,7 +124,7 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh } - RuleDataDecisionMaker ruleDataDecisionMaker = new RuleDataDecisionMaker(); + List ruleDataList = ruleDataDecisionMaker.getData(selectorData.getId()); if (CollectionUtils.isEmpty(ruleDataList)) { return ruleDataDecisionMaker.handleEmpty(pluginName, exchange, chain); 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 index 7513086ac087..f02624af5fea 100644 --- 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 @@ -19,7 +19,7 @@ public Mono handleEmpty(String pluginName, ServerWebExchange exchange, She } @Override - protected PluginData matchData(ServerWebExchange exchange, List dataList, String path) { + public PluginData matchData(ServerWebExchange exchange, List dataList, String path) { return dataList.isEmpty() ? null : dataList.get(0); } 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 index d75a11c8282d..3136095767d9 100644 --- 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 @@ -24,7 +24,7 @@ public RuleData matchData(ServerWebExchange exchange, List dataList, S } @Override - protected boolean shouldContinue(RuleData data) { + public boolean shouldContinue(RuleData data) { return data != null && 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/SelectorDataDecisionMaker.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/SelectorDataDecisionMaker.java index ff373d026f8c..6b270d8c7cb5 100644 --- 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 @@ -25,6 +25,6 @@ public SelectorData matchData(ServerWebExchange exchange, List dat @Override public boolean shouldContinue(SelectorData data) { - return data != null && data.getEnabled(); + return data != null && data.getEnabled() && data.getContinued(); } } \ 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 index 7bed0ebf1195..c2d8a5b5b97d 100644 --- 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 @@ -2,6 +2,22 @@ import java.util.List; +/** + * A generic interface for providing data based on a given key. + *

+ * Implementations of this interface are responsible for retrieving a list of data items + * of type {@code T} associated with the specified key. This can be used in plugin systems + * or other contexts where data needs to be fetched dynamically. + * + * @param Names of various data types + */ 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 + */ 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 index b5e6697ad3d0..b31987c9f01f 100644 --- 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 @@ -6,7 +6,42 @@ import java.util.Collections; import java.util.List; +/** + * A concrete implementation of the data provider pattern for retrieving plugin data + * in the ShenYu gateway system. + *

+ * This class is responsible for fetching {@link PluginData} objects from the underlying + * data cache ({@link BaseDataCache}) based on the provided plugin name. It implements + * the {@code DataProvider} interface, providing a standardized way to + * access plugin configuration data throughout the application. + *

+ *

+ * In the ShenYu plugin architecture, this class serves as a specific layer in the data + * access mechanism, interacting with the system's caching infrastructure through an + * data provider interface to efficiently supply plugin information to + * upstream services or components that require it. + *

+ * + * @see org.apache.shenyu.plugin.base.cache.BaseDataCache + * @see PluginData + */ public class PluginDataProvider implements DataProvider { + + /** + * Retrieves plugin data from the base data cache for the specified plugin name. + *

+ * This method queries the internally maintained cache instance. If plugin data + * matching the given name is found, it is wrapped in a singleton list and returned. + * If no matching data is found (i.e., the plugin name does not exist in the cache), + * an empty immutable list is returned. + *

+ * + * @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(String pluginName) { PluginData data = BaseDataCache.getInstance().obtainPluginData(pluginName); 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 index d50dc4f80122..9339d2b4a239 100644 --- 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 @@ -1,8 +1,5 @@ package org.apache.shenyu.plugin.base.provider; - - - import org.apache.shenyu.common.dto.SelectorData; import org.apache.shenyu.plugin.base.cache.BaseDataCache; From 0148888a668c38edcc79624c14941cd08c334357 Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Tue, 2 Dec 2025 17:38:54 +0800 Subject: [PATCH 4/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DAI=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E7=9A=84=E6=B2=A1=E6=9C=89=E5=8C=B9=E9=85=8D=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/base/AbstractShenyuPlugin.java | 127 ++--------------- .../maker/AbstractMatchDecisionMaker.java | 4 +- .../base/maker/RuleDataDecisionMaker.java | 43 +++++- .../base/maker/SelectorDataDecisionMaker.java | 131 +++++++++++++++++- 4 files changed, 180 insertions(+), 125 deletions(-) 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 9eea4dab173d..0414c2fa21eb 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 @@ -63,13 +63,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 static final String URI_CONDITION_TYPE = "uri"; - private ShenyuTrie selectorTrie; - private ShenyuTrie ruleTrie; - private ShenyuConfig.SelectorMatchCache selectorMatchConfig; + private ShenyuTrie ruleTrie; private ShenyuConfig.RuleMatchCache ruleMatchConfig; @@ -77,6 +75,8 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin { private RuleDataDecisionMaker ruleDataDecisionMaker = new RuleDataDecisionMaker(); + private PluginDataDecisionMaker pluginDataDecisionMaker = new PluginDataDecisionMaker(); + /** * this is Template Method child has implements your own logic. * @@ -102,7 +102,7 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh final String pluginName = named(); final String path = getRawPath(exchange); - PluginDataDecisionMaker pluginDataDecisionMaker = new PluginDataDecisionMaker(); + List pluginDataList = pluginDataDecisionMaker.getData(pluginName); if (CollectionUtils.isEmpty(pluginDataList) || !pluginDataDecisionMaker.shouldContinue(pluginDataList.get(0))) { return pluginDataDecisionMaker.handleEmpty(pluginName, exchange, chain); @@ -113,7 +113,7 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } - SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange, selectorDataList, path); + SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange,pluginName, selectorDataList, path, selectorMatchConfig, selectorTrie); if (selectorData == null) { return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } @@ -169,25 +169,6 @@ 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. @@ -242,45 +223,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)) @@ -312,62 +254,9 @@ private Boolean filterRule(final RuleData ruleData, final ServerWebExchange exch 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); 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 index 2f823b8d918f..555283b8addb 100644 --- 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 @@ -1,8 +1,10 @@ package org.apache.shenyu.plugin.base.maker; +import org.apache.shenyu.common.config.ShenyuConfig; import org.apache.shenyu.common.dto.BaseData; import org.apache.shenyu.plugin.api.ShenyuPluginChain; import org.apache.shenyu.plugin.base.provider.DataProvider; +import org.apache.shenyu.plugin.base.trie.ShenyuTrie; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @@ -21,7 +23,7 @@ public List getData(String key) { protected abstract Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain); - protected abstract T matchData(ServerWebExchange exchange, List dataList, String path); + protected abstract T matchData(ServerWebExchange exchange, String dataName, List dataList, String path, ShenyuConfig.SelectorMatchCache selectorMatchConfig, ShenyuTrie selectorTrie); 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/RuleDataDecisionMaker.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/maker/RuleDataDecisionMaker.java index 3136095767d9..ec7fd69e7068 100644 --- 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 @@ -1,12 +1,21 @@ package org.apache.shenyu.plugin.base.maker; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.shenyu.common.config.ShenyuConfig; import org.apache.shenyu.common.dto.RuleData; +import org.apache.shenyu.common.dto.SelectorData; +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.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.List; +import java.util.Objects; public class RuleDataDecisionMaker extends AbstractMatchDecisionMaker { public RuleDataDecisionMaker() { @@ -19,12 +28,42 @@ public Mono handleEmpty(String pluginName, ServerWebExchange exchange, She } @Override - public RuleData matchData(ServerWebExchange exchange, List dataList, String path) { - return dataList.isEmpty() ? null : dataList.get(0); + public RuleData matchData(ServerWebExchange exchange, String ruleName, List dataList, String path, ShenyuConfig.SelectorMatchCache selectorMatchConfig, ShenyuTrie selectorTrie) { + return dataList.isEmpty() ? trieMatchRule() : dataList.get(0); } @Override public boolean shouldContinue(RuleData data) { return data != null && data.getEnabled(); } + + + 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; + } } \ 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 index 6b270d8c7cb5..a87d53247960 100644 --- 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 @@ -1,14 +1,39 @@ 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.List; +import java.util.*; +import java.util.stream.Collectors; + +import static org.apache.shenyu.plugin.api.ShenyuPlugin.LOG; public class SelectorDataDecisionMaker extends AbstractMatchDecisionMaker { + + private static final String URI_CONDITION_TYPE = "uri"; + + private ShenyuConfig.SelectorMatchCache selectorMatchConfig; + + private ShenyuTrie selectorTrie; + public SelectorDataDecisionMaker() { super(new SelectorDataProvider()); } @@ -19,12 +44,112 @@ public Mono handleEmpty(String pluginName, ServerWebExchange exchange, She } @Override - public SelectorData matchData(ServerWebExchange exchange, List dataList, String path) { - return dataList.isEmpty() ? null : dataList.get(0); + public SelectorData matchData(ServerWebExchange exchange,String pluginName, List dataList, String path,ShenyuConfig.SelectorMatchCache selectorMatchConfig,ShenyuTrie selectorTrie) { + return dataList.isEmpty() ? trieMatchSelector(exchange, pluginName, path, selectorMatchConfig, selectorTrie) : dataList.get(0); } @Override public boolean shouldContinue(SelectorData data) { return data != null && data.getEnabled() && data.getContinued(); } + + 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()); + } + } + + + private SelectorData trieMatchSelector(final ServerWebExchange exchange, final String pluginName, final String path, ShenyuConfig.SelectorMatchCache selectorMatchConfig, ShenyuTrie selectorTrie) { + 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; + } + + 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 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 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 void cacheSelectorData(final String path, final SelectorData selectorData,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 From 6a8ae43684ec351230fad0682fa5532a74033a98 Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Wed, 3 Dec 2025 17:46:59 +0800 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DAI=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/base/AbstractShenyuPlugin.java | 129 +-------- .../maker/AbstractMatchDecisionMaker.java | 6 +- .../base/maker/PluginDataDecisionMaker.java | 82 +++++- .../base/maker/RuleDataDecisionMaker.java | 249 +++++++++++++++++- .../base/maker/SelectorDataDecisionMaker.java | 171 +++++++++++- .../base/provider/SelectorDataProvider.java | 46 ++++ 6 files changed, 535 insertions(+), 148 deletions(-) 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 0414c2fa21eb..7f1fbf69408e 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 @@ -63,14 +63,6 @@ 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 ruleTrie; - - private ShenyuConfig.RuleMatchCache ruleMatchConfig; - private SelectorDataDecisionMaker selectorDataDecisionMaker = new SelectorDataDecisionMaker(); private RuleDataDecisionMaker ruleDataDecisionMaker = new RuleDataDecisionMaker(); @@ -98,7 +90,6 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin { */ @Override public Mono execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) { - initCacheConfig(); final String pluginName = named(); final String path = getRawPath(exchange); @@ -113,7 +104,7 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } - SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange,pluginName, selectorDataList, path, selectorMatchConfig, selectorTrie); + SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange,pluginName, selectorDataList, path, null); if (selectorData == null) { return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } @@ -136,7 +127,7 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return doExecute(exchange, chain, selectorData, rule); } - RuleData ruleData = ruleDataDecisionMaker.matchData(exchange, ruleDataList, path); + RuleData ruleData = ruleDataDecisionMaker.matchData(exchange,named(), ruleDataList, path, selectorData); if (ruleData == null) { return ruleDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } @@ -149,47 +140,6 @@ 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 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(); @@ -223,81 +173,6 @@ protected Mono handleRuleIfNull(final String pluginName, final ServerWebEx return chain.execute(exchange); } - 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 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} 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 index 555283b8addb..869aa721bb27 100644 --- 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 @@ -1,10 +1,9 @@ package org.apache.shenyu.plugin.base.maker; -import org.apache.shenyu.common.config.ShenyuConfig; 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.apache.shenyu.plugin.base.trie.ShenyuTrie; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @@ -12,6 +11,7 @@ public abstract class AbstractMatchDecisionMaker { private final DataProvider dataProvider; + protected static final String URI_CONDITION_TYPE = "uri"; protected AbstractMatchDecisionMaker(DataProvider dataProvider) { this.dataProvider = dataProvider; @@ -23,7 +23,7 @@ public List getData(String key) { protected abstract Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain); - protected abstract T matchData(ServerWebExchange exchange, String dataName, List dataList, String path, ShenyuConfig.SelectorMatchCache selectorMatchConfig, ShenyuTrie selectorTrie); + protected abstract T matchData(ServerWebExchange exchange, String dataName, List dataList, String path, SelectorData selectorData); 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 index f02624af5fea..bb6671703872 100644 --- 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 @@ -1,6 +1,7 @@ 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; @@ -8,21 +9,100 @@ 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: + *

    + *
  1. Plugin Level: Determines if a plugin is enabled and should process the request
  2. + *
  3. Selector Level: Handles coarse-grained routing decisions
  4. + *
  5. Rule Level: Applies fine-grained processing rules
  6. + *
+ * 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(String pluginName, ServerWebExchange exchange, 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(ServerWebExchange exchange, List dataList, String path) { + public PluginData matchData(ServerWebExchange exchange, String dataName, List dataList, String path, 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(PluginData data) { return data != null && data.getEnabled(); 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 index ec7fd69e7068..1d8fd05fa044 100644 --- 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 @@ -1,51 +1,180 @@ 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.List; -import java.util.Objects; +import java.util.*; +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: + *

    + *
  1. Selector Level: Coarse-grained routing using {@link SelectorData}
  2. + *
  3. Rule Level: Fine-grained processing logic using {@link RuleData}
  4. + *
+ * 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(String pluginName, ServerWebExchange exchange, 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: + *

    + *
  1. Direct matching: Uses pre-loaded rule data from the provided list when available
  2. + *
  3. Trie-based matching: Falls back to sophisticated trie-based path matching + * when the data list is empty
  4. + *
+ * 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(ServerWebExchange exchange, String ruleName, List dataList, String path, ShenyuConfig.SelectorMatchCache selectorMatchConfig, ShenyuTrie selectorTrie) { - return dataList.isEmpty() ? trieMatchRule() : dataList.get(0); + public RuleData matchData(ServerWebExchange exchange, String ruleName, List dataList, String path, 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(RuleData data) { return data != null && 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(); + } - private RuleData trieMatchRule(final ServerWebExchange exchange, final SelectorData selectorData, final String path) { + 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, 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", named()); + LogUtils.info(LOG, "{} rule match path from shenyu trie", ruleName); List collection = shenyuTrieNode.getPathCache().get(selectorData.getId()); if (CollectionUtils.isNotEmpty(collection)) { Pair ruleDataPair; @@ -66,4 +195,112 @@ private RuleData trieMatchRule(final ServerWebExchange exchange, final SelectorD } 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: + *

    + *
  1. Groups rules by the number of AND conditions matched
  2. + *
  3. Selects the group with the highest number of AND conditions
  4. + *
  5. Within that group, chooses the rule with the lowest sort value
  6. + *
+ * 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 index a87d53247960..99f5da82761e 100644 --- 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 @@ -26,33 +26,114 @@ 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 static final String URI_CONDITION_TYPE = "uri"; - 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(String pluginName, ServerWebExchange exchange, 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: + *

    + *
  1. If pre-filtered data is available in the dataList, use the first match
  2. + *
  3. Otherwise, perform trie-based matching on the request path
  4. + *
  5. Apply condition filtering for custom flow selectors
  6. + *
  7. Handle multiple matches using priority-based selection
  8. + *
+ *

+ * + * @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(ServerWebExchange exchange,String pluginName, List dataList, String path,ShenyuConfig.SelectorMatchCache selectorMatchConfig,ShenyuTrie selectorTrie) { - return dataList.isEmpty() ? trieMatchSelector(exchange, pluginName, path, selectorMatchConfig, selectorTrie) : dataList.get(0); + public SelectorData matchData(ServerWebExchange exchange, String pluginName, List dataList, String path, 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(SelectorData data) { return data != null && 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); @@ -64,8 +145,28 @@ private void initCacheConfig() { } } - - private SelectorData trieMatchSelector(final ServerWebExchange exchange, final String pluginName, final String path, ShenyuConfig.SelectorMatchCache selectorMatchConfig, ShenyuTrie selectorTrie) { + /** + * 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; } @@ -86,13 +187,26 @@ private SelectorData trieMatchSelector(final ServerWebExchange exchange, final S } selectorData = selectorDataPair.getRight(); if (selectorDataPair.getLeft() && Objects.nonNull(selectorData)) { - cacheSelectorData(path, selectorData,selectorMatchConfig); + 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)) @@ -105,7 +219,17 @@ private Pair matchSelector(final ServerWebExchange exchan } } - + /** + * 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())) { @@ -116,8 +240,22 @@ private Boolean filterSelector(final SelectorData selector, final ServerWebExcha 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, + // 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 -> { @@ -133,7 +271,18 @@ private SelectorData manyMatchSelector(final List filterCollectors return pairs.stream().map(Pair::getRight).min(Comparator.comparing(SelectorData::getSort)).orElse(null); } - private void cacheSelectorData(final String path, final SelectorData selectorData,ShenyuConfig.SelectorMatchCache selectorMatchConfig) { + /** + * 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, ShenyuConfig.SelectorMatchCache selectorMatchConfig) { if (Boolean.FALSE.equals(selectorMatchConfig.getCache().getEnabled()) || Objects.isNull(selectorData) || Boolean.TRUE.equals(selectorData.getMatchRestful())) { return; 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 index 9339d2b4a239..cca559c24174 100644 --- 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 @@ -6,7 +6,53 @@ import java.util.Collections; 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: + *

    + *
  1. Selectors perform coarse-grained routing based on request attributes
  2. + *
  3. Rules apply fine-grained processing logic within matched selectors
  4. + *
+ *

+ * + *

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(String selectorName) { List data = BaseDataCache.getInstance().obtainSelectorData(selectorName); From 4b81131e10644bd1cda7304881ed87efedc2f33c Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Wed, 17 Dec 2025 14:44:01 +0800 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20=E8=A1=A5=E5=85=85=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=E7=B1=BB=E7=9A=84=E7=B1=BB=E6=B3=A8=E9=87=8A=E5=92=8C=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/base/AbstractShenyuPlugin.java | 22 -------- .../maker/AbstractMatchDecisionMaker.java | 53 +++++++++++++++++-- .../base/provider/RuleDataProvider.java | 17 +++++- 3 files changed, 65 insertions(+), 27 deletions(-) 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 7f1fbf69408e..c70d07f87765 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,43 +18,21 @@ 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.handler.PluginDataHandler; 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.apache.shenyu.plugin.base.trie.ShenyuTrie; -import org.apache.shenyu.plugin.base.trie.ShenyuTrieNode; 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. 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 index 869aa721bb27..964deb4b53da 100644 --- 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 @@ -6,24 +6,71 @@ 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 { private final DataProvider dataProvider; protected static final String URI_CONDITION_TYPE = "uri"; + /** + * Constructs an AbstractMatchDecisionMaker with the specified data provider. + * + * @param dataProvider the data provider used to retrieve data for matching + */ protected AbstractMatchDecisionMaker(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(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/provider/RuleDataProvider.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/RuleDataProvider.java index f6b73c8b9b88..baf39bb9c13e 100644 --- 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 @@ -1,12 +1,25 @@ package org.apache.shenyu.plugin.base.provider; + import org.apache.shenyu.common.dto.RuleData; -import org.apache.shenyu.common.dto.SelectorData; import org.apache.shenyu.plugin.base.cache.BaseDataCache; - import java.util.Collections; 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(String ruleName) { List data = BaseDataCache.getInstance().obtainRuleData(ruleName); From 2bc515b44115561d5c0df630bd96d8cd16176d7c Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Mon, 12 Jan 2026 09:24:01 +0800 Subject: [PATCH 7/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DCI=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E7=9A=84=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=A0=BC=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/apache/shenyu/common/dto/SelectorData.java | 1 + 1 file changed, 1 insertion(+) 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 c688b72b1aaf..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 @@ -366,6 +366,7 @@ public static final class Builder extends BaseData { private Boolean matchRestful; private List beforeConditionList; + /** * no args constructor. */ From b809e7e2cd71b321c0bcb8d4fb99cedab0e78606 Mon Sep 17 00:00:00 2001 From: aias00 Date: Thu, 15 Jan 2026 15:01:33 +0800 Subject: [PATCH 8/9] Update shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/provider/DataProvider.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../plugin/base/provider/DataProvider.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 index c2d8a5b5b97d..a149e14969e2 100644 --- 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 @@ -10,8 +10,24 @@ * or other contexts where data needs to be fetched dynamically. * * @param Names of various data types + +/** + * A generic interface for providing data based on a given key. + *

+ * Implementations of this interface are responsible for retrieving a list of data items + * of type {@code T} associated with the specified key. This can be used in plugin systems + * or other contexts where data needs to be fetched dynamically. + * + * @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. * From 80554c08e265754a79738776dd08f8b739793f51 Mon Sep 17 00:00:00 2001 From: gumaomao <474317126@qq.com> Date: Mon, 19 Jan 2026 17:21:04 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=89=A9=E4=BD=99?= =?UTF-8?q?69=E4=B8=AACI=E6=8F=90=E7=A4=BA=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/base/AbstractShenyuPlugin.java | 12 +++--- .../maker/AbstractMatchDecisionMaker.java | 27 ++++++++++-- .../base/maker/PluginDataDecisionMaker.java | 25 +++++++++-- .../base/maker/RuleDataDecisionMaker.java | 37 +++++++++++++--- .../base/maker/SelectorDataDecisionMaker.java | 35 ++++++++++++--- .../plugin/base/provider/DataProvider.java | 30 +++++++------ .../base/provider/PluginDataProvider.java | 43 +++++++++---------- .../base/provider/RuleDataProvider.java | 23 ++++++++-- .../base/provider/SelectorDataProvider.java | 22 ++++++++-- 9 files changed, 185 insertions(+), 69 deletions(-) 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 c70d07f87765..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 @@ -33,6 +33,7 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.List; +import java.util.Objects; /** * abstract shenyu plugin please extends. @@ -71,7 +72,6 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh final String pluginName = named(); final String path = getRawPath(exchange); - List pluginDataList = pluginDataDecisionMaker.getData(pluginName); if (CollectionUtils.isEmpty(pluginDataList) || !pluginDataDecisionMaker.shouldContinue(pluginDataList.get(0))) { return pluginDataDecisionMaker.handleEmpty(pluginName, exchange, chain); @@ -82,8 +82,8 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } - SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange,pluginName, selectorDataList, path, null); - if (selectorData == null) { + SelectorData selectorData = selectorDataDecisionMaker.matchData(exchange, pluginName, selectorDataList, path, null); + if (Objects.isNull(selectorData)) { return selectorDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } @@ -92,8 +92,6 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return doExecute(exchange, chain, selectorData, defaultRuleData(selectorData)); } - - List ruleDataList = ruleDataDecisionMaker.getData(selectorData.getId()); if (CollectionUtils.isEmpty(ruleDataList)) { return ruleDataDecisionMaker.handleEmpty(pluginName, exchange, chain); @@ -105,8 +103,8 @@ public Mono execute(final ServerWebExchange exchange, final ShenyuPluginCh return doExecute(exchange, chain, selectorData, rule); } - RuleData ruleData = ruleDataDecisionMaker.matchData(exchange,named(), ruleDataList, path, selectorData); - if (ruleData == null) { + RuleData ruleData = ruleDataDecisionMaker.matchData(exchange, named(), ruleDataList, path, selectorData); + if (Objects.isNull(ruleData)) { return ruleDataDecisionMaker.handleEmpty(pluginName, exchange, chain); } 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 index 964deb4b53da..95d6ce703554 100644 --- 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 @@ -1,3 +1,20 @@ +/* + * 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; @@ -10,6 +27,7 @@ /** * 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, @@ -24,15 +42,18 @@ * @param the type of data to be matched, extending {@link BaseData} */ public abstract class AbstractMatchDecisionMaker { - private final DataProvider dataProvider; + 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(DataProvider dataProvider) { + protected AbstractMatchDecisionMaker(final DataProvider dataProvider) { this.dataProvider = dataProvider; } @@ -42,7 +63,7 @@ protected AbstractMatchDecisionMaker(DataProvider dataProvider) { * @param key the key used to retrieve data * @return a list of data objects associated with the key */ - public List getData(String key) { + public List getData(final String key) { return dataProvider.getData(key); } 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 index bb6671703872..b851770239d2 100644 --- 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 @@ -1,3 +1,20 @@ +/* + * 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; @@ -61,7 +78,7 @@ public PluginDataDecisionMaker() { * @return a Mono indicating completion of the empty handler operation */ @Override - public Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain) { + public Mono handleEmpty(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) { return chain.execute(exchange); } @@ -85,7 +102,7 @@ public Mono handleEmpty(String pluginName, ServerWebExchange exchange, She * @return the matched PluginData, or null if the data list is empty */ @Override - public PluginData matchData(ServerWebExchange exchange, String dataName, List dataList, String path, SelectorData selectorData) { + 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); } @@ -104,7 +121,7 @@ public PluginData matchData(ServerWebExchange exchange, String dataName, List { private ShenyuConfig.RuleMatchCache ruleMatchConfig; + private ShenyuTrie ruleTrie; /** @@ -86,7 +108,7 @@ public RuleDataDecisionMaker() { * @return a Mono indicating completion of the empty handler operation */ @Override - public Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain) { + public Mono handleEmpty(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) { return chain.execute(exchange); } @@ -111,7 +133,7 @@ public Mono handleEmpty(String pluginName, ServerWebExchange exchange, She * @return the matched RuleData, or null if no suitable rule is found */ @Override - public RuleData matchData(ServerWebExchange exchange, String ruleName, List dataList, String path, SelectorData selectorData) { + 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); } @@ -126,9 +148,10 @@ public RuleData matchData(ServerWebExchange exchange, String ruleName, List { private ShenyuConfig.SelectorMatchCache selectorMatchConfig; + private ShenyuTrie selectorTrie; /** @@ -78,7 +101,7 @@ public SelectorDataDecisionMaker() { * @return a Mono indicating completion of the empty handling operation */ @Override - public Mono handleEmpty(String pluginName, ServerWebExchange exchange, ShenyuPluginChain chain) { + public Mono handleEmpty(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) { return chain.execute(exchange); } @@ -107,7 +130,7 @@ public Mono handleEmpty(String pluginName, ServerWebExchange exchange, She * */ @Override - public SelectorData matchData(ServerWebExchange exchange, String pluginName, List dataList, String path, SelectorData selectorData) { + 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); } @@ -123,8 +146,8 @@ public SelectorData matchData(ServerWebExchange exchange, String pluginName, Lis * false otherwise */ @Override - public boolean shouldContinue(SelectorData data) { - return data != null && data.getEnabled() && data.getContinued(); + public boolean shouldContinue(final SelectorData data) { + return data.getEnabled() && data.getContinued(); } /** @@ -282,7 +305,7 @@ private SelectorData manyMatchSelector(final List filterCollectors * @param selectorData the selector data to cache * @param selectorMatchConfig the cache configuration parameters */ - private void cacheSelectorData(final String path, final SelectorData selectorData, ShenyuConfig.SelectorMatchCache selectorMatchConfig) { + 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; 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 index a149e14969e2..0a23ee1b8bfa 100644 --- 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 @@ -1,22 +1,26 @@ +/* + * 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. - *

- * Implementations of this interface are responsible for retrieving a list of data items - * of type {@code T} associated with the specified key. This can be used in plugin systems - * or other contexts where data needs to be fetched dynamically. - * - * @param Names of various data types - -/** - * A generic interface for providing data based on a given key. - *

- * Implementations of this interface are responsible for retrieving a list of data items - * of type {@code T} associated with the specified key. This can be used in plugin systems - * or other contexts where data needs to be fetched dynamically. * * @param the type of data provided by this provider */ 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 index b31987c9f01f..2b5d1d2b2e83 100644 --- 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 @@ -1,3 +1,20 @@ +/* + * 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; @@ -5,36 +22,16 @@ 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. - *

- * This class is responsible for fetching {@link PluginData} objects from the underlying - * data cache ({@link BaseDataCache}) based on the provided plugin name. It implements - * the {@code DataProvider} interface, providing a standardized way to - * access plugin configuration data throughout the application. - *

- *

- * In the ShenYu plugin architecture, this class serves as a specific layer in the data - * access mechanism, interacting with the system's caching infrastructure through an - * data provider interface to efficiently supply plugin information to - * upstream services or components that require it. - *

- * - * @see org.apache.shenyu.plugin.base.cache.BaseDataCache - * @see PluginData */ public class PluginDataProvider implements DataProvider { /** * Retrieves plugin data from the base data cache for the specified plugin name. - *

- * This method queries the internally maintained cache instance. If plugin data - * matching the given name is found, it is wrapped in a singleton list and returned. - * If no matching data is found (i.e., the plugin name does not exist in the cache), - * an empty immutable list is returned. - *

* * @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 @@ -43,8 +40,8 @@ public class PluginDataProvider implements DataProvider { * is found, otherwise an empty immutable list. The return value will never be null. */ @Override - public List getData(String pluginName) { + public List getData(final String pluginName) { PluginData data = BaseDataCache.getInstance().obtainPluginData(pluginName); - return data != null ? Collections.singletonList(data) : Collections.emptyList(); + 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 index baf39bb9c13e..80fad15bc714 100644 --- 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 @@ -1,8 +1,25 @@ +/* + * 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.Collections; + import java.util.List; /** @@ -21,8 +38,8 @@ public class RuleDataProvider implements DataProvider { * */ @Override - public List getData(String ruleName) { + public List getData(final String ruleName) { List data = BaseDataCache.getInstance().obtainRuleData(ruleName); - return data != null ? data : Collections.emptyList(); + 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 index cca559c24174..b9448349c2da 100644 --- 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 @@ -1,9 +1,25 @@ +/* + * 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.Collections; import java.util.List; /** @@ -54,8 +70,8 @@ public class SelectorDataProvider implements DataProvider { * */ @Override - public List getData(String selectorName) { + public List getData(final String selectorName) { List data = BaseDataCache.getInstance().obtainSelectorData(selectorName); - return data != null ? data : Collections.emptyList(); + return data; } } \ No newline at end of file