1

I will now admit to having read many questions, answers, websites about this on and off for several months now in my attempt to create a Spring Boot app with embedded Tomcat (and deployable to an actual Tomcat) with a JNDI DataSource. After repeated failures I'm starting to think it's not even possible at all, it's just a big conspiracy!

In a last ditch attempt before I give up forever, I will now ask : how the **** do you actually get this to work?

Here's what I have so far: My main class:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class FinanceApplication {

  public static void main(String[] args) throws Exception {
    SpringApplication.run(FinanceApplication.class, args);
  }

  @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 resource = new ContextResource();
            resource.setName("jdbc/finance");
            resource.setType(DataSource.class.getName());
            resource.setProperty("driverClassName", "com.mysql.jdbc.Driver");
            resource.setProperty("url", "jdbc:mysql://localhost:3306/finance");
            resource.setProperty("username", "xxxx");
            resource.setProperty("password", "yyyy");

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

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

My Servlet config (java based):

public class ServletInitializer extends SpringBootServletInitializer {

  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(FinanceApplication.class);
  }

}

I am using Maven, my pom.xml is based on this one

The problems:

When I run it in Eclipse, I get these exceptions (cut to the chase):

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is org.springframework.jndi.JndiLookupFailureException: JndiObjectTargetSource failed to obtain new target object; nested exception is javax.naming.NameNotFoundException: Name [comp/env/jdbc/finance] is not bound in this Context. Unable to find [comp].

When I deploy to a standalone Tomcat instance, using Maven, I get this exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; 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
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1119)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:956)
...
NickJ
  • 9,380
  • 9
  • 51
  • 74
  • I haven't used JNDI in Spring Boot before so I can't really help (sorry about that :( but I do have one question. Since you are using embedded Tomcat, why would you want to use JNDI? Why not use the standard Spring Boot conventions to setup the datasource? It will save you a whole lot of trouble – geoand Sep 15 '15 at 15:09
  • It should work via both embedded and separate Tomcat instances. Still have not found the answer. I don't think it's even possible. – NickJ Sep 16 '15 at 13:54
  • I would propose that you use Spring Profiles. When the embedded profile is active, the datasource will be configured based on the properties file. When the classic profile is enabled you can use Spring Boot's JNDI autoconfiguration. Have a look [here](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-profile-specific-properties) – geoand Sep 16 '15 at 14:03

1 Answers1

3

The problem was not in my application at all, but in my Tomcat configuration. Thanks to the accepted answer to this question, I found out that I needed to add a ResourceLink element to Tomcat's context.xml. I wonder why the Tomcat JNDI HowTo makes no mention of it?

Community
  • 1
  • 1
NickJ
  • 9,380
  • 9
  • 51
  • 74
  • Is there any example how your code worked in the end? Can you share your last version? Tks, – Carlos Alberto Jun 01 '16 at 20:26
  • Actually, it does mention it briefly below [global configuration](https://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html): `Tomcat maintains a separate namespace of global resources for the entire server. These are configured in the element of $CATALINA_BASE/conf/server.xml. You may expose these resources to web applications by using a to include it in the per-web-application context.` <-- I suppose saying explicitly __context.xml__ would help understanding. – coderatchet May 04 '17 at 01:04