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

Doc: ConfigurationClassParser's property source composition requires distinct (Resource)PropertySources names #28886

Closed
djechelon opened this issue Jul 29, 2022 · 2 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: documentation A documentation task
Milestone

Comments

@djechelon
Copy link
Contributor

Thanks to the team for taking the time.

Affects: 5.3.18 and main branch

Affected code

if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}

Discussion

The addPropertySource method is affected by a bug IMO where the effect of the if statement is reverted by the fact that PropertySources are hashed using their name property.

When code reaches the affected if-statement, it means that a PropertySource of name foo is already registered in the Spring context, and that PropertySource is assigned to the existing variable. The idea is to replace (L499) the old property source with name foo with the CompositePropertySource containing both.

The problem is, given that

  • CompositePropertySource holds a Set of PropertySources
  • PropertySources are hashed by their getName(), which is not extended currently
  • Both PropertySources to compose have same name

In the end, the second PropertySource won't be added and CompositePropertySource will hold only one PropertySource of name foo

Affected case

We found a problem when multiple teams work on different Java libraries, each declaring some property sources from classpath files.

In the end, we discovered that if two libraries hold the same env.yaml in different packages, but the associated PropertySource name is computed by the file name (not fully qualified path), two libraries define a PropertySource named env.yaml and clash.

We expect this to happen even if teams define any kind of MapPropertySource with the same non-unique name. We have implemented a workaround by using the fully qualified resource URL.

Suggested solution

Have CompositePropertySource use List than Set?

Test case

Not yet available

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 29, 2022
@rstoyanchev rstoyanchev added the in: core Issues in core modules (aop, beans, core, context, expression) label Feb 9, 2023
@snicoll
Copy link
Member

snicoll commented Oct 3, 2023

@djechelon thank you, this looks legit to me. The logic has moved to PropertySourceProcessor so it should be easier for you to write a test case for it if have the time.

@jhoeller jhoeller added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 27, 2023
@jhoeller jhoeller self-assigned this Dec 27, 2023
@jhoeller jhoeller added this to the 6.1.3 milestone Dec 27, 2023
@jhoeller
Copy link
Contributor

jhoeller commented Dec 27, 2023

This turns out to be by design since CompositePropertySource is not meant to hold property sources with different names.

In ConfigurationClassParser, the logic is primarily focused on ResourcePropertySource which is able to expose the full resource path as name. For custom property sources with hard names, they replace each other in case of the same name even then. Since existing setups may rely on the latter as well, we'll rather avoid changing this.

Instead, the recommended solution is exactly what you went with: let the algorithm choose a distinct name - such as the fully qualified resource path - as ResourcePropertySource name, or provide a similarly distinct name for a custom PropertySource if necessary. I assume your setup involves a custom PropertySourceFactory where this needs to be taken into account specifically. I'll turn this ticket into a documentation ticket for it.

@jhoeller jhoeller added type: documentation A documentation task and removed type: bug A general bug labels Dec 27, 2023
@jhoeller jhoeller changed the title ConfigurationClassParser won't add PropertySources of the same name correctly Doc: ConfigurationClassParser's property source composition requires distinct (Resource)PropertySources names Dec 27, 2023
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: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

5 participants