Skip to content

Property Placeholder Configurer Incorrectly Reports "Circular placeholder reference" Error For Repeated Nested Placeholders [SPR-5369] #10042

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

Closed
spring-projects-issues opened this issue Dec 17, 2008 · 4 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

Jason Petersen opened SPR-5369 and commented

If a nested property placeholder (for instance "${${Innter}.Outer}") is referenced twice within a single property string, Spring incorrectly reports a circular placeholder reference. Run the attached code for an example. To run, just throw Spring/log4j/commons-logging on the classpath and execute the main method. Pwd needs to be the root of the unzipped archive (so the res/constants.properties file is properly referenced).

In the attached code, a simple bean has a string field set to ${Top}. The properties file defines Top as ${Child}${Child}. Child is defined as ${${Differentiator}.Grandchild}. Differentiator is equal to First, and First.Grandchild equals ActualValue.

It is clear that this is a directed tree; no circular references in resolving placeholders. The final interpolated value should be "ActualValueActualValue", but an exception is thrown after the first value is resolved.

I believe the root cause is in the inner workings of the parseStringValue method (in PropertyPlaceholderConfigurer). It appears that, on the way in, ${Differentiator}.Grandchild is added to the "visitedPlaceholders" collection. On the way back out (recursively speaking), ${Differentiator} has already been resolved, and an attempt is made to remove (in this case) "First.Grandchild" from the visitedPlaceholders collection (which value has never been in the collection to begin with). Hence the collection is left in a dirty state even after the method has exited all the way back up the chain.

The problem comes during the evaluation of the second occurrence of ${Child}. Because the vistedPlaceholders collection still contains ${Differentiator}.Grandchild, visitedPlaceholders.add(placeholder) returns false while trying to add this placeholder when it is seen a second time (on the way down). So 'throw new BeanDefinitionStoreException("Circular placeholder reference '" + placeholder + "' in property definitions");' is executed.

It is clear that there is no technical reason why this case should not be feasible, so this is filed as a bug. In real-world scenarios, this issue might show up anytime someone wants to repeat a value within a single string. Examples: within an Oracle JdbcUrl, the TNS name appears twice. Or: A command to setup an ssh tunnel from a remote port to the same port on localhost needs to repeat that port number.

The repetition alone is not enough to cause this bug, it is caused by the repetition of a placeholder utilizing nesting.


Affects: 2.5.2, 2.5.3, 2.5.4, 2.5.5, 2.5.6

Attachments:

Referenced from: commits 18006c7, 4169898, 98bf01a

3 votes, 4 watchers

@spring-projects-issues
Copy link
Collaborator Author

Jason Petersen commented

Oh yeah, the workaround is to just make the repetition go away. In the case of the tns name, instead of having:

JdbcUrl=jdbc:whatever${TnsName}stuff${PortNumber}morethings${TnsName}
TnsName=${${StackName}.TnsName}
Test.TnsName=value
Prod.TnsName=otherValue

Just unwrap the relevant portion:

JdbcUrl=${${StackName}.JdbcUrl}

Test.JdbcUrl=jdbc:whatevervaluestuff${PortNumber}morethingsvalue
Prod.JdbcUrl=jdbc:whateverotherValuestuff${PortNumber}morethingsotherValue

This workaround is ok, but it is clearer to be able to specifically state only the difference between these two strings (in this case, the TNS Name), so a long-term fix is preferable.

@spring-projects-issues
Copy link
Collaborator Author

Stevo Slavić commented

Created pull request with fix for this bug.

@spring-projects-issues
Copy link
Collaborator Author

Chris Beams commented

Thanks Jason for the report, and sorry for the very long wait!

And thanks Stevo for the fix.

commit 18006c72b014246946fd487159de7e133d173a17
Author:     Stevo Slavic <sslavic@gmail.com>
AuthorDate: Sat Feb 18 17:44:04 2012 +0100
Commit:     Chris Beams <cbeams@vmware.com>
CommitDate: Thu May 17 14:48:18 2012 +0300

    Fix circular placeholder prevention
    
    A set of resolved placeholder references is used for circular
    placeholder prevention. For complex property definitions this mechanism
    would put property values with unresolved inner placeholder references
    in the set, but would try to remove property values with placeholders
    resolved, leaving the set in an invalid state and the mechanism broken.
    
    This fix makes sure that the value that is put in the set is same one
    that is removed from it, and by doing so avoids false positives in
    reporting circular placeholders.
    
    Issue: SPR-5369

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Backported to 3.1.3 as well now.

Juergen

@spring-projects-issues spring-projects-issues added type: bug A general bug in: core Issues in core modules (aop, beans, core, context, expression) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 3.1.3 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants