Closed lhazlewood closed 8 years ago
P.S. I believe this should be in the 1.0 release because it could impact anyone evaluating the Stormpath Default Spring Boot Starter for the first time and it was nasty to try to figure out (took a while debugging w/ break points and digging deep into Spring and Stormpath code internals).
It turns out that this is a problem even when solving the DelegatingMessageSource/parent issue. If the user does not declare an actual bean named messageSource
(or configure a spring.messages.basename
property which automatically produces the equivalent bean), the MessageSource instance we create still won't be used.
@lhazlewood I haven't been able to repo this, I grab the spring-boot-webmvc example and inject the MessageSource without declaring one and did the following:
@Controller
public class HelloController {
@Autowired
private MessageSource messageSource;
@RequestMapping("/")
public String home(HttpServletRequest request, Model model) {
String name = messageSource.getMessage("world", new Object[]{}, Locale.getDefault());
Account account = AccountResolver.INSTANCE.getAccount(request);
if (account != null) {
name = account.getGivenName();
model.addAttribute(account);
}
model.addAttribute("name", name);
return "hello";
}
}
And property world was resolved.
Could you provide the exact case you where working on so I can try and reproduce it.
Thanks.
I'll see if I can provide a sample app that demonstrates this
@jarias please see this example:
https://github.com/stormpath/stormpath-spring-boot-i18n-test
After checkout, uncomment the @Bean
annotation in MessagesConfig
and then run it.
@jarias if this only occurs when running Spring Cloud Config Server, let's put it back on the 'ready' column and work on it last. We still need to solve it, but it might not be critical for 1.0.
ok @lhazlewood
According to this question on Stack Overflow, this is a bug in Spring Boot. However, it should be fixed in Spring Boot 1.3.0+.
I tried adding the following test to stormpath-spring-boot-i18n-test:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestApplication.class)
public class MessagesourceBugApplicationTests {
@Autowired
private MessageSource messageSource;
@Test
public void loadsMessage() {
Assert.assertEquals("Login", messageSource.getMessage("stormpath.web.login.form.title", null, Locale.getDefault()));
}
}
However, it blows up when I try to run it:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:249)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityWebAppConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.setObjectPostProcessor(org.springframework.security.config.annotation.ObjectPostProcessor); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
... 29 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.setObjectPostProcessor(org.springframework.security.config.annotation.ObjectPostProcessor); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:661)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 45 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:618)
... 47 more
@lhazlewood @jarias I created a PR so you can see how this test fails: https://github.com/stormpath/stormpath-spring-boot-i18n-test/pull/1
I was able to make the resource handler work for CSS and JS by doing the following.
TestApplication.java
, I added @ComponentScan("com.stormpath.spring.config")
to the class. This causes StormpathWebMvcStaticResourceConfigurer#addResourceHandlers
to be called. Without it, this method is not called.StormpathWebMvcStaticResourceConfigurer#addResourceHandlers
, I added registry.setOrder(-300);
. Component scanning declarations definitely shouldn't be necessary for Spring Boot apps since we declare necessary things in various spring.factories
files.
Also, we currently use the Spring Boot 1.3.5 dependency, so it doesn't appear to be fixed in 1.3.x? (or we've done something wrong).
@mraible StormpathWebMvcConfiguration
(which contains StormpathWebMvcStaticResourceConfigurer
) should only be used in non-SpringBoot Spring apps. Did you enable that component scan in a Spring Boot app?
@lhazlewood I noticed that StormpathWebMvcAutoConfiguration
does get hit, but StormpathWebMvcConfiguration
does not get hit. My brute-force debugging method was to put logging messages in StormpathWebMvcStaticResourceConfigurer#addResourceHandlers
:
@SuppressWarnings("SpringFacetCodeInspection")
@Configuration
public static class StormpathWebMvcStaticResourceConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
System.out.println("**********************************\n\n\n");
System.out.println("adding resource handlers");
System.out.println("**********************************\n\n\n");
I did this in stormpath-spring-boot-i18n-test
, which is a Spring Boot app.
How are stormpath.js
and stormpath.css
mapped without this?
@lhazlewood This app no longer works with the latest Stormpath code. I'm guessing it's because it uses Spring Boot 1.3.5 and we use Spring Boot 1.4.0. Here's the error on startup:
java.lang.IllegalStateException: Error processing condition on com.stormpath.spring.boot.autoconfigure.StormpathWebMvcAutoConfiguration.stormpathApplicationResolver
at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:64)
at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:102)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:178)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:140)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:116)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:333)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:243)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:273)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:98)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:678)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:520)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180)
at com.stormpath.test.TestApplication.main(TestApplication.java:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:478)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: @ConditionalOnMissingBean annotations must specify at least one bean (type, name or annotation)
at org.springframework.util.Assert.isTrue(Assert.java:68)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition$BeanSearchSpec.validate(OnBeanCondition.java:279)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition$BeanSearchSpec.<init>(OnBeanCondition.java:275)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchOutcome(OnBeanCondition.java:111)
at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
... 23 common frames omitted
If I change ${spring.boot.version}
to be 1.4.0.RELEASE
, I get a different error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tomcatEmbeddedServletContainerFactory' defined in class path resource [org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat.class]: Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer.addErrorPages([Lorg/springframework/boot/context/embedded/ErrorPage;)V
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:199)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:162)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:134)
... 14 more
Caused by: java.lang.NoSuchMethodError: org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer.addErrorPages([Lorg/springframework/boot/context/embedded/ErrorPage;)V
at org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$ErrorPageCustomizer.customize(ErrorMvcAutoConfiguration.java:269)
at org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization(EmbeddedServletContainerCustomizerBeanPostProcessor.java:68)
at org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization(EmbeddedServletContainerCustomizerBeanPostProcessor.java:54)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
... 22 more
I noticed that you're not using spring-boot-starter-parent
as a parent, so I changed that and removed some versions that are overriding the defaults. Now it starts up and the CSS and JS are rendered correctly.
--- a/pom.xml
+++ b/pom.xml
@@ -5,9 +5,9 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>7</version>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-parent</artifactId>
+ <version>1.4.0.RELEASE</version>
</parent>
<groupId>com.stormpath.test</groupId>
@@ -21,7 +21,7 @@
<url>https://stormpath.com</url>
</organization>
- <!--
+ <!--
<scm>
<connection>scm:git:git@github.com:stormpath/chancellor.git</connection>
<developerConnection>scm:git:git@github.com:stormpath/chancellor.git</developerConnection>
@@ -37,11 +37,9 @@
<maven.enforcer.plugin.version>1.4</maven.enforcer.plugin.version>
<samza.version>0.9.1</samza.version>
<slf4j.version>1.7.21</slf4j.version>
- <spring.version>4.2.6.RELEASE</spring.version>
- <spring.boot.version>1.3.5.RELEASE</spring.boot.version>
+ <spring.boot.version>1.4.0.RELEASE</spring.boot.version>
<spring.cloud.version>Brixton.RELEASE</spring.cloud.version>
<spring.cloud.config.server.version>1.1.0.RELEASE</spring.cloud.config.server.version>
- <spring.security.version>4.0.4.RELEASE</spring.security.version>
<stormpath.sdk.version>1.0.0.RC-SNAPSHOT</stormpath.sdk.version>
<!-- test-related properties: -->
Unfortunately, there's still an issue with i18n.
I'm able to get i18n to work if I remove spring-cloud-config-server
as a dependency in pom.xml
. Previously, I only had to remove the @EnableConfigServer
annotation, so it seems that spring-cloud is overriding the MessageSource
.
I was unable to fix this issue, but I did create a ticket in the Spring Cloud Config project. https://github.com/spring-cloud/spring-cloud-config/issues/462
Closing - merged to the 1.1.x branch.
AbstractStormpathWebMvcConfiguration#stormpathSpringMessageSource()
checks to see if the specified message source is null or a placeholder, where 'placeholder' is indicated if it is a DelegatingMessageSource that does not have a parent.I encountered a scenario today where the message source was a DelegatingMessageSource and it did have a parent. However, the parent was itself a DelegatingMessageSource (which did not have a parent). I suspect this occurs when nesting application contexts or other 'frameworky' scenarios.
I believe the best/safest thing to do is to keep walking the parent hierarchy until we find one that is 1) not a DelegatingMessageSource and 2) not null.
If these two conditions are met, the CompositeMessageSource instance should be created using the Stormpath i18nPropertiesMessageSource and the discovered non-DelegatingMessageSource.
If these two conditions are not met, then just create/return the Stormpath i18nPropertiesMessageSource.
The quick workaround for this scenario until this bug is fixed is for the app developer to explicitly declare a MessageSource bean in their Spring configuration:
And the
stormpathSpringMessageSource
won't be created (preferring the user default).