Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringBoot JNDI datasource throws java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory

Similar questions have been asked before and I went through all of those but not able to solve problem. Related Questions - Q1,Q2,Q3, Q4, Q5, Q6

I have a Spring Batch project with Spring Boot and trying to use DB connection pools. I am using embedded tomcat container with version 8.5.x.

Everything works fine if I use application.properties to specify data source and pool settings.

But when I try to use JNDI, I get exception -

Caused by: java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory

I don't see any jar names tomcat-dbcp-** in Maven jars so I am not sure if I need to include any new dependency or need to set default data source factory and how to go about it.

Below is my JNDI beans set up, Question. I have blanked out certain values.

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

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

            @Override
            protected void postProcessContext(Context context) {
                ContextResource resource = new ContextResource();
                resource.setName("jdbc/myDataSource");
                resource.setType(DataSource.class.getName());
                resource.setProperty("driverClassName", "com.ibm.db2.jcc.DB2Driver");
                resource.setProperty("url", "url");
                resource.setProperty("username", "user");
                resource.setProperty("password", "*****");
                context.getNamingResources().addResource(resource);
            }
        };
    }

    @Lazy
    @Bean(destroyMethod="")
    public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
        JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
        bean.setJndiName("java:comp/env/jdbc/myDataSource");
        bean.setProxyInterface(DataSource.class);
        bean.setLookupOnStartup(false);
        bean.afterPropertiesSet();
        return (DataSource)bean.getObject();
    }

My pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <packaging>war</packaging>

    <groupId>***</groupId>
    <artifactId>***</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.2.1.RELEASE</version>
        </dependency>   

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>db2</groupId>
            <artifactId>db2jcc</artifactId>
            <version>4.0</version>
        </dependency>

        <dependency>
            <groupId>db2</groupId>
            <artifactId>db2jcc_license_cu</artifactId>
            <version>4.0</version>
        </dependency>
    </dependencies>

    <build>

        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.4.0.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                <source>1.7</source>
                <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
like image 298
Sabir Khan Avatar asked Sep 02 '16 05:09

Sabir Khan


People also ask

How to use Apache JDBC DataSource in Spring Boot?

1) To use Apache JDBC datasource, you don't need to add any dependency as it is already provided in the Tomcat Spring Boot starter but you have to change the default factory class to org.apache.tomcat.jdbc.pool.DataSourceFactory to use it.

How to configure DataSource in JNDI context in Apache Tomcat?

Apache Tomcat provide three ways to configure DataSource in JNDI context. Application context.xml - This is the easiest way to configure DataSource, all we need is a context.xml file in META-INF directory. We have to define Resource element in the context file and container will take care of loading and configuring it.

How to use JDBC DataSource in Tomcat?

using the Tomcat JDBC datasource. using any other datasource : for example HikariCP. 1) To use Apache JDBC datasource, you don't need to add any dependency as it is already provided in the Tomcat Spring Boot starter but you have to change the default factory class to org.apache.tomcat.jdbc.pool.DataSourceFactory to use it.

Is it possible to check dependencies in Spring Boot?

But you can check your project dependencies, if you use the spring-boot-starter-jdbc or spring-boot-starter-data-jpa ‘starters’ you will automatically get a dependency to "tomcat-jdbc". What would be those jndi reltead dependencies ? Also, I guess, I am setting same property in bean definition. Show activity on this post.


1 Answers

I solved the problem by setting factory attribute in my Resource Definition. resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");

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

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

        @Override
        protected void postProcessContext(Context context) {
            ContextResource resource = new ContextResource();
            resource.setName("jdbc/myDataSource");
            resource.setType(DataSource.class.getName());
            resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
            resource.setProperty("driverClassName", "com.ibm.db2.jcc.DB2Driver");
            resource.setProperty("url", "url");
            resource.setProperty("username", "user");
            resource.setProperty("password", "*****");
            context.getNamingResources().addResource(resource);
        }
    };
}

As per tomcat 8 documentation, it is supposed to automatically infer db pool factory type by looking at DataSource type and somehow it defaults to DBCP factory and that class is not there in my class path.

I guess so issue can be solved by making tomcat-dbcp-** jars available but I am not sure how to do that with spring boot or even if that is possible with spring boot.

What I find weird is Spring Boot not including tomcat-dbcp dependencies as part of starter POM but using DBCP DataSource factory as default factory.

like image 148
Sabir Khan Avatar answered Oct 02 '22 03:10

Sabir Khan