@ -1,5 +1,5 @@
/ *
/ *
* Copyright 2002 - 2024 the original author or authors .
* Copyright 2002 - 2025 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,11 +19,13 @@ package org.springframework.context.support;
import java.util.Optional ;
import java.util.Optional ;
import java.util.Properties ;
import java.util.Properties ;
import org.junit.jupiter.api.Nested ;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.Test ;
import org.springframework.beans.factory.BeanCreationException ;
import org.springframework.beans.factory.BeanCreationException ;
import org.springframework.beans.factory.BeanDefinitionStoreException ;
import org.springframework.beans.factory.BeanDefinitionStoreException ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.beans.factory.config.BeanDefinition ;
import org.springframework.beans.factory.support.DefaultListableBeanFactory ;
import org.springframework.beans.factory.support.DefaultListableBeanFactory ;
import org.springframework.beans.testfixture.beans.TestBean ;
import org.springframework.beans.testfixture.beans.TestBean ;
import org.springframework.context.annotation.AnnotationConfigApplicationContext ;
import org.springframework.context.annotation.AnnotationConfigApplicationContext ;
@ -432,6 +434,150 @@ class PropertySourcesPlaceholderConfigurerTests {
}
}
/ * *
* Tests that use the escape character ( or disable it ) with nested placeholder
* resolution .
* /
@Nested
class EscapedNestedPlaceholdersTests {
@Test // gh-34861
void singleEscapeWithDefaultEscapeCharacter ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "admin" )
. withProperty ( "my.property" , "\\DOMAIN\\${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
ppc . postProcessBeanFactory ( bf ) ;
// \DOMAIN\${user.home} resolves to \DOMAIN${user.home} instead of \DOMAIN\admin
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "\\DOMAIN${user.home}" ) ;
}
@Test // gh-34861
void singleEscapeWithCustomEscapeCharacter ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "admin\\~${nested}" )
. withProperty ( "my.property" , "DOMAIN\\${user.home}\\~${enigma}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
// Set custom escape character.
ppc . setEscapeCharacter ( '~' ) ;
ppc . postProcessBeanFactory ( bf ) ;
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "DOMAIN\\admin\\${nested}\\${enigma}" ) ;
}
@Test // gh-34861
void singleEscapeWithEscapeCharacterDisabled ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "admin\\" )
. withProperty ( "my.property" , "\\DOMAIN\\${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
// Disable escape character.
ppc . setEscapeCharacter ( null ) ;
ppc . postProcessBeanFactory ( bf ) ;
// \DOMAIN\${user.home} resolves to \DOMAIN\admin
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "\\DOMAIN\\admin\\" ) ;
}
@Test // gh-34861
void tripleEscapeWithDefaultEscapeCharacter ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "admin\\\\\\" )
. withProperty ( "my.property" , "DOMAIN\\\\\\${user.home}#${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
ppc . postProcessBeanFactory ( bf ) ;
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "DOMAIN\\\\${user.home}#admin\\\\\\" ) ;
}
@Test // gh-34861
void tripleEscapeWithCustomEscapeCharacter ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "admin\\~${enigma}" )
. withProperty ( "my.property" , "DOMAIN~~~${user.home}#${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
// Set custom escape character.
ppc . setEscapeCharacter ( '~' ) ;
ppc . postProcessBeanFactory ( bf ) ;
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "DOMAIN~~${user.home}#admin\\${enigma}" ) ;
}
@Test // gh-34861
void singleEscapeWithDefaultEscapeCharacterAndIgnoreUnresolvablePlaceholders ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "${enigma}" )
. withProperty ( "my.property" , "\\${DOMAIN}${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
ppc . setIgnoreUnresolvablePlaceholders ( true ) ;
ppc . postProcessBeanFactory ( bf ) ;
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "${DOMAIN}${enigma}" ) ;
}
@Test // gh-34861
void singleEscapeWithCustomEscapeCharacterAndIgnoreUnresolvablePlaceholders ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "${enigma}" )
. withProperty ( "my.property" , "~${DOMAIN}\\${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
// Set custom escape character.
ppc . setEscapeCharacter ( '~' ) ;
ppc . setIgnoreUnresolvablePlaceholders ( true ) ;
ppc . postProcessBeanFactory ( bf ) ;
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "${DOMAIN}\\${enigma}" ) ;
}
@Test // gh-34861
void tripleEscapeWithDefaultEscapeCharacterAndIgnoreUnresolvablePlaceholders ( ) {
MockEnvironment env = new MockEnvironment ( )
. withProperty ( "user.home" , "${enigma}" )
. withProperty ( "my.property" , "X:\\\\\\${DOMAIN}${user.home}" ) ;
DefaultListableBeanFactory bf = createBeanFactory ( ) ;
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer ( ) ;
ppc . setEnvironment ( env ) ;
ppc . setIgnoreUnresolvablePlaceholders ( true ) ;
ppc . postProcessBeanFactory ( bf ) ;
assertThat ( bf . getBean ( TestBean . class ) . getName ( ) ) . isEqualTo ( "X:\\\\${DOMAIN}${enigma}" ) ;
}
private static DefaultListableBeanFactory createBeanFactory ( ) {
BeanDefinition beanDefinition = genericBeanDefinition ( TestBean . class )
. addPropertyValue ( "name" , "${my.property}" )
. getBeanDefinition ( ) ;
DefaultListableBeanFactory bf = new DefaultListableBeanFactory ( ) ;
bf . registerBeanDefinition ( "testBean" , beanDefinition ) ;
return bf ;
}
}
private static class OptionalTestBean {
private static class OptionalTestBean {
private Optional < String > name ;
private Optional < String > name ;