0

spring configuration: Trying to instantiate a JmsMessageListenerContainerFactory Bean that will use a specific connection factory bean instance using annotations.

@Configuration
public class DemoJmsConfig {

    @Value("${jms.context.factory}") 
    private String initialContextFactory;

    @Value("${jms.provider.url}") 
    String jmsProviderUrl;

    //Inbound
    @Value("${jms.inbound.qcf.userName}") 
    private String inboundQcfUserName;

    @Value("${jms.inbound.qcf.password}") 
    private String inboundQcfPassword;

    @Value("${jms.inbound.queue.connectionfactory}") 
    private String inboundQueueConnectionFactory;

    @Value("${jms.inbound.queue.name}")
    private String inboundQueueName;

    @Value("${jms.inbound.initial.cache.size}") 
    private String inboundInitCacheSize;

    //Outbound
    @Value("${jms.outbound.topic.connectionfactory}") 
    private String outboundTopicConnectionFactory;

    @Value("${jms.outbound.topic.name}")
    private String outboundTopicName;

    @Value("${jms.outbound.tcf.userName}") 
    private String outboundTcfUserName;

    @Value("${jms.outbound.tcf.password}") 
    private String outboundTcfPassword;

    @Value("${jms.outbound.initial.cache.size}") 
    private String outboundInitCacheSize;

    @Bean
    public InitialContext initialContext() throws NamingException {
        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY,
                initialContextFactory);
        props.put(Context.PROVIDER_URL, jmsProviderUrl);

        // Create the initial context from the properties we just created
        return new InitialContext(props);
    }

    @Bean(name="inboundConnectionFactory")
    @Qualifier("inboundConnectionFactory")
    public ConnectionFactory inboundConnectionFactory() throws NamingException {

        //Create a Connection Factory adapter with user credentials
        UserCredentialsConnectionFactoryAdapter uccfa = new UserCredentialsConnectionFactoryAdapter();
        ConnectionFactory cf = (ConnectionFactory) initialContext().lookup(inboundQueueConnectionFactory);
        uccfa.setTargetConnectionFactory(cf);
        uccfa.setUsername(inboundQcfUserName);
        uccfa.setPassword(inboundQcfPassword);

        CachingConnectionFactory ccf = new CachingConnectionFactory();
        if(inboundInitCacheSize != null && Integer.parseInt(inboundInitCacheSize) > 0){
            ccf.setSessionCacheSize(Integer.parseInt(inboundInitCacheSize)); //Default is 1
        }
        ccf.setTargetConnectionFactory(uccfa);
        return ccf;

    }

    @Bean(name="inboundJMSTemplate")
    @Qualifier("inboundJMSTemplate")
    public JmsTemplate inboundJMSTemplate(@Qualifier("inboundConnectionFactory") ConnectionFactory inboundConnectionFactory ) throws NamingException{
        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setConnectionFactory(inboundConnectionFactory);
        jmsTemplate.setDefaultDestinationName(inboundQueueName);
        return jmsTemplate;

    }


    @Bean(name="inboundJmsListenerContainerFactory")
    @Qualifier("inboundJmsListenerContainerFactory")
    public JmsListenerContainerFactory<?> inboundJmsListenerContainerFactory(
            DefaultJmsListenerContainerFactoryConfigurer configurer,
            @Qualifier("inboundConnectionFactory") ConnectionFactory inboundConnectionFactory)
            throws NamingException {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        // This provides all boot's default to this factory, including the
        // message converter
        configurer.configure(factory, inboundConnectionFactory);
        // You could still override some of Boot's default if necessary.
        return factory;
    }


    @Bean(name="outboundConnectionFactory")
    @Qualifier("outboundConnectionFactory")
    public ConnectionFactory outboundConnectionFactory() throws NamingException {

        //Create a Connection Factory adapter with user credentials
        UserCredentialsConnectionFactoryAdapter uccfa = new UserCredentialsConnectionFactoryAdapter();
        ConnectionFactory cf = (ConnectionFactory) initialContext().lookup(outboundTopicConnectionFactory);
        uccfa.setTargetConnectionFactory(cf);
        uccfa.setUsername(outboundTcfUserName);
        uccfa.setPassword(outboundTcfPassword);

        CachingConnectionFactory ccf = new CachingConnectionFactory();
        if(outboundInitCacheSize != null && Integer.parseInt(outboundInitCacheSize) > 0){
            ccf.setSessionCacheSize(Integer.parseInt(outboundInitCacheSize)); //Default is 1
        }
        ccf.setTargetConnectionFactory(uccfa);
        return ccf;

    }

    @Bean(name="outboundErrorJMSTemplate")
    @Qualifier("outboundErrorJMSTemplate")
    public JmsTemplate outboundErrorJMSTemplate(@Qualifier("outboundConnectionFactory") ConnectionFactory outboundConnectionFactory ) throws NamingException{
        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setConnectionFactory(outboundConnectionFactory);
        jmsTemplate.setDefaultDestinationName(outboundTopicName);
        return jmsTemplate;

    }



}

Expected result: The JmsListenerContainerFactory is instantiated.

Actual:

*************************** APPLICATION FAILED TO START
***************************

Description:

Parameter 1 of method jmsListenerContainerFactory in org.springframework.boot.autoconfigure.jms.JmsAnnotationDrivenConfiguration required a single bean, but 2 were found:
    - inboundConnectionFactory: defined by method 'inboundConnectionFactory' in class path resource [com/example/demo/config/DemoJmsConfig.class]
    - outboundConnectionFactory: defined by method 'outboundConnectionFactory' in class path resource [com/example/demo/config/DemoJmsConfig.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

[WARNING]  java.lang.reflect.InvocationTargetException  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 org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:527)    at java.lang.Thread.run(Thread.java:745) Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jmsListenerContainerFactory' defined in class path resource [org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.class]: Unsatisfied dependency expressed through method 'jmsListenerContainerFactory' parameter 1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.jms.ConnectionFactory' available: expected single matching bean but found 2: inboundConnectionFactory,outboundConnectionFactory   at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)  at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:467)    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1173)     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1067)    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)     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:761)   at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)  at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)  at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)   at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)   at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)  at com.example.demo.DemoApplication.main(DemoApplication.java:10)   ... 6 more Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.jms.ConnectionFactory' available: expected single matching bean but found 2: inboundConnectionFactory,outboundConnectionFactory  at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)   at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)     at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)     at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)  ... 25 more

I have tried specifying qualifiers, bean names.. still getting errors. Can anyone suggest what I should do to resolve this. I need both the inbound and outbound connections since the application will consume and publish

sbelvadi
  • 47
  • 2
  • 9
  • 1
    This might help: https://stackoverflow.com/questions/2902335/instantiating-multiple-beans-of-the-same-class-with-spring-annotations, and this google phrase will return the motherlode of results: "spring @Configuration multiple beans same class" –  Dec 02 '17 at 17:50
  • @ClayFerguson: Thanks for your suggestion. – sbelvadi Dec 02 '17 at 18:52
  • digging through found that spring boot was auto configuring a JmsListenerContainerFactory bean (due to spring jms jars in class path) and that was throwing the exception. so annotated the inbound connection factory as Primary and this seems to have resolved the issue. – sbelvadi Dec 02 '17 at 18:54
  • Your original post made it sound like you had tried @Primary already or else i would have said that. Glad you got it working. –  Dec 02 '17 at 22:59

2 Answers2

3

resolved by adding @Primary to inbound Connection factory

sbelvadi
  • 47
  • 2
  • 9
0

Check this question Spring Boot multiple JMS connections that can help you. If in your case are using a mix of Spring XML and annotations to make a connection factory, maybe you can have problems with SpringBoot, in this case thy disable autostart of spring-boot-jms with this in your application:

 @SpringBootApplication(exclude = {JmsAutoConfiguration.class})

this was my case.

Carlos Lacerda
  • 699
  • 6
  • 14