John Doe
John Doe

Reputation: 2924

JmsListenerContainerFacotory Not available: NoSuchBeanDefinitionException, even though have configured a DefaultJmsListenerContainerFactory

I am writing unit/integration test to check whether the JMS listener using IBM MQ is getting called correctly. There are two listeners in my IncomingDataListener. They are for two types of data comming in the same queue, processed by MyDataProcessorService and MyOtherDataProcessorService.

As can be seen below I have mocked the two services MyDataProcessorService and MyOtherDataProcessorService.

But I am not using JmsTemplate to send the message to my queue, to test whether the correct method is being called.

Instead I am reading the xml file directly by directly calling the recieveMessage() method and checking the correct data processor service is being called or not.

Do let me know whether I should rather be sending message using the JmsTemplate to the queue, rather than going ahead with this approach of directly calling the reciever?

@RunWith(SpringRunner.class)
@Import({IncomingDataListener.class, DefaultJmsListenerContainerFactory.class})
@ContextConfiguration(classes = JmsConfig.class)
@PropertySource("classpath:config/application-test.properties")
public class IncomingDataListenerTest {

    @Autowired
    private IncomingDataListener incomingDataListener;

    @MockBean
    private MyDataProcessorService myDataProcessorService;

    @MockBean
    private MyOtherDataProcessorService myOtherDataProcessorService;


    private String myDataFileString;
    
    @PostConstruct
    public void setup() throws IOException {
        myDataFileString = XmlUtils.readFile("FileHavingXMLData.xml");
    }

    @Test
    public void testProcessMyData() throws StaticDataValidationException {
        incomingDataListener.receiveMessage(myDataFileString);// Directly  I am calling the IncomingDataListener classes recieveMessage method, is it right or wrong??
        verify(myDataProcessorService, times(1)).process(any());
        verify(myOtherDataProcessorService, times(0)).processOther(any());
        
    }
}

Here is the config class, initially I thought of creating a JmsTemplate to send the message to the queue and then test at the listener side whether the correct processor is getting called!!

@Configuration
@PropertySource("classpath:config/application-test.properties")
@Slf4j
public class JmsConfig {

    @Value("${mq.jms.host}")
    private String host;

    @Value("${mq.jms.port}")
    private Integer port;

    @Value("${mq.jms.queue.manager}")
    private String queueManager;

    @Value("${mq.jms.channel}")
    private String channel;

    @Value("${mq.inbound.queue}")
    private String queue;

    @Value("${mq.timeout}")
    private Long timeout;

    @Bean
    public MQQueueConnectionFactory getConnectionFactory() {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
        try {
            mqQueueConnectionFactory.setHostName(host);
            mqQueueConnectionFactory.setQueueManager(queueManager);
            mqQueueConnectionFactory.setChannel(channel);
        } catch (JMSException e){
            log.debug("Error creating Connection Factory");
        }
        return mqQueueConnectionFactory;
    }

    @Bean
    public JmsTemplate jmsTemplateFactory(MQQueueConnectionFactory mqQueueConnectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(mqQueueConnectionFactory);
        return jmsTemplate;
    }

    @Bean
    public JmsListenerContainerFactory queueContainer(MQQueueConnectionFactory mqQueueConnectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(mqQueueConnectionFactory);
        return factory;
    }
}

To my knowledge I have done all the things required, but I am getting this exception as Listed below.

java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123)
    at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.postProcessFields(MockitoTestExecutionListener.java:95)
    at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.injectFields(MockitoTestExecutionListener.java:79)
    at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.prepareTestInstance(MockitoTestExecutionListener.java:54)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
    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:246)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    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:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'jmsListenerContainerFactory' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:814)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1282)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
    at org.springframework.jms.config.JmsListenerEndpointRegistrar.resolveContainerFactory(JmsListenerEndpointRegistrar.java:159)
    at org.springframework.jms.config.JmsListenerEndpointRegistrar.registerAllEndpoints(JmsListenerEndpointRegistrar.java:143)
    at org.springframework.jms.config.JmsListenerEndpointRegistrar.afterPropertiesSet(JmsListenerEndpointRegistrar.java:135)
    at org.springframework.jms.annotation.JmsListenerAnnotationBeanPostProcessor.afterSingletonsInstantiated(JmsListenerAnnotationBeanPostProcessor.java:210)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:912)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:275)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:243)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    ... 25 more

Please tell me how to come out of this conundrum.

Upvotes: 1

Views: 5006

Answers (2)

Marek Żylicz
Marek Żylicz

Reputation: 469

I think that one of following should work for you

  1. In the config file rename the method name queueContainer to jmsListenerContainerFactory
  2. In the config file use @Bean("jmsListenerContainerFactory") instead of the @Bean
  3. Change the type of the attribute to which you inject the factory to JmsListenerContainerFactory
  4. Change the name of the attribute to which you inject the factory to jmsListenerContainerFactory

You can learn more about how spring names beans and wires them to classes from the following sources:

Upvotes: 1

Gary Russell
Gary Russell

Reputation: 174729

Your factory has a non-standard bean name; the default is jmsListenerContainerFactory; if you name it something else, you must specify the bean name on the @JmsListener annotation.

Either

   @Bean
    public JmsListenerContainerFactory jmsListenerContainerFactory(MQQueueConnectionFactory mqQueueConnectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(mqQueueConnectionFactory);
        return factory;
    }

or

   @Bean("jmsListenerContainerFactory")
    public JmsListenerContainerFactory queueContainer(MQQueueConnectionFactory mqQueueConnectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(mqQueueConnectionFactory);
        return factory;
    }
    /**
     * The bean name of the {@link org.springframework.jms.config.JmsListenerContainerFactory}
     * to use to create the message listener container responsible for serving this endpoint.
     * <p>If not specified, the default container factory is used, if any.
     */
    String containerFactory() default "";

Upvotes: 3

Related Questions