How to add an active spring profile from an environment variable? - spring

Until now, I'm setting the following environment variable in my ~/.bash_profile :
export SPRING_PROFILES_ACTIVE=local
This set my active spring profile. But now, I want to add the local profile to other profiles defined in application.properties and not replace them.
In the Spring Boot documentation, there is a section about adding active profile, but I see nothing about adding active profile from an environment variable.
I've tried to set the SPRING_PROFILES_INCLUDE environment variable, but this has no effect.
How to do this?
P.S.: I'm using Spring Boot 1.4.2.

With default addition profile
You can introduce your own environment variable in the application.properties file, next to the defined profiles using an expression. For instance, if your current file looks like this:
spring.profiles.active=profile1,profile2
with a custom environment variable it will change into:
spring.profiles.active=profile1,profile2,${ADDITIONAL_APP_PROFILES:local}
where ADDITIONAL_APP_PROFILES is the name of the environment variable which you set instead of SPRING_PROFILES_ACTIVE.
The value local is used when the variable is not set on a current environment. In that case, the profile called local will be activated. If you don't set the default value and the environment variable is not present, the whole expression will be used as the name of an active profile.
Without default addition profile
If you like to avoid activating the default profile, you can remove the placeholder value and the comma before the variable expression:
spring.profiles.active=profile1,profile2${ADDITIONAL_APP_PROFILES}
but in that case the variable set on a current environment have to start with a comma:
export ADDITIONAL_APP_PROFILES=,local

The next sentence in the documentation you linked to:
Sometimes it is useful to have profile-specific properties that add to the active profiles rather than replace them. The spring.profiles.include property can be used to unconditionally add active profiles.
So you can launch your application with a command-line parameter:
-Dspring.profiles.include=${SPRING_PROFILES_INCLUDE}

This is an example of adding a programmatically additional active profile from system env or jvm arg.
#Configuration
public class ApplicationInitializer implements WebApplicationInitializer, ApplicationContextInitializer<ConfigurableWebApplicationContext> {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("contextInitializerClasses", this.getClass().getCanonicalName());
}
#Override
public void initialize(ConfigurableWebApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
environment.addActiveProfile(System.getProperty("myProperty"));
environment.addActiveProfile(System.getEnv("myProperty"));
}
}

Here is not the Spring way, but also could be useful in some cases.
public static void main(String[] args) {
System.setProperty("spring.profiles.active", "local");
SpringApplication.run(MainApplication.class, args);
}

To support bash environment, available values are SPRING_PROFILES_ACTIVE and SPRING_PROFILES_DEFAULT
not, SPRING_PROFILES_INCLUDE
you probably have to resort commandline way -Dspring.profiles.include or pro grammatically workout with ConfigurableEnvironment
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/env/AbstractEnvironment.html#ACTIVE_PROFILES_PROPERTY_NAME

Related

override command line Spring properties with bootstrap.yml

i have a tomcat box , which has spring active profile set already, as a command line argument every time a spring app is deployed through catalina.
i am using spring cloud config server , so in config client i specify active profile in bootstrap.yml , but as i mention earlier it is overriden by tomcat command line argument .
how to override the command line argument passed through tomcat , with my boostrap.yml at the time of bootstrap context loading so that i can pass active profile from my bootstrap.yml to config server.
Tomcat set environment command (which i cannot change as i dont have access)
JAVA_OPTS="$JAVA_OPTS -Djava.library.path=/path -Dspring.profiles.active=e2"
bootstrap.yml
spring:
profiles:
active: e2,cron
cloud:
config:
uri: http://localhost:8888
application:
name: heartbeat_monitor.
Command line argument(-Dspring.profiles.active=e2) will always override your properties file, no matter how many hardcoded profiles you specify in your yaml file. I would suggest you to add additional profile to be set programatically at the runtime and keep two property files with -profilename before the .yml extension.
This could be done as follows:
ApplicationMain.java
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DemoApplication.class);
app.setAdditionalProfiles("cron");
app.run(args);
}
bootstrap-e2.yml
// Keep all the properties which is specific to e2 profile.
bootstrap-cron.yml
// Keep all the properties which is specific to cron profile.
In this way, you can keep both the profiles in use .But, if a property is common in both the bootstrap files, then program will pick the property from that bootstrap file whose profile matches with the runtime args : -Dspring.profiles.active

How to programatically tell Spring Boot application that application.yml is in app user home directory?

I am attempting to move my application.yml outside of my application to the user directory that the application runs under. I am aware that a common approach is to use startup params at runtime like -Dconfig.location=/path/to/external.properties (which incidentally I can't seem to make work propertly), but I need to be able to do this without changing the startup script if at all possible.
My goal was to do this in the main() method of the application groovy file that starts the app. In that method, I am detecting the user's home directory, and am attempting to set that as a property for the app to use. However, all approaches I have attempted have ended up with a FileNotFound (application.yml). can someone offer any advice on achieving what I want? Below is the most recent attempt
static void main(String[] args) throws NoSuchAlgorithmException, IOException, URISyntaxException {
String configPath = "${System.getProperty('user.home')}"
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(Angular4SpringbootApplication)
.properties("spring.config.name:application,conf",
"spring.config.location=classpath:$configPath/application.yml")
.build().run(args)
SpringApplication.run(Angular4SpringbootApplication, args)
}
Since you pass the command line parameters to SpringApplication.run you could simply modify them before. I don't know much about Groovy, but I think this should work:
static void main(String[] args) {
SpringApplication.run(Angular4SpringbootApplication, ["--spring.config.location=file:${System.getProperty('user.home')}/application.yml"] + args)
}
You could also set a system property before starting the Spring context:
static void main(String[] args) {
System.setProperty('spring.config.location', "${System.getProperty('user.home')}/application.yml")
SpringApplication.run(Angular4SpringbootApplication, args)
}
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. You can also refer to an
explicit location by using the spring.config.location environment
property (which is a comma-separated list of directory locations or
file paths). The following example shows how to specify a different
file name:
java -jar myproject.jar --spring.config.name=myproject
Or you can use location (if rour file is outside your app, prefix with file: ) :
java -jar myproject.jar --spring.config.location=classpath:/default.properties
spring.config.name and spring.config.location are used very early to
determine which files have to be loaded, so they must be defined as an
environment property (typically an OS environment variable, a system
property, or a command-line argument).
Edit
If you want to do it without changing the startup script, you can do it like this :
#SpringBootApplication
public class SimpleBoot {
public static void main(String[] args) {
System.setProperty("spring.config.location","file:/path/to/application.yml")
SpringApplication.run(SimpleBoot.class, args);
}
}

Spring ignores `spring.cloud.config.discovery` set as default properties

I'm trying to create Spring Application without referring to any external files. This is supposed to be a module that you'd then include as a dependency, configure and use to plug in the service into an existing ecosystem. This is how I'm doing that:
Map<String, Object> properties = new HashMap<>();
properties.put("server.address", "0.0.0.0")
properties.put("server.port", 8080)
properties.put("spring.profiles.active", "cloud")
properties.put("spring.application.name", "someApp")
properties.put("spring.cloud.config.failFast", true)
properties.put("spring.cloud.config.discovery.enabled", true)
properties.put("spring.cloud.config.discovery.serviceId", "config")
properties.put("eureka.instance.preferIpAddress", true)
properties.put("eureka.instance.statusPageUrlPath", "/health")
new SpringApplicationBuilder()
.bannerMode(Banner.Mode.OFF)
.properties(properties)
.sources(SpringConfiguration.class)
.web(false)
.registerShutdownHook(true)
.build()
I then go on to provide Eureka default zone in the run command, via environmental variables:
--env eureka_client_serviceUrl_defaultZone='http://some-host:8765/eureka/' --env SPRING_CLOUD_CONFIG_LABEL='dev' --env SPRING_CLOUD_INETUTILS_PREFERRED_NETWORKS='10.0'
Application registers successfully in Eureka, but unfortunately it tries to fetch the config prior to that and it's looking for it under the default URL (http://localhost:8888) instead of fetching config server IP from the registry. And yes, it does work if I put all of those properties in the bootstrap.yml file. Can I somehow make it work without using file-resources?
You are passing the properties using SpringApplicationBuilder which is responsible for SpringApplication and ApplicationContext instances.
From the documentation , the properties provided here will be part of ApplicationContext NOT the BootstrapContext. ApplicationContext is the child of BootstrapContext.
You can read more about the Bootstrap Context here -
http://cloud.spring.io/spring-cloud-commons/1.3.x/single/spring-cloud-commons.html#_the_bootstrap_application_context
Bootstrap.yml/properties is used to configure your Bootstrap Context.
You can look at these properties to change the name or location of the file -
spring.cloud.bootstrap.name - bootstrap(default)
spring.cloud.bootstrap.location
You will have to use a file resource(yml or properties).

How to externalize configuration in Spring Boot using profiles?

I have an application where I would like to change a datasource password that is stored in a application.yml file. The password in the YML file is stored such as this:
----
spring:
profiles: production
datasource:
password: prodpassword
Note: I also have profiles for development and stage.
The password prop is set on a class using ConfigurationProperties such as follows:
#Component
#ConfigurationProperties(prefix="datasource")
public class DataSourceConnector {
private password;
public void setPassword(String password) {
this.password = password;
}
Now, I try to override the prodpassword with prodpa$$word via a command line arg but it doesn't work:
java -Dspring.profiles.active=production -jar /usr/share/myapp/myapp-1.0.jar --datasource.password='prodpa$$word'
I also tried creating an identical (except the new password) application.yml file outside of the jar. That doesn't work either.
java -Dspring.profiles.active=production -jar /usr/share/myapp/myapp-1.0.jar --spring.config.location=/usr/share/myapp/
Note: I left out the file name in the location param due to this note from http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-profile-specific-properties:
If you have specified any files in spring.config.location, profile-specific variants of those files will not be considered. Use directories inspring.config.location if you also want to also use profile-specific properties.
How can I override datasource.password within the application.yml of the jar?
Edit:
The application is being started / stopped using supervisorctl.
After changing the config file that contains the java command, supervisorctl must reread the change:
supervisorctl reread
Next, activate the changes with:
supervisorctl update

#PropertySource working with spel (systemEnvironment and systemProperties)

I have in src/main/resources the following files
bpp-dev.properties
bpp-prod.properties
bpp-test.properties
Through my STS I can define the key envB, it in two places
how a VM argument such as -DenvB=dev
how an Environment such as Variable envB and Value prod
If in a Configuration class I have the following.
#Configuration
#PropertySource("classpath:/com/manuel/jordan/properties/bpp-${envB}.properties")
public class PropertiesConfiguration {
It works fine but always has preference the System Properties over Environment Variables, it is the default behaviour. I have no problem here.
But if I want work explicitly with Environment Variables, the following fails
#Configuration
#PropertySource("classpath:/com/manuel/jordan/properties/bpp-#{systemEnvironment['envB']}.properties")
public class PropertiesConfiguration {
Always I receive:
Caused by: java.io.FileNotFoundException:
class path resource
[com/manuel/jordan/properties/bpp-#{systemEnvironment['envB']}.properties]
cannot be opened because it does not exist
How I can fix this?
If I use the functional #PropertySource and just playing in the same #Configuration class I work with the following:
#Value("#{systemProperties['envB']}")
private String propertiesEnvB;
#Value("#{systemEnvironment['envB']}")
private String environmentEnvB;
to be printed later, both works fine.

Resources