Make use of custom types configurable in YamlProcessor

Prior to this commit, there was no easy way to restrict what types could
be loaded from a YAML document in subclasses of YamlProcessor such as
YamlPropertiesFactoryBean and YamlMapFactoryBean.

This commit introduces a setSupportedTypes(Class<?>...) method in
YamlProcessor in order to address this. If no supported types are
configured, all types encountered in YAML documents will be supported.
If an unsupported type is encountered, an IllegalStateException will be
thrown when the corresponding YAML node is processed.

Closes gh-25152
This commit is contained in:
Sam Brannen
2020-05-29 13:32:52 +02:00
parent 16ab43d957
commit 008d011d37
2 changed files with 117 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,15 @@
package org.springframework.beans.factory.config;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.yaml.snakeyaml.constructor.ConstructorException;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.scanner.ScannerException;
@@ -34,6 +37,7 @@ import static org.junit.Assert.*;
*
* @author Dave Syer
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class YamlProcessorTests {
@@ -45,7 +49,7 @@ public class YamlProcessorTests {
@Test
public void arrayConvertedToIndexedBeanReference() {
this.processor.setResources(new ByteArrayResource("foo: bar\nbar: [1,2,3]".getBytes()));
setYaml("foo: bar\nbar: [1,2,3]");
this.processor.process((properties, map) -> {
assertEquals(4, properties.size());
assertEquals("bar", properties.get("foo"));
@@ -61,13 +65,13 @@ public class YamlProcessorTests {
@Test
public void testStringResource() {
this.processor.setResources(new ByteArrayResource("foo # a document that is a literal".getBytes()));
setYaml("foo # a document that is a literal");
this.processor.process((properties, map) -> assertEquals("foo", map.get("document")));
}
@Test
public void testBadDocumentStart() {
this.processor.setResources(new ByteArrayResource("foo # a document\nbar: baz".getBytes()));
setYaml("foo # a document\nbar: baz");
this.exception.expect(ParserException.class);
this.exception.expectMessage("line 2, column 1");
this.processor.process((properties, map) -> {});
@@ -75,7 +79,7 @@ public class YamlProcessorTests {
@Test
public void testBadResource() {
this.processor.setResources(new ByteArrayResource("foo: bar\ncd\nspam:\n foo: baz".getBytes()));
setYaml("foo: bar\ncd\nspam:\n foo: baz");
this.exception.expect(ScannerException.class);
this.exception.expectMessage("line 3, column 1");
this.processor.process((properties, map) -> {});
@@ -83,7 +87,7 @@ public class YamlProcessorTests {
@Test
public void mapConvertedToIndexedBeanReference() {
this.processor.setResources(new ByteArrayResource("foo: bar\nbar:\n spam: bucket".getBytes()));
setYaml("foo: bar\nbar:\n spam: bucket");
this.processor.process((properties, map) -> {
assertEquals("bucket", properties.get("bar.spam"));
assertEquals(2, properties.size());
@@ -92,7 +96,7 @@ public class YamlProcessorTests {
@Test
public void integerKeyBehaves() {
this.processor.setResources(new ByteArrayResource("foo: bar\n1: bar".getBytes()));
setYaml("foo: bar\n1: bar");
this.processor.process((properties, map) -> {
assertEquals("bar", properties.get("[1]"));
assertEquals(2, properties.size());
@@ -101,7 +105,7 @@ public class YamlProcessorTests {
@Test
public void integerDeepKeyBehaves() {
this.processor.setResources(new ByteArrayResource("foo:\n 1: bar".getBytes()));
setYaml("foo:\n 1: bar");
this.processor.process((properties, map) -> {
assertEquals("bar", properties.get("foo[1]"));
assertEquals(1, properties.size());
@@ -111,7 +115,7 @@ public class YamlProcessorTests {
@Test
@SuppressWarnings("unchecked")
public void flattenedMapIsSameAsPropertiesButOrdered() {
this.processor.setResources(new ByteArrayResource("foo: bar\nbar:\n spam: bucket".getBytes()));
setYaml("foo: bar\nbar:\n spam: bucket");
this.processor.process((properties, map) -> {
assertEquals("bucket", properties.get("bar.spam"));
assertEquals(2, properties.size());
@@ -124,4 +128,47 @@ public class YamlProcessorTests {
});
}
@Test
public void customTypeSupportedByDefault() throws Exception {
URL url = new URL("https://localhost:9000/");
setYaml("value: !!java.net.URL [\"" + url + "\"]");
this.processor.process((properties, map) -> {
assertEquals(1, properties.size());
assertEquals(1, map.size());
assertEquals(url, properties.get("value"));
assertEquals(url, map.get("value"));
});
}
@Test
public void customTypesSupportedDueToExplicitConfiguration() throws Exception {
this.processor.setSupportedTypes(URL.class, String.class);
URL url = new URL("https://localhost:9000/");
setYaml("value: !!java.net.URL [!!java.lang.String [\"" + url + "\"]]");
this.processor.process((properties, map) -> {
assertEquals(1, properties.size());
assertEquals(1, map.size());
assertEquals(url, properties.get("value"));
assertEquals(url, map.get("value"));
});
}
@Test
public void customTypeNotSupportedDueToExplicitConfiguration() {
this.processor.setSupportedTypes(List.class);
setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]");
this.exception.expect(ConstructorException.class);
this.exception.expectMessage("Unsupported type encountered in YAML document: java.net.URL");
this.processor.process((properties, map) -> {});
}
private void setYaml(String yaml) {
this.processor.setResources(new ByteArrayResource(yaml.getBytes()));
}
}