Description
Affects: 6.1.8
While upgrading a project from spring boot 3.1.12 to spring boot 3.2.6 I've encountered issues with my @Value
injection path to system file on my Windows machine being rebuilt incorrectly by StringUtils.cleanPath(...)
. I believe this concerns the way Spring handles \
characters when loading maven properties in application.yml
. I've created a demo I'll attach showcasing the issue.
My file is loaded like this:
@Value("${path.to.a.file}")
private RSAPublicKey resource;
where the property in application.yml
looks like this:
path.to.a.file: file:@path.to.a.key.file@
the maven property referenced is:
<properties>
<main.basedir>${project.basedir}${file.separator}..</main.basedir>
<path.to.a.key.file>${main.basedir}${file.separator}lib${file.separator}someFile</path.to.a.key.file>
</properties>
(the original project is a multi-module project)
From my testing this is related to the change to https://github.com/spring-projects/spring-framework/blob/v6.1.8/spring-core/src/main/java/org/springframework/util/ResourceUtils.java#L412
in previous major version this would build directly without any changes to location:
https://github.com/spring-projects/spring-framework/blob/v6.0.21/spring-core/src/main/java/org/springframework/util/ResourceUtils.java#L419
I don't think this is an issue with StringUtils.cleanPath(...)
, but with the fact that Spring will inject this maven property with double \\
, rather than just one \
. Example images from the demo project I have attached, how application.yml
looks like in target folder:
And debugging, what the String is equal to since I was confused by the IntelliJ output:
how cleanPath
outputs the wrong path when given input with double \
:
after removing one escaped \
, cleanPath
works as expected:
From what I can tell maven won't use double backslashes so these are added by Spring I believe (output of maven command to check how properties look like):
Here is the stack trace:
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-06-05T18:05:06.429+03:00 ERROR 28320 --- [ main] o.s.boot.SpringApplication : Application run failedorg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.Main$ExampleComponent': Unsatisfied dependency expressed through field 'resource': Failed to convert value of type 'java.lang.String' to required type 'java.security.interfaces.RSAPublicKey'; Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.security.interfaces.RSAPublicKey] for value [file:C:\Users\...\IdeaProjects\demo_resourceUtils_issue\child\..\lib\someFile]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:787) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:767) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:508) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1421) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.8.jar:6.1.8]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.6.jar:3.2.6]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.6.jar:3.2.6]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.6.jar:3.2.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.2.6.jar:3.2.6]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149) ~[spring-boot-3.2.6.jar:3.2.6]
at com.example.Main.main(Main.java:14) ~[classes/:na]
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.security.interfaces.RSAPublicKey'; Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.security.interfaces.RSAPublicKey] for value [file:C:\Users\...\IdeaProjects\demo_resourceUtils_issue\child\..\lib\someFile]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:87) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:71) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1381) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:784) ~[spring-beans-6.1.8.jar:6.1.8]
... 19 common frames omitted
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.security.interfaces.RSAPublicKey] for value [file:C:\Users\...\IdeaProjects\demo_resourceUtils_issue\child\..\lib\someFile]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:182) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:131) ~[spring-beans-6.1.8.jar:6.1.8]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:80) ~[spring-beans-6.1.8.jar:6.1.8]
... 23 common frames omitted
Caused by: java.io.UncheckedIOException: java.io.FileNotFoundException: C:\Users...\IdeaProjects\demo_resourceUtils_issue\child\lib\someFile (The system cannot find the path specified)
at org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor$ResourceKeyConverterAdapter.toInputStream(RsaKeyConversionServicePostProcessor.java:158) ~[spring-security-config-6.2.4.jar:6.2.4]
at org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor$ResourceKeyConverterAdapter.lambda$pemInputStreamConverter$0(RsaKeyConversionServicePostProcessor.java:146) ~[spring-security-config-6.2.4.jar:6.2.4]
at org.springframework.core.convert.converter.Converter.lambda$andThen$0(Converter.java:62) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor$ResourceKeyConverterAdapter.convert(RsaKeyConversionServicePostProcessor.java:132) ~[spring-security-config-6.2.4.jar:6.2.4]
at org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor$ResourceKeyConverterAdapter.convert(RsaKeyConversionServicePostProcessor.java:113) ~[spring-security-config-6.2.4.jar:6.2.4]
at org.springframework.core.convert.support.GenericConversionService$ConverterAdapter.convert(GenericConversionService.java:358) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-6.1.8.jar:6.1.8]
... 26 common frames omitted
Caused by: java.io.FileNotFoundException: C:\Users...\IdeaProjects\demo_resourceUtils_issue\child\lib\someFile (The system cannot find the path specified)
at java.base/java.io.FileInputStream.open0(Native Method) ~[na:na]
at java.base/java.io.FileInputStream.open(FileInputStream.java:216) ~[na:na]
at java.base/java.io.FileInputStream.(FileInputStream.java:157) ~[na:na]
at java.base/java.io.FileInputStream.(FileInputStream.java:111) ~[na:na]
at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86) ~[na:na]
at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:189) ~[na:na]
at org.springframework.core.io.UrlResource.getInputStream(UrlResource.java:233) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor$ResourceKeyConverterAdapter.toInputStream(RsaKeyConversionServicePostProcessor.java:155) ~[spring-security-config-6.2.4.jar:6.2.4]
... 32 common frames omitted
This can easily be fixed by editing the string with SpEL: @Value("#{'${path.to.a.file}'.replace('\\\\', '\\')}")
or just telling maven to add the files to my classpath, but I am opening this in case this is an issue worth considering, I apologize otherwise.
Demo project: demo_resourceUtils_issue.zip