Disable JMS for profile - spring-boot

In Springboot 2 I can do something like this to disable the embedded servlet container:
spring:
main:
web-application-type: none
Now I am looking for a similar setting to disable JMS. Currently I am using a profile, something like this:
#Profile("!nojms")
public class MQListener {
...
and then using an application-lala.yaml with content:
spring:
main:
web-application-type: none
profiles:
active: nojms
But now when I use the profile "lala" then the JMS listener is still starting.

As you're activating via CLI args the profiles.active won't get triggered as you've essentially already activated a profile.
You can add spring.profiles.include to the application-lala to unconditionally active other profiles.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-adding-active-profiles
The spring.profiles.active property follows the same ordering rules as other properties: The highest PropertySource wins. This means that you can specify active profiles in application.properties and then replace them by using the command line switch.
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. The SpringApplication entry point also has a Java API for setting additional profiles (that is, on top of those activated by the spring.profiles.active property). See the setAdditionalProfiles() method in SpringApplication.
spring:
main:
web-application-type: none
profiles:
include: nojms

Related

Spring boot 2.4.x cannot handle multi document yml files from config server

Java version: 8
Spring Boot version: 2.4.1
Spring Cloud version: 2020.0.0, specifically I use a Spring Cloud Config Server connected to GIT and our services are Spring Cloud Config Clients.
I have migrated away from using bootstrap.yml and started using spring.config.import and spring.config.activate.on-profile as mentioned in the documentation here and here
My configuration in my service, who is a client to the config server looks like this:
server.port: 9001
spring:
application.name: my-rest-service
config.import: configserver:http://localhost:8888
cloud.config.profile: ${spring.profiles.active}
My configuration in the config server looks like this:
application.yml (has two documents separated by the ---)
logging:
file.name: <omitted>
level:
root: INFO
---
spring:
config.activate.on-profile: dev
logging.level.root: DEBUG
my-rest-sercive.yml (has two documents separated by the ---)
spring:
datasource:
driver-class-name: <omitted>
username: <omitted>
password: <omitted>
---
spring:
config.activate.on-profile: dev
datasource.url: <omitted>
Because there is a profile "dev" active, I successfully get the following 4 configurations from config server:
application.yml: general logging level
application.yml: specific logging for dev
my-rest-sercive.yml: general datasource properties
my-rest-sercive.yml: specific datasource url for dev
I can see these 4 sources successfully being fetched when I use my browser or when I debug or in the logs when I lower the loglevel to trace:
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\my-rest-service.yml'
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\my-rest-service.yml'
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\application.yml'
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\application.yml'
However, notice that because I use multi document yml files, out of these 4 property sources only TWO unique names are used.
In a later step, when Spring creates the data source bean, he complains he cannot find the data source URL. If I debug the spring bean factory I can indeed see that out of the 4 property files returned by the config server, only two have remained (the ones that don't contain the dev profile specific configuration). I assume this is because they have an identical name and they overwrite each other. This is an effect of this piece of code in the MutablePropertySource.class:
public void addLast(PropertySource<?> propertySource) {
synchronized(this.propertySourceList) {
this.removeIfPresent(propertySource); <-- this is the culrprit!
this.propertySourceList.add(propertySource);
}
}
This is a breaking change from Spring 2.3/Spring Cloud Hoxton where it correctly collected all properties. I think spring cloud needs to change the config server so that every document within a yml has has a unique name when returned to Spring. This is exactly how Spring Boot handles multi document yml files, by appending the String (documenyt #1) to the property source name
I found an interesting note about profiles and multi document yml, basically saying it is not supported, but this doesn't apply to my use case because my yml files are not profiles based (there is no -{profileName} in the last part of the file name).
This is a known issue with the new release. We can track the issue here on the spring cloud config server github page.
The workaround seems to be stop using multi document yml files and use multiple distinct files with the profile name in the filename.

Spring Profiles application properties order

We have many environments that have multiple active Spring profiles, but what is the precedence of the application-{profile}.yml files?
If I have spring.profiles.active=test-us-west-2-p1, test-us-west-2, test
In what order do the files application-test.yml, application-test-us-west-2.yml, application-test-us-west-2-p1.yml get loaded? If I have the same property in each file, which "wins"?
Also, has this changed from Spring-Boot 1.5.x to 2.x? It seems like it may have.
The profile's properties are loaded in the same order as you specify them, and if the same property is defined in different profiles the last one wins.
This behavior applies to both Spring Boot versions 1.5.x and 2.x, and if I recall correctly, it applies to all versions of Spring.
Spring always loads appication.yml. And afterwards, if some profile is specified, it will load that profile's property file. And if after that profile another profile is specified, it will load that profile's propperty file. Always overriding current properties's value with the new one.
So, let's say you have profile1 and profile2. And you have these property files:
application.yml:
property1: bob
property2: alice
property3: eve
application-profile1.yml:
property2: alice1
property3: eve1
application-profile2.yml:
property3: eve2
And you start your application with: spring.profiles.active=profile1, profile2
Your will get:
property1: bob
property2: alice1
property3: eve2
First of all, we need to find out the final set of all active profiles. There are ways of setting/replacing active profiles and adding active profiles on top of existing active ones. For instance, active profiles set with the spring.profiles.active property are replaced with the -Dspring.profiles.active command line option. (And this can get really complex.)
On the other hand, the SpringApplicationBuilder's profiles method adds to the existing active profiles. We can use the following code to figure out the final set of active profiles:
#Autowired
private Environment environment;
...
System.out.println("Active profiles: " +
Arrays.toString(environment.getActiveProfiles()));
Now we have to consider what Spring documentation calls last-wins strategy.
If several profiles are specified, a last-wins strategy applies.
So, if we have the following code and all other options excluded:
new SpringApplicationBuilder(Application.class)
.profiles("dev", "prod")
.run(args);
both application-dev.properties and application-prod.properties files are loaded and the keys with the same name in the latter one (the production) override the former one.
I posted important notice on profiles order handling. See how jar resources files are handled in this process here.

Unable to generate different spring-cloud-gateway routes based on active Spring profile

I'm trying to define different routes in Spring Cloud Gateway using Spring profiles. Everything I've read about how Spring manages profiles using yaml sems to indicate it should work, but for the life of me it just ... doesn't seem to. (My other Spring apps use properties, so I'm unused to the yaml config -- it's possible I'm missing something.)
Effectively what I'm trying to do is to have a "prod" profile which contains the URIs for my production servers, and a "dev" profile which contains the localhost equivalents.
I have two profiles, dev and prod. My application.yml file looks like this
spring:
profiles:
default: prod
management:
endpoint:
health:
enabled: true
endpoints:
web:
exposure:
include: hystrix.stream, gateway
---
spring
profiles: prod
cloud:
gateway:
routes:
- id: test_route
uri: http://foo.mycompany.com
predicates:
- Path=/status
- Method=GET
---
spring
profiles: dev
cloud:
gateway:
routes:
- id: test_route
uri: http://localhost:8080
predicates:
- Path=/status
- Method=GET
My understanding is as follows:
the spring.profiles.default property tells Spring that, if no profile is specified, to use the prod profile
Spring will treat the --- as a "file separator" and re-evaluate each set of properties and overwrite previous values if the spring.profiles parameter evaluates true
Given this understanding, I would expect Spring to parse the "default" properties first, learning that the default activated profile should be prod. Then it will parse the prod properties. Since "prod" is an active profile (the only active profile, being the default), it should parse and apply the prod routes. Then it would parse the dev routes, but recognize that dev is not an active profile, and not overwrite those values. This is my understanding from reading the documentation on how to change config based on the environment.
However, when I load this, and I hit the actuator endpoint -- /actuator/gateway/routes -- I get back [] where I would expect to see the prod routes. I do see in my logs that the prod profile is activated, but it seems like not having the properties in the "default" section at top causes them to not be applied when the parser reads them out of the profile section.
The other thing I tried was putting the "dev" properties as the defaults, and then attempting to use the "prod" profile properties to overwrite the URIs. A similar issue happened there -- I hit the actuator endpoint and got back routes, but they were just the dev ones from the default.
How can I leverage Spring profiles to configure different Spring Cloud Config routes in my application.yml ?
Versions:
spring-cloud-gateway 2.0.1.BUILD-SNAPSHOT (to get a workaround for this bug, probably not relevant)
spring-cloud-starter-gateway
spring-boot 2.0.3.RELEASE
spring-boot-starter-webflux
spring-boot-starter-actuator
(I can't use Spring Cloud Config for political reasons. My company's chief architect has a severe case of Not Invented Here Syndrome.)
You cannot use spring.profiles.default in the property file. It will be too late for setting such value.
So you can set it using program argument (or System property). E.g.
java -jar --spring.profiles.default=dev your-app.jar
Or you can do it in the code by hardcoding the default profile:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
ConfigurableEnvironment environment = new StandardEnvironment();
environment.setDefaultProfiles("dev");
application.setEnvironment(environment);
application.run(args);
}
}
Some related information you can find here by reading all comments:
https://github.com/spring-projects/spring-boot/issues/1219

