Browse Source

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
pull/26538/head
Brian Clozel 4 years ago
parent
commit
58e9b187fe
  1. 16
      spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java
  2. 34
      spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -52,6 +52,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer * @author Dave Syer
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @author Brian Clozel
* @since 4.1 * @since 4.1
*/ */
public abstract class YamlProcessor { public abstract class YamlProcessor {
@ -128,10 +129,11 @@ public abstract class YamlProcessor {
/** /**
* Set the supported types that can be loaded from YAML documents. * Set the supported types that can be loaded from YAML documents.
* <p>If no supported types are configured, all types encountered in YAML * <p>If no supported types are configured, only Java standard classes
* documents will be supported. If an unsupported type is encountered, an * (as defined in {@link org.yaml.snakeyaml.constructor.SafeConstructor})
* {@link IllegalStateException} will be thrown when the corresponding YAML * encountered in YAML documents will be supported.
* node is processed. * If an unsupported type is encountered, an {@link IllegalStateException}
* will be thrown when the corresponding YAML node is processed.
* @param supportedTypes the supported types, or an empty array to clear the * @param supportedTypes the supported types, or an empty array to clear the
* supported types * supported types
* @since 5.1.16 * @since 5.1.16
@ -182,13 +184,9 @@ public abstract class YamlProcessor {
protected Yaml createYaml() { protected Yaml createYaml() {
LoaderOptions loaderOptions = new LoaderOptions(); LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setAllowDuplicateKeys(false); loaderOptions.setAllowDuplicateKeys(false);
if (!this.supportedTypes.isEmpty()) {
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(), return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
new DumperOptions(), loaderOptions); new DumperOptions(), loaderOptions);
} }
return new Yaml(loaderOptions);
}
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) { private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
int count = 0; int count = 0;

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,9 +17,11 @@
package org.springframework.beans.factory.config; package org.springframework.beans.factory.config;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.constructor.ConstructorException; import org.yaml.snakeyaml.constructor.ConstructorException;
@ -28,7 +30,6 @@ import org.yaml.snakeyaml.scanner.ScannerException;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.entry;
@ -39,10 +40,12 @@ import static org.assertj.core.api.Assertions.entry;
* @author Dave Syer * @author Dave Syer
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @author Brian Clozel
*/ */
class YamlProcessorTests { class YamlProcessorTests {
private final YamlProcessor processor = new YamlProcessor() {}; private final YamlProcessor processor = new YamlProcessor() {
};
@Test @Test
@ -127,8 +130,8 @@ class YamlProcessorTests {
Map<String, Object> bar = (Map<String, Object>) map.get("bar"); Map<String, Object> bar = (Map<String, Object>) map.get("bar");
assertThat(bar.get("spam")).isEqualTo("bucket"); assertThat(bar.get("spam")).isEqualTo("bucket");
List<Object> keysFromProperties = properties.keySet().stream().collect(toList()); List<Object> keysFromProperties = new ArrayList<>(properties.keySet());
List<String> keysFromFlattenedMap = flattenedMap.keySet().stream().collect(toList()); List<String> keysFromFlattenedMap = new ArrayList<>(flattenedMap.keySet());
assertThat(keysFromProperties).containsExactlyInAnyOrderElementsOf(keysFromFlattenedMap); assertThat(keysFromProperties).containsExactlyInAnyOrderElementsOf(keysFromFlattenedMap);
// Keys in the Properties object are sorted. // Keys in the Properties object are sorted.
assertThat(keysFromProperties).containsExactly("bar.spam", "cat", "foo"); assertThat(keysFromProperties).containsExactly("bar.spam", "cat", "foo");
@ -138,16 +141,25 @@ class YamlProcessorTests {
} }
@Test @Test
void customTypeSupportedByDefault() throws Exception { void standardTypesSupportedByDefault() throws Exception {
URL url = new URL("https://localhost:9000/"); setYaml("value: !!set\n ? first\n ? second");
setYaml("value: !!java.net.URL [\"" + url + "\"]");
this.processor.process((properties, map) -> { this.processor.process((properties, map) -> {
assertThat(properties).containsExactly(entry("value", url)); assertThat(properties).containsExactly(entry("value[0]", "first"), entry("value[1]", "second"));
assertThat(map).containsExactly(entry("value", url)); assertThat(map.get("value")).isInstanceOf(Set.class);
Set<String> set = (Set<String>) map.get("value");
assertThat(set).containsExactly("first", "second");
}); });
} }
@Test
void customTypeNotSupportedByDefault() throws Exception {
URL url = new URL("https://localhost:9000/");
setYaml("value: !!java.net.URL [\"" + url + "\"]");
assertThatExceptionOfType(ConstructorException.class)
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
}
@Test @Test
void customTypesSupportedDueToExplicitConfiguration() throws Exception { void customTypesSupportedDueToExplicitConfiguration() throws Exception {
this.processor.setSupportedTypes(URL.class, String.class); this.processor.setSupportedTypes(URL.class, String.class);

Loading…
Cancel
Save