Skip to content

Commit 58e9b18

Browse files
committed
Enforce standard Java types in YamlProcessor
`spring-beans` ships a `YamlProcessor` that's used as a base class by `YamlMapFactoryBean` and `YamlPropertiesFactoryBean`. These implementations have a clear use case: mapping application-internal Yaml documents for configuration or infrastructure purposes. Since this use case rarely requires extended types support from the underlying library, and since we're offering ways to list custom types (since #25152), we'll restrict to java standard types only by default. This simplifies the setup and focuses the abstract class on the core use cases. Closes gh-26530
1 parent 9b0c2cc commit 58e9b18

File tree

2 files changed

+36
-26
lines changed

2 files changed

+36
-26
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@
5252
* @author Dave Syer
5353
* @author Juergen Hoeller
5454
* @author Sam Brannen
55+
* @author Brian Clozel
5556
* @since 4.1
5657
*/
5758
public abstract class YamlProcessor {
@@ -128,10 +129,11 @@ public void setResources(Resource... resources) {
128129

129130
/**
130131
* Set the supported types that can be loaded from YAML documents.
131-
* <p>If no supported types are configured, all types encountered in YAML
132-
* documents will be supported. If an unsupported type is encountered, an
133-
* {@link IllegalStateException} will be thrown when the corresponding YAML
134-
* node is processed.
132+
* <p>If no supported types are configured, only Java standard classes
133+
* (as defined in {@link org.yaml.snakeyaml.constructor.SafeConstructor})
134+
* encountered in YAML documents will be supported.
135+
* If an unsupported type is encountered, an {@link IllegalStateException}
136+
* will be thrown when the corresponding YAML node is processed.
135137
* @param supportedTypes the supported types, or an empty array to clear the
136138
* supported types
137139
* @since 5.1.16
@@ -182,12 +184,8 @@ protected void process(MatchCallback callback) {
182184
protected Yaml createYaml() {
183185
LoaderOptions loaderOptions = new LoaderOptions();
184186
loaderOptions.setAllowDuplicateKeys(false);
185-
186-
if (!this.supportedTypes.isEmpty()) {
187-
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
188-
new DumperOptions(), loaderOptions);
189-
}
190-
return new Yaml(loaderOptions);
187+
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
188+
new DumperOptions(), loaderOptions);
191189
}
192190

193191
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {

spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
1717
package org.springframework.beans.factory.config;
1818

1919
import java.net.URL;
20+
import java.util.ArrayList;
2021
import java.util.LinkedHashMap;
2122
import java.util.List;
2223
import java.util.Map;
24+
import java.util.Set;
2325

2426
import org.junit.jupiter.api.Test;
2527
import org.yaml.snakeyaml.constructor.ConstructorException;
@@ -28,7 +30,6 @@
2830

2931
import org.springframework.core.io.ByteArrayResource;
3032

31-
import static java.util.stream.Collectors.toList;
3233
import static org.assertj.core.api.Assertions.assertThat;
3334
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3435
import static org.assertj.core.api.Assertions.entry;
@@ -39,10 +40,12 @@
3940
* @author Dave Syer
4041
* @author Juergen Hoeller
4142
* @author Sam Brannen
43+
* @author Brian Clozel
4244
*/
4345
class YamlProcessorTests {
4446

45-
private final YamlProcessor processor = new YamlProcessor() {};
47+
private final YamlProcessor processor = new YamlProcessor() {
48+
};
4649

4750

4851
@Test
@@ -79,8 +82,8 @@ void badDocumentStart() {
7982
void badResource() {
8083
setYaml("foo: bar\ncd\nspam:\n foo: baz");
8184
assertThatExceptionOfType(ScannerException.class)
82-
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
83-
.withMessageContaining("line 3, column 1");
85+
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
86+
.withMessageContaining("line 3, column 1");
8487
}
8588

8689
@Test
@@ -127,8 +130,8 @@ void flattenedMapIsSameAsPropertiesButOrdered() {
127130
Map<String, Object> bar = (Map<String, Object>) map.get("bar");
128131
assertThat(bar.get("spam")).isEqualTo("bucket");
129132

130-
List<Object> keysFromProperties = properties.keySet().stream().collect(toList());
131-
List<String> keysFromFlattenedMap = flattenedMap.keySet().stream().collect(toList());
133+
List<Object> keysFromProperties = new ArrayList<>(properties.keySet());
134+
List<String> keysFromFlattenedMap = new ArrayList<>(flattenedMap.keySet());
132135
assertThat(keysFromProperties).containsExactlyInAnyOrderElementsOf(keysFromFlattenedMap);
133136
// Keys in the Properties object are sorted.
134137
assertThat(keysFromProperties).containsExactly("bar.spam", "cat", "foo");
@@ -138,16 +141,25 @@ void flattenedMapIsSameAsPropertiesButOrdered() {
138141
}
139142

140143
@Test
141-
void customTypeSupportedByDefault() throws Exception {
142-
URL url = new URL("https://localhost:9000/");
143-
setYaml("value: !!java.net.URL [\"" + url + "\"]");
144-
144+
void standardTypesSupportedByDefault() throws Exception {
145+
setYaml("value: !!set\n ? first\n ? second");
145146
this.processor.process((properties, map) -> {
146-
assertThat(properties).containsExactly(entry("value", url));
147-
assertThat(map).containsExactly(entry("value", url));
147+
assertThat(properties).containsExactly(entry("value[0]", "first"), entry("value[1]", "second"));
148+
assertThat(map.get("value")).isInstanceOf(Set.class);
149+
Set<String> set = (Set<String>) map.get("value");
150+
assertThat(set).containsExactly("first", "second");
148151
});
149152
}
150153

154+
@Test
155+
void customTypeNotSupportedByDefault() throws Exception {
156+
URL url = new URL("https://localhost:9000/");
157+
setYaml("value: !!java.net.URL [\"" + url + "\"]");
158+
assertThatExceptionOfType(ConstructorException.class)
159+
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
160+
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
161+
}
162+
151163
@Test
152164
void customTypesSupportedDueToExplicitConfiguration() throws Exception {
153165
this.processor.setSupportedTypes(URL.class, String.class);
@@ -168,8 +180,8 @@ void customTypeNotSupportedDueToExplicitConfiguration() {
168180
setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]");
169181

170182
assertThatExceptionOfType(ConstructorException.class)
171-
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
172-
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
183+
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
184+
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
173185
}
174186

175187
private void setYaml(String yaml) {

0 commit comments

Comments
 (0)