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
This commit is contained in:
		
							parent
							
								
									9b0c2cca95
								
							
						
					
					
						commit
						58e9b187fe
					
				| 
						 | 
					@ -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,12 +184,8 @@ public abstract class YamlProcessor {
 | 
				
			||||||
	protected Yaml createYaml() {
 | 
						protected Yaml createYaml() {
 | 
				
			||||||
		LoaderOptions loaderOptions = new LoaderOptions();
 | 
							LoaderOptions loaderOptions = new LoaderOptions();
 | 
				
			||||||
		loaderOptions.setAllowDuplicateKeys(false);
 | 
							loaderOptions.setAllowDuplicateKeys(false);
 | 
				
			||||||
 | 
							return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
 | 
				
			||||||
		if (!this.supportedTypes.isEmpty()) {
 | 
									new DumperOptions(), loaderOptions);
 | 
				
			||||||
			return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
 | 
					 | 
				
			||||||
					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) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -79,8 +82,8 @@ class YamlProcessorTests {
 | 
				
			||||||
	void badResource() {
 | 
						void badResource() {
 | 
				
			||||||
		setYaml("foo: bar\ncd\nspam:\n  foo: baz");
 | 
							setYaml("foo: bar\ncd\nspam:\n  foo: baz");
 | 
				
			||||||
		assertThatExceptionOfType(ScannerException.class)
 | 
							assertThatExceptionOfType(ScannerException.class)
 | 
				
			||||||
			.isThrownBy(() -> this.processor.process((properties, map) -> {}))
 | 
									.isThrownBy(() -> this.processor.process((properties, map) -> {}))
 | 
				
			||||||
			.withMessageContaining("line 3, column 1");
 | 
									.withMessageContaining("line 3, column 1");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@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,14 +141,23 @@ class YamlProcessorTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void customTypeSupportedByDefault() throws Exception {
 | 
						void standardTypesSupportedByDefault() throws Exception {
 | 
				
			||||||
 | 
							setYaml("value: !!set\n  ? first\n  ? second");
 | 
				
			||||||
 | 
							this.processor.process((properties, map) -> {
 | 
				
			||||||
 | 
								assertThat(properties).containsExactly(entry("value[0]", "first"), entry("value[1]", "second"));
 | 
				
			||||||
 | 
								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/");
 | 
							URL url = new URL("https://localhost:9000/");
 | 
				
			||||||
		setYaml("value: !!java.net.URL [\"" + url + "\"]");
 | 
							setYaml("value: !!java.net.URL [\"" + url + "\"]");
 | 
				
			||||||
 | 
							assertThatExceptionOfType(ConstructorException.class)
 | 
				
			||||||
		this.processor.process((properties, map) -> {
 | 
									.isThrownBy(() -> this.processor.process((properties, map) -> {}))
 | 
				
			||||||
			assertThat(properties).containsExactly(entry("value", url));
 | 
									.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
 | 
				
			||||||
			assertThat(map).containsExactly(entry("value", url));
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					@ -168,8 +180,8 @@ class YamlProcessorTests {
 | 
				
			||||||
		setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]");
 | 
							setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertThatExceptionOfType(ConstructorException.class)
 | 
							assertThatExceptionOfType(ConstructorException.class)
 | 
				
			||||||
			.isThrownBy(() -> this.processor.process((properties, map) -> {}))
 | 
									.isThrownBy(() -> this.processor.process((properties, map) -> {}))
 | 
				
			||||||
			.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
 | 
									.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private void setYaml(String yaml) {
 | 
						private void setYaml(String yaml) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue