5
votes

In current versions spring boot can also configure a ConnectionFactory when it detects that ActiveMQ is available on the classpath. If the broker is present, an embedded broker is started and configured automatically.

This seems to be true when using the JMSTemplate. If I want to use spring integration auto configuration then unfortunately this does not work. ActiveMQ seems to be configured AFTER spring integration. Spring boot reports error for missing connection factory. I am using spring boot version 1.1.4 and most current version of spring integration for this.

I get this stacktrace from spring boot:

2014-08-08 09:24:21.050 ERROR 6728 --- [           main]    
o.s.boot.SpringApplication               : Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 
'org.springframework.integration.jms.JmsSendingMessageHandler#0': 
Cannot create inner bean '(inner bean)#54930080' 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)#54930080': Cannot resolve reference to 
bean 'connectionFactory' while setting bean property 'connectionFactory'; nested 
exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean 
named 'connectionFactory' is defined
at 
org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(
BeanDefinitionValueResolver.java:290)
at   
org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveValueIfNecessary(BeanDefinitionValueResolver.java:129)

For me it seems that the dependencies in spring boot auto configurations are not correct with respect of spring integration and jms template. Standard JMS auto configuration looks like the following:

@ConditionalOnClass(JmsTemplate.class)
@ConditionalOnBean(ConnectionFactory.class)
@EnableConfigurationProperties(JmsProperties.class)
@AutoConfigureAfter({ HornetQAutoConfiguration.class, 
ActiveMQAutoConfiguration.class })
public class JmsAutoConfiguration 

Spring integration looks like the following:

@Configuration
@ConditionalOnClass(EnableIntegration.class)
@AutoConfigureAfter(JmxAutoConfiguration.class)
public class IntegrationAutoConfiguration {

Shouldn't there be at least some kind of auto configuration for the dynamic jms template that spring integration creates regarding connection factory and active mq. Considering the spring boot ref docs I would expect correct auto configuration with jms for spring integration as well?

3
Please describe further - much more than "does not work"; include configuration, stack traces etc. I suggest you take a DEBUG log; if you can't figure it out from the log, post it somewhere and I am sure someone will help.Gary Russell
Such cases is the reason you would want to wire up the components manually - to control "depends-on" and startup order. I have no answer to the problem though.Petter Nordlander
What auto configuration are you talking about exactly?Stephane Nicoll
I added some more detail to my question, hopefully it gets clearer what my issue is. I can solve this defining an connection factory bean manually but then spring boot should mention this in the ref docs that for spring integration the jms auto configuration together with active mq auto configuration does not workAndreas Falk
I am not sure to understand what JMX has to do with that. Can you post a project somewhere that reproduces the problem?Stephane Nicoll

3 Answers

2
votes

Ok - got it. It's a bug (I think).

ActiveMQConnectionFactoryConfiguration creates a bean called "jmsConnectionFactory" but looking at your stacktrace (above) Spring Integration is looking for the bean to be named: 'connectionFactory'

Edit: INT-3941 opened

Workaround:

@Configuration
public static class SpringBootVsIntegraionWorkaround {
    @Autowired
    GenericApplicationContext genericApplicationContext;

    @PostConstruct
    public void init() {
        genericApplicationContext.registerAlias("jmsConnectionFactory", "connectionFactory");
    }
}
0
votes

I've replicated this issue. Here's an example:

Before (just to show everything works):

@SpringBootApplication
public class HelloWorldSiActivemqApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldSiActivemqApplication.class, args);
    }

    @Service
    public static class SayHelloService {
        @Autowired
        ConnectionFactory connectionFactory;

        public void sayHello(String name){
            System.out.println("JMS Connection Factory: " + connectionFactory);
            System.out.println("##  Hello " + name + "!!!" );
        }
    }

    @Component
    public static class HelloListener {
        @Autowired
        SayHelloService sayHelloService;

        @JmsListener(destination="helloJMSQueue")
        public void sayHello(String name){
            sayHelloService.sayHello(name);
        }
    }
}

application.properties

spring.activemq.broker-url=tcp://0.0.0.0:61613
spring.activemq.user=admin
spring.activemq.password=password

That works just fine. When I hit the JMS endpoint with a unit test I get:

JMS Connection Factory: org.apache.activemq.ActiveMQConnectionFactory@4b137f92
##  Hello SayHelloServiceTest!!!

However, when I introduce Spring Integration:

@ImportResource("classpath*:/spring/si-config.xml")

si-config.xml

<jms:message-driven-channel-adapter id="helloJMSAdapater" destination="helloJMSQueue"
        channel="helloChannel"/>

<integration:channel id="helloChannel"/>

..and re-run my test I get:

No bean named 'connectionFactory' is defined

Hope this helps!

0
votes

When an 'connectionFactory' bean is automatically set up a simple workaround is to use an alias to have both available:

    <alias name="connectionFactory" alias="jmsConnectionFactory" />