Use new implementation in PropertyPlaceholderHelper
This commit removes the previous implementation in favor of the new PlaceholderParser. The only noticeable side effect is that the exception is no longer an IllegalArgumentException, but rather the dedicated PlaceholderResolutionException. See gh-9628
This commit is contained in:
parent
00e05e603d
commit
e3aa5b6b11
|
@ -101,8 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
|
||||||
|
|
||||||
Using the above configuration ensures Spring initialization failure if any `${}`
|
Using the above configuration ensures Spring initialization failure if any `${}`
|
||||||
placeholder could not be resolved. It is also possible to use methods like
|
placeholder could not be resolved. It is also possible to use methods like
|
||||||
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
|
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
|
||||||
placeholders.
|
`setEscapeCharacter` to customize placeholders.
|
||||||
|
|
||||||
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
|
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
|
||||||
will get properties from `application.properties` and `application.yml` files.
|
will get properties from `application.properties` and `application.yml` files.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -100,6 +100,8 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
|
||||||
/** Default value separator: {@value}. */
|
/** Default value separator: {@value}. */
|
||||||
public static final String DEFAULT_VALUE_SEPARATOR = ":";
|
public static final String DEFAULT_VALUE_SEPARATOR = ":";
|
||||||
|
|
||||||
|
/** Default escape character: {@value}. */
|
||||||
|
public static final Character DEFAULT_ESCAPE_CHARACTER = '\\';
|
||||||
|
|
||||||
/** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
|
/** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
|
||||||
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
|
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
|
||||||
|
@ -111,6 +113,10 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
|
||||||
@Nullable
|
@Nullable
|
||||||
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
|
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
|
||||||
|
|
||||||
|
/** Defaults to {@value #DEFAULT_ESCAPE_CHARACTER}. */
|
||||||
|
@Nullable
|
||||||
|
protected Character escapeCharacter = DEFAULT_ESCAPE_CHARACTER;
|
||||||
|
|
||||||
protected boolean trimValues = false;
|
protected boolean trimValues = false;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -151,6 +157,17 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
|
||||||
this.valueSeparator = valueSeparator;
|
this.valueSeparator = valueSeparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the escape character to use to ignore placeholder prefix
|
||||||
|
* or value separator, or {@code null} if no escaping should take
|
||||||
|
* place.
|
||||||
|
* <p>Default is {@value #DEFAULT_ESCAPE_CHARACTER}.
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
public void setEscapeCharacter(@Nullable Character escsEscapeCharacter) {
|
||||||
|
this.escapeCharacter = escsEscapeCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify whether to trim resolved values before applying them,
|
* Specify whether to trim resolved values before applying them,
|
||||||
* removing superfluous whitespace from the beginning and end.
|
* removing superfluous whitespace from the beginning and end.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -234,7 +234,8 @@ public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport
|
||||||
|
|
||||||
public PlaceholderResolvingStringValueResolver(Properties props) {
|
public PlaceholderResolvingStringValueResolver(Properties props) {
|
||||||
this.helper = new PropertyPlaceholderHelper(
|
this.helper = new PropertyPlaceholderHelper(
|
||||||
placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
|
placeholderPrefix, placeholderSuffix, valueSeparator,
|
||||||
|
ignoreUnresolvablePlaceholders, escapeCharacter);
|
||||||
this.resolver = new PropertyPlaceholderConfigurerResolver(props);
|
this.resolver = new PropertyPlaceholderConfigurerResolver(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -193,6 +193,7 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
|
||||||
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
|
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
|
||||||
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
|
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
|
||||||
propertyResolver.setValueSeparator(this.valueSeparator);
|
propertyResolver.setValueSeparator(this.valueSeparator);
|
||||||
|
propertyResolver.setEscapeCharacter(this.escapeCharacter);
|
||||||
|
|
||||||
StringValueResolver valueResolver = strVal -> {
|
StringValueResolver valueResolver = strVal -> {
|
||||||
String resolved = (this.ignoreUnresolvablePlaceholders ?
|
String resolved = (this.ignoreUnresolvablePlaceholders ?
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -40,6 +40,7 @@ import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.io.support.EncodedResource;
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||||
import org.springframework.core.io.support.PropertySourceFactory;
|
import org.springframework.core.io.support.PropertySourceFactory;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -132,7 +133,7 @@ class PropertySourceAnnotationTests {
|
||||||
void withUnresolvablePlaceholder() {
|
void withUnresolvablePlaceholder() {
|
||||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
|
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
|
||||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
.withCauseInstanceOf(PlaceholderResolutionException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -28,6 +28,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -136,7 +137,7 @@ class ContextNamespaceHandlerTests {
|
||||||
assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() ->
|
assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() ->
|
||||||
new ClassPathXmlApplicationContext("contextNamespaceHandlerTests-location-placeholder.xml", getClass()))
|
new ClassPathXmlApplicationContext("contextNamespaceHandlerTests-location-placeholder.xml", getClass()))
|
||||||
.havingRootCause()
|
.havingRootCause()
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(PlaceholderResolutionException.class)
|
||||||
.withMessage("Could not resolve placeholder 'foo' in value \"${foo}\"");
|
.withMessage("Could not resolve placeholder 'foo' in value \"${foo}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.testfixture.env.MockPropertySource;
|
import org.springframework.core.testfixture.env.MockPropertySource;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -170,7 +171,7 @@ class PropertySourcesPlaceholderConfigurerTests {
|
||||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||||
.isThrownBy(() -> ppc.postProcessBeanFactory(bf))
|
.isThrownBy(() -> ppc.postProcessBeanFactory(bf))
|
||||||
.havingCause()
|
.havingCause()
|
||||||
.isExactlyInstanceOf(IllegalArgumentException.class)
|
.isExactlyInstanceOf(PlaceholderResolutionException.class)
|
||||||
.withMessage("Could not resolve placeholder 'my.name' in value \"${my.name}\"");
|
.withMessage("Could not resolve placeholder 'my.name' in value \"${my.name}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +202,8 @@ class PropertySourcesPlaceholderConfigurerTests {
|
||||||
assertThatExceptionOfType(BeanCreationException.class)
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
.isThrownBy(context::refresh)
|
.isThrownBy(context::refresh)
|
||||||
.havingCause()
|
.havingCause()
|
||||||
.isExactlyInstanceOf(IllegalArgumentException.class)
|
.isExactlyInstanceOf(PlaceholderResolutionException.class)
|
||||||
.withMessage("Could not resolve placeholder 'enigma' in value \"${enigma}\"");
|
.withMessage("Could not resolve placeholder 'enigma' in value \"${enigma}\" <-- \"${my.key}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -102,6 +102,7 @@ dependencies {
|
||||||
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api")
|
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
||||||
|
testImplementation("org.mockito:mockito-core")
|
||||||
testImplementation("org.skyscreamer:jsonassert")
|
testImplementation("org.skyscreamer:jsonassert")
|
||||||
testImplementation("org.xmlunit:xmlunit-assertj")
|
testImplementation("org.xmlunit:xmlunit-assertj")
|
||||||
testImplementation("org.xmlunit:xmlunit-matchers")
|
testImplementation("org.xmlunit:xmlunit-matchers")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -521,6 +521,11 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||||
this.propertyResolver.setValueSeparator(valueSeparator);
|
this.propertyResolver.setValueSeparator(valueSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEscapeCharacter(@Nullable Character escapeCharacter) {
|
||||||
|
this.propertyResolver.setEscapeCharacter(escapeCharacter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
|
public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
|
||||||
this.propertyResolver.setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvableNestedPlaceholders);
|
this.propertyResolver.setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvableNestedPlaceholders);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -61,6 +61,9 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
|
||||||
@Nullable
|
@Nullable
|
||||||
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
|
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Character escapeCharacter = SystemPropertyUtils.ESCAPE_CHARACTER;
|
||||||
|
|
||||||
private final Set<String> requiredProperties = new LinkedHashSet<>();
|
private final Set<String> requiredProperties = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,6 +124,19 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
|
||||||
this.valueSeparator = valueSeparator;
|
this.valueSeparator = valueSeparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the escape character to use to ignore placeholder prefix
|
||||||
|
* or value separator, or {@code null} if no escaping should take
|
||||||
|
* place.
|
||||||
|
* <p>The default is "\".
|
||||||
|
* @since 6.2
|
||||||
|
* @see org.springframework.util.SystemPropertyUtils#ESCAPE_CHARACTER
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setEscapeCharacter(@Nullable Character escapeCharacter) {
|
||||||
|
this.escapeCharacter = escapeCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether to throw an exception when encountering an unresolvable placeholder
|
* Set whether to throw an exception when encountering an unresolvable placeholder
|
||||||
* nested within the value of a given property. A {@code false} value indicates strict
|
* nested within the value of a given property. A {@code false} value indicates strict
|
||||||
|
@ -232,7 +248,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
|
||||||
|
|
||||||
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
|
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
|
||||||
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
|
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
|
||||||
this.valueSeparator, ignoreUnresolvablePlaceholders);
|
this.valueSeparator, ignoreUnresolvablePlaceholders, this.escapeCharacter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
|
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -74,6 +74,14 @@ public interface ConfigurablePropertyResolver extends PropertyResolver {
|
||||||
*/
|
*/
|
||||||
void setValueSeparator(@Nullable String valueSeparator);
|
void setValueSeparator(@Nullable String valueSeparator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the escape character to use to ignore placeholder prefix
|
||||||
|
* or value separator, or {@code null} if no escaping should take
|
||||||
|
* place.
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
void setEscapeCharacter(@Nullable Character escapeCharacter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether to throw an exception when encountering an unresolvable placeholder
|
* Set whether to throw an exception when encountering an unresolvable placeholder
|
||||||
* nested within the value of a given property. A {@code false} value indicates strict
|
* nested within the value of a given property. A {@code false} value indicates strict
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -36,6 +36,7 @@ import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,8 +94,8 @@ public class PropertySourceProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (RuntimeException | IOException ex) {
|
catch (RuntimeException | IOException ex) {
|
||||||
// Placeholders not resolvable (IllegalArgumentException) or resource not found when trying to open it
|
// Placeholders not resolvable or resource not found when trying to open it
|
||||||
if (ignoreResourceNotFound && (ex instanceof IllegalArgumentException || isIgnorableException(ex) ||
|
if (ignoreResourceNotFound && (ex instanceof PlaceholderResolutionException || isIgnorableException(ex) ||
|
||||||
isIgnorableException(ex.getCause()))) {
|
isIgnorableException(ex.getCause()))) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
|
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -16,14 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.util;
|
package org.springframework.util;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
@ -37,31 +30,12 @@ import org.springframework.lang.Nullable;
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class PropertyPlaceholderHelper {
|
public class PropertyPlaceholderHelper {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(PropertyPlaceholderHelper.class);
|
private final PlaceholderParser parser;
|
||||||
|
|
||||||
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<>(4);
|
|
||||||
|
|
||||||
static {
|
|
||||||
wellKnownSimplePrefixes.put("}", "{");
|
|
||||||
wellKnownSimplePrefixes.put("]", "[");
|
|
||||||
wellKnownSimplePrefixes.put(")", "(");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final String placeholderPrefix;
|
|
||||||
|
|
||||||
private final String placeholderSuffix;
|
|
||||||
|
|
||||||
private final String simplePrefix;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private final String valueSeparator;
|
|
||||||
|
|
||||||
private final boolean ignoreUnresolvablePlaceholders;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +45,7 @@ public class PropertyPlaceholderHelper {
|
||||||
* @param placeholderSuffix the suffix that denotes the end of a placeholder
|
* @param placeholderSuffix the suffix that denotes the end of a placeholder
|
||||||
*/
|
*/
|
||||||
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
|
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
|
||||||
this(placeholderPrefix, placeholderSuffix, null, true);
|
this(placeholderPrefix, placeholderSuffix, null, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,23 +56,35 @@ public class PropertyPlaceholderHelper {
|
||||||
* and the associated default value, if any
|
* and the associated default value, if any
|
||||||
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should
|
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should
|
||||||
* be ignored ({@code true}) or cause an exception ({@code false})
|
* be ignored ({@code true}) or cause an exception ({@code false})
|
||||||
|
* @deprecated in favor of {@link PropertyPlaceholderHelper#PropertyPlaceholderHelper(String, String, String, boolean, Character)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.2", forRemoval = true)
|
||||||
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
|
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
|
||||||
@Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
|
@Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
|
||||||
|
|
||||||
|
this(placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and suffix.
|
||||||
|
* @param placeholderPrefix the prefix that denotes the start of a placeholder
|
||||||
|
* @param placeholderSuffix the suffix that denotes the end of a placeholder
|
||||||
|
* @param valueSeparator the separating character between the placeholder variable
|
||||||
|
* and the associated default value, if any
|
||||||
|
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should
|
||||||
|
* be ignored ({@code true}) or cause an exception ({@code false})
|
||||||
|
* @param escapeCharacter the escape character to use to ignore placeholder prefix
|
||||||
|
* or value separator, if any
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
|
||||||
|
@Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders,
|
||||||
|
@Nullable Character escapeCharacter) {
|
||||||
|
|
||||||
Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
|
Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
|
||||||
Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
|
Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
|
||||||
this.placeholderPrefix = placeholderPrefix;
|
this.parser = new PlaceholderParser(placeholderPrefix, placeholderSuffix,
|
||||||
this.placeholderSuffix = placeholderSuffix;
|
ignoreUnresolvablePlaceholders, valueSeparator, escapeCharacter);
|
||||||
String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
|
|
||||||
if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
|
|
||||||
this.simplePrefix = simplePrefixForSuffix;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.simplePrefix = this.placeholderPrefix;
|
|
||||||
}
|
|
||||||
this.valueSeparator = valueSeparator;
|
|
||||||
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,94 +109,11 @@ public class PropertyPlaceholderHelper {
|
||||||
*/
|
*/
|
||||||
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
|
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
|
||||||
Assert.notNull(value, "'value' must not be null");
|
Assert.notNull(value, "'value' must not be null");
|
||||||
return parseStringValue(value, placeholderResolver, null);
|
return parseStringValue(value, placeholderResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String parseStringValue(
|
protected String parseStringValue(String value, PlaceholderResolver placeholderResolver) {
|
||||||
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
|
return this.parser.replacePlaceholders(value, placeholderResolver);
|
||||||
|
|
||||||
int startIndex = value.indexOf(this.placeholderPrefix);
|
|
||||||
if (startIndex == -1) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder(value);
|
|
||||||
while (startIndex != -1) {
|
|
||||||
int endIndex = findPlaceholderEndIndex(result, startIndex);
|
|
||||||
if (endIndex != -1) {
|
|
||||||
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
|
|
||||||
String originalPlaceholder = placeholder;
|
|
||||||
if (visitedPlaceholders == null) {
|
|
||||||
visitedPlaceholders = new HashSet<>(4);
|
|
||||||
}
|
|
||||||
if (!visitedPlaceholders.add(originalPlaceholder)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
|
|
||||||
}
|
|
||||||
// Recursive invocation, parsing placeholders contained in the placeholder key.
|
|
||||||
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
|
|
||||||
// Now obtain the value for the fully resolved key...
|
|
||||||
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
|
|
||||||
if (propVal == null && this.valueSeparator != null) {
|
|
||||||
int separatorIndex = placeholder.indexOf(this.valueSeparator);
|
|
||||||
if (separatorIndex != -1) {
|
|
||||||
String actualPlaceholder = placeholder.substring(0, separatorIndex);
|
|
||||||
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
|
|
||||||
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
|
|
||||||
if (propVal == null) {
|
|
||||||
propVal = defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (propVal != null) {
|
|
||||||
// Recursive invocation, parsing placeholders contained in the
|
|
||||||
// previously resolved placeholder value.
|
|
||||||
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
|
|
||||||
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Resolved placeholder '" + placeholder + "'");
|
|
||||||
}
|
|
||||||
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
|
|
||||||
}
|
|
||||||
else if (this.ignoreUnresolvablePlaceholders) {
|
|
||||||
// Proceed with unprocessed value.
|
|
||||||
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalArgumentException("Could not resolve placeholder '" +
|
|
||||||
placeholder + "'" + " in value \"" + value + "\"");
|
|
||||||
}
|
|
||||||
visitedPlaceholders.remove(originalPlaceholder);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
startIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
|
||||||
int index = startIndex + this.placeholderPrefix.length();
|
|
||||||
int withinNestedPlaceholder = 0;
|
|
||||||
while (index < buf.length()) {
|
|
||||||
if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
|
|
||||||
if (withinNestedPlaceholder > 0) {
|
|
||||||
withinNestedPlaceholder--;
|
|
||||||
index = index + this.placeholderSuffix.length();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
|
|
||||||
withinNestedPlaceholder++;
|
|
||||||
index = index + this.simplePrefix.length();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -44,12 +44,17 @@ public abstract class SystemPropertyUtils {
|
||||||
/** Value separator for system property placeholders: {@value}. */
|
/** Value separator for system property placeholders: {@value}. */
|
||||||
public static final String VALUE_SEPARATOR = ":";
|
public static final String VALUE_SEPARATOR = ":";
|
||||||
|
|
||||||
|
/** Default escape character: {@value}. */
|
||||||
|
public static final Character ESCAPE_CHARACTER = '\\';
|
||||||
|
|
||||||
|
|
||||||
private static final PropertyPlaceholderHelper strictHelper =
|
private static final PropertyPlaceholderHelper strictHelper =
|
||||||
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
|
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR,
|
||||||
|
false, ESCAPE_CHARACTER);
|
||||||
|
|
||||||
private static final PropertyPlaceholderHelper nonStrictHelper =
|
private static final PropertyPlaceholderHelper nonStrictHelper =
|
||||||
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
|
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR,
|
||||||
|
true, ESCAPE_CHARACTER);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.core.convert.ConverterNotFoundException;
|
import org.springframework.core.convert.ConverterNotFoundException;
|
||||||
import org.springframework.core.testfixture.env.MockPropertySource;
|
import org.springframework.core.testfixture.env.MockPropertySource;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -227,7 +228,7 @@ class PropertySourcesPropertyResolverTests {
|
||||||
MutablePropertySources propertySources = new MutablePropertySources();
|
MutablePropertySources propertySources = new MutablePropertySources();
|
||||||
propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
|
propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
|
||||||
PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
|
PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
resolver.resolveRequiredPlaceholders("Replace this ${key} plus ${unknown}"));
|
resolver.resolveRequiredPlaceholders("Replace this ${key} plus ${unknown}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,11 +291,11 @@ class PropertySourcesPropertyResolverTests {
|
||||||
assertThat(pr.getProperty("p2")).isEqualTo("v2");
|
assertThat(pr.getProperty("p2")).isEqualTo("v2");
|
||||||
assertThat(pr.getProperty("p3")).isEqualTo("v1:v2");
|
assertThat(pr.getProperty("p3")).isEqualTo("v1:v2");
|
||||||
assertThat(pr.getProperty("p4")).isEqualTo("v1:v2");
|
assertThat(pr.getProperty("p4")).isEqualTo("v1:v2");
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
pr.getProperty("p5"))
|
pr.getProperty("p5"))
|
||||||
.withMessageContaining("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\"");
|
.withMessageContaining("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\"");
|
||||||
assertThat(pr.getProperty("p6")).isEqualTo("v1:v2:def");
|
assertThat(pr.getProperty("p6")).isEqualTo("v1:v2:def");
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
pr.getProperty("pL"))
|
pr.getProperty("pL"))
|
||||||
.withMessageContaining("Circular");
|
.withMessageContaining("Circular");
|
||||||
}
|
}
|
||||||
|
@ -315,7 +316,7 @@ class PropertySourcesPropertyResolverTests {
|
||||||
|
|
||||||
// placeholders nested within the value of "p4" are unresolvable and cause an
|
// placeholders nested within the value of "p4" are unresolvable and cause an
|
||||||
// exception by default
|
// exception by default
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
pr.getProperty("p4"))
|
pr.getProperty("p4"))
|
||||||
.withMessageContaining("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\"");
|
.withMessageContaining("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\"");
|
||||||
|
|
||||||
|
@ -327,7 +328,7 @@ class PropertySourcesPropertyResolverTests {
|
||||||
// resolve[Nested]Placeholders methods behave as usual regardless the value of
|
// resolve[Nested]Placeholders methods behave as usual regardless the value of
|
||||||
// ignoreUnresolvableNestedPlaceholders
|
// ignoreUnresolvableNestedPlaceholders
|
||||||
assertThat(pr.resolvePlaceholders("${p1}:${p2}:${bogus}")).isEqualTo("v1:v2:${bogus}");
|
assertThat(pr.resolvePlaceholders("${p1}:${p2}:${bogus}")).isEqualTo("v1:v2:${bogus}");
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
pr.resolveRequiredPlaceholders("${p1}:${p2}:${bogus}"))
|
pr.resolveRequiredPlaceholders("${p1}:${p2}:${bogus}"))
|
||||||
.withMessageContaining("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\"");
|
.withMessageContaining("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\"");
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,10 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.core.SpringProperties;
|
import org.springframework.core.SpringProperties;
|
||||||
import org.springframework.core.testfixture.env.MockPropertySource;
|
import org.springframework.core.testfixture.env.MockPropertySource;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
import static org.springframework.core.env.AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME;
|
import static org.springframework.core.env.AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME;
|
||||||
import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME;
|
import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME;
|
||||||
|
@ -207,9 +209,9 @@ class StandardEnvironmentTests {
|
||||||
void defaultProfileWithCircularPlaceholder() {
|
void defaultProfileWithCircularPlaceholder() {
|
||||||
try {
|
try {
|
||||||
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "${spring.profiles.default}");
|
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "${spring.profiles.default}");
|
||||||
assertThatIllegalArgumentException()
|
assertThatExceptionOfType(PlaceholderResolutionException.class)
|
||||||
.isThrownBy(environment::getDefaultProfiles)
|
.isThrownBy(environment::getDefaultProfiles)
|
||||||
.withMessage("Circular placeholder reference 'spring.profiles.default' in property definitions");
|
.withMessageContaining("Circular placeholder reference 'spring.profiles.default'");
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
System.clearProperty(DEFAULT_PROFILES_PROPERTY_NAME);
|
System.clearProperty(DEFAULT_PROFILES_PROPERTY_NAME);
|
||||||
|
|
|
@ -21,8 +21,10 @@ import java.beans.PropertyEditor;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.core.env.StandardEnvironment;
|
import org.springframework.core.env.StandardEnvironment;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +98,7 @@ class ResourceEditorTests {
|
||||||
PropertyEditor editor = new ResourceEditor(new DefaultResourceLoader(), new StandardEnvironment(), false);
|
PropertyEditor editor = new ResourceEditor(new DefaultResourceLoader(), new StandardEnvironment(), false);
|
||||||
System.setProperty("test.prop", "foo");
|
System.setProperty("test.prop", "foo");
|
||||||
try {
|
try {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() -> {
|
||||||
editor.setAsText("${test.prop}-${bar}");
|
editor.setAsText("${test.prop}-${bar}");
|
||||||
editor.getValue();
|
editor.getValue();
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,10 +32,12 @@ import org.springframework.core.env.StandardEnvironment;
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
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.assertThatNoException;
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PropertySourceProcessor}.
|
* Tests for {@link PropertySourceProcessor}.
|
||||||
|
@ -73,8 +75,8 @@ class PropertySourceProcessorTests {
|
||||||
class FailOnErrorTests {
|
class FailOnErrorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void processorFailsOnIllegalArgumentException() {
|
void processorFailsOnPlaceholderResolutionException() {
|
||||||
assertProcessorFailsOnError(IllegalArgumentExceptionPropertySourceFactory.class, IllegalArgumentException.class);
|
assertProcessorFailsOnError(PlaceholderResolutionExceptionPropertySourceFactory.class, PlaceholderResolutionException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -98,7 +100,7 @@ class PropertySourceProcessorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void processorIgnoresIllegalArgumentException() {
|
void processorIgnoresIllegalArgumentException() {
|
||||||
assertProcessorIgnoresFailure(IllegalArgumentExceptionPropertySourceFactory.class);
|
assertProcessorIgnoresFailure(PlaceholderResolutionExceptionPropertySourceFactory.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -134,11 +136,11 @@ class PropertySourceProcessorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class IllegalArgumentExceptionPropertySourceFactory implements PropertySourceFactory {
|
private static class PlaceholderResolutionExceptionPropertySourceFactory implements PropertySourceFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PropertySource<?> createPropertySource(String name, EncodedResource resource) {
|
public PropertySource<?> createPropertySource(String name, EncodedResource resource) {
|
||||||
throw new IllegalArgumentException("bogus");
|
throw mock(PlaceholderResolutionException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -24,9 +24,10 @@ import org.springframework.core.env.StandardEnvironment;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.FileUrlResource;
|
import org.springframework.core.io.FileUrlResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.util.PlaceholderResolutionException;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link ResourceArrayPropertyEditor}.
|
* Tests for {@link ResourceArrayPropertyEditor}.
|
||||||
|
@ -81,7 +82,7 @@ class ResourceArrayPropertyEditorTests {
|
||||||
false);
|
false);
|
||||||
System.setProperty("test.prop", "foo");
|
System.setProperty("test.prop", "foo");
|
||||||
try {
|
try {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
editor.setAsText("${test.prop}-${bar}"));
|
editor.setAsText("${test.prop}-${bar}"));
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -19,7 +19,6 @@ package org.springframework.util;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
@ -29,11 +28,11 @@ import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PropertyPlaceholderHelper}.
|
* Tests for {@link PropertyPlaceholderHelper}.
|
||||||
|
@ -116,16 +115,15 @@ class PropertyPlaceholderHelperTests {
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.setProperty("foo", "bar");
|
props.setProperty("foo", "bar");
|
||||||
|
|
||||||
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}", null, false);
|
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}", null, false, null);
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
helper.replacePlaceholders(text, props));
|
helper.replacePlaceholders(text, props));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class DefaultValueTests {
|
class DefaultValueTests {
|
||||||
|
|
||||||
private final PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}", ":", true);
|
private final PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}", ":", true, null);
|
||||||
|
|
||||||
@ParameterizedTest(name = "{0} -> {1}")
|
@ParameterizedTest(name = "{0} -> {1}")
|
||||||
@MethodSource("defaultValues")
|
@MethodSource("defaultValues")
|
||||||
|
@ -137,12 +135,11 @@ class PropertyPlaceholderHelperTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled("gh-26268")
|
|
||||||
void defaultValueIsNotEvaluatedEarly() {
|
void defaultValueIsNotEvaluatedEarly() {
|
||||||
PlaceholderResolver resolver = mockPlaceholderResolver("one", "1");
|
PlaceholderResolver resolver = mockPlaceholderResolver("one", "1");
|
||||||
assertThat(this.helper.replacePlaceholders("This is ${one:or${two}}",resolver)).isEqualTo("This is 1");
|
assertThat(this.helper.replacePlaceholders("This is ${one:or${two}}", resolver)).isEqualTo("This is 1");
|
||||||
verify(resolver).resolvePlaceholder("one");
|
verify(resolver).resolvePlaceholder("one");
|
||||||
verifyNoMoreInteractions(resolver);
|
verify(resolver, never()).resolvePlaceholder("two");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Stream<Arguments> defaultValues() {
|
static Stream<Arguments> defaultValues() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -21,7 +21,7 @@ import java.util.Map;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
|
@ -97,7 +97,7 @@ class SystemPropertyUtilsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void replaceWithNoDefault() {
|
void replaceWithNoDefault() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
assertThatExceptionOfType(PlaceholderResolutionException.class).isThrownBy(() ->
|
||||||
SystemPropertyUtils.resolvePlaceholders("${test.prop}"));
|
SystemPropertyUtils.resolvePlaceholders("${test.prop}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -68,7 +68,7 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
||||||
|
|
||||||
private String defaultUserDestinationPrefix = "/queue";
|
private String defaultUserDestinationPrefix = "/queue";
|
||||||
|
|
||||||
private final PropertyPlaceholderHelper placeholderHelper = new PropertyPlaceholderHelper("{", "}", null, false);
|
private final PropertyPlaceholderHelper placeholderHelper = new PropertyPlaceholderHelper("{", "}", null, false, null);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private MessageHeaderInitializer headerInitializer;
|
private MessageHeaderInitializer headerInitializer;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -586,7 +586,7 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
|
||||||
private final PlaceholderResolver resolver;
|
private final PlaceholderResolver resolver;
|
||||||
|
|
||||||
public StaticStringValueResolver(Map<String, String> values) {
|
public StaticStringValueResolver(Map<String, String> values) {
|
||||||
this.helper = new PropertyPlaceholderHelper("${", "}", ":", false);
|
this.helper = new PropertyPlaceholderHelper("${", "}", ":", false, null);
|
||||||
this.resolver = values::get;
|
this.resolver = values::get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
@ -39,11 +39,13 @@ public abstract class ServletContextPropertyUtils {
|
||||||
|
|
||||||
private static final PropertyPlaceholderHelper strictHelper =
|
private static final PropertyPlaceholderHelper strictHelper =
|
||||||
new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
|
new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
|
||||||
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, false);
|
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR,
|
||||||
|
false, SystemPropertyUtils.ESCAPE_CHARACTER);
|
||||||
|
|
||||||
private static final PropertyPlaceholderHelper nonStrictHelper =
|
private static final PropertyPlaceholderHelper nonStrictHelper =
|
||||||
new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
|
new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
|
||||||
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
|
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR,
|
||||||
|
true, SystemPropertyUtils.ESCAPE_CHARACTER);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue