Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support escaping prefix and separator in property placeholders [SPR-4953] #9628

Closed
spring-projects-issues opened this issue Jun 25, 2008 · 22 comments
Assignees
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jun 25, 2008

Chris Lee opened SPR-4953 and commented

There is presently no way to inject a placeholder expression (${...}) that won't be picked up by PropertyPlaceholderConfigurer. Ideally we should be able to inject a string that contains ${...} for later use in its target bean without involvement from PropertyPlaceholderConfigurer.

Perhaps a simple escape mechanism using the conventional backslash, such as "The current value is \\${...}" would suffice.


Affects: 2.5.4, 2.5.5

11 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

Timo Thomas commented

Apache Jakarta Commons-Lang has a similar technology known as "StrSubstitutor" (org.apache.commons.lang.text.StrSubstitutor). There, the default escape character is "$", but it is configurable (independently from the prefix and suffix).

I'm using StrSubstitutor on expressions in bean properties, and if there is no escaping, the only (ugly) option left for me is to change one of the prefixes.

IMHO not having an escape character nowadays should be considered as a production-critical feature (what if changing the prefix is not an option? The workarounds necessary then are even more ugly) - if not as a bug.

@spring-projects-issues
Copy link
Collaborator Author

Eric Haszlakiewicz commented

It seems that you can work around this by defining a property that has a value of '$'. e.g.
dollarsign=$

and in the xml config to get a value of e.g. '$foo', you'd write '${dollarsign}foo'

It's not pretty, but at least it works.

@spring-projects-issues
Copy link
Collaborator Author

Jim Utter commented

The workaround does not work when the replacement needs the braces passed through.
For example:

properties:
var=$

wiring:
"${var}{foo}"

or even
var=${

"${var}foo}"

or finally
var=${foo}

"${var}"

still results in an error like:

Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'id' defined in class path resource [applicationContext.xml]: Could not resolve placeholder 'foo'
at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:268)

For me, I'd like to define concatenated strings like "${var1}-${var2}${var3}" etc.. where the variables would be internally replaced by velocity (requiring the braces when concatenated).

@spring-projects-issues
Copy link
Collaborator Author

Joshua Caplan commented

The following blog post details a workaround that is available starting from Spring 3.0.x (or whenever SpEL was introduced):

http://jazzjuice.blogspot.com/2011/06/escaping-property-placeholders-in.html

I still agree that native escaping would be useful, though.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Feb 8, 2012

Aleksandr Dubinsky commented

Indeed, #{'$'}{...} seems to be a valid escape sequence for ${...}.

However, it is too long (5 characters!). It is poorly documented. It exists by sheer accident.

It is really shocking that an escape sequence does not exist. Btw, the alternative of defining a different placeholder sequence is a really, really bad idea. That feature should NOT even be there. Allowing users to arbitrarily re-define the basic syntax of a language leads to confusion.

@spring-projects-issues
Copy link
Collaborator Author

Michel Nolard commented

@Aleksandr Dubinsky: I agree to a 100% with you.

Interesting fact: there is a lot advices on the net to use your trick -- often without referencing your post properly, but this does not remove the need for this issue to be solved anyway !

@spring-projects-issues
Copy link
Collaborator Author

Jan Zidek commented

In YAML configuration files, # is the line comment sign, the rest of the line is ignored. If I escape it with backslah, so I use

\#{'$'}

then it is not ignored, but when the parameter is set by @ConfigurationProperties, the Java string simply contains the sequence as was written in YAML.

@spring-projects-issues spring-projects-issues added status: waiting-for-triage An issue we've not yet triaged or decided on type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import and removed type: enhancement A general enhancement labels Jan 11, 2019
@rstoyanchev rstoyanchev added status: bulk-closed An outdated, unresolved issue that's closed in bulk as part of a cleaning process and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 11, 2019
@spring-projects-issues
Copy link
Collaborator Author

Bulk closing outdated, unresolved issues. Please, reopen if still relevant.

@mar1ged
Copy link

mar1ged commented Jun 14, 2019

I really would welcome a "fix" for this.

In my app I externalized config into application.yml, this configuration contains ${var} entries which I later on fill in using PropertyPlaceholderHelper in my own code.

When I have ${var} in my application.yml the app does not load because the value of var will be filled in later and depends on user input.

I worked around this issue by putting @{var} into the YAML file and replacing @ with $ at runtime. But this is not very elegant because I have to do something like this:

	@Value("${my.app.var}")
	private String var;

	public String getVar() {
		return fixed(var);
	}

	private String fixed(String in) {
		return in.replace('@', '$');
	}

@sbrannen
Copy link
Member

sbrannen commented Jun 14, 2019

Interestingly enough, core support for ignoring nested properties appears to have been in place since Spring Framework 3.2 via the ConfigurablePropertyResolver.setIgnoreUnresolvableNestedPlaceholders(boolean) flag.

However, PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(ConfigurableListableBeanFactory) instantiates a PropertySourcesPropertyResolver (which is a subtype of ConfigurablePropertyResolver) without providing the option to set any flags in the property resolver.

Here's a JUnit Jupiter based test case for experimentation:

package example;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.assertj.core.api.Assertions.assertThat;

@SpringJUnitConfig
// Using the following SpEL hack allows the test to pass.
// @TestPropertySource(properties = "my.app.var = #{'$'}{var}")
@TestPropertySource(properties = "my.app.var = ${var}")
class IgnoredPlaceholderTests {

	@Value("${my.app.var}")
	String value;

	@Test
	void test() {
		assertThat(value).isEqualTo("${var}");
	}

	@Configuration
	static class Config {

		@Bean
		static PropertySourcesPlaceholderConfigurer pspc() {
			PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
			pspc.setIgnoreUnresolvablePlaceholders(true);
			return pspc;
		}

	}

}

@jhoeller, what do you think about either introducing support for escaping placeholders or providing a mechanism for configuring the PropertySourcesPropertyResolver used by PropertySourcesPlaceholderConfigurer?

@sbrannen sbrannen reopened this Jun 14, 2019
@sbrannen sbrannen added status: pending-design-work Needs design work before any code can be developed and removed status: bulk-closed An outdated, unresolved issue that's closed in bulk as part of a cleaning process labels Jun 14, 2019
@libinglong
Copy link

libinglong commented Aug 2, 2020

it's amazing the issue lasts for more than ten years.

When I use sharding-jdbc which configuration also need '${xxx}', I faced this problem.

@unrec
Copy link

unrec commented Jul 14, 2021

Will it be fixed at all?

@honza-zidek
Copy link

honza-zidek commented Jul 14, 2021

@jhoeller, @rstoyanchev, @sbrannen, @sdeleuze, @bclozel, @poutsma, @snicoll, @izeye - any idea if this may be fixed? :)
It has been opened for over 13 years without much attention, so I am trying to attract your eyes here...

@honza-zidek
Copy link

Any progress? Any comment? :)

@sdeleuze sdeleuze added this to the Triage Queue milestone Dec 17, 2021
@sbrannen sbrannen self-assigned this Jan 18, 2022
@sbrannen sbrannen added the type: enhancement A general enhancement label Jan 18, 2022
@sbrannen sbrannen removed this from the Triage Queue milestone Jan 18, 2022
bdshadow added a commit to bdshadow/spring-framework that referenced this issue Nov 1, 2023
This commit allows to escape property placeholders to later processed
by user. PropertyPlaceholderHelper allows to set custom escape character. "\" is used as the default one.

Closes spring-projectsgh-9628

Signed-off-by: Dmitrii Bocharov <bdshadow@gmail.com>
@honza-zidek

This comment was marked as resolved.

@snicoll

This comment was marked as resolved.

@honza-zidek

This comment was marked as resolved.

@snicoll

This comment was marked as resolved.

@honza-zidek
Copy link

I realize that you're probably frustrated, but sarcasm such as this isn't helpful.

@snicoll : my frustration stems mostly from the feeling that the team waste their valuable time with destructive changes which nobody wants, instead of addressing the actual issues or even bugs, reported and complained about for 10+ years. The recent "trailing slash upgrade" broke literally thousands of enterprise applications. You should not be surprised that someone may be frustrated.

@snicoll snicoll changed the title Support escaping placeholder prefix in property placeholders [SPR-4953] Support escaping prefix and separator in property placeholders [SPR-4953] Dec 19, 2023
@snicoll snicoll self-assigned this Dec 21, 2023
@snicoll snicoll removed the status: pending-design-work Needs design work before any code can be developed label Dec 29, 2023
@snicoll snicoll modified the milestones: 6.x Backlog, 6.2.0-M1 Dec 29, 2023
snicoll added a commit to snicoll/spring-framework that referenced this issue Feb 2, 2024
This commit provides a rewrite of the parser for properties containing
potentially placeholders.

Assuming a source where `firstName` = `John` and `lastName` = `Smith`,
the "${firstName}-${lastName}" property is evaluated as "John-Smith".

Compared with the existing implementation in PropertyPlaceholderHelper,
the new implementation offers the following extra features:

1. Placeholder can be escaped using a configurable escape character.
When a placeholder is escaped it is rendered as is. This does apply to
any nested placeholder that wouldn't be escaped. For instance,
"\${firstName}" is evaluated as "${firstName}".
2. The default separator can also be escaped the same way. When the
separator is escaped, the left and right parts are not considered as
the key and the default value respectively. Rather the two parts
combined, including the separator (but not the escape separator) are
used for resolution. For instance, ${java\:comp/env/test} is looking
for a "java:comp/env/test" property.
3. Placeholders are resolved lazily. Previously, all nested placeholders
were resolved before considering if a separator was present. This
implementation only attempts the resolution of the default value if the
key does not provide a value.
4. Failure to resolve a placeholder are more rich, with a dedicated
PlaceholderResolutionException that contains the resolution chain.

See spring-projectsgh-9628
See spring-projectsgh-26268
snicoll added a commit to snicoll/spring-framework that referenced this issue Feb 2, 2024
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 spring-projectsgh-9628
snicoll added a commit that referenced this issue Feb 15, 2024
This commit provides a rewrite of the parser for properties containing
potentially placeholders.

Assuming a source where `firstName` = `John` and `lastName` = `Smith`,
the "${firstName}-${lastName}" property is evaluated as "John-Smith".

Compared with the existing implementation in PropertyPlaceholderHelper,
the new implementation offers the following extra features:

1. Placeholder can be escaped using a configurable escape character.
When a placeholder is escaped it is rendered as is. This does apply to
any nested placeholder that wouldn't be escaped. For instance,
"\${firstName}" is evaluated as "${firstName}".
2. The default separator can also be escaped the same way. When the
separator is escaped, the left and right parts are not considered as
the key and the default value respectively. Rather the two parts
combined, including the separator (but not the escape separator) are
used for resolution. For instance, ${java\:comp/env/test} is looking
for a "java:comp/env/test" property.
3. Placeholders are resolved lazily. Previously, all nested placeholders
were resolved before considering if a separator was present. This
implementation only attempts the resolution of the default value if the
key does not provide a value.
4. Failure to resolve a placeholder are more rich, with a dedicated
PlaceholderResolutionException that contains the resolution chain.

See gh-9628
See gh-26268
snicoll added a commit that referenced this issue Feb 15, 2024
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
snicoll added a commit that referenced this issue Mar 2, 2024
To smooth upgrade from 6.1.x, this commit makes sure that code that used
to catch an IAE to ignore a faulty placeholder resolution still works.

See gh-9628
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.