When using spring integration JMS support and boot default configuration with ActiveMQ there is an error on startup:

...Cannot create inner bean '(inner bean)#5c49045d' of type [org.springframework.integration.jms.DynamicJmsTemplate] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#5c49045d': Cannot resolve reference to bean 'jmsConnectionFactory' while setting bean property 'connectionFactory'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'jmsConnectionFactory' available

It is related to https://docs.spring.io/spring-integration/reference/html/jms.html#jms-inbound-channel-adapter

By default, all of the JMS adapters that require a reference to the ConnectionFactory automatically look for a bean named jmsConnectionFactory. That is why you do not see a connection-factory attribute in many of the examples. However, if your JMS ConnectionFactory has a different bean name, you need to provide that attribute.

It looks like it is caused that by default spring boot ActiveMQ auto configuration creates cachingJmsConnectionFactory bean instead of jmsConnectionFactory

The issue can be resolved by configuration

spring.jms.cache.enabled=false

Or by alias

It looks like https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java#L75 should have additional names defined.

From outside it is hard to see it other auto configured beans (nonXaJmsConnectionFactory, pooledJmsConnectionFactory) should be also aliased to jmsConnectionFactory

Tested with Spring Boot 2.1.2 Spring Integration 5.1.5

Attaching sample application. spring-boot-activemq-integration.zip

Comment From: snicoll

@AGrzes thanks for the report, I wasn't aware of that jmsConnectionFactory requirement, sounds like a sensible thing to do.

Paging @artembilan for feedback.

Comment From: AGrzes

I located some relevant info from spring integration side https://jira.spring.io/browse/INT-3941 https://github.com/spring-projects/spring-integration/pull/1878

Comment From: artembilan

This discussion is only relevant for XML configuration in Spring Integration: we try to auto-discover bean with that name instead of requesting and explicit one. When you configure with annotations or Java DSL (or Kotlin one), you definitely would auto-wire a particular bean by type.

Since we really can't guarantee that auto-configured bean for ConnectionFactory is always going to be as a jmsConnectionFactory and we never advertised that Spring Boot always provides for us exactly this name (looks like that JIRA is now out-of-date), I would say we should close this as works as designed. It is not Spring Boot responsibility to support Spring Integration XML configuration. More over even if we fix it here in Spring Boot native auto-configuration via aliasing, it doesn't mean that many other side projects for vendor-specifc cloud auto-configuration are going to follow this naming rule.

The aliasing is a solution in the target project. Looks like a MergedBeanDefinitionPostProcessor can help us with the that during application context cconfiguration phase.

Comment From: AGrzes

@artembilan I understand Your point, and for me it does not matter much (I already have knowledge how to fix this problem in my application) but I feel leaving it as is creates trap for others.

  • From spring integration discussion and history I infer that they at some point held belief that '' is some kind of "contract" (maybe to strong word) from the boot side (reference). And with that, there is documentation that suggest this should work out of the box, that may confuse someone.
  • It works in some cases already (With XA transaction manager or with disabled cache) so unrelated change may surface this issue unexpectedly.
  • With bot "magic" one expect simple things to simply work - and conversely figuring out the actual bean name that gets creates from auto configuration code is not trivial. The error tells me what is wrong but the name cachingJmsConnectionFactory have to be found by reading the code.
  • I believe cost of "fixing" it in spring boot is small - for the benefit. I mean the name jmsConnectionFactory is already used by auto configuration so adding it as alias to the bean should be safe.

Comment From: artembilan

I understand that a cat is out of the bag, but the problem is much wider though. Any XML configuration requires to know bean names for references. And that was long before Spring Boot was invented. This is not the way Spring Boot has been designed since it's aiming for Java & annotations auto-wiring by type in most cases. What I mean if we would agree to fix this concern for JMS auto-configuration, we would probably need to re-think everything else in Spring Boot: even if we don't talk about jmsConnectionFactory bean auto-discovery, we need to know names for many other beans we want to be injected into other XML configurations.

If there is a way to autowire by type in XML configuration, I'd be glad to investigate that in Spring Integration, but I definitely don't want to open a can with worms pursuing aliasing in Spring Boot to meet some XML (not only Spring Integration) configuration requirements.

@snicoll , any thoughts?

Thanks

Comment From: snicoll

There are some conventions in that area though. We do have conventions for the default listener container factory to use so that you don't have to specify one. I wasn't aware of the fact it was specific to XML and I see your point @artembilan. Flagging for team attention to see what the rest of the team thinks.

Comment From: AGrzes

Random tought - not shure how it is practical. It looks like if you define @Autowired property on a class and then create instance using xml configuration without setting the property explicitly then autowiring by type works.

I'm not sure it will also work for beans defined using BeanDefinition but it may also work.

In this case * org.springframework.integration.jms.DynamicJmsTemplate could override setConnectionFactory to add annotations (@Autowired and qualifiers if needed) * JmsParserUtils.parseJmsTemplateBeanDefinition could be modified not to set connection factory property reference if not set explicitly - leaving it for autowiring

Comment From: artembilan

I think I can give it a try. The @Autowired means that we have to drop the auto-discovery by the jmsConnectionFactory bean name though. Plus I believe that @Autowired should work in the Spring Boot application context since annotation configuration is enabled any way. For everyone else who uses plain XML configuration that will mean to always specify a ConnecFactory bean name. Slightly drawback and kinda regression for plain XML configuration, but that is going to be consistent with everything else where we definitely don't have auto-discovery for similar options, e.g. MQTT, WebSocket, RScoket, MongoDB, Redis etc..

Or if Spring Boot decides to alias its ConnecFactory beans, I don't need to do anything in Spring Integration. πŸ˜„

Comment From: snicoll

We've discussed this at the team meeting and decided to bring back the alias for those options. @artembilan, if you have time to investigate how to improve things in Spring Integration that would be great!

Comment From: artembilan

Thanks, @snicoll , for feed back! Let's hope it is going to work as expected! Actually we have never advertised an auto-injection by type in setters. Even if it is possible, it doesn't sound reasonable compromise for framework components which code is definitely out of end-user control. So, we stayed away from that feature for a long time and now I'm changing my mind and would like not to add an @Autowired into the framework component setters. I would change that jmsConnectionFactory auto-discovery into an explicit bean reference, like in many other places, but this is going to be harmless after fixing aliasing here in Spring Boot.