Spring boot setting included profiles via environment variables - java

I am wondering has anyone else noticed this issue when using Spring Boot 2.0.3. In my application.properties, I have set the spring.profiles.active to the global profiles I will use. Then, depending on the environment being deployed to, I set spring.profiles.include in the environment variables. However, when the application runs, it only picks up the profiles specified with the include property. So
application.properties
spring.profiles.active=http
environment
spring.profiles.include=dev
Results in
2018-08-09 13:45:35.025 INFO [main] Application : The following profiles are active: dev
Instead of
2018-08-09 13:45:35.025 INFO [main] Application : The following profiles are active: dev,http
If both properties (spring.profiles.active and spring.profiles.include) are set in the same place, this works fine. This seems like a bug to me, or am I doing something stupid?
From what I understand, Spring will go through each of it's property sources until it finds the property - this doesn't appear to be happening here.

TL;DR : don't use spring.profiles.active : it's not exactly what you believed it is.
Long version
Uuum, I tried to debug a bit how the profiles were loaded in spring-boot :
To mimic your case, I put a
spring.profiles.include=dev in my environment
spring.profiles.active=demo in my application.yml
I reached ConfigFileApplicationListener$Loader and that's where the magic happens :
During initializeActiveProfiles() ,the loader looked for the spring.profiles.active in my environment (because environment takes precedence over properties) and then spring.profiles.include (still in the environment). It then activated the profile(s) found, in my case dev.
Afterwards, it tries to load subsequent profiles from its propertySources, where it indeed finds spring.profiles.active=dem...
BUT, in maybeActivateProfiles(), it happens that this.activatedProfiles is set to true (duh, dev had already been activated), and thus, demo is voluntarily not activated.
My take on this is that spring.profiles.active is ONLY used (as a switch) to activate the first profile(s) and then, only include applies. The funny thing here is that include takes precedence over active when it is found in a document with greater priority.

Well, maybe it's not the nicest solution but you can enumerate ALL the profiles in the environment variables:
spring.profiles.active=http, dev

Related

One of Spring profiles is not actived despite adding it in environment variable

I have problem with activation of one of the Spring profiles. I've tried adding it as VM Option, environment variable, but with no success.
What is weird is the fact, that all other profiles are working properly.
I've tried to list active profiles using:
for (String profileName : environment.getActiveProfiles()) {
System.out.println("Currently active profile - " + profileName)
}
And one I'm trying to enable is visible as "''".
Here are my VM Options (Intellij) and listed profiles while app is running.
I have classes in my application that use #Profile("inappreports) annotation.
Anyone know what can be the cause?
It turned out the problem was caused by WildFly config that was overriding env variable.

Java Microprofile change properties on already deployed app (Wildfly)

I have an ear artifact deployed on a wildfly server. On some beans I used the following configuration injection
#Inject
private Config config;
I want to change the properties specified on the "microprofile-config.properties" file on runtime. It is not necessary to change the file itself, I just want to change the properties. I think there might be a way using the console, but I cannot find if there is any.
If you take a look at the spec or even at articles like this, you will see that, by default, Microprofile config reads configuration values from the following 3 places in this order - i.e. from wherever it finds it first:
System.getProperties()
System.getenv()
The configuration file
So, you can override values in the configuration file in 2 ways:
Defining -D command line arguments to the VM (e.g. java -DXXX=yyy ...)
Defining system environment variables (e.g. export XXX=yyy in bash or set XXX=yyy in Windows)
Note that there are some rules for defining environment variables and matching them to actual configurations, e.g. for a configuration aaa.bbb.ccc you may need to set an environment variable as AAA_BBB_CCC. Read ch. 5.3.1 in the specs, and experiment a little.
You can always extend the configuration sources with your own custom ones (to read configuration from JNDI, DB, Zookeeper, whatever).

Disable spring-context-indexer by profile

I want to enable Spring Context Indexer on a project but I am having issues with Swagger3 (check here and here).
I understand the limitations and would like to enable it, at least, at the DEV profile where we do not need Swagger running.
My goal is to disable indexing on PROD environment. From docs:
you can fallback to a regular classpath arrangement (as though no index was present at all) by setting spring.index.ignore to true, either as a system property or in a spring.properties file at the root of the classpath.
My first approach (without success) was setting an env var (Windows 10) with the following:
SPRING_APPLICATION_JSON={"spring":{"index":{"ignore":true}}}
If I create a spring.properties file and set the value accordingly it works. But I can't figure how to use different properties for each profile, I imagined it was something like the application.properties file but I was wrong.
How can I achieve that?
EDIT:
Just to be clear, this config (spring.index.ignore) will just work if inside a spring.properties file, not an application.properties one. (Just double-checked before this edit)
Actually I have one application-prod.properties and one application-dev.properties.
I don't know the difference between spring.properties and application.properties but the first one doesn't seem to work with multiple profiles as the later.
Edit 2:
Just went through org.springframework.context.index.CandidateComponentsIndexLoader and it uses SpringProperties.getFlag(IGNORE_INDEX) to read the value.
SpringProperties class is clear about the file it uses:
Reads a spring.properties file from the root of the Spring library classpath, and also allows for programmatically setting properties through setProperty. When checking a property, local entries are being checked first, then falling back to JVM-level system properties through a System.getProperty check.
I think I will need to pass a property to Java runner during initialization. Will research a little bit more about it.
you can use different application.properties file as follow:
add the following files to the resource folder:
application.properties
spring.profiles.active=dev #place profile name you want to use
application-dev.properties
#dev properties
...
You can create many application-env.properties you may wish
application-env.properties
#env properties
...
Spring Boot supports profile-specific properties files. You have to name these files with the following format: application-{profile}.properties. You can activate a profile via JVM system parameter: -Dspring.profiles.active=dev. You can read more about this subject here: https://www.baeldung.com/spring-profiles

Spring properties file set null to property

Currently I have a few property files in my src/main/resources folder:
- application.properties
- application-dev.properties
- application-test.properties
Now, when I specify a profile, it loads both that profile's specific file and the general application.properties, overwriting everything with the former.
However, when my application is deployed in production, no profile is passed, so application.properties must be my production file.
That's fine because I can overwrite everything in the profile specific ones. There is one problem, however; on production, I now need to set:
spring.datasource.jndi-name=jdbc/appname
When I add that to application.properties, every profile also inherits that, and then, when I run dev server or a test, it gives me this error:
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Because I indeed don't use this JNDI stuff on my own environments; I use the following properties:
spring.datasource.url=jdbc:url
spring.datasource.username=user
spring.datasource.password=pass
My problem is: I cannot overwrite this property for my life. If I add any of these on the env specific files, it will be ignored and the error persists:
spring.datasource.jndi-name= # empty string, does nothing
spring.datasource.jndi-name=<null> # read somewhere it meant null, doesn't work
spring.datasource.jndi-name=${inexistentProp} # I though it might return null, but gives an error
So, what can I do? I think of several solutions:
A way to set a property value as proper null or undefined in a spring property file (since empty doesn't do the trick)
A way to change the commonly inherited property file to something else (I tried #PropertySource, but it only add more options, always falling back to application.properties eventually)
Disable JNDI altogether via properties file, despite the former property (I tried spring.jndi.ignore=true as per here, but to no avail)
But either I don't know how or they don't work.
Spring Boot documentation, 24.4 Profile-specific Properties:
In addition to application.properties files, profile-specific properties can also be defined by using the following naming convention: application-{profile}.properties. The Environment has a set of default profiles (by default, [default]) that are used if no active profiles are set. In other words, if no profiles are explicitly activated, then properties from application-default.properties are loaded.
25.1 Adding Active Profiles:
The spring.profiles.include property can be used to unconditionally add active profiles.
So, create a application-default.properties file with the following property:
spring.profiles.include=prod
Now move the spring.datasource.jndi-name=jdbc/appname property from the application.properties file to the application-prod.properties file.
When you run code with a specific profile, the prod profile will not be used, and spring.datasource.jndi-name will be undefined.
When you run code in production, where no profile is specified, the prod profile is included by default, and spring.datasource.jndi-name will be defined.
You can of course just put the production properties in the application-default.properties itself, but the above approach makes it more clear what you're doing.
It also makes it easier if you end up with multiple profiles. E.g. you have prod vs dev vs test. But what if you have independent set of profiles such as foo vs bar, and that default production environment should be prod,foo? Allowing alternate production environment to use prod,bar, and each can be tested separately (test,foo and test,bar).
By using the default profile to only include other profiles, without otherwise define any properties, you can now manually mix and match profiles, as needed.

Set Spring Boot application.properties based on Environment Variable

I have a Spring Boot application that will run in various environments, and based on the environment it runs in, it will connect to a different database. I have a few application.properties files, one for each environment which look like such:
application-local.properties:
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=dbuser
spring.datasource.password=123456789
application-someserver.properties:
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://someserver:5432/myproddb
spring.datasource.username=produser
spring.datasource.password=productionpass
etc. etc.
On each of my environments, I have a environment variable called MYENV which is set to the type of environment it is, for example local or someserver (the name of the application-{env}.properties files perfectly matches the environment name).
How can I get spring boot to read this environment variable and automatically choose the correct .properties file? I don't want to have to do the whole -Dspring.profiles.active=someserver because of the way this package is deployed (it will not be running as a jar).
How can I get spring boot to read this environment variable and
automatically choose the correct .properties file?
Tell Spring Boot to select the Active Profile from the MYENV property or environment variable. One way of doing this is to add the following into your application.properties:
spring.profiles.active=${MYENV}
This way Spring Boot will set spring.profiles.active to the value of MYENV environment variable.
I don't to have to do the whole -Dspring.profiles.active=someserver
because of the way this package is deployed (it will not be running as
a jar)
You can use the corresponding environment variable to that spring.profiles.active, if you don't like to pass a system property through -D arguments. That corresponding environment variable is SPRING_PROFILES_ACTIVE. For example if you set the SPRING_PROFILES_ACTIVE to local, the local profile will be activated. If you're insisting to use MYENV environment variable, the first solution is the way to go.
If you are deploying that application to some container (tomcat, weblogic, ...) you can specify environment variable. For example lets specify environment variable application1.spring.profiles.active=someserver and then in your application.properties set property spring.profiles.active=${application1.spring.profiles.active}
Using Spring context 5.0 I have successfully achieved loading correct property file based on system environment via the following annotation
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource("classpath:application-${MYENV:test}.properties")})
Here MYENV value is read from system environment and if system environment is not present then default test environment property file will be loaded, if I give a wrong MYENV value - it will fail to start the application.
Note: for each profile, you want to maintain you need to make an application-[profile].property file and although I used Spring context 5.0 & not Spring boot - I believe this will also work on Spring 4.1
This is the best solution I believe for my AWS Lambda - with minimal dependencies

Categories