user3458271
user3458271

Reputation: 660

Spring boot JNDI is not working and tomcat not getting started

I am working on spring boot application and I trying to connect datasource using JNDI but it is not working and giving me below error please help if any one knows the reason:

This is the error it is showing:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'i': Invocation of init method failed; 
nested exception is org.springframework.jndi.JndiLookupFailureException: JndiObjectTargetSource failed to obtain new target object; 
nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, 
or in an application resource file:  java.naming.factory.initial

For the pom.xml dependency file I have included following dependencies:

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

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

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

    <!-- Added from here -->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.25</version>
    </dependency>

As I am using postgresql database I have used it's dependencies and in application.properties file I have mentioned as below:

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
# Datasource settings
spring.datasource.initialize=true
spring.datasource.jndi-name=jdbc/IDB

spring.jmx.enabled: false
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=ROOT
spring.datasource.type=javax.sql.DataSource
spring.datasource.separator=;

And for tomcat context naming initialization I have defined bean as below:

@Bean
public TomcatServletWebServerFactory tomcatFactory() {
    return new TomcatServletWebServerFactory() {
        @Override
        protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
            tomcat.enableNaming();
            try {
                tomcat.addContext("/aiv/appimages", imgLoc);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return super.getTomcatWebServer(tomcat);
        }

        @Override
        protected void postProcessContext(org.apache.catalina.Context context) {
            ContextResource resource = new ContextResource();
            resource.setName("jdbc/ActiveIDB");
            resource.setType(DataSource.class.getName());

            resource.setProperty("driverClassName", "org.postgresql.Driver");
            resource.setProperty("url", "jdbc:postgresql://localhost:5432/postgres");
            resource.setProperty("username", "postgres");
            resource.setProperty("password", "ROOT");

            context.getNamingResources().addResource(resource);
        }
    };
}


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

Please any one can let me know what is wrong am I doing why it is giving me that error. Help appriteated.

Upvotes: 2

Views: 2517

Answers (1)

Slongtong
Slongtong

Reputation: 121

You have to do 5 things:

  1. tomcat.enableNaming();
  2. add ContextResource to NamingResources (use same names: jdbc/IDB and jdbc/ActiveIDB have different hash codes :) )
  3. spring.datasource.jndi-name: jdbc/DS_NAME
  4. add org.springframework:spring-jdbc (class EmbeddedDatabaseType) to classpath (it will activate JndiDataSourceAutoConfiguration)

at this moment, spring will try to create datasource but fails, because of org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory class is not in classpath (it's default factory for javax.sql.DataSource resource)

  1. add dependency org.apache.tomcat:tomcat-dbcp:8.5.4 [or hier version] (it contains org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory)

 package com.example;

 import org.apache.catalina.Context;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.tomcat.util.descriptor.web.ContextResource;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
 import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
 import org.springframework.context.annotation.Bean;

 import javax.servlet.ServletException;
 import javax.sql.DataSource;
 import java.io.IOException;
 import java.sql.SQLException;

 @SpringBootApplication
 public class Application {

     /**
      * Or you can copy full implementation from
      * org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration.EmbeddedTomcat
      * override only getTomcatWebServer(Tomcat)
      * and provide TomcatContextCustomizer instead of overriding 'postProcessContext(Context)'
      */
     @Bean
     public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
         return new TomcatServletWebServerFactory() {

             @Override
             protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
                 //1 enable naming
                 tomcat.enableNaming();
                 return super.getTomcatWebServer(tomcat);
             }

             @Override
             protected void postProcessContext(Context context) {
                 //2 create resource
                 ContextResource resource = new ContextResource();
                 resource.setName("jdbc/h2DS");
                 resource.setType(DataSource.class.getName());
                 resource.setProperty("driverClassName", "org.h2.Driver");
                 resource.setProperty("url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
                 resource.setProperty("username", "sa");
                 resource.setProperty("password","");
                 context.getNamingResources().addResource(resource);
             }
         };
     }

     public static void main(String[] args) throws ServletException, IOException, SQLException {
         //3 JndiDataSourceAutoConfiguration will provide that bean if
         // a) spring.datasource.jndi-name: jdbc/h2DS
         // b) org.springframework:spring-jdbc (class EmbeddedDatabaseType) is in classpath
         SpringApplication.run(Application.class, args)
             .getBean(DataSource.class)
             .getConnection();
     }
 }
 

dependencies

  1. org.springframework.boot:spring-boot-starter-web
  2. org.springframework:spring-jdbc
  3. com.h2database:h2:2.1.214 (runtime)
  4. org.apache.tomcat:tomcat-dbcp:8.5.4 [or hier] (runtime)

application.properties

spring.datasource.jndi-name=jdbc/h2DS

Upvotes: 2

Related Questions