Hi,

after upgrading from spring boot 2.3.6 to 2.4.0 our application fails to startup. We tracked the error to our startup options, we use the following to setup the configuration resources paths: --spring.config.location=classpath:/,file:../etc/

This results in the following error message:

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

Description:

Config data resource 'class path resource []' via location 'classpath:/' does not exist

Action:

Check that the value 'classpath:/' is correct, or prefix it with 'optional:'

07:53:54.024 [main] ERROR com.xxx.server.launcher.XxxServerApplication - Error starting up / running context
org.springframework.boot.context.config.ConfigDataResourceNotFoundException: Config data resource 'class path resource []' via location 'classpath:/' cannot be found
        at org.springframework.boot.context.config.ConfigDataResourceNotFoundException.withLocation(ConfigDataResourceNotFoundException.java:97)
        at org.springframework.boot.context.config.ConfigDataImporter.handle(ConfigDataImporter.java:133)
        at org.springframework.boot.context.config.ConfigDataImporter.resolve(ConfigDataImporter.java:104)
        at org.springframework.boot.context.config.ConfigDataImporter.resolve(ConfigDataImporter.java:93)
        at org.springframework.boot.context.config.ConfigDataImporter.resolveAndLoad(ConfigDataImporter.java:81)
        at org.springframework.boot.context.config.ConfigDataEnvironmentContributors.withProcessedImports(ConfigDataEnvironmentContributors.java:118)
        at org.springframework.boot.context.config.ConfigDataEnvironment.processInitial(ConfigDataEnvironment.java:230)
        at org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:217)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:88)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:80)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:100)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:86)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:203)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:196)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:170)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:148)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:82)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:63)
        at java.util.ArrayList.forEach(ArrayList.java:1257)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:117)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:111)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:62)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:362)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298)
        at com.xxx.xxx.server.launcher.XxxServerApplication.main(XxxServerApplication.java:49)

The same setting works fine on the previous spring boot version. We need this setting as we ship some default profiles within our jar, but also allow the user to use custom profiles within the "etc" folder. Prefixing the "classpath:" with "optional:" doesn't print any error, but then the embedded profiles are not found.

Thanks, Simon

Comment From: wilkinsona

Thanks for the report, @bratkartoffel. I've tried to reproduce the behaviour that you have described but have been unable to do so. Specifying the root of the classpath as a spring.config.location works as expected for me both when running an application in my IDE and when packaged as a jar file.

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: bratkartoffel

I've created a minimal example and could reproduce this. While testing I found out, that this only happens when running on Java 8. Using Java 11 is fine. Running in IntelliJ works also fine. See the "reproduce.sh" script on how to reproduce.

GH24143.zip

Comment From: wilkinsona

Thanks for the sample, @bratkartoffel. I've reproduced the problem.

The failure seems to be due to a difference in how the root of the classpath is resolved. As you have observed, this difference is specific to Java 8. It also only occurs when there are only jars on the classpath. If I modify the GH24143 script to add a directory to the classpath (I added .), the problem does not occur and the properties are resolved as expected:

./bin/GH24143 --spring.profiles.active=embedded,external --spring.config.location=classpath:/,file:../../../etc/


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

2020-11-13 09:05:19.702  INFO 29925 --- [           main] org.example.TestApplication              : Starting TestApplication using Java 1.8.0_252 on wilkinsona-a01.vmware.com with PID 29925 (/Users/awilkinson/Downloads/GH24143/build/install/GH24143/lib/GH24143-1.0-SNAPSHOT.jar started by awilkinson in /Users/awilkinson/Downloads/GH24143/build/install/GH24143)
2020-11-13 09:05:19.704  INFO 29925 --- [           main] org.example.TestApplication              : The following profiles are active: embedded,external
2020-11-13 09:05:20.343  INFO 29925 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-11-13 09:05:20.351  INFO 29925 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-11-13 09:05:20.351  INFO 29925 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-13 09:05:20.394  INFO 29925 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-11-13 09:05:20.394  INFO 29925 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 656 ms
2020-11-13 09:05:20.514  INFO 29925 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-13 09:05:20.639  INFO 29925 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-13 09:05:20.646  INFO 29925 --- [           main] org.example.TestApplication              : Started TestApplication in 1.242 seconds (JVM running for 1.522)
2020-11-13 09:05:20.648  INFO 29925 --- [           main] org.example.TestApplication              : example.embedded=1
2020-11-13 09:05:20.649  INFO 29925 --- [           main] org.example.TestApplication              : example.external=2
2020-11-13 09:05:20.715  INFO 29925 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