Override single entry in application.yml for all tests

Within the SpringBoot application you can provide the configuration via src/main/resource/application.yml.
One single entry should be overriden by the tests (see How to mock Eureka when doing Integration Tests in Spring? ). I tried to provide a test configuration with src/integration-test/resource/application.yml but it overrides the complete configuration.
eureka:
client:
enabled: false
How can I modify one entry of the configuration file for all tests?
Create a application-test.yml in src/main/resource/ with your desired configuration (eureka.client.enabled=false) and also other configurations that you require for your application to start up,
Once your application-test.yml is complete You just need to add following annotation with desired value to your test class ,
#SpringBootTest(value={"spring.profiles.active=test"})
Here spring.profiles.active=test because we have set its value to test because we want to read configurations from application-test.yml.
Hope it helps !

Including profiles with spring.profiles.include seems to override instead of include

I'm trying to partition configuration properties for several Spring Boot applications. I'm using Spring Boot 1.1.6, and our configuration properties are expressed in YAML in the usual application.yml style. I've created various profiles for common base parameters, common DB parameters, etc. I was trying to use the include feature mentioned in the Spring Boot reference docs, but it seems to work as an override and not an include. I.e. exactly the opposite of what I want. Given the following content in application.yml, I would have expected the property name to have the value bar when the bar profile is active, but instead it gets set to foo (from the included profile). I thought the notion of including meant that it was loaded first, and any identically named properties set in the new profile would override those from the included profile. Kind of like if a subclass is shadowing a field from the superclass, any instance of the subclass would reflect the shadowed value. Here's the file:
spring:
profiles: foo
name: foo
--- # New YAML doc starts here
spring:
profiles:
include: foo
profiles: bar
name: bar
If I run this in a test case with the "bar" profile explicitly activated, the name property will still be foo:
SpringApplicationBuilder builder = new SpringApplicationBuilder(Application.class);
SpringApplication app = builder.application();
builder.profiles("bar");
ConfigurableApplicationContext ctxt = app.run();
String name = ctxt.getEnvironment().getProperty("name"); // Is always "foo" much to my surprise
However, if I comment out the include:
spring:
profiles: bar
# profiles:
# include: foo
and activate the two profiles explicitly in my code:
builder.profiles("foo", "bar");
Then it works as I would expect, and the name property is set to bar. The main reason I would rather handle the includes in the YAML files is that it's less impact on my actual code, and I can manage all profile inclusion in one place. With the other approach, I'd have to search for profile strings and possible #Profile annotations in my entire project if I were to ever rename the profiles. That's definitely more error prone. I think a more flexible solution would be to explicitly be able to express whether or not the included profile overrides the sub profile values or not. Maybe something like:
spring:
profiles: bar
profiles:
include: foo
override: false
Maybe I'm just missing something here. Is there a better way of doing this? Thanks.
Try the following for foo to include but override bar, seems to be working for my solution
spring:
profiles:
include: bar
active: foo,bar
edit: please mind, that it's a "hack", not officially supported and it's for 2016 version

Resources