Spring boot : 2.5.6 Spring cloud : 2020.0.4
Hi, I have tried configuring my app to work with both config server and local classpath config file Here is my experience with what works and what does not by trying several configuration formats of spring.config.import
Summary
- Only case with application.properties or yaml list format work (see https://github.com/spring-cloud/spring-cloud-config/issues/1849)
- yaml set format doesn't works with parsing errors
Conclusion
- At least I think it might be nice to update the documentation to clarify the working way for setting up import with yaml file only.
- And in a second time, fix behavior with the set format of spring.config.import in yaml file
Details
Disclaimer : in first place we only use yaml config file. And in all example my config server is deliberately stopped with optional import to generate log even if bootstrap success
1. Failed with yaml config file only, config import with set format and config server at first place
application.yml
spring:
application:
name: my-service
config:
import: "optional:configserver:", classpath:monitoring.yaml
Result
ERROR - 10:58:29.290 - Saas-DB - [c:|n:] [SpringApplication]: Application run failed org.yaml.snakeyaml.parser.ParserException: while parsing a block mapping in 'reader', line 5, column 5: import: "optional:configserver:" ... ^ expected
, but found ',' in 'reader', line 5, column 37: import: "optional:configserver:", classpath:monitoring.yaml
Full stack here
ERROR - 10:58:29.290 - Saas-DB - [c:|n:] [SpringApplication]: Application run failed
org.yaml.snakeyaml.parser.ParserException: while parsing a block mapping
in 'reader', line 5, column 5:
import: "optional:configserver:" ...
^
expected <block end>, but found ','
in 'reader', line 5, column 37:
import: "optional:configserver:", classpath:monitoring.yaml
at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:617)
at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:165)
at org.yaml.snakeyaml.comments.CommentEventsCollector$1.peek(CommentEventsCollector.java:59)
at org.yaml.snakeyaml.comments.CommentEventsCollector$1.peek(CommentEventsCollector.java:45)
at org.yaml.snakeyaml.comments.CommentEventsCollector.collectEvents(CommentEventsCollector.java:140)
at org.yaml.snakeyaml.comments.CommentEventsCollector.collectEvents(CommentEventsCollector.java:119)
at org.yaml.snakeyaml.composer.Composer.composeScalarNode(Composer.java:221)
at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:191)
at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:313)
at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:304)
at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:288)
at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:195)
at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:313)
at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:304)
at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:288)
at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:195)
at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:313)
at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:304)
at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:288)
at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:195)
at org.yaml.snakeyaml.composer.Composer.getNode(Composer.java:115)
at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:135)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.getData(OriginTrackedYamlLoader.java:99)
at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:512)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:198)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:166)
at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:84)
at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:50)
at org.springframework.boot.context.config.StandardConfigDataLoader.load(StandardConfigDataLoader.java:54)
at org.springframework.boot.context.config.StandardConfigDataLoader.load(StandardConfigDataLoader.java:36)
at org.springframework.boot.context.config.ConfigDataLoaders.load(ConfigDataLoaders.java:107)
at org.springframework.boot.context.config.ConfigDataImporter.load(ConfigDataImporter.java:128)
at org.springframework.boot.context.config.ConfigDataImporter.resolveAndLoad(ConfigDataImporter.java:86)
at org.springframework.boot.context.config.ConfigDataEnvironmentContributors.withProcessedImports(ConfigDataEnvironmentContributors.java:121)
at org.springframework.boot.context.config.ConfigDataEnvironment.processInitial(ConfigDataEnvironment.java:240)
at org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:227)
at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:102)
at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:94)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:82)
at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:63)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
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:374)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:332)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
at com.my.MyServiceApplication.main(MyServiceApplication.java:18)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
2. Failed with yaml config file only, config import with set format and config server at last place
application.yml
spring:
application:
name: my-service
config:
import: classpath:monitoring.yaml, "optional:configserver:"
Result
ERROR - 11:17:19.034 - Saas-DB - [c:|n:] [SpringApplication]: Application run failed java.lang.IllegalStateException: Unable to load config data from '"optional:configserver:"' Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/' or File.separator
Full stack here
ERROR - 11:17:19.034 - Saas-DB - [c:|n:] [SpringApplication]: Application run failed
java.lang.IllegalStateException: Unable to load config data from '"optional:configserver:"'
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferences(StandardConfigDataLocationResolver.java:141)
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferences(StandardConfigDataLocationResolver.java:126)
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.resolve(StandardConfigDataLocationResolver.java:119)
at org.springframework.boot.context.config.ConfigDataLocationResolvers.lambda$resolve$1(ConfigDataLocationResolvers.java:115)
at org.springframework.boot.context.config.ConfigDataLocationResolvers.resolve(ConfigDataLocationResolvers.java:126)
at org.springframework.boot.context.config.ConfigDataLocationResolvers.resolve(ConfigDataLocationResolvers.java:115)
at org.springframework.boot.context.config.ConfigDataLocationResolvers.resolve(ConfigDataLocationResolvers.java:107)
at org.springframework.boot.context.config.ConfigDataImporter.resolve(ConfigDataImporter.java:105)
at org.springframework.boot.context.config.ConfigDataImporter.resolve(ConfigDataImporter.java:97)
at org.springframework.boot.context.config.ConfigDataImporter.resolveAndLoad(ConfigDataImporter.java:85)
at org.springframework.boot.context.config.ConfigDataEnvironmentContributors.withProcessedImports(ConfigDataEnvironmentContributors.java:121)
at org.springframework.boot.context.config.ConfigDataEnvironment.processInitial(ConfigDataEnvironment.java:240)
at org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:227)
at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:102)
at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:94)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:82)
at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:63)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
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:374)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:332)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
at com.my.MyServiceApplication.main(MyServiceApplication.java:18)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/' or File.separator
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferencesForFile(StandardConfigDataLocationResolver.java:229)
at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferences(StandardConfigDataLocationResolver.java:138)
... 36 common frames omitted
3. Success with config server import extracted in application.properties file
application.yml
spring:
application:
name: my-service
config:
import: classpath:monitoring.yaml
application.properties
spring.config.import=optional:configserver:
Result
INFO - 11:25:56.012 - [c:|n:] [MyServiceApplication]: Starting MyServiceApplicationusing Java 11.0.9.1 on ... INFO - 11:25:56.012 - [c:|n:] [MyServiceApplication]: The following profiles are active: dev WARN - 11:25:56.112 - [c:|n:] [ConfigDataLoader]: Could not locate PropertySource ([ConfigServerConfigDataResource@e090afd uris = array
['http://localhost:8888'], optional = true, profiles = list['dev']]): I/O error on GET request for "http://localhost:8888/my-service/dev": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect ... INFO - 11:26:17.743 - [c:|n:] [MyServiceApplication]: Started MyServiceApplicationin 26.415 seconds (JVM running for 29.519)
4. Success with yaml config file only, config import with list format
application.yml
spring:
application:
name: my-service
config:
import:
- classpath:monitoring.yaml
- "optional:configserver:"
Result
INFO - 11:25:56.012 - [c:|n:] [MyServiceApplication]: Starting MyServiceApplicationusing Java 11.0.9.1 on ... INFO - 11:25:56.012 - [c:|n:] [MyServiceApplication]: The following profiles are active: dev WARN - 11:25:56.112 - [c:|n:] [ConfigDataLoader]: Could not locate PropertySource ([ConfigServerConfigDataResource@e090afd uris = array
['http://localhost:8888'], optional = true, profiles = list['dev']]): I/O error on GET request for "http://localhost:8888/my-service/dev": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect ... INFO - 11:26:17.743 - [c:|n:] [MyServiceApplication]: Started MyServiceApplicationin 26.415 seconds (JVM running for 29.519)
5. Success with yaml config file only, config import with set format and explicit url
application.yml
spring:
application:
name: my-service
config:
import: classpath:monitoring.yaml, optional:configserver:http://localhost:8888
or
spring:
application:
name: my-service
config:
import: optional:configserver:http://localhost:8888, classpath:monitoring.yaml
Please note that, I don't around optional:configserver: with quote else I get the same exception as case 1 and case 2
Result
INFO - 11:25:56.012 - [c:|n:] [MyServiceApplication]: Starting MyServiceApplicationusing Java 11.0.9.1 on ... INFO - 11:25:56.012 - [c:|n:] [MyServiceApplication]: The following profiles are active: dev WARN - 11:25:56.112 - [c:|n:] [ConfigDataLoader]: Could not locate PropertySource ([ConfigServerConfigDataResource@e090afd uris = array
['http://localhost:8888'], optional = true, profiles = list['dev']]): I/O error on GET request for "http://localhost:8888/my-service/dev": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect ... INFO - 11:26:17.743 - [c:|n:] [MyServiceApplication]: Started MyServiceApplicationin 26.415 seconds (JVM running for 29.519)
Comment From: ryanjbaxter
For scenario 1. your YAML is invalid so its a non starter. There should not be any other characters after the closing quote.
For scenario 2, you configuration is invalid. Essentially what you are doing is adding quotes around optional
so you are turning optional
into "optional:..."
and that is not valid. It needs to be optional
without any quotes.