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

moremind 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 7c4031579e fix: prevent NPE in instance state checks for null states 
(#6706) (#7052)
7c4031579e is described below

commit 7c4031579ee26b8752df4f049be2565865c4f98b
Author: Southern <[email protected]>
AuthorDate: Thu Sep 10 21:48:36 2026 +0800

    fix: prevent NPE in instance state checks for null states (#6706) (#7052)
    
    - Normalize null instance states to the default code when loading instance 
data.
      - Replace unsafe primitive/object comparisons with Objects.equals.
      - Ignore null states when collecting instance state statistics.
      - Preserve nullable instanceState in InstanceInfoVO.
      - Add regression tests for null state handling in loading, scheduled 
checks, and visualization.
---
 .../shenyu/admin/model/vo/InstanceInfoVO.java      |  6 ++---
 .../admin/service/impl/InstanceCheckService.java   | 15 ++++++++---
 .../service/impl/InstanceInfoServiceImpl.java      |  2 ++
 .../service/impl/InstanceCheckServiceTest.java     | 30 ++++++++++++++++++++++
 4 files changed, 46 insertions(+), 7 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java
index 722f33a785..2eb204e235 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/InstanceInfoVO.java
@@ -56,7 +56,7 @@ public class InstanceInfoVO implements Serializable {
     /**
      * status.
      */
-    private int instanceState;
+    private Integer instanceState;
     
     /**
      * created time.
@@ -204,7 +204,7 @@ public class InstanceInfoVO implements Serializable {
      *
      * @return instanceState
      */
-    public int getInstanceState() {
+    public Integer getInstanceState() {
         return instanceState;
     }
 
@@ -213,7 +213,7 @@ public class InstanceInfoVO implements Serializable {
      *
      * @param instanceState instanceState
      */
-    public void setInstanceState(final int instanceState) {
+    public void setInstanceState(final Integer instanceState) {
         this.instanceState = instanceState;
     }
 
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java
index 0e06761f68..f6b1c92c18 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceCheckService.java
@@ -95,6 +95,9 @@ public class InstanceCheckService {
     public void fetchInstanceData() {
         List<InstanceInfoVO> list = instanceInfoService.list();
         list.forEach(instanceInfoVO -> {
+            if (Objects.isNull(instanceInfoVO.getInstanceState())) {
+                
instanceInfoVO.setInstanceState(InstanceStatusEnum.DELETED.getCode());
+            }
             String instanceKey = getInstanceKey(instanceInfoVO);
             instanceHealthBeatInfo.put(instanceKey, instanceInfoVO);
         });
@@ -146,7 +149,7 @@ public class InstanceCheckService {
     private void doCheck() {
         instanceHealthBeatInfo.values().forEach(instance -> {
             if (System.currentTimeMillis() - instance.getLastHeartBeatTime() > 
instanceHeartBeatTimeOut) {
-                if (InstanceStatusEnum.ONLINE.getCode() == 
instance.getInstanceState()) {
+                if (Objects.equals(InstanceStatusEnum.ONLINE.getCode(), 
instance.getInstanceState())) {
                     
LOG.info("[instanceHealthInfo]namespace:{},type:{},Ip:{},Port:{} offline!",
                             instance.getNamespaceId(), 
instance.getInstanceType(), instance.getInstanceIp(), 
instance.getInstancePort());
                     
instance.setInstanceState(InstanceStatusEnum.OFFLINE.getCode());
@@ -157,7 +160,7 @@ public class InstanceCheckService {
                 instance.setInstanceState(InstanceStatusEnum.ONLINE.getCode());
             }
             if (System.currentTimeMillis() - instance.getLastHeartBeatTime() > 
deleteTimeout) {
-                if (InstanceStatusEnum.OFFLINE.getCode() == 
instance.getInstanceState()) {
+                if (Objects.equals(InstanceStatusEnum.OFFLINE.getCode(), 
instance.getInstanceState())) {
                     
LOG.info("[instanceHealthInfo]namespace:{},type:{},Ip:{},Port:{} deleted!",
                             instance.getNamespaceId(), 
instance.getInstanceType(), instance.getInstanceIp(), 
instance.getInstancePort());
                     
instance.setInstanceState(InstanceStatusEnum.DELETED.getCode());
@@ -208,7 +211,9 @@ public class InstanceCheckService {
 
     private void collectStateData() {
         if (!CollectionUtils.isEmpty(instanceHealthBeatInfo)) {
-            Map<Integer, Long> pieData = 
instanceHealthBeatInfo.values().stream().collect(Collectors.groupingBy(InstanceInfoVO::getInstanceState,
 Collectors.counting()));
+            Map<Integer, Long> pieData = 
instanceHealthBeatInfo.values().stream()
+                    .filter(instance -> 
Objects.nonNull(instance.getInstanceState()))
+                    
.collect(Collectors.groupingBy(InstanceInfoVO::getInstanceState, 
Collectors.counting()));
             updateStateHistory(pieData);
         }
     }
@@ -219,7 +224,9 @@ public class InstanceCheckService {
         if (StringUtils.isNotBlank(namespaceId)) {
             instanceInfoVOS = instanceInfoVOS.stream().filter(vo -> 
namespaceId.equals(vo.getNamespaceId())).collect(Collectors.toList());
         }
-        Map<Integer, Long> pieData = 
instanceInfoVOS.stream().collect(Collectors.groupingBy(InstanceInfoVO::getInstanceState,
 Collectors.counting()));
+        Map<Integer, Long> pieData = instanceInfoVOS.stream()
+                .filter(instance -> 
Objects.nonNull(instance.getInstanceState()))
+                
.collect(Collectors.groupingBy(InstanceInfoVO::getInstanceState, 
Collectors.counting()));
         List<InstanceDataVisualLineVO> lineList = new ArrayList<>();
         for (Integer state : Arrays.asList(0, 1, 2)) {
             Deque<Long> queue = stateHistoryMap.getOrDefault(state, new 
ArrayDeque<>(MAX_HISTORY_SIZE));
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java
index 2cf0ffdb95..62fcf2dd9c 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/InstanceInfoServiceImpl.java
@@ -109,9 +109,11 @@ public class InstanceInfoServiceImpl implements 
InstanceInfoService {
         instanceInfoVO.setInstancePort(instanceInfoDO.getInstancePort());
         instanceInfoVO.setInstanceType(instanceInfoDO.getInstanceType());
         instanceInfoVO.setInstanceInfo(instanceInfoDO.getInstanceInfo());
+        instanceInfoVO.setInstanceState(instanceInfoDO.getInstanceState());
         instanceInfoVO.setNamespaceId(instanceInfoDO.getNamespaceId());
         instanceInfoVO.setDateCreated(instanceInfoDO.getDateCreated());
         instanceInfoVO.setDateUpdated(instanceInfoDO.getDateUpdated());
+        
instanceInfoVO.setLastHeartBeatTime(instanceInfoDO.getLastHeartBeatTime());
         return instanceInfoVO;
     }
 }
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/InstanceCheckServiceTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/InstanceCheckServiceTest.java
index e8e5740eba..2499602b46 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/InstanceCheckServiceTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/InstanceCheckServiceTest.java
@@ -25,6 +25,7 @@ import 
org.apache.shenyu.register.common.dto.InstanceBeatInfoDTO;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.util.ReflectionTestUtils;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -75,6 +76,16 @@ public final class InstanceCheckServiceTest {
         assertEquals(vo.getInstanceIp(), cached.getInstanceIp());
     }
 
+    @Test
+    void testFetchInstanceDataNormalizesNullState() {
+        vo.setInstanceState(null);
+        
when(instanceInfoService.list()).thenReturn(Collections.singletonList(vo));
+
+        instanceCheckService.fetchInstanceData();
+
+        assertEquals(0, vo.getInstanceState());
+    }
+
     @Test
     void testGetInstanceKeyForVOAndDTO() {
         String keyVo = instanceCheckService.getInstanceKey(vo);
@@ -162,6 +173,25 @@ public final class InstanceCheckServiceTest {
         assertThat(nsAData.getPieData(), hasSize(1));
     }
 
+    @Test
+    void testGetInstanceDataVisualIgnoresNullState() {
+        InstanceBeatInfoDTO dto = buildDTO("3.3.3.3", "8083", "grpc", "nsC");
+        instanceCheckService.handleBeatInfo(dto);
+        
instanceCheckService.getInstanceHealthBeatInfo(dto).setInstanceState(null);
+
+        assertDoesNotThrow(() -> 
instanceCheckService.getInstanceDataVisual(""));
+    }
+
+    @Test
+    void testDoCheckWithNullStateDoesNotThrow() {
+        InstanceBeatInfoDTO dto = buildDTO("4.4.4.4", "8084", "grpc", "nsD");
+        instanceCheckService.handleBeatInfo(dto);
+        InstanceInfoVO cached = 
instanceCheckService.getInstanceHealthBeatInfo(dto);
+        cached.setInstanceState(null);
+
+        assertDoesNotThrow(() -> 
ReflectionTestUtils.invokeMethod(instanceCheckService, "doCheck"));
+    }
+
     private InstanceInfoVO buildVO(final String ip, final String port, final 
String type, final String ns) {
         InstanceInfoVO v = new InstanceInfoVO();
         v.setInstanceIp(ip);

Reply via email to