Spring doesnt take environment variables - java

In my project I have seperate application.yml files for each environments, inside each folder for an environment.
NOTE: Below red color yml file made temporarily, to make the code work. But should remove this after fixing. So what I want is to use separate application.yml file according to environment. Specially I need to use local/application.yml for local development
Below has an example of getting env variables in my project
#Component
#Configuration
public class ApplicationProperties {
#Value("${ex.my.url}")
private String myServiceUrl;
// getters setters and nedded stuff
}
But it doesn't work, since could not find a way to mention the needed environment. Because it is in a seperate folder. All the other examples mention the way to get the yml file inside resource folder, without seperate folders.
Any fix for the issue?

in my projects, I specify a profile with the VM option :
-Dspring.profiles.active=local
Then I have a file named application-local.yml
in production :
-Dspring.profiles.active=prod , will use the file application-prod.yml

In the resources folder
Do make file of application.yml, application-local.yml, application-dev.yml etc.. what ever you want
and then in application.yml
spring.profile.active = ${ENV}
now during the run specify the ENV variables from run/debug configuration under Intellij or mention the profile for which you want to build the jar in application.yml.
Alternative, you can use the -D spring.profile.active=dev

at-first, from the documentation:
SpringApplication loads properties from application.properties files in the following locations and adds them to the Spring Environment:
A /config subdirectory of the current directory
The current directory
A classpath /config package
The classpath root
So, with passed directory hierarchy you'll get problems.
at-second, for file specification, you could use Profiles. This works as follows:
if no specified profiles - application.properties would be used
for any additional profile also would be used profile with name application-<name>.properties
Thus, if you specify dev and cool profiles, properties application.properties, application-dev.properties, `application-cool.properties, would be in use
UPDATE:
You could pass spring.config.location for property file path specify, but if you want directory hierarchy as you have - you need some customizations using context.initializers.classes and ApplicationContextInitializer

The classic solution is to setup one config file
(I prefer properties because I'm sane)
and allow for an overrides file to be placed on each installed host.
Spring supports this out-of-the-box.
Here is an example:
public static void main(final String[] argumentArray)
{
final StringApplicationBuilder springApplicationBuilder;
springApplicationBuilder = new SpringApplicationBuilder(YourSpringBootApplication.class)
springApplicationBuilder.properties(
"spring.config.location=classpath:/yourConfig.properties,/some/path/to/overrides/directory/yourConfig.properties");
springApplicationBuilder.build().run(argumentArray);
}

Related

Sping Boot - Separate config file other than application.yaml

I have a spring boot maven java application. I have the application.yml file and the properties specified here are used by a jar dependency of the project and I want to keep this file for them dependencies alone.
There is some additional config I want to use in the application. Is there a way to specify a second configuration file to be used within the project?
Thanks
You can use #PropertySource({"classpath:first.properties", "classpath:second.properties"}) like given below,
#Component
#PropertySource("classpath:config.properties")
public class MySeparateProperties
{
#Value( "${property.path.name}" )
private String prop;
}
Now use this bean to use the properties where ever you want in your application
The only issue is that, if the property path is same for application.yml and config.properties application.yml will have higher priority
Edit 1
If the file is located at resources/config/config.properties then you have to give classpath:config/config.properties
You can specify a different name using the property spring.config.name.
From the docs:
If you do not like application.properties as the configuration file name, you can switch to another file name by specifying a spring.config.name environment property.
Create property file as example x.properties and use #PropertySourse(“classpath:x.propeties”)
When need to use it
Configuration files can be set by spring properties, you can use:
spring.config.name to select the names of the configuration files (separated by comma)
spring.config.location to set the paths where the configuration files are located (separted by comma)
Try to look at springboot reference doc here for details.

SpringBoot application with multiple application.properties running under Tomcat

I need two application.properties in my Spring Boot App.
I know that using the annotation #PropertySource I can specify more than 1 property files.
I tried to use: #PropertySource({"classpath:application.properties","classpath:external.properties"})
The idea of it is having application.properties with the machine independent properties and this file will be included inside the war file.
The other file (external.properties), will leave in the machine, and won't be included in the war file. Here I want to leave properties like the database connection and so on.
I've already changed catalina.properties for adding the external.properties location into the classpath, but unfortunately when running on Eclipse it doesn't work (complains about the missing database properties.).
If the external properties file will be available in a known location on the machine, then have an environment variable, system property, or command-line argument set up with the path to the file. Then, reference the file in you #PropertySource annotation using file: rather than classpath:
Example: #PropertySource("file:${CONF_DIR}/external.properties")
References:
Spring boot docs on external configuration
PropertySource documentation
Blog post regarding PropertySource

Using two yaml files for configuration properties

We are using a spring boot application, where properties are loaded from application.yml file instead of application.properties, located at src/main/resources/ which looks like below:
config:
host: localhost:8080
server: 123
And they are being pulled in a .java file like this
#ConfigurationProperties( prefix="config")
public class ConnectionImpl implements Connection{
#Value("${config.host}")
private Stringhost;
}
I am able to retrieve properties this way.
But we are trying to move the config properties from application.yml to a different .yml file which is located at a different location. (src/main/resources/env-config).
Now I am not able to retrieve properties same way, i.e, using #Value annotation. Is there any other annotation I need to add ?
From the documentation:
SpringApplication will load properties from application.properties (or application.yml) files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The class path root
If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).
The default search path classpath:,classpath:/config,file:,file:config/ is always used, irrespective of the value of spring.config.location. This search path is ordered from lowest to highest precedence (file:config/ wins). If you do specify your own locations, they take precedence over all of the default locations and use the same lowest to highest precedence ordering. In that way you can set up default values for your application in application.properties (or whatever other basename you choose with spring.config.name) and override it at runtime with a different file, keeping the defaults.
You need to supply a command line argument that tells SpringApplication where specifically to look. If everything in resources/ is added to the classpath root, then your command line would look like:
java -jar myproject.jar --Dspring.config.location=classpath:/env-config/service-config.yml
If you have a general application.yml under resources/, the properties in there will still be loaded but will take a lower precedence to the properties file specified on the command line.
Your question doesn't really say what you intend to do, but if you want to have a different configuration for different environments (e.g. development, test, production), there is a simple solution for that.
Place your config files in a file hierarchy like this inside your project:
src/
main/
resources/
application.yml
application-development.yml
application-test.yml
application-production.yml
When you now start your application with
java -jar mySpringApplication.jar -Dspring.profiles.active=development
the configuration from application.yml will be taken as a "base layer", overridden by the configuration in application-development.yml. By this, you can have "default" settings for all environments in application.yml and environment-specific configuration in the application-ENV.yml files. The same works for test and production.
No.
You'll be in a much better position if you avoid hard-coding file path like that within your code base. #ConfigurationProperties used to have a locations attribute but it's deprecated and already removed in 1.5.
In Spring Boot, you configure the Environment which is a single source of truth for your configuration. Rather than having settings buried in code, you should configure Spring Boot to read the files that you want. Read the documentation for spring.config.location. If you want to do this in a more transparent manner, perhaps EnvironmentPostProcessor is what you need

Overwriting Spring Boot application.properties properties in plugin and through Tomcat?

We have a base project, example-platform and an extending customer project, example-customer1 that uses the platform as a dependency. The application.properties of the platform defines the default properties and the customer project overwrites these. For example, the platform can be run on its own so it has a spring.datasource.url for the database it uses and the customer project uses another.
Initially, I had hoped that this could be done simply by having an application.properties in the customer project that changed the necessary properties, but instead, only the properties in that file were used instead of anything in the platform's application.properties. At first I just copied over the application.properties file and changed some properties, but I don't like the idea of having to update the customer project if a new property is needed for platform. To fix that, I put the application.properties of the customer project into config/application.properties. That way I could have the properties from platform and override only the ones I needed to in the customer project.
Now I need to be able to override the properties when it's deployed as a War with Tomcat. That is, I need properties in example-platform / application.properties to be overridden by example-customer1 / config / application.properties to be overridden by some application.properties for Tomcat.
Before I put application.properties into the config folder for the customer project, I was able to have an application.properties on Tomcat under props/config and the properties were loaded from that. Now that the application.properties of the customer plugin is under /config, however, I think that's being used instead of the properties on Tomcat.
What is the most elegant "Spring" way to accomplish this hierarchy of properties?
edit: To clarify on what I want, let's say we have a property appName. In example-platform/application.properties I would have appName=platform-app, in application.properties of example-customer1/application.properties, appName=customer-app. Then in Tomcat I would like to have an application.properties to be able to override this again, so for one deployment it could be appName=cusomter-app-deployment1.
The properties are loaded in this order (source):
...
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
Application properties outside of your packaged jar (application.properties and YAML variants).
Application properties packaged inside your jar (application.properties and YAML variants).
...
By default, Spring-boot will look for application.properties in your classpath (folder and inside jars) at the locations ./ and ./config (either on disk or in a jar).
Quick solution
Specify a spring profile (customer1) when initializing your spring boot app
use an example-platform/application.properties
and example-customer1/application-customer1.properties.
Same solution - long version
(1) Platform project
Create your default src/main/resources/application.properties
Make sure that this application.properties is located in ./config or ./ in your packaged jar.
(2) Customer1 project
Create an application-${profile}.properties for profile customer1 where you'll override the properties: src/main/resources/application-customer1.properties.
Don't create a default application.properties here.
(3) Launching
Whether you launch your spring boot application from a jar or as a deployed war, you need to specify the profile customer1:
#SpringBootApplication
public class SpringBootApp extends SpringBootServletInitializer{
public static void main(String[] args){
new SpringApplicationBuilder() //
.sources(SpringBootApp.class)//
.profiles("customer1")
.run(args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringBootApp.class)//
.profiles("customer1");
}
}
That's it. Spring Boot will load the default application.properties from its classpasth (example-platform.jar), then load the application-customer1.properties over it.

Maven Dependencies that read property files

I have a java project named dbstuff which reads properties from db.properties with getClass().getResourceAsStream("/db.properties"). The values are things like connection strings, etc. This dbstuff is used in various projects most of them are web.
I now have a dropwizard project (maven) which uses dbstuff.jar as a dependency, this project is compiled as a fat jar as described here, at the moment the dbstuff only read values from db.properties if the file is present in /src/main/resources, and when the package is created the db.properties becomes embedded in the jar.
What i want to achieve is to make db.properties external, so that i can have various db.properties (one for each environment) but only one dropwizard jar, is this possible?
thank you
You could pass in the path to the external db.properties file at runtime via a system property, e.g.:
java -jar myjar.jar -DdbConfig=/path/to/db.properties
Then in your code within dbstuff.jar you can load the properties file:
final String path = System.getProperty("dbConfig");
final Properties properties = Properties.load(new FileInputStream(path));
Alternatively, as you've mentioned you are using Dropwizard, within your configuration file (the yaml file or whatever you are using) have a property which specifies where the external db.properties file is, load the properties during server initialisation and then pass them on to whatever requires it in dbstuff.jar.
You could then take this a step further, forget about the db.properties file and have an entire section in your Dropwizard config that specifies all the properties, e.g.:
db:
url: jdbc://....
user: dbuser
# etc...
And then pass on what the objects in the dbstuff.jar need. See Dropwizard's configuration documentation for more information on how to do this.
I think you can create a configuration class in DropWizard and then use it to return the correct configuration for each environment - https://dropwizard.readthedocs.org/en/latest/manual/hibernate.html.
As for your question, if you have multiple resources on the same path, even if on different jars, there is no guarantee of which one Java will be using. When you add a file named db.properties to /src/main/resources, it becomes the resource "db.properties", and is read as such:
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

Categories