This is an automated email from the ASF dual-hosted git repository.
zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new e11281fcfc0 Normalize null collections to empty in YAML constructor to
avoid NPE (#37450)
e11281fcfc0 is described below
commit e11281fcfc076aaea950edc212ab5de528f33b49
Author: Liang Zhang <[email protected]>
AuthorDate: Sat Dec 20 23:30:52 2025 +0800
Normalize null collections to empty in YAML constructor to avoid NPE
(#37450)
* Normalize null collections to empty in YAML constructor to avoid NPE
* Normalize null collections to empty in YAML constructor to avoid NPE
* Normalize null collections to empty in YAML constructor to avoid NPE
* Normalize null collections to empty in YAML constructor to avoid NPE
* Normalize null collections to empty in YAML constructor to avoid NPE
---
.../infra/yaml/data/pojo/YamlRowStatistics.java | 3 +-
.../constructor/ShardingSphereYamlConstructor.java | 47 ++++++++++++++++++++++
.../ClasspathResourceDirectoryReaderTest.java | 4 +-
.../infra/util/yaml/YamlEngineTest.java | 15 +++++++
.../YamlNullCollectionConfigurationFixture.java} | 18 +++++----
.../src/test/resources/yaml/null-collections.yaml | 21 ++++++++++
6 files changed, 98 insertions(+), 10 deletions(-)
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
index fdde3e390dd..27c16efa378 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
@@ -21,6 +21,7 @@ import lombok.Getter;
import lombok.Setter;
import org.apache.shardingsphere.infra.util.yaml.YamlConfiguration;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -32,5 +33,5 @@ public final class YamlRowStatistics implements
YamlConfiguration {
private String uniqueKey;
- private List<Object> rows;
+ private List<Object> rows = new ArrayList<>();
}
diff --git
a/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
b/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
index 29c18fd9c67..54dbe85e60b 100644
---
a/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
+++
b/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
@@ -18,6 +18,7 @@
package org.apache.shardingsphere.infra.util.yaml.constructor;
import com.google.common.base.Preconditions;
+import lombok.SneakyThrows;
import org.apache.shardingsphere.infra.spi.ShardingSphereServiceLoader;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import
org.apache.shardingsphere.infra.util.yaml.shortcuts.ShardingSphereYamlShortcuts;
@@ -25,11 +26,18 @@ import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.constructor.Construct;
import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
/**
* ShardingSphere YAML constructor.
@@ -65,6 +73,45 @@ public final class ShardingSphereYamlConstructor extends
Constructor {
return construct.isPresent() ? construct.get() :
super.getConstructor(node);
}
+ @Override
+ public Object constructObject(final Node node) {
+ Object result = super.constructObject(node);
+ if (isMappingNode(node, result)) {
+
getPropertyUtils().getProperties(result.getClass()).stream().filter(Property::isWritable).forEach(each
-> setEmptyCollectionIfNull(result, each));
+ }
+ return result;
+ }
+
+ private static boolean isMappingNode(final Node node, final Object target)
{
+ return null != target && node instanceof MappingNode && !(target
instanceof Map) && !(target instanceof Collection);
+ }
+
+ @SneakyThrows(Exception.class)
+ private void setEmptyCollectionIfNull(final Object target, final Property
property) {
+ if (null == property.get(target)) {
+ property.set(target, getEmptyCollection(property.getType()));
+ }
+ }
+
+ private Object getEmptyCollection(final Class<?> propertyType) {
+ if (Properties.class.isAssignableFrom(propertyType)) {
+ return new Properties();
+ }
+ if (Map.class.isAssignableFrom(propertyType)) {
+ return Collections.emptyMap();
+ }
+ if (Set.class.isAssignableFrom(propertyType)) {
+ return Collections.emptySet();
+ }
+ if (List.class.isAssignableFrom(propertyType)) {
+ return Collections.emptyList();
+ }
+ if (Collection.class.isAssignableFrom(propertyType)) {
+ return Collections.emptyList();
+ }
+ return null;
+ }
+
@Override
protected Class<?> getClassForName(final String className) throws
ClassNotFoundException {
Preconditions.checkArgument(className.equals(rootClass.getName()),
"Class `%s` is not accepted", className);
diff --git
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
index f8c58ff35e2..8ee6924309f 100644
---
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
+++
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
@@ -41,9 +41,9 @@ class ClasspathResourceDirectoryReaderTest {
@Test
void assertReadTest() {
List<String> resourceNameList =
ClasspathResourceDirectoryReader.read("yaml").collect(Collectors.toList());
- assertThat(resourceNameList.size(), is(5));
+ assertThat(resourceNameList.size(), is(6));
assertThat(resourceNameList, hasItems("yaml/accepted-class.yaml",
"yaml/customized-obj.yaml", "yaml/empty-config.yaml",
- "yaml/shortcuts-fixture.yaml", "yaml/fixture/fixture.yaml"));
+ "yaml/shortcuts-fixture.yaml", "yaml/null-collections.yaml",
"yaml/fixture/fixture.yaml"));
}
@Test
diff --git
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
index 962321f1fab..f93275f98be 100644
---
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
+++
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
@@ -18,6 +18,7 @@
package org.apache.shardingsphere.infra.util.yaml;
import org.apache.shardingsphere.infra.util.file.SystemResourceFileUtils;
+import
org.apache.shardingsphere.infra.util.yaml.fixture.YamlNullCollectionConfigurationFixture;
import
org.apache.shardingsphere.infra.util.yaml.fixture.shortcuts.YamlShortcutsConfigurationFixture;
import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.composer.ComposerException;
@@ -95,6 +96,20 @@ class YamlEngineTest {
assertTrue(actual.isEmpty());
}
+ @Test
+ void assertUnmarshalWithNullCollections() {
+ String yamlContent =
SystemResourceFileUtils.readFile("yaml/null-collections.yaml");
+ YamlNullCollectionConfigurationFixture actual =
YamlEngine.unmarshal(yamlContent, YamlNullCollectionConfigurationFixture.class);
+ assertNotNull(actual.getMap());
+ assertTrue(actual.getMap().isEmpty());
+ assertNotNull(actual.getSet());
+ assertTrue(actual.getSet().isEmpty());
+ assertNotNull(actual.getList());
+ assertTrue(actual.getList().isEmpty());
+ assertNotNull(actual.getCollection());
+ assertTrue(actual.getCollection().isEmpty());
+ }
+
@Test
void assertMarshal() {
YamlShortcutsConfigurationFixture actual = new
YamlShortcutsConfigurationFixture();
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/fixture/YamlNullCollectionConfigurationFixture.java
similarity index 72%
copy from
infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
copy to
infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/fixture/YamlNullCollectionConfigurationFixture.java
index fdde3e390dd..1497fbfe20d 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
+++
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/fixture/YamlNullCollectionConfigurationFixture.java
@@ -15,22 +15,26 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.infra.yaml.data.pojo;
+package org.apache.shardingsphere.infra.util.yaml.fixture;
import lombok.Getter;
import lombok.Setter;
import org.apache.shardingsphere.infra.util.yaml.YamlConfiguration;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
-/**
- * YAML row statistics.
- */
@Getter
@Setter
-public final class YamlRowStatistics implements YamlConfiguration {
+public final class YamlNullCollectionConfigurationFixture implements
YamlConfiguration {
+
+ private Map<String, String> map;
+
+ private Set<String> set;
- private String uniqueKey;
+ private List<String> list;
- private List<Object> rows;
+ private Collection<String> collection;
}
diff --git a/infra/util/src/test/resources/yaml/null-collections.yaml
b/infra/util/src/test/resources/yaml/null-collections.yaml
new file mode 100644
index 00000000000..f17a40e0805
--- /dev/null
+++ b/infra/util/src/test/resources/yaml/null-collections.yaml
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+map:
+set:
+list:
+collection: