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

SpringBootTest AutoConfigureMockMvc issue with config manager not found via SessionExpiryFilter #3322

Closed
KalCramer opened this issue Mar 22, 2024 · 7 comments

Comments

@KalCramer
Copy link

Hello,

I am trying out hawtio-springboot using the following pom dependency.

<dependency>
	<groupId>io.hawt</groupId>
	<artifactId>hawtio-springboot</artifactId>
	<version>4.0.0-RC1</version>
</dependency>

When running in a basic springboot app things starting up fine and I'm able to hit the endpoint and see the UI

However, I have a simple SpringBootTest configured with AutoConfigureMockMvc and I get the following error when the SessionExpiryFilter gets called:

Caused by: java.lang.RuntimeException: Hawtio config manager not found, cannot proceed Hawtio configuration
	at io.hawt.web.auth.AuthenticationConfiguration.<init>(AuthenticationConfiguration.java:138)
	at io.hawt.web.auth.AuthenticationConfiguration.getConfiguration(AuthenticationConfiguration.java:185)
	at io.hawt.web.auth.SessionExpiryFilter.init(SessionExpiryFilter.java:50)
	at org.springframework.test.web.servlet.setup.MockMvcFilterDecorator.initIfRequired(MockMvcFilterDecorator.java:201)
	at org.springframework.test.web.servlet.setup.AbstractMockMvcBuilder.build(AbstractMockMvcBuilder.java:191)
	at org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration.mockMvc(MockMvcAutoConfiguration.java:97)

I'm able to add a configuration to the AutoConfigureMockMvc of addFilters = false to workaround this but was wondering if I'm doing something wrong in initiating hawtio for this test or if this is by design.

Here is a simple project with similar test that fails.

@tadayosi
Copy link
Member

Thanks for your reporting. Normally, to make it work with Spring MVC SpringHawtioContextListener needs to be initialised, which in turn runs the following at runtime:

servletContextEvent.getServletContext().setAttribute(ConfigManager.CONFIG_MANAGER, configManager);

So if it doesn't work by default in a mocked environment, you'd need to assign a ConfigManager instance to a mocked servlet context like this:

mockServletContext.setAttribute(ConfigManager.CONFIG_MANAGER, new ConfigManager());

Please try it and let us know if it works.

@KalCramer
Copy link
Author

It seems like Spring's @AutoConfigureMockMvc will create the MockMvc which will fail with this error.

I'm not seeing a way to configure the ServletContext before this happens if using the annotation. Removing @AutoConfigureMockMvc I have got multiple other workarounds, the best one being still grabbing the WebApplicationContext myself calling setAttribute like you mentioned and creating mockMvc, this makes it so I still don't have to manually specify the Controllers myself and use @Autowired ServletContext.

Your first suggestion of SpringHawtioContextListener needing to be initialized seems like something to try but I wasn't seeing how I'd tie in the contextInitialized call.

I think these workaround are good enough for me so I'm okay with closing the issue. If you think @AutoConfigureMockMvc should work out of the box, such as having the ConfigManager be initialized differently rather than throwing Runtime Exception I could see this being something to implement otherwise workarounds are fine for me.

Thank you.

@tadayosi
Copy link
Member

Thank you for your feedback. Glad that you found a workaround.

If you don't mind, let's keep the issue open and later we'll investigate the issue a bit more with your reproducer to see if there's any room for improvement from the Hawtio side.

@omri-s-electreon
Copy link

omri-s-electreon commented May 7, 2024

Hi @tadayosi & @KalCramer ,

I'm just working on upgrading our microservices to Spring Boot 3. We waited especially for the hawtio-springboot library to support it (v4.0) and are facing the exact same issue as described here - tests with MockMVC are failing as it cannot be started.
The main cause is that SpringHawtioContextListener.contextInitialized() is not called when the @AutoConfigureMockMvc annotation is in place, so the ServletContext has no value for the attribute ConfigManager. This throws a runtime exception in AuthenticationConfiguration.

Failed to load ApplicationContext for [WebMergedContextConfiguration@76034fc0 testClass = com.electreon.service.metadata.ElectreonMetadataServiceIntegrationTest, locations = [], classes = [com.electreon.service.metadata.ElectreonMetadataServiceApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [PropertySourceDescriptor[locations=[], ignoreResourceNotFound=false, name=null, propertySourceFactory=null, encoding=null]], propertySourceProperties = ["spring.config.location=classpath:conf-default/security-base.yml,classpath:conf-default/application.yml,file:src/test/resources/conf-test/application.yml", "logging.config=file:conf/logback-spring.xml", "org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4b3fa0b3, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@192d74fb, [ImportsContextCustomizer@332d2db key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, com.electreon.service.metadata.config.TestSpringConfig, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@a8c1f44, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a8fa663, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@e9586891, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@b968a76, io.zonky.test.db.EmbeddedDatabaseContextCustomizerFactory$EmbeddedDatabaseContextCustomizer@e70a0b42, org.springframework.boot.test.context.SpringBootTestAnnotation@94147f51], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@76034fc0 testClass = com.electreon.service.metadata.ElectreonMetadataServiceIntegrationTest, locations = [], classes = [com.electreon.service.metadata.ElectreonMetadataServiceApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [PropertySourceDescriptor[locations=[], ignoreResourceNotFound=false, name=null, propertySourceFactory=null, encoding=null]], propertySourceProperties = ["spring.config.location=classpath:conf-default/security-base.yml,classpath:conf-default/application.yml,file:src/test/resources/conf-test/application.yml", "logging.config=file:conf/logback-spring.xml", "org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4b3fa0b3, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@192d74fb, [ImportsContextCustomizer@332d2db key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, com.electreon.service.metadata.config.TestSpringConfig, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@a8c1f44, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a8fa663, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@e9586891, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@b968a76, io.zonky.test.db.EmbeddedDatabaseContextCustomizerFactory$EmbeddedDatabaseContextCustomizer@e70a0b42, org.springframework.boot.test.context.SpringBootTestAnnotation@94147f51], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:180)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
	at io.zonky.test.db.EmbeddedDatabaseTestExecutionListener.forEachDatabase(EmbeddedDatabaseTestExecutionListener.java:90)
	at io.zonky.test.db.EmbeddedDatabaseTestExecutionListener.resetDatabases(EmbeddedDatabaseTestExecutionListener.java:58)
	at io.zonky.test.db.EmbeddedDatabaseTestExecutionListener.beforeTestClass(EmbeddedDatabaseTestExecutionListener.java:34)
	at org.springframework.test.context.TestContextManager.beforeTestClass(TestContextManager.java:220)
	at org.springframework.test.context.junit.jupiter.SpringExtension.beforeAll(SpringExtension.java:133)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks$12(ClassBasedTestDescriptor.java:396)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:396)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:212)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:85)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:110)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:90)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:85)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
	Suppressed: java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context for [WebMergedContextConfiguration@76034fc0 testClass = com.electreon.service.metadata.ElectreonMetadataServiceIntegrationTest, locations = [], classes = [com.electreon.service.metadata.ElectreonMetadataServiceApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [PropertySourceDescriptor[locations=[], ignoreResourceNotFound=false, name=null, propertySourceFactory=null, encoding=null]], propertySourceProperties = ["spring.config.location=classpath:conf-default/security-base.yml,classpath:conf-default/application.yml,file:src/test/resources/conf-test/application.yml", "logging.config=file:conf/logback-spring.xml", "org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4b3fa0b3, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@192d74fb, [ImportsContextCustomizer@332d2db key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, com.electreon.service.metadata.config.TestSpringConfig, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@a8c1f44, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a8fa663, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@e9586891, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@b968a76, io.zonky.test.db.EmbeddedDatabaseContextCustomizerFactory$EmbeddedDatabaseContextCustomizer@e70a0b42, org.springframework.boot.test.context.SpringBootTestAnnotation@94147f51], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
		at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:145)
		at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
		at io.zonky.test.db.EmbeddedDatabaseTestExecutionListener.forEachDatabase(EmbeddedDatabaseTestExecutionListener.java:90)
		at io.zonky.test.db.EmbeddedDatabaseTestExecutionListener.resetDatabases(EmbeddedDatabaseTestExecutionListener.java:58)
		at io.zonky.test.db.EmbeddedDatabaseTestExecutionListener.afterTestClass(EmbeddedDatabaseTestExecutionListener.java:54)
		at org.springframework.test.context.TestContextManager.afterTestClass(TestContextManager.java:538)
		at org.springframework.test.context.junit.jupiter.SpringExtension.afterAll(SpringExtension.java:144)
		at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$18(ClassBasedTestDescriptor.java:462)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$19(ClassBasedTestDescriptor.java:462)
		at org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder(CollectionUtils.java:217)
		at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:461)
		at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:236)
		at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:85)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:161)
		... 48 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mockMvc' defined in class path resource [org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.class]: Failed to instantiate [org.springframework.test.web.servlet.MockMvc]: Factory method 'mockMvc' threw exception with message: Hawtio config manager not found, cannot proceed Hawtio configuration
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334)
	at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
	at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1454)
	at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152)
	... 62 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.test.web.servlet.MockMvc]: Factory method 'mockMvc' threw exception with message: Hawtio config manager not found, cannot proceed Hawtio configuration
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644)
	... 86 more
Caused by: java.lang.RuntimeException: Hawtio config manager not found, cannot proceed Hawtio configuration
	at io.hawt.web.auth.AuthenticationConfiguration.<init>(AuthenticationConfiguration.java:138)
	at io.hawt.web.auth.AuthenticationConfiguration.getConfiguration(AuthenticationConfiguration.java:185)
	at io.hawt.web.auth.SessionExpiryFilter.init(SessionExpiryFilter.java:50)
	at org.springframework.test.web.servlet.setup.MockMvcFilterDecorator.initIfRequired(MockMvcFilterDecorator.java:201)
	at org.springframework.test.web.servlet.setup.AbstractMockMvcBuilder.build(AbstractMockMvcBuilder.java:191)
	at org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration.mockMvc(MockMvcAutoConfiguration.java:97)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140)
	... 87 more



I am trying to implement the suggestions above but it seesm that there is no way to do it before it fails.
Was wondering if & how you handled it. If possible - please share.

Thanks,
Omri

@grgrzybek
Copy link
Contributor

I searched Spring Boot and Spring Framework source code, but I didn't see any example or place where ServletContextListener classes are used with @AutoConfigureMockMvc... Probably ServletContainerInitializers (SCIs) wont work as well.

I checked your example and I see that io.hawt.springboot.HawtioManagementConfiguration#hawtioContextListener() is properly called and org.springframework.boot.web.servlet.ServletListenerRegistrationBean bean is registered, however there's no way that ServletContext events (initialized, destroyed) are sent.

So, as @tadayosi said, you need to add this attribute on your own.

I checked different methods and the best one I could find in ~1 hour search is in this PR: KalCramer/spring-playground#1.

When you want listeners (and SCIs) you can't use MVC mocks - you need something more :)

@omri-s-electreon
Copy link

omri-s-electreon commented May 8, 2024

Thanks @grgrzybek

I eventually did the following:

  1. Created a @TestConfiguration class with the following code. This uses a Spring's BeanPostProcessor interface to customize the DefaultMockMvcBuilder bean provided by Spring's auto configuration.
import io.hawt.system.ConfigManager;
import io.hawt.web.auth.SessionExpiryFilter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.web.context.WebApplicationContext;

import static org.mockito.Mockito.mock;

@TestConfiguration
public class TestSpringConfig {

    @Bean
    public static BeanPostProcessor hawtioPostProcessor(
            @Autowired WebApplicationContext context,
            @Autowired FilterRegistrationBean<SessionExpiryFilter> filter
    ) {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
            }

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof DefaultMockMvcBuilder builder) {
                   //NOTE: Here the code adjusts the servlet context with the ConfigManager & sets the filter
                    context.getServletContext().setAttribute(ConfigManager.CONFIG_MANAGER, new ConfigManager());
                    builder.addFilter(filter.getFilter());
                }

                return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
            }
        };
    }

}
  1. This class is then used with an @Import(...) on the test class:
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc 
@SpringBootTest
@Import(TestSpringConfig.class) // <-------   NOTE: Imported here
public class ServiceIntegrationTest {
       // Tests methods here
        ...
}

The code is inspierd by Spring's MockMvcAutoConfiguration.java (Library: org.sprinframework.boot:spring-boot-test-autoconfigurer).

In our case (lots of test classes from different git repos) - this was the simplest to deploy on all of the code repositories.

Thanks,

Omri

@grgrzybek
Copy link
Contributor

Thanks for your solution - Spring has many customization mechanisms ;)
Closing as explained.

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

No branches or pull requests

4 participants