This is an automated email from the ASF dual-hosted git repository.

Aias00 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 69399fe5af  fix: avoid NPE when selector/rule sync data is null 
(#6890) (#6925)
69399fe5af is described below

commit 69399fe5af8ddddf55fbc863d4c6c428de8ac4f6
Author: Southern <[email protected]>
AuthorDate: Fri Aug 21 10:18:58 2026 +0800

     fix: avoid NPE when selector/rule sync data is null (#6890) (#6925)
    
    When the http sync data is null, CollectionUtils.isEmpty(null) is true and 
the original code called data.forEach 
(pluginDataSubscriber::unSelectorSubscribe),
      which threw a NullPointerException. The forEach on the empty/null data 
was dead
      code in the empty-list case and the NPE trigger in the null case.
    
      Restructure refresh() to clear the cache unconditionally first, following 
the
      same implementation as PluginDataRefresh, and return early when the data 
is
      empty, so a null payload clears the cache instead of crashing. Behavior 
for
      empty and non-empty data is unchanged.
    
      Add regression tests covering refresh with null data for both refreshers.
    
    Co-authored-by: aias00 <[email protected]>
---
 .../shenyu/sync/data/http/refresh/RuleDataRefresh.java    |  9 +++------
 .../sync/data/http/refresh/SelectorDataRefresh.java       |  9 +++------
 .../sync/data/http/refresh/RuleDataRefreshTest.java       | 15 +++++++++++++++
 .../sync/data/http/refresh/SelectorDataRefreshTest.java   | 15 +++++++++++++++
 4 files changed, 36 insertions(+), 12 deletions(-)

diff --git 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefresh.java
 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefresh.java
index 08b7542d74..f85dc69cb7 100644
--- 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefresh.java
+++ 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefresh.java
@@ -69,14 +69,11 @@ public class RuleDataRefresh extends 
AbstractDataRefresh<RuleData> {
 
     @Override
     protected void refresh(final List<RuleData> data) {
+        pluginDataSubscriber.refreshRuleDataAll();
         if (CollectionUtils.isEmpty(data)) {
             LOG.info("clear all rule cache, old cache");
-            data.forEach(pluginDataSubscriber::unRuleSubscribe);
-            pluginDataSubscriber.refreshRuleDataAll();
-        } else {
-            // update cache for UpstreamCacheManager
-            pluginDataSubscriber.refreshRuleDataAll();
-            data.forEach(pluginDataSubscriber::onRuleSubscribe);
+            return;
         }
+        data.forEach(pluginDataSubscriber::onRuleSubscribe);
     }
 }
diff --git 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefresh.java
 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefresh.java
index 7d6fecd93b..29cce1b63e 100644
--- 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefresh.java
+++ 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefresh.java
@@ -69,14 +69,11 @@ public class SelectorDataRefresh extends 
AbstractDataRefresh<SelectorData> {
 
     @Override
     protected void refresh(final List<SelectorData> data) {
+        pluginDataSubscriber.refreshSelectorDataAll();
         if (CollectionUtils.isEmpty(data)) {
             LOG.info("clear all selector cache, old cache");
-            data.forEach(pluginDataSubscriber::unSelectorSubscribe);
-            pluginDataSubscriber.refreshSelectorDataAll();
-        } else {
-            // update cache for UpstreamCacheManager
-            pluginDataSubscriber.refreshSelectorDataAll();
-            data.forEach(pluginDataSubscriber::onSelectorSubscribe);
+            return;
         }
+        data.forEach(pluginDataSubscriber::onSelectorSubscribe);
     }
 }
diff --git 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefreshTest.java
 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefreshTest.java
index 29f0729782..6536bfb0a3 100644
--- 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefreshTest.java
+++ 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/RuleDataRefreshTest.java
@@ -32,6 +32,10 @@ import java.util.List;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 public final class RuleDataRefreshTest {
 
@@ -78,4 +82,15 @@ public final class RuleDataRefreshTest {
         ruleDataList.add(ruleData);
         ruleDataRefresh.refresh(ruleDataList);
     }
+
+    @Test
+    public void testRefreshWithNullData() {
+        final PluginDataSubscriber subscriber = 
mock(PluginDataSubscriber.class);
+        final RuleDataRefresh ruleDataRefresh = new 
RuleDataRefresh(subscriber);
+        // data is null, should not throw NPE; cache is still refreshed 
unconditionally, then return early
+        ruleDataRefresh.refresh((List<RuleData>) null);
+        verify(subscriber).refreshRuleDataAll();
+        verify(subscriber, never()).unRuleSubscribe(any(RuleData.class));
+        verify(subscriber, never()).onRuleSubscribe(any(RuleData.class));
+    }
 }
diff --git 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefreshTest.java
 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefreshTest.java
index 854a4a766b..d2ca4c2d85 100644
--- 
a/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefreshTest.java
+++ 
b/shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/SelectorDataRefreshTest.java
@@ -28,6 +28,10 @@ import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -78,4 +82,15 @@ public final class SelectorDataRefreshTest {
         selectorDataList.add(selectorData);
         selectorDataRefresh.refresh(selectorDataList);
     }
+
+    @Test
+    public void testRefreshWithNullData() {
+        final PluginDataSubscriber subscriber = 
mock(PluginDataSubscriber.class);
+        final SelectorDataRefresh selectorDataRefresh = new 
SelectorDataRefresh(subscriber);
+        // data is null, should not throw NPE; cache is still refreshed 
unconditionally, then return early
+        selectorDataRefresh.refresh((List<SelectorData>) null);
+        verify(subscriber).refreshSelectorDataAll();
+        verify(subscriber, 
never()).unSelectorSubscribe(any(SelectorData.class));
+        verify(subscriber, 
never()).onSelectorSubscribe(any(SelectorData.class));
+    }
 }

Reply via email to