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

Failed to bind properties to List<Resource> #38556

Closed
groat-mike opened this issue Nov 27, 2023 · 11 comments
Closed

Failed to bind properties to List<Resource> #38556

groat-mike opened this issue Nov 27, 2023 · 11 comments
Labels
for: external-project For an external project and not something we can fix

Comments

@groat-mike
Copy link

Hello friends. While upgrading to spring-boot v3.2.0, I believe I found a regression in the binding of properties to a List<org.springframework.core.io.Resource>. A small sample will demonstrate:

@SpringBootApplication
@EnableConfigurationProperties(CoolConfigProperties.class)
public class SampleApp {

    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }
}

@ConfigurationProperties(prefix = "cool")
record CoolConfigProperties(List<Resource> resources) {}

For this example, we will set an environment variable COOL_RESOURCES=classpath:foo.properties,file:./bar.properties.

Resources are bound to ConfigurationProperties as expected when running with spring-boot v3.1.6:
image

However spring-boot v3.2.0 will fail to start:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.0)

2023-11-26T23:17:14.625-05:00  INFO 32744 --- [           main] com.example.SampleApp                    : Starting SampleApp using Java 21.0.1 with PID 32744 (C:\Users\groat\Desktop\list-property-sample\target\classes started by groat in C:\Users\groat\Desktop\list-property-sample)
2023-11-26T23:17:14.628-05:00  INFO 32744 --- [           main] com.example.SampleApp                    : No active profile set, falling back to 1 default profile: "default"
2023-11-26T23:17:14.854-05:00  WARN 32744 --- [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'cool-com.example.CoolConfigProperties': Could not bind properties to 'CoolConfigProperties' : prefix=cool, ignoreInvalidFields=false, ignoreUnknownFields=true
2023-11-26T23:17:14.857-05:00  INFO 32744 --- [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2023-11-26T23:17:14.866-05:00 ERROR 32744 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'cool.resources' to java.util.List<org.springframework.core.io.Resource>:

    Property: cool.resources
    Value: "classpath:/foo.properties,file:./bar.properties"
    Origin: System Environment Property "COOL_RESOURCES"
    Reason: failed to convert java.lang.String to java.util.List<org.springframework.core.io.Resource> (caused by java.lang.IllegalArgumentException: Cannot convert value of type 'java.lang.String' to required type 'java.util.List': PropertyEditor [org.springframework.core.io.support.ResourceArrayPropertyEditor] returned inappropriate value of type 'org.springframework.core.io.ClassPathResource')

Action:

Update your application's configuration


Process finished with exit code 1
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 27, 2023
@quaff
Copy link
Contributor

quaff commented Nov 27, 2023

It's a spring boot regression introduced by 0e3a196.

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

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.TestPropertySource;

@SpringBootTest
@EnableConfigurationProperties(ResourcesInjectionTests.ConfigProperties.class)
@TestPropertySource(properties = "resources=classpath:foo.properties,file:./bar.properties")
public class ResourcesInjectionTests {

	@Autowired
	ConfigProperties configProperties;

	@Test
	void test() {
		assertThat(configProperties.resources).hasSize(2);
		assertThat(configProperties.resources.get(0)).isInstanceOf(ClassPathResource.class);
		assertThat(configProperties.resources.get(1)).isInstanceOf(FileUrlResource.class);
	}

	@ConfigurationProperties
	record ConfigProperties(List<Resource> resources) {
	}

}

This test case works fine with 3.1.6 but failed with 3.2.0.

@wilkinsona
Copy link
Member

There's also a related problem with Resource[] binding.

With Spring Boot 3.1.6 you get [class path resource [foo.properties], URL [file:bar.properties]] but with 3.2.0 you get [class path resource [foo.properties,file:bar.properties]]. With Spring Boot 3.1.6 and Framework overridden to 6.1.1 you still get [class path resource [foo.properties], URL [file:bar.properties]]. There may be a Boot problem here as well as a Framework problem (spring-projects/spring-framework#31693).

@gznglor

This comment was marked as off-topic.

@wilkinsona

This comment was marked as resolved.

@quaff
Copy link
Contributor

quaff commented Nov 28, 2023

There's also a related problem with Resource[] binding.

With Spring Boot 3.1.6 you get [class path resource [foo.properties], URL [file:bar.properties]] but with 3.2.0 you get [class path resource [foo.properties,file:bar.properties]]. With Spring Boot 3.1.6 and Framework overridden to 6.1.1 you still get [class path resource [foo.properties], URL [file:bar.properties]]. There may be a Boot problem here as well as a Framework problem (spring-projects/spring-framework#31693).

It's fixed by spring-projects/spring-framework#31700.

@jagneray
Copy link

jagneray commented Nov 28, 2023

Hello same probleme here :

I have two databases mongoDb, and in version 3.1.5 everything was fine and now i have this error :

Failed to bind properties under 'spring.data.mongodb' to com.adeo.sofi.configuration.CustomMongoProperties$MongoDbProperties:

Here my configuration

data:
    mongodb:
      sofi:
        database: ***
        uri: ****
      referential:
        database: ***
        uri: ****

@wilkinsona
Copy link
Member

wilkinsona commented Dec 4, 2023

@jagneray without knowing anything about your CustomMongoProperties$MongoDbProperties class, it's not clear to me how your snippet of YAML relates to this issue. I can't see anything that looks like it would be binding a List<Resource>.

@wilkinsona
Copy link
Member

Here's a minimal example that tries to capture the status of things both for the case described above and for the wildcard case that #15835 sought to address:

package com.example.gh38556;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;

import com.example.gh38556.Gh38556Application.CoolConfigProperties;

@SpringBootApplication
@EnableConfigurationProperties(CoolConfigProperties.class)
public class Gh38556Application {
	
	@Value("${cool.list}")
	private List<Resource> list;
	
	@Value("${cool.array}")
	private Resource[] array;
	
	@Value("${cool.wildcard-list}")
	private List<Resource> wildcardList;
	
	@Value("${cool.wildcard-array}")
	private Resource[] wildcardArray;

	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(Gh38556Application.class,
				"--cool.list=classpath:foo.properties,file:./bar.properties",
				"--cool.array=classpath:foo.properties,file:./bar.properties",
				"--cool.wildcard-list=classpath*:com/example/**/*.class",
				"--cool.wildcard-array=classpath*:com/example/**/*.class");
		
		CoolConfigProperties coolConfigProperties = context.getBean(CoolConfigProperties.class);
		System.out.println("@ConfigurationProperties:");
		System.out.println("  " + coolConfigProperties.list());
		System.out.println("  " + Arrays.toString(coolConfigProperties.array()));
		System.out.println("  " + coolConfigProperties.wildcardList());
		System.out.println("  " + Arrays.toString(coolConfigProperties.wildcardArray()));
		Gh38556Application app = context.getBean(Gh38556Application.class);
		System.out.println("@Value:");
		System.out.println("  " + app.list);
		System.out.println("  " + Arrays.toString(app.array));
		System.out.println("  " + app.wildcardList);
		System.out.println("  " + Arrays.toString(app.wildcardArray));
	}
	
	@ConfigurationProperties(prefix = "cool")
	record CoolConfigProperties(List<Resource> list, Resource[] array,
			List<Resource> wildcardList, Resource[] wildcardArray) {}

}

With Spring Boot 3.1.6, the following is output:

@ConfigurationProperties:
  [class path resource [foo.properties], URL [file:./bar.properties]]
  [class path resource [foo.properties], URL [file:./bar.properties]]
  [class path resource [classpath*:com/example/**/*.class]]
  [class path resource [classpath*:com/example/**/*.class]]
@Value:
  [class path resource [foo.properties,file:bar.properties]]
  [class path resource [foo.properties,file:bar.properties]]
  [class path resource [classpath*:com/example/**/*.class]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]

With Spring Boot 3.2.1-SNAPSHOT and Spring Framework 6.1.2-SNAPSHOT, the following is output:

@ConfigurationProperties:
  [class path resource [foo.properties,file:bar.properties]]
  [class path resource [foo.properties,file:bar.properties]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]
@Value:
  [class path resource [foo.properties,file:bar.properties]]
  [class path resource [foo.properties,file:bar.properties]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]

The behavior of @ConfigurationProperties and @Value is now consistent. We have, however, lost the support for comma-separated values being automatically expanded into multiple resources. spring-projects/spring-framework#31700 should address that but it remains to be seen in what version of Framework that change could be made.

@wilkinsona
Copy link
Member

@snicoll @jhoeller do you have a feel for if and when spring-projects/spring-framework#31700 might land?

@wilkinsona wilkinsona added the status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team label Dec 5, 2023
@snicoll
Copy link
Member

snicoll commented Dec 6, 2023

@wilkinsona this should be available in Spring Framework 6.1.2-SNAPSHOT.

@wilkinsona
Copy link
Member

With the latest 6.1.2-SNAPSHOT, I now see the following:

@ConfigurationProperties:
  [class path resource [foo.properties], URL [file:bar.properties]]
  [class path resource [foo.properties], URL [file:bar.properties]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]
@Value:
  [class path resource [foo.properties], URL [file:bar.properties]]
  [class path resource [foo.properties], URL [file:bar.properties]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]
  [file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application$CoolConfigProperties.class], file [/Users/awilkinson/dev/temp/gh-38556/bin/main/com/example/gh38556/Gh38556Application.class]]

Things are now consistent and comma-separated values are being handled. Thanks, @snicoll and @quaff. I'll close this one in favour of the changes that have been made in Framework.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Dec 6, 2023
@wilkinsona wilkinsona added for: external-project For an external project and not something we can fix and removed status: waiting-for-triage An issue we've not yet triaged status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team labels Dec 6, 2023
zorglube added a commit to zorglube/Pre-Liquibase that referenced this issue Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants