I'm building a backend servie using spring boot. The packaging type is war.
I externalized the main application config, including RSA private keys in a file named env.properties
located at the project root folder(right next to pom.xml).
In the main application.properties
file, I have all the same fields that reference the fields in the env.properties
file.
I put application.properties
in version control, not the env.properties
.
My purpose of doing so is that I can keep the real production config away from version control and make each delopyment flexible in terms of configuration.
During development, the setup works fine, but the problem comes when I try to deploy the application. After running mvn clean build, I get a .war file, which won't start (404) when I deploy it on tomcat9. If I try to run the .war file with java -jar xxx.war, the output is
$ java -jar xxx.war
23:04:12.260 [main] DEBUG org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.boot.context.config.ConfigDataResourceNotFoundException: Config data resource 'file [env.properties]' via location 'file:env.properties' cannot be found
at org.springframework.boot.context.config.ConfigDataResourceNotFoundException.withLocation(ConfigDataResourceNotFoundException.java:97)
at org.springframework.boot.context.config.ConfigDataImporter.handle(ConfigDataImporter.java:145)
at org.springframework.boot.context.config.ConfigDataImporter.load(ConfigDataImporter.java:136)
at org.springframework.boot.context.config.ConfigDataImporter.resolveAndLoad(ConfigDataImporter.java:86)
at org.springframework.boot.context.config.ConfigDataEnvironmentContributors.withProcessedImports(ConfigDataEnvironmentContributors.java:116)
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:85)
at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:339)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:297)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
at com.jnairport.yqpay.YqpayApplication.main(YqpayApplication.java:10)
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.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.WarLauncher.main(WarLauncher.java:59)
23:04:12.267 [main] ERROR org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
Config data resource 'file [env.properties]' via location 'file:env.properties' does not exist
Action:
Check that the value 'file:env.properties' at class path resource [application.properties] - 1:22 is correct, or prefix it with 'optional:'
It seems when I package the application for deployment, maven didn't pick up the env.properties
file. The whole app won't run at all.
I've tried to search for solutions but got no clue. Please help, thanks.
CodePudding user response:
I sense two issues (writing sense as it's hard to be sure without a reproducible example):
- referencing a file by
file:env.properties
is error-prone, as this path is different on every system - maven doesn't know about the
env.properties
file and there for it doesn't include it in the package
Let's tackle that one by one
Resource location
Your reference file:env.properties
is ambiguous. When you define your resource location with file
it uses FileSystemResource
underneath. See spring documentation on FileSystemResource
Caveats.
So when you write your test, your IDE looks for a project root path. But when you are running the jar file - the path might be different. That might be the reason why app is unable to find a properties file.
Probably the best approach would be to pack the env.properties
into the war file during the build process or (even better) set the values in the properties file as environment variables
Packaging with Maven
You can include your env.properties
in your application classpath, without versioning it. That way you can refer to the file with classpath:env.properties
within your application codebase
You will need to extend your Maven build file with additional resource location
CodePudding user response:
Thanks for the reply from Jakub Marchwicki, it's quite detailed. I believe it's going to help a lot of people who may have searched for a similar issue in the future.
I solved the problem by ditching the whole idea of using the env.properties file for externalized config. After some study I found a better way of using spring profiles, where I keep config files for different profiles and reference the needed one in application.properties file.
For example, I could have a application-development.properties
file and a application-production.properties
file. When in development, the first config file is referenced in the main application.properties
file whereas in production, the second one is referenced. The config file for production is not version controlled of course.
You may find this article useful: https://springframework.guru/spring-profiles/
Hope it helps, cheers!