Search code examples
spring-bootdatasourcejndiembedded-tomcat-8

Spring Boot Embedded Tomcat - No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3


I am working on my spring boot application and running with embedded tomcat 8.x. I am trying to configure three different oracle data sources using JNDI and followed this link. Below are my different files:

application-dev.properties

spring.oracle.datasource.oracleDS1.jndi-name=jdbc/oracleDS1
spring.oracle.datasource.oracleDS2.jndi-name=jdbc/oracleDS2
spring.oracle.datasource.oracleDS3.jndi-name=jdbc/oracleDS3

OracleDataSourceConfiguration

package com.adp.orbis.requesttracker.orbisrequesttracker;

import javax.sql.DataSource;

    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.context.annotation.Profile;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;

    @Configuration
    public class OracleDataSourceConfiguration {

        @Value("${spring.oracle.datasource.oracleDS1.jndi-name}")
        private String oracleDS1;

        @Value("${spring.oracle.datasource.oracleDS2.jndi-name}")
        private String oracleDS2;

        @Value("${spring.oracle.datasource.oracleDS3.jndi-name}")
        private String oracleDS3;

        @Bean(name="dataSource1", destroyMethod = "")
        @Profile("dev")
        @Primary
        public DataSource evolutionDataSource() {
            JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
            return dataSourceLookup.getDataSource(oracleDS1);
        }

        @Bean(name="dataSource2", destroyMethod = "")
        @Profile("dev")
        @Primary
        public DataSource orbisQueryOnlyDataSource() {
            JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
            return dataSourceLookup.getDataSource(oracleDS2);
        }

        @Bean(name="dataSource3", destroyMethod = "")
        @Profile("dev")

        public DataSource orbisExportDataSource() {
            JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
            return dataSourceLookup.getDataSource(oracleDS3);
        }
    }

TomcatEmbeddedServletContainerFactory

@Bean
    public TomcatEmbeddedServletContainerFactory tomcatFactory() {
        return new TomcatEmbeddedServletContainerFactory() {

            @Override
            protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
                tomcat.enableNaming();
                return super.getTomcatEmbeddedServletContainer(tomcat);
            }

            @Override
            protected void postProcessContext(Context context) {
                ContextResource oracleDS1JNDIResource = new ContextResource();
                oracleDS1JNDIResource.setName("jdbc/oracleDS1");
                oracleDS1JNDIResource.setType(DataSource.class.getName());
                oracleDS1JNDIResource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
                oracleDS1JNDIResource.setProperty("url", "jdbc:oracle:thin:@localhost:1521/mydbservice1");
                oracleDS1JNDIResource.setProperty("username", "db-user-name");
                oracleDS1JNDIResource.setProperty("password", "db-user-pass");
                oracleDS1JNDIResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
                context.getNamingResources().addResource(oracleDS1JNDIResource);

                ContextResource oracleDS2JNDIResource = new ContextResource();
                oracleDS2JNDIResource.setName("jdbc/oracleDS2");
                oracleDS2JNDIResource.setType(DataSource.class.getName());
                oracleDS2JNDIResource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
                oracleDS2JNDIResource.setProperty("url", "jdbc:oracle:thin:@localhost:1521/mydbservice2");
                oracleDS2JNDIResource.setProperty("username", "db-user-name");
                oracleDS2JNDIResource.setProperty("password", "db-user-pass");
                oracleDS2JNDIResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
                context.getNamingResources().addResource(oracleDS2JNDIResource);

                ContextResource oracleDS3JNDIResource = new ContextResource();
                oracleDS3JNDIResource.setName("jdbc/oracleDS3");
                oracleDS3JNDIResource.setType(DataSource.class.getName());
                oracleDS3JNDIResource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
                oracleDS3JNDIResource.setProperty("url", "jdbc:oracle:thin:@localhost:1521/mydbservice3");
                oracleDS3JNDIResource.setProperty("username", "db-user-name");
                oracleDS3JNDIResource.setProperty("password", "db-user-pass");
                oracleDS3JNDIResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
                context.getNamingResources().addResource(oracleDS3JNDIResource);                

            }
        };
    }

But when I run this spring boot application, it throws below exception. Could you please help me what I am missing here? I tried adding @Primary with all DataSource bean. But no luck.

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: dataSource1,dataSource2,dataSource3

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: dataSource1,dataSource2,dataSource3
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1092) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init(DataSourceInitializer.java:77) ~[spring-boot-autoconfigure-1.5.9.RELEASE.jar:1.5.9.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_112]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_112]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_112]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_112]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:134) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    ... 38 common frames omitted

Solution

  • I am able to resolve this issue. The problem was, I had defined all data sources in my spring boot application and it was also importing dependent spring context file. That means, I had same data sources configured into my imported spring context file too. I removed all data source beans from OracleDataSourceConfiguration(as mentioned in my question on top) and used TomcatEmbeddedServletContainerFactory to initialize JNDI data sources (referred this) with the same name which are mentioned in my imported spring context file. It worked for me.

     <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDataSource" />