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 Boot 1.4 - RabbitMQ Cannot override primary ConnectionFactory and create secondary ConnectionFactory #6559

Closed
chrisdadej opened this issue Aug 4, 2016 · 8 comments
Assignees
Labels
type: blocker An issue that is blocking us from releasing type: bug A general bug
Milestone

Comments

@chrisdadej
Copy link

chrisdadej commented Aug 4, 2016

Since upgrading to Spring Boot 1.4 I've noticed that all of my microservices that override the primary ConnectionFactory AND create a secondary ConnectionFactory stopped working due to a NoSuchBeanDefinitionException.

This works - not overriding the primary ConnectionFactory, just creating a secondary one

@Bean(name = "pcConnectionFactory")
ConnectionFactory pcConnectionFactory(RabbitProperties config) throws Exception {
  ..
}

This works - overriding the primary ConnectionFactory without a secondary one

@Bean
@Primary
ConnectionFactory connectionFactory(RabbitProperties config) throws Exception {
  ..
}

This fails - overriding the primary ConnectionFactory AND creating a secondary one

@Bean
@Primary
ConnectionFactory connectionFactory(RabbitProperties config) throws Exception {
  ..
}

@Bean(name = "pcConnectionFactory")
ConnectionFactory pcConnectionFactory(RabbitProperties config) throws Exception {
  ..
}

Stack Trace:

Caused by: org.springframework.context.ApplicationContextException: Failed to start bean 'outputBindingLifecycle'; nested exception is java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$RabbitTemplateConfiguration.rabbitTemplate
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:176)
    at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:51)
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:346)
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:149)
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:112)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:874)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:144)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:369)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    ... 24 more
Caused by: java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$RabbitTemplateConfiguration.rabbitTemplate
    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:681)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:523)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:369)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134)
    at org.springframework.cloud.stream.binder.DefaultBinderFactory.getBinder(DefaultBinderFactory.java:185)
    at org.springframework.cloud.stream.binding.ChannelBindingService.getBinderForChannel(ChannelBindingService.java:142)
    at org.springframework.cloud.stream.binding.ChannelBindingService.bindProducer(ChannelBindingService.java:104)
    at org.springframework.cloud.stream.binding.BindableProxyFactory.bindOutputs(BindableProxyFactory.java:206)
    at org.springframework.cloud.stream.binding.OutputBindingLifecycle.start(OutputBindingLifecycle.java:57)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:173)
    ... 38 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'pcConnectionFactory' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:702)
    at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getPrimaryBeans(OnBeanCondition.java:234)
    at org.springframework.boot.autoconfigure.condition.OnBeanCondition.hasSingleAutowireCandidate(OnBeanCondition.java:227)
    at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchOutcome(OnBeanCondition.java:97)
    at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
    ... 58 more
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Aug 4, 2016
@snicoll
Copy link
Member

snicoll commented Aug 4, 2016

No bean named 'pcConnectionFactory' is defined

That doesn't sound legit to me. It looks like something is looking for a bean with that name and Spring Boot won't do that. Can you share a sample that reproduces the problem?

@snicoll snicoll 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 Aug 4, 2016
@chrisdadej
Copy link
Author

chrisdadej commented Aug 5, 2016

That doesn't sound legit to me

Couldn't agree more but after removing all references to pcConnectionFactory the same error kept coming up. I've created a really basic sample that reproduces the problem.

The zip file attachment here never works for me, so I've dropped the zip file with the sample here:
Location #1
Location #2

@chrisdadej chrisdadej reopened this Aug 5, 2016
@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 Aug 5, 2016
@snicoll
Copy link
Member

snicoll commented Aug 5, 2016

@chrisdadej all the Spring Cloud stuff is really polluting the sample. I've taken your class, added it to a Spring Boot 1.4 project created from start.spring.io and it didn't throw any issue. I think the issue is a bit more complicated. I can't reproduce and I have no idea where that lookup on your named connection factory is coming from. But it doesn't seem to be in Spring Boot.

Can you try to simplify your sample a bit? Maybe the issue is in another project.

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Aug 5, 2016
@chrisdadej
Copy link
Author

It looks like an incompatibility between Spring Boot 1.4 and Spring Cloud Brixton.
Switching out:

compile "org.springframework.cloud:spring-cloud-starter-bus-amqp:${springCloudStarterVersion}"

for:

compile "org.springframework.amqp:spring-rabbit:1.6.1.RELEASE"

works as expected.

I'm assuming you were able to replicate the issue as is with my sample? You just weren't able to replicate it after taking out the Spring Cloud components.

I can post this to the Spring Cloud team for them to have a look at but there's definitely been a change made in 1.4 that isn't backwards compatible.

@snicoll
Copy link
Member

snicoll commented Aug 8, 2016

@chrisdadej the cross post is far from ideal. I am closing this one for now, let's see what the spring cloud team says.

@snicoll snicoll closed this as completed Aug 8, 2016
@snicoll snicoll added status: duplicate A duplicate of another issue and removed status: feedback-provided Feedback has been provided labels Aug 8, 2016
@buwi
Copy link

buwi commented Aug 18, 2016

I ran into the same issue as Chrisdadej. I have an application with multiple Rabbit ConnectionFactory beans and it resulted in a NoSuchBeanDefinitionException.

I think the problem is with spring-boot-autoconfigure project and not with spring-cloud. I was able to debug the problem to OnBeanCondition class in spring-boot-autoconfigure project.

First of all, ConditionalOnSingleCandidate(ConnectionFactory.class) (¹) annotation was added to rabbitTemplate bean definition in org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$RabbitTemplateConfiguration class as part of commit 9c73312.

    @Bean
    @ConditionalOnSingleCandidate(ConnectionFactory.class)¹
    @ConditionalOnMissingBean(RabbitTemplate.class)
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        ...
        return rabbitTemplate;
    }

When there are multiple connection factories, evaluation of ConditionalOnSingleCandidate fails to find any beans of type ConnectionFactory.

org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchingBeans(ConditionContext, BeanSearchSpec) (²) method checks for matching bean definitions of type ConnectionFactory in the supplied beanFactory and the parent beanFactory references.

However OnBeanCondition.hasSingleAutowireCandidate(ConfigurableListableBeanFactory beanFactory, List beanNames) (³) method does not recursively check in the parent beanFactory references. That results in a NoSuchBeanDefinitionException.

public ConditionOutcome getMatchOutcome(ConditionContext context,
        AnnotatedTypeMetadata metadata) {
    StringBuilder matchMessage = new StringBuilder();
    if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
        BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
                ConditionalOnBean.class);
        List<String> matching = getMatchingBeans(context, spec);
        if (matching.isEmpty()) {
            return ConditionOutcome
                    .noMatch("@ConditionalOnBean " + spec + " found no beans");
        }
        matchMessage.append("@ConditionalOnBean ").append(spec)
                .append(" found the following ").append(matching);
    }
    if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
        BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
                ConditionalOnSingleCandidate.class);
***     List<String> matching = getMatchingBeans(context, spec); ²
        if (matching.isEmpty()) {
            return ConditionOutcome.noMatch(
                    "@ConditionalOnSingleCandidate " + spec + " found no beans");
        }
***     else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matching)) { ³
            return ConditionOutcome.noMatch("@ConditionalOnSingleCandidate " + spec
                    + " found no primary candidate amongst the" + " following "
                    + matching);
        }
        matchMessage.append("@ConditionalOnSingleCandidate ").append(spec)
                .append(" found a primary candidate amongst the following ")
                .append(matching);
    }
    if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
        BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
                ConditionalOnMissingBean.class);
        List<String> matching = getMatchingBeans(context, spec);
        if (!matching.isEmpty()) {
            return ConditionOutcome.noMatch("@ConditionalOnMissingBean " + spec
                    + " found the following " + matching);
        }
        matchMessage.append(matchMessage.length() == 0 ? "" : " ");
        matchMessage.append("@ConditionalOnMissingBean ").append(spec)
                .append(" found no beans");
    }
    return ConditionOutcome.match(matchMessage.toString());
}

This issue is preventing me from upgrading to 1.4.0.RELEASE of spring-boot.

@chrisdadej @snicoll could this issue be reopened?

@snicoll
Copy link
Member

snicoll commented Aug 19, 2016

@buwi can you share a sample that reproduces the problem please?

@snicoll snicoll reopened this Aug 19, 2016
@snicoll snicoll added type: bug A general bug type: blocker An issue that is blocking us from releasing and removed status: duplicate A duplicate of another issue labels Aug 19, 2016
@snicoll snicoll added this to the 1.3.8 milestone Aug 19, 2016
@buwi
Copy link

buwi commented Aug 19, 2016

@snicoll I have created a sample SpringBoot application and posted it here

Thanks.

@wilkinsona wilkinsona self-assigned this Aug 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: blocker An issue that is blocking us from releasing type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants