This is an automated email from the ASF dual-hosted git repository.
yu199195 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 567142e072 Fixed selector and rule match bug (#7055)
567142e072 is described below
commit 567142e07261b3e615ae8850b30f4421f455cc5d
Author: xiaoyu <[email protected]>
AuthorDate: Fri Sep 11 15:38:20 2026 +0800
Fixed selector and rule match bug (#7055)
* chore: add .gitnexus to .gitignore
* feat: add GitNexus documentation and configuration files
* chore: update .gitignore to include .claude and .agents
* fix: improve null handling in selector and rule data processing
* feat: add CLAUDE.md for project guidance and documentation
---------
Co-authored-by: yu.xiao <[email protected]>
---
.gitignore | 5 +-
.idea/vcs.xml | 7 +++
CLAUDE.md | 59 ++++++++++++++++++++++
.../shenyu/plugin/base/AbstractShenyuPlugin.java | 12 +++--
4 files changed, 77 insertions(+), 6 deletions(-)
diff --git a/.gitignore b/.gitignore
index e266500fd7..a152504936 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@ target/
*.class
*.jar
*.war
-*.zip
*.tar
*.tar.gz
.flattened-pom.xml
@@ -18,7 +17,8 @@ cobertura.ser
.settings/
.project
.classpath
-
+.claude
+.agents
# idea ignore
.idea/
!/.idea/icon.svg
@@ -53,3 +53,4 @@ Thumbs.db
# local worktrees
.worktrees/
+.gitnexus
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 35eb1ddfbb..1364320233 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
+ <component name="GitSharedSettings">
+ <option name="FORCE_PUSH_PROHIBITED_PATTERNS">
+ <list>
+ <option value="main" />
+ </list>
+ </option>
+ </component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000000..c97a4d203b
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,59 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository.
+
+## Project
+
+Apache ShenYu — a reactive API gateway for microservices. Java 17, Maven
multi-module build (root `pom.xml`, version `2.7.2-SNAPSHOT`). Use the wrapper
`./mvnw`, not a local `mvn`.
+
+## Commands
+
+```bash
+# Full build (checkstyle + RAT license check run automatically at validate
phase)
+./mvnw clean install -DskipTests
+
+# Faster build of one module and its dependencies
+./mvnw -pl shenyu-admin -am clean install -DskipTests
-Dmaven.javadoc.skip=true -Drat.skip=true -Djacoco.skip=true
+
+# Run all tests in a module
+./mvnw test -pl shenyu-common
+
+# Run a single test class / method (surefire)
+./mvnw test -pl shenyu-common -Dtest=GsonUtilsTest
+./mvnw test -pl shenyu-common -Dtest='GsonUtilsTest#testToJson'
+
+# Checkstyle only
+./mvnw checkstyle:check
+```
+
+- **Checkstyle is enforced on every build** (validate phase). Config:
`script/shenyu_checkstyle.xml`. It is strict (javadoc, import order, final
parameters); match the style of surrounding files.
+- **Apache RAT** enforces license headers: every new source file needs the
Apache 2.0 header (`script/checkstyle-header.txt`).
+- Distribution packages / Docker images: `make build-admin`, `make
build-bootstrap`, `make build-all-image` (see `Makefile`; builds via
`shenyu-dist/*`).
+
+## Architecture
+
+ShenYu is split into a **control plane** and a **data plane** that communicate
only through data-sync channels — the gateway never reads the database.
+
+- **`shenyu-admin`** — control plane. Spring MVC + MyBatis app (port 9095)
with a web dashboard. Persists plugins/selectors/rules/metadata (DB init
scripts under `db/`; H2 by default, MySQL/PG/Oracle supported). Structure:
`controller` → `service` → `mapper`, with `listener` publishing config-change
events.
+- **`shenyu-admin-listener`** + **`shenyu-sync-data-center`** — config sync
from admin to gateways. One submodule per channel: websocket (default), http
long-polling, zookeeper, nacos, etcd, consul, apollo, polaris. Gateway-side
subscribers update in-memory caches; changed config takes effect without
restart.
+- **`shenyu-bootstrap`** — data plane. A thin Spring WebFlux launcher; its
behavior is composed by which starters are on its classpath
(`shenyu-spring-boot-starter-*`). Port 9195.
+- **`shenyu-web`** — gateway runtime core. `ShenyuWebHandler`
(`shenyu-web/.../web/handler/ShenyuWebHandler.java`) executes an ordered chain
of plugins per request.
+- **`shenyu-plugin`** — all gateway features are plugins.
+ - `shenyu-plugin-api`: `ShenyuPlugin` (reactive `execute(exchange, chain)` +
`getOrder()`), `ShenyuPluginChain`, `ShenyuContext`.
+ - `shenyu-plugin-base`: `AbstractShenyuPlugin` implements selector → rule
matching against cached config, then calls the concrete plugin's `doExecute`;
`PluginDataHandler` implementations receive config updates from data sync into
per-plugin caches.
+ - Feature plugins grouped by category: `shenyu-plugin-proxy` (dubbo, grpc,
spring-cloud, sofa, tars, websocket, mqtt...), `-security`, `-logging`,
`-cache`, `-fault-tolerance`, `-ai`, `-mcp-server`, etc. A plugin is activated
by adding its starter to the bootstrap pom.
+- **`shenyu-spi`** — custom SPI extension mechanism: `@SPI` on the interface,
`@Join` on implementations, registration files under `META-INF/shenyu/`. Used
for load balancers, condition matchers, etc. (`shenyu-loadbalancer` plugs in
this way).
+- **`shenyu-client`** + **`shenyu-register-center`** — client-side API
registration. Backend services annotate endpoints (e.g.
`@ShenyuSpringMvcClient`) and register metadata/URIs to admin through a
register channel (http, zookeeper, nacos...). Admin consumes registrations
through `shenyu-disruptor` (async event pipeline).
+- **`shenyu-common`** — shared model (`PluginData`, `SelectorData`,
`RuleData`, `MetaData`), enums, constants, utils. Config semantics live here,
so changes ripple to both admin and gateway.
+
+Typical config flow: dashboard/API change in admin → DB write + change event →
sync channel → gateway subscriber updates cache → `AbstractShenyuPlugin`
matches subsequent requests against new selectors/rules.
+
+## Testing modules
+
+- Unit tests live per module (surefire).
+- `shenyu-integrated-test` and `shenyu-e2e` are Docker-based integration/e2e
suites run in CI; not part of a normal local build.
+- `shenyu-examples` contains sample backend services for manually exercising
the gateway.
+
+## GitNexus code intelligence
+
+This repo is indexed by the GitNexus MCP server (repo name `shenyu`). Prefer
`query`/`context` for finding execution flows across the admin↔gateway
boundary, and `impact` before refactoring widely-used symbols (e.g. anything in
`shenyu-common`). Skill docs live under `.claude/skills/gitnexus/`.
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 83b1e9fa81..87b4214c05 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
@@ -99,9 +99,11 @@ public abstract class AbstractShenyuPlugin implements
ShenyuPlugin {
if (Objects.nonNull(selectorData) &&
StringUtils.isBlank(selectorData.getId())) {
return handleSelectorIfNull(pluginName, exchange, chain);
}
- selectorData = defaultMatchSelector(exchange, selectors, path);
if (Objects.isNull(selectorData)) {
- return handleSelectorIfNull(pluginName, exchange, chain);
+ selectorData = defaultMatchSelector(exchange, selectors, path);
+ if (Objects.isNull(selectorData)) {
+ return handleSelectorIfNull(pluginName, exchange, chain);
+ }
}
printLog(selectorData, pluginName);
if (!selectorData.getContinued()) {
@@ -125,9 +127,11 @@ public abstract class AbstractShenyuPlugin implements
ShenyuPlugin {
if (Objects.nonNull(ruleData) && Objects.isNull(ruleData.getId())) {
return handleRuleIfNull(pluginName, exchange, chain);
}
- ruleData = defaultMatchRule(exchange, rules, path);
if (Objects.isNull(ruleData)) {
- return handleRuleIfNull(pluginName, exchange, chain);
+ ruleData = defaultMatchRule(exchange, rules, path);
+ if (Objects.isNull(ruleData)) {
+ return handleRuleIfNull(pluginName, exchange, chain);
+ }
}
printLog(ruleData, pluginName);
return doExecute(exchange, chain, selectorData, ruleData);