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);