We'll continue to investigate to see what a potential fix may be.

Rather than adding a directory to the classpath, an alternative workaround is to enable legacy configuration file processing by setting spring.config.use-legacy-processing=true One way to do this is as a system property:

JAVA_OPTS=-Dspring.config.use-legacy-processing=true ./bin/GH24143 --spring.profiles.active=embedded,external --spring.config.location=classpath:/,file:../../../etc/

Comment From: wilkinsona

The following references are being resolved:

  • classpath:/application.properties
  • classpath:/application.xml
  • classpath:/application.yml
  • classpath:/application.yaml

The directory for each of these is classpath:/ represented by a ClassPathResource with a path that's the empty string. The existence of these resources is checked by calling ClassLoader.getResource("") and requiring a non-null result. The result is null on Java 8 and non-null on Java 11.

Modifying the sample's main method to output the result of the getResource("") call produces jar:file:/Users/awilkinson/Downloads/GH24143/build/install/GH24143/lib/log4j-api-2.13.3.jar!/META-INF/versions/9/ with Java 11 and null with Java 8.

Modifying the sample to apply Spring Boot's plugin and building and running a fat jar produces jar:file:/Users/awilkinson/Downloads/GH24143/build/libs/GH24143-1.0-SNAPSHOT.jar!/BOOT-INF/classes!/ with both Java 8 and Java 11.

Modifying the sample to remove log4j-api-2.13.3.jar from the classpath causes it to fail with Java 11 (and continue to fail with Java 8). In other words, it only works with Java 11 when there's only jars on the classpath if one of those jars is a multi-release jar.

Comment From: wilkinsona

I think we've been here before: https://github.com/spring-projects/spring-framework/issues/16711.

Comment From: lakxtxue

Please pay attention to this issue again.I encountered the same problem in version 2.4.1, and I returned to 2.4.0 and it was normal. I use graalvm 20.3.0 OpenJDK11.0.9

Description:

Config data location 'classpath:/' does not exist

Action:

Check that the value 'classpath:/' is correct, or prefix it with 'optional:'

This is my code public static void main(String[] args) { ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(DemoApplication.class) .properties("spring.config.name:application,cache", "spring.config.location:classpath:/,classpath:/config/properties/") .build().run(args); } I created a demo example that reproduces the problem demo.zip

Comment From: snicoll

Thanks for letting us know. FWIW this project works fine for me with Spring Boot 2.4.2-SNAPSHOT.

Comment From: lakxtxue

I can't use 2.4.2-SNAPSHOT and the following error is displayed:

Plugin [id: 'org.springframework.boot', version: '2.4.2-SNAPSHOT'] was not found in any of the following sources:

Even if I use spring initializr to generate new projects, the same error occurs.

I noticed the modification of 2.4.1 on this issue. If there is no property file in the classpath:/ path, an error will be thrown:

Config data location'classpath:/' does not exist

If I add the properties file in the classpath:/ path, everything is normal. I do need to have no properties files in the directory. Can the modification ignore this judgment?

Comment From: snicoll

Even if I use spring initializr to generate new projects, the same error occurs.

A project generated using start.spring.io works fine for me. Perhaps you're trying to adapt the new project to your existing project and you forgot to update settings.gradle? If you can reproduce the problem with 2.4.2-SNAPSHOT, please create a separate issue as this one is closed in a released version and we won't reopen it. If you need guidance on how to switch to the snapshot, please follow-up on StackOverflow or Gitter.

Comment From: lakxtxue

The project I generated at start.spring.io used gradle project before, but now I use maven and it is indeed normal. This is the demo I used to reproduce the issue with version 2.4.2-SNAPSHOT demo.zip

Comment From: snicoll

@lakxtxue thank you for creating the issue (#24696).

Comment From: taoshide

SpringBoot ConfigDataResourceNotFoundException incorrectly thrown for valid classpath locations

Comment From: ghost

Hello, i upgrated spring boot from 2.2 to 2.4.1 ,i have a config as following :

1-context xml file :Environment name="spring.config.additional-location" value="file:/opt/etc/" type="java.lang.String" 2- in /opt/etc/ i have this file:idra-app-war.properties 3- war is the active profile before the upgrade every thing work fine,after the upgrade i had this error:

Caused by: org.springframework.boot.context.config.ConfigDataLocationNotFoundException: Config data location 'classpath:/opt/etc/' cannot be found

Comment From: wilkinsona

@wafim From what you've described, that doesn't sound related to this issue. If you'd like us to investigate, please open a new issue and provide a minimal sample that reproduces the problem.