Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional profiles are processed too late when legacy processing is used #25817

Conversation

nguyensach
Copy link
Contributor

@nguyensach nguyensach commented Mar 29, 2021

In post-processing the given environment of ConfigDataEnvironmentPostProcessor, add SpringApplication's additional profiles that are set programmatically into the given environment when legacy processing is used.

This commit updates the following areas:

  • Add configureAdditionalProfiles method into ConfigDataEnvironmentPostProcessor, that add the additional profiles into the given environment.

  • When legacy processing is used, call configureAdditionalProfiles method to add the additional profiles into the given environment.

  • Remove unnecessary configureAdditionalProfiles method in SpringApplication.

  • Supplement unit test for SpringApplicationTests.

Fixes #25704
Fixes #26189

@nguyensach nguyensach force-pushed the enable_additional_profiles_in_2.4 branch from 1aa39f5 to bd4fa3c Compare March 29, 2021 02:21
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 29, 2021
@wilkinsona
Copy link
Member

Thanks for the pull request, @nguyensach. Additional profiles configured via SpringApplication are activated by including them in the Environment's active profiles:

private void configureAdditionalProfiles(ConfigurableEnvironment environment) {
if (!CollectionUtils.isEmpty(this.additionalProfiles)) {
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles()));
if (!profiles.containsAll(this.additionalProfiles)) {
profiles.addAll(this.additionalProfiles);
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
}
}

IIRC, this means that they should be picked up by ConfigFileApplicationListener without any special treatment. I believe this was the case in Spring Boot 2.3 and, ideally, we'd make it the case in Spring Boot 2.4 as well. If you explored this approach and it didn't work out, can you please explain what stopped it from working?

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Mar 29, 2021
@nguyensach nguyensach force-pushed the enable_additional_profiles_in_2.4 branch 2 times, most recently from 4002310 to 5ba2234 Compare March 30, 2021 03:31
@nguyensach nguyensach changed the title Add additional profiles to ConfigFileApplicationListener Add additional profiles into environment when legacy processing is used Mar 30, 2021
@nguyensach
Copy link
Contributor Author

@wilkinsona

Thanks for your information!

If you explored this approach and it didn't work out, can you please explain what stopped it from working?

In Spring Boot 2.4, when legacy processing is used and additional profiles configured via SpringApplication, it is not worked as expected because of the method configureProfiles of SpringApplication.

/**
* Configure which profiles are active (or active by default) for this application
* environment. Additional profiles may be activated during configuration file
* processing via the {@code spring.profiles.active} property.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureEnvironment(ConfigurableEnvironment, String[])
* @see org.springframework.boot.context.config.ConfigFileApplicationListener
*/
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
}

In Spring Boot 2.3, the implementation of the method configureProfiles of SpringApplication is like below.

/**
* Configure which profiles are active (or active by default) for this application
* environment. Additional profiles may be activated during configuration file
* processing via the {@code spring.profiles.active} property.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureEnvironment(ConfigurableEnvironment, String[])
* @see org.springframework.boot.context.config.ConfigFileApplicationListener
*/
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

In Spring Boot 2.3, by the method configureEnvironment of SpringApplication that calls the method configureProfiles, additional profiles configured via SpringApplication are activated by including them in the Environment's active profiles. Then, that Environment is passed into ConfigFileApplicationListener at the below point on ApplicationEnvironmentPreparedEvent event.

Then, in ConfigFileApplicationListener, the additional profiles are loaded into the Environment's propertySources, therefore, in Spring Boot 2.3, the properties of the additional profiles is got by the Environment's getProperty.

However, In Spring Boot 2.4, the method configureProfiles of SpringApplication is empty, therefore, the additional profiles configured via SpringApplication are not activated by including them in the Environment's active profiles before jumping into ConfigFileApplicationListener. So, the additional profiles are not loaded into the Environment's propertySources.

IIRC, this means that they should be picked up by ConfigFileApplicationListener without any special treatment.

In Spring Boot 2.4, when using legacy processing, the additional profiles are not picked up by ConfigFileApplicationListener without any special treatment.

After rethinking about order of the additional profiles, the included profiles, the other actived profiles in the ConfigFileApplicationListener's initializeProfiles method, I fixed this PR. Could you help me to review my PR?

By the way, in my PR, the CI is fail at the gradle's spring-boot-project:spring-boot:checkstyleMain task. I run format task in my local pc but it is not auto correct for me. Is there any way to auto fix the issues of the checkstyleMain.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 30, 2021
@@ -558,16 +558,6 @@ protected void configurePropertySources(ConfigurableEnvironment environment, Str
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
}

private void configureAdditionalProfiles(ConfigurableEnvironment environment) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The additional profiles are included in the Environment's active profiles at ConfigDataEnvironmentPostProcessor when legacy processing is used, or at ConfigDataEnvironment as below

this.environment.setActiveProfiles(StringUtils.toStringArray(profiles.getActive()));

Therefore, configureAdditionalProfiles becomes unnecessary.

@wilkinsona
Copy link
Member

@philwebb could you take a look at this one please?

@wilkinsona
Copy link
Member

By the way, in my PR, the CI is fail at the gradle's spring-boot-project:spring-boot:checkstyleMain task. I run format task in my local pc but it is not auto correct for me. Is there any way to auto fix the issues of the checkstyleMain.

There's no auto-fix for Checkstyle problems, unfortunately. Looking at the diff, I suspect the problem is due to the changes to the imports. They need to be kept in their current ordering and not use wildcards.

@nguyensach
Copy link
Contributor Author

By the way, in my PR, the CI is fail at the gradle's spring-boot-project:spring-boot:checkstyleMain task. I run format task in my local pc but it is not auto correct for me. Is there any way to auto fix the issues of the checkstyleMain.

There's no auto-fix for Checkstyle problems, unfortunately. Looking at the diff, I suspect the problem is due to the changes to the imports. They need to be kept in their current ordering and not use wildcards.

@wilkinsona Thanks for your response! I will fix it manually.

In post-processing the given environment of ConfigDataEnvironmentPostProcessor,
add SpringApplication's additional profiles that are set by programmatically into environment
when legacy processing is used.

This commit updates the following areas:

- Add configureAdditionalProfiles method into ConfigDataEnvironmentPostProcessor,
that add the additional profiles into the given environment.

- When legacy processing is used, call configureAdditionalProfiles method to
add the additional profiles into the given environment.

- Remove unnecessary configureAdditionalProfiles method in SpringApplication.

- Supplement unit test for SpringApplicationTests.

Closes spring-projects#25704
@mbhave
Copy link
Contributor

mbhave commented Apr 30, 2021

@nguyensach This change makes sense to me. I have left some comments. Can you please take a look?

@mbhave mbhave added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Apr 30, 2021
@nguyensach
Copy link
Contributor Author

This change makes sense to me. I have left some comments.

@mbhave
I'm sorry! I don't see any of your comments. Can you please check it?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 1, 2021
@snicoll snicoll added the for: team-attention An issue we'd like other members of the team to review label May 3, 2021
@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label May 3, 2021
this.context = application.run();
assertThat(this.context.getEnvironment().acceptsProfiles(Profiles.of("dev"))).isTrue();
assertThat(this.context.getEnvironment().getProperty("my.property")).isEqualTo("fromdevpropertiesfile");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this test as it is essentially a duplicate of the test below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


According to the above comment, this test reproduces reported issues. Like you commented, it is essentially a duplicate of another test, so I will remove it.

application.setWebApplicationType(WebApplicationType.NONE);
application.setAdditionalProfiles("other", "dev");
this.context = application.run();
assertThat(this.context.getEnvironment().getProperty("my.property")).isEqualTo("fromdevpropertiesfile");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't seem to be giving us much either and I don't think it is required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only want to test when there are many additional profiles rather than one.
In this test case, I want to check that which additional profile should win.

Maybe this test isn't related to the bug. Could I add this test to another PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, we don't need the test for the single additional profile and we can keep this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mbhave

In that case, we don't need the test for the single additional profile and we can keep this one.

You don't need the test for the multiple additional profiles, right?
If so, I will delete this test case.

application.setWebApplicationType(WebApplicationType.NONE);
this.context = application.run("--spring.profiles.active=bar,spam", "--spring.profiles.include=foo");
// Before Boot 2.4 included profiles should always be first
assertThat(this.context.getEnvironment().getActiveProfiles()).containsExactly("foo", "bar", "spam");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't seem to be related to the bug that this issue is trying to address. The issue is about additionalProfiles and I don't think this test is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test isn't related to the bug. However, I think that we are lacking test cases of the order included profiles and active profiles. So, Could I add this test to another PR?

Because of lacking test cases of the order additional profiles and active profiles, it results in #26189

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be testing active and additional profiles though. It's testing active and included profiles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mbhave
#25817 (comment)
As your above comment, there is already a test case that tests active and included profiles when using legacy processing, so I will delete this test case.

@@ -82,6 +83,7 @@ void postProcessEnvironmentWhenNoLoaderCreatesDefaultLoaderInstance() {
verify(this.postProcessor).getConfigDataEnvironment(any(), this.resourceLoaderCaptor.capture(), any());
verify(this.configDataEnvironment).processAndApply();
assertThat(this.resourceLoaderCaptor.getValue()).isInstanceOf(DefaultResourceLoader.class);
assertThat(this.environment.getActiveProfiles()).isEmpty();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nguyensach What is the reason for adding this (and similiar ones below) assertion here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ConfigDataEnvironmentPostProcessor, I added the below code.

https://github.com/spring-projects/spring-boot/pull/25817/files#diff-73c99017027ef3e130148a82d7075ffa9e95826b56aef5b495f1e48c1f58fac3R118-R125

It adds the additional profiles to the environment's active profiles, so I want to check the environment's active profiles here

this.context = application.run("--spring.profiles.active=bar,spam", "--spring.profiles.include=foo");
// Since Boot 2.4 included profiles should always be last
assertThat(environment.getActiveProfiles()).containsExactly("bar", "spam", "foo");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also seems unrelated to this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As my above reply, Could I add this test to another PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we already cover the active and include case in this test.

Copy link
Contributor Author

@nguyensach nguyensach May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mbhave
In this test case, I want to test active and included profiles when using Boot 2.4 or later without using legacy processing.
It seems that the order of the active and included profiles has to be changed since boot 2.4 or later without using legacy processing.
Could I add this test to another PR?

this.postProcessor.postProcessEnvironment(this.environment, this.application);
verifyNoInteractions(this.configDataEnvironment);
verify(legacyListener).addPropertySources(eq(this.environment), any(DefaultResourceLoader.class));
assertThat(this.environment.getActiveProfiles()).containsExactly("dev");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of just checking if the environment contains the additional profiles (which it does even today because SpringApplication configures them in configureAdditionalProfiles), we should check if the profiles are actually processed by ConfigFileApplicationListener).

Copy link
Contributor Author

@nguyensach nguyensach May 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the scope of ConfigDataEnvironmentPostProcessor, I am only checking if the environment contains the additional profiles.

we should check if the profiles are actually processed by ConfigFileApplicationListener

In order to check it, I added two below tests.
https://github.com/spring-projects/spring-boot/pull/25817/files#diff-82a4d071283a2194fc5c0e8cffbdddbeda4e53d4e5676138f93b46fcaa16e516R1154-R1163

https://github.com/spring-projects/spring-boot/pull/25817/files#diff-82a4d071283a2194fc5c0e8cffbdddbeda4e53d4e5676138f93b46fcaa16e516R1184-R1192

@mbhave
Copy link
Contributor

mbhave commented May 3, 2021

@nguyensach Sorry about that! I forgot to click on submit 🤦🏽‍♀️ Hopefully you can see them now.

@mbhave mbhave added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels May 3, 2021
@nguyensach
Copy link
Contributor Author

@mbhave Thanks for your review! I replied to all your comments.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 3, 2021
@mbhave mbhave added for: team-meeting An issue we'd like to discuss as a team to make progress type: bug A general bug and removed for: team-meeting An issue we'd like to discuss as a team to make progress status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels May 7, 2021
mbhave pushed a commit that referenced this pull request May 11, 2021
Additional profiles were being processed after config file processing
when legacy processing was used.
This commit also restores the order in which additional profiles are added
when legacy processing is used.
Active profiles take precedence over additional profiles.

See gh-25817
@mbhave mbhave closed this in 38ef6b5 May 11, 2021
@mbhave mbhave changed the title Add additional profiles into environment when legacy processing is used Additional profiles are processed too late when legacy processing is used May 11, 2021
@mbhave mbhave added this to the 2.4.x milestone May 11, 2021
mbhave pushed a commit that referenced this pull request May 11, 2021
Additional profiles were being processed after config file processing
when legacy processing was used.
This commit also restores the order in which additional profiles are added
when legacy processing is used.
Active profiles take precedence over additional profiles.

See gh-25817
@mbhave
Copy link
Contributor

mbhave commented May 11, 2021

Thanks for the PR @nguyensach. This has now been merged into main along with this polish commit.

@mbhave mbhave modified the milestones: 2.4.x, 2.4.6 May 11, 2021
@nguyensach nguyensach deleted the enable_additional_profiles_in_2.4 branch May 12, 2021 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
7 participants