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

spring.profiles.include working w/ 2.3.9, broken w/ 2.4.2 #25759

Closed
workmanw opened this issue Mar 23, 2021 · 11 comments
Closed

spring.profiles.include working w/ 2.3.9, broken w/ 2.4.2 #25759

workmanw opened this issue Mar 23, 2021 · 11 comments

Comments

@workmanw
Copy link

While upgrading to 2.4.2 we started seeing this exception on startup:

java.lang.IllegalStateException: Failed to load ApplicationContext

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
	at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.postProcessFields(MockitoTestExecutionListener.java:105)
	at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.injectFields(MockitoTestExecutionListener.java:89)
	at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.prepareTestInstance(MockitoTestExecutionListener.java:56)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
	at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassBasedTestDescriptor.java:350)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:355)
        ...
Caused by: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property 'spring.profiles.include' imported from location 'class path resource [application-it.yml]' is invalid in a profile specific resource [origin: class path resource [application-it.yml] - 54:14]
	at org.springframework.boot.context.config.InvalidConfigDataPropertyException.lambda$throwOrWarn$1(InvalidConfigDataPropertyException.java:124)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at java.base/java.util.Collections$UnmodifiableCollection.forEach(Collections.java:1085)
	at org.springframework.boot.context.config.InvalidConfigDataPropertyException.throwOrWarn(InvalidConfigDataPropertyException.java:121)
	at org.springframework.boot.context.config.ConfigDataEnvironment.checkForInvalidProperties(ConfigDataEnvironment.java:354)
	at org.springframework.boot.context.config.ConfigDataEnvironment.applyToEnvironment(ConfigDataEnvironment.java:323)
	at org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:236)
	at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:97)
	at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:89)
	at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:100)
	at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:86)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
	at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:82)
	at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:63)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:117)
	at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:111)
	at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:62)
	at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:362)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
	... 68 more

Originally we thought the issue was related to: #24733 , however after investigating further and asking for advice it was suggested I open a new issue.

In our use case we have a common library for all of our microservices platforms that configures JSON logging. Each service uses spring.profiles.include to import the logging configuration. We use include this way because active profile does not work due to how early in the lifecycle DefaultCacheAwareContextLoaderDelegate is invoked.

I made a simple reproduction: https://github.com/workmanw/spring-bug-demo . This demo shows it working correctly with 2.3.9 and if you change the pom to 2.4.2 it will demonstrate the exception.

@workmanw
Copy link
Author

@philwebb Here you go. Please let me know if you have any questions at all regarding the reproduction. I tried to make it as simple as possible. Thank you for your time!

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 23, 2021
@philwebb
Copy link
Member

Thanks for the demo @workmanw. Unfortunately the error you're seeing is due to an intentional change we made in 2.4. We no longer allow spring.profiles.include values from profile specific documents. There's a little bit in the migration guide about why we made this change.

For your sample, I wonder if using spring.profiles.include directly on the test would work? Something like this:

@ExtendWith(SpringExtension.class)
@SpringBootTest(
	webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
	properties = "spring.profiles.include=logging"
)
class DemoApplicationTests {
    // ...
}

That would be allowed since the include property is not longer in a profile specific section. With that change, the sample gives the following output:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.2)

[[DEMO CONFIG]] ----- 19:30:29.699 [main] INFO  c.example.demo.DemoApplicationTests - Starting DemoApplicationTests using Java 11.0.8 on pwebb-a01.vmware.com with PID 77498 (started by pwebb in /Users/pwebb/projects/spring-boot/samples/spring-bug-demo)
[[DEMO CONFIG]] ----- 19:30:29.699 [main] INFO  c.example.demo.DemoApplicationTests - The following profiles are active: logging
[[DEMO CONFIG]] ----- 19:30:30.311 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
[[DEMO CONFIG]] ----- 19:30:30.320 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
[[DEMO CONFIG]] ----- 19:30:30.321 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
[[DEMO CONFIG]] ----- 19:30:30.321 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.41]
[[DEMO CONFIG]] ----- 19:30:30.385 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
[[DEMO CONFIG]] ----- 19:30:30.385 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 671 ms
[[DEMO CONFIG]] ----- 19:30:30.583 [main] INFO  o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
[[DEMO CONFIG]] ----- 19:30:30.769 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
[[DEMO CONFIG]] ----- 19:30:30.793 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
[[DEMO CONFIG]] ----- 19:30:30.801 [main] INFO  c.example.demo.DemoApplicationTests - Started DemoApplicationTests in 1.43 seconds (JVM running for 2.127)
[[DEMO CONFIG]] ----- 19:30:31.142 [http-nio-8080-exec-1] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[[DEMO CONFIG]] ----- 19:30:31.142 [http-nio-8080-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
[[DEMO CONFIG]] ----- 19:30:31.143 [http-nio-8080-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
[[DEMO CONFIG]] ----- 19:30:31.211 [SpringContextShutdownHook] INFO  o.s.s.c.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'

Does that change work for your real application?

@philwebb philwebb added the status: waiting-for-feedback We need additional information before we can continue label Mar 23, 2021
@workmanw
Copy link
Author

workmanw commented Mar 23, 2021

@philwebb For our Integration Tests removing the spring.profiles.include from the .yaml configuration and adding to every test class does seem to resolve the issue for testing. However this issue still exists for runtime. Each microservice has application-dev.yaml, application-qa.yaml and application-prod.yaml that each include this logging profile.

Backstory: Our application-local.yaml and application-it.yaml use different logging configurations. For dev/qa/prod we have logging configuration that has very verbose and rich JSON structure design to be consumed by splunk. For local and IT tests we use a more minimal logging configuration focus around development and debugging. You cannot override an include the same way you can with an active profile value because include values concatenate. So for that reason, each of our includes reside in separate profiles for the different runtime environments, rather than doing it in the application.yaml.

Do you have any suggestions on how to solve this?

If you would like me to amend the reproduction to encompass this secondary scenario I'd be happy to do so.

@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 23, 2021
@philwebb
Copy link
Member

@workmanw You can try the new spring.profiles.group property. It's designed to help with that type of situation (although I must admit it's unfortunately not as easy to use).

If you add the following to your application.yaml then when you activate dev, qa or prod you'll also get logging:

spring:
  profiles:
    group:
      "dev": "logging"
      "qa": "logging"
      "prod": "logging"

@philwebb philwebb added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 23, 2021
@workmanw
Copy link
Author

(although I must admit it's unfortunately not as easy to use).

Actually it seems pretty straight forward to me, unless there is something I'm overlooking. I don't love that it has to go into the main application.yaml rather than the individual application-prod.yaml ones, but it's by no means a deal breaker.

Stepping back, am I approaching this problem right by using profiles this way? Is there a better solution? We definitely want to align with best practices as much as possible.

Sorry if this is turning into a support issue. Just trying to figure out what is the best path forward. Thank you for all your help!

@spring-projects-issues spring-projects-issues removed the status: waiting-for-feedback We need additional information before we can continue label Mar 23, 2021
@philwebb
Copy link
Member

If your main goal is to extract common config so that you can include it, you might want to try spring.config.import. That's a new feature in Spring Boot 2.4 and it allows you to import one config file from another.

So you might have a file called json-logging.yaml:

logging:
  config: "classpath:demo-logback.xml"
...

Then you can import that file whenever you need to. E.g. in application-prod.yaml:

spring:
  config:
    import: "./json-logging.yaml`
...

We added this feature because we found a lot of people were adding profiles just as a way of getting common config imported. If you're not actually using the logging profile in your code then it might work out better.

@philwebb
Copy link
Member

As the group property is working out, I'm going to close this one. Thanks for the providing the feedback.

@philwebb philwebb removed the status: feedback-provided Feedback has been provided label Mar 23, 2021
@workmanw
Copy link
Author

@philwebb Thanks again!

@beardy247
Copy link

I have to say I'm disappointed in this breaking change. The whole point for us of having separate application-dev.yml files which aren't under source control is to keep dev concerns out of the main application.yml file.

@workmanw
Copy link
Author

@beardy247 FWIW That was my initial reaction as well. But once you wrap your head around spring.profiles.include and spring.config.import it does start to make sense. We were able to switch from spring.profiles.include to spring.config.import without issue. Just a little bit of work.

@wangxiang4
Copy link

wangxiang4 commented May 22, 2023

@workmanw yes Just switch spring.config.import to spring.profiles.include or spring.profiles.active to spring.config.activate.on-profile will do a good job migrate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants