Narendra Verma
Narendra Verma

Reputation: 2422

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

Upvotes: 1

Views: 3393

Answers (2)

Narendra Verma
Narendra Verma

Reputation: 2422

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" />

Upvotes: 0

Afridi
Afridi

Reputation: 6932

There should be atleast 1 bean with name dataSource. So either replace any one of the existing bean with name for exp dataSource1 to dataSource, or create a new one.

Trying to say, change

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

To

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

OR
Simply remove @Primary from any of the existing dataSource* beans. At most 1 bean can have @Primary under same @Profile

Upvotes: 0

Related Questions