2

Am using Spring Integration XML configuration for polling some database tables and then doing some processing. Everything was working fine, until we introduced multiple data sources. After that we are getting intermittent NoUniqueBeanDefinitionException exceptions.

Problem is we dont know from which poller is this exception coming (we are using xml based configuration), since we have a lot of pollers. Since the stack trace shows the AbstractPollingEndpoint.java, we looked at all the pollers and verified that the transaction-manager is set properly.

Code:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="dataSource" ref="dataSource" />
</bean>

<bean id="transactionManagerSec" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactorySecondary"/>
        <property name="dataSource" ref="dataSourceSec" />
</bean>

<jpa:repositories base-package="com.acme.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
<jpa:repositories base-package="com.acme.tmpdao" entity-manager-factory-ref="entityManagerFactorySecondary" transaction-manager-ref="transactionManagerSec" />

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" primary="true" id="entityManagerFactory">
        <property name="dataSource" ref="dataSource"/>
        <property name="persistenceUnitName" value="${spring.persistence.unitname}" />
        <property name="persistenceProvider" ref="eclipsePP" />
</bean>

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" primary="false" id="entityManagerFactorySecondary">
        <property name="dataSource" ref="dataSourceSec"/>
        <property name="persistenceUnitName" value="${spring.secondary.persistence.unitname}" />
        <property name="persistenceProvider" ref="eclipsePPSec" />
</bean>

<bean class="org.eclipse.persistence.jpa.PersistenceProvider" id="eclipsePP" />
<bean class="org.eclipse.persistence.jpa.PersistenceProvider" id="eclipsePPSec" />

<int:transaction-synchronization-factory id="txSyncFactory">
        <int:before-commit channel="updateEntityChannel" />
</int:transaction-synchronization-factory>

<int-jpa:inbound-channel-adapter id="procStateAdapter"  channel="inProcStateChannel"
                    entity-manager="entityManagerFactory"
                    auto-startup="true"
                    jpa-query="select <SqlQuery> order by en.lastUpdatedTime"
                    max-results="10"
                    >
        <int:poller max-messages-per-poll="10" fixed-rate="10" >
            <int:transactional propagation="REQUIRES_NEW" transaction-manager="transactionManager" synchronization-factory="txSyncFactory"/>
        </int:poller>
    </int-jpa:inbound-channel-adapter>
2019-10-13 07:51:16,625 [task-scheduler-5] ERROR o.s.i.h.LoggingHandler:handleMessageInternal:192 - org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: transactionManager,transactionManagerSec
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:368)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:366)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
        at com.sun.proxy.$Proxy409.toString(Unknown Source)
        at java.lang.String.valueOf(String.java:2994)
        at java.lang.StringBuilder.append(StringBuilder.java:131)
        at org.springframework.integration.endpoint.PollingConsumer.handleMessage(PollingConsumer.java:141)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:272)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint.access$000(AbstractPollingEndpoint.java:58)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:190)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:186)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:353)
        at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:55)
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
        at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:344)
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
        at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

Is there any way to find out the id of the poller that is at the beginning of the AbstractPollingEndpoint$Poller$1.run from the stack below so we can debug this further?

Or anything else that is missing from our config for multiple data source.

Khushbu
  • 295
  • 4
  • 14
  • 1
    your main problem is you have two beans **transactionManager** and **transactionManagerSec** for **JpaTransactionManager**, you need to define one bean as primary **** – Shailesh Chandra Oct 16 '19 at 04:34
  • 1
    Starting with version `5.2` we log the whole `BeanDefinition` for the component when exception happens: https://github.com/spring-projects/spring-integration/issues/2748. So, such an information must appear in logs for that `PollingConsumer` instance. – Artem Bilan Oct 16 '19 at 14:12

1 Answers1

2

In your stacktrace it can be seen that :

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected 
single matching bean but found 2: transactionManager,transactionManagerSec

So the issue is that spring finds two qualifying beans for autowiring. Try setting one of your bean as primary using the attribute as @Shailesh suggested :

<bean primary="true|false"/>

or use a qualifier to set the two transaction beans apart and then specify which transaction manager to be used in code like :

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="dataSource" ref="dataSource" />
        <qualifier value="transactionManagerOne"/>
</bean>

<bean id="transactionManagerSec" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactorySecondary"/>
        <property name="dataSource" ref="dataSourceSec" />
        <qualifier value="transactionManagerSecond"/>
</bean>

and when you want to use a particular transaction manager specify it using the annotation like :

@Transactional("transactionManagerOne")

Note: You could configure this via xml as well

You can use the value attribute of the @Transactional annotation to optionally specify the identity of the PlatformTransactionManager to be used. This can either be the bean name or the qualifier value of the transaction manager bean.The default target bean name, transactionManager, is still used if no specifically qualified PlatformTransactionManager bean is found.

Official Doc

Ananthapadmanabhan
  • 5,706
  • 6
  • 22
  • 39
  • We have already taken care of that. There are two Eclipselink Jpa Auto configuration files, one having the `@Primary` annotation. Ill update the question with that detail. – Khushbu Oct 16 '19 at 05:54
  • @KhushbuTry using `@Transactional` in code to specify the transaction manager to be used. – Ananthapadmanabhan Oct 16 '19 at 05:55
  • But then why is the problem intermittent? It comes once in a while. Most of the time it works. That is why we wanted to find out from which `Spring Integration poller` this exception is coming, so we can debug only that `poller` – Khushbu Oct 16 '19 at 06:08
  • @KhushbuI won't be able to determine that with the available information.Perhaps you could add more logs to the code and test it to identify the path which causes this issue and share the same with us – Ananthapadmanabhan Oct 16 '19 at 06:32