Search code examples
springspring-bootjakarta-mailjnditomcat8

Spring-boot + mail/Session JNDI resource = NoSuchBeanDefinitionException (JavaMailSender)


I've been struggling for a couple of days (and no progress) with the configuration of the mail/Session as a JNDI resource (declared on the Tomcat side), to be picked up by an spring-boot app. The idea is to avoid having any critical information in the application, such as user/password data.

(NOTE 1: Credentials and conection through SMTP have been tested, with an application side configuration, hard-coded in the application.properties, so that is not a problem).

The current configuration I'm using is the following:

server.xml ($TOMCAT/conf)

<Resource
   name="mail/Session"
   auth="Container"
   type="javax.mail.Session"
   username="[email protected]"
   password="XXX"
   mail.user="[email protected]"
   mail.password="XXX"
   mail.transport.protocol="smtp"
   mail.smtp.host="smtp.test.com"
   mail.smtp.auth="true"
   mail.smtp.port="587"/>

context.xml (webapp/META-INF)

<Context>
    <ResourceLink 
        name="mail/Session"
        global="mail/Session"
        auth="Container"
        type="javax.mail.Session"/>
</Context>

application.properties

spring.mail.jndi-name=java:comp/env/mail/Session

In the pom.xml I have the spring-boot-starter-mail dependency (as well as other necessary dependencies). And the class in which I'm inyecting the JavaMailSender bean is this one (simplified):

(NOTE 2: The spring-boot version used is 1.2.8.RELEASE)

@Service("emailService")
public class EmailService {

    @Autowired
    public JavaMailSender javaMailSender;

    public void sendSimpleMail(/* ... */) throws MessagingException {

        //Create message
        MimeMessage message = javaMailSender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message);
        helper.setFrom(fromAddress);
        helper.setTo(toAddress);
        helper.setSubject(subject);
        helper.setText(body);

        //Send message
        javaMailSender.send(message);
    }
}

I understand that spring-boot autoconfiguration should create the JavaMailSender bean once it finds either jndiName or host,port,username,password... spring.mail properties (the later works when I set host, port, username and pasword in application.properties), but it throws the following stack when I use the jndiName option:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'emailService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: public org.springframework.mail.javamail.JavaMailSender com.testapp.service.EmailService.javaMailSender; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.mail.javamail.JavaMailSender] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1208)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    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.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:690)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322)
    at org.springframework.boot.context.web.SpringBootServletInitializer.run(SpringBootServletInitializer.java:135)
    at org.springframework.boot.context.web.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:126)
    at org.springframework.boot.context.web.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:82)
    at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5240)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    ... 10 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: public org.springframework.mail.javamail.JavaMailSender com.testapp.service.EmailService.javaMailSender; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.mail.javamail.JavaMailSender] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 29 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.mail.javamail.JavaMailSender] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 31 more

I have a dataSource configured as a JNDI resource in the same way, and it works perfectly. Does the (mail)Session need a different aproach or am I missing something on it's configuration?


Solution

  • As @m-deinum pointed out in the comments, the problem wasn't the configuration, but the lack of JNDI support in spring-boot versions under 1.3.x

    To solve the issue, I ended up heeding to @m-deinum's tip and upgrading spring-boot from 1.2.8 to 1.3.5... dealing with some deprecated classes and new configurations and ready to go!