Spring Boot with SMTP connection pool

1.6k Views Asked by At

I'm facing problem while integrating SMTPconnection pool into my spring boot application. Please suggest me what I'm doing wrong in below configuration.I want to execute bulk email execution in Asynchronous way.

Referred material: https://gist.github.com/manish-in-java/c71e974fa178d3cdaf51bb4dd8f61aec

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.library.demo</groupId>
<artifactId>Library_management</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Library_management</name>
<description>Genpact project for use cases</description>

<properties>
    <java.version>1.8</java.version>
    <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</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>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.github.nithril</groupId>
        <artifactId>smtp-connection-pool</artifactId>
        <version>1.4.0</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>               
        </plugin>
    </plugins>
</build>

application.yml:

server:
 port: 8081
# Spring datasource configuration
spring:
 h2:
 console:
  path: /h2
  enabled: true
datasource:
 platform: h2
 url: jdbc:h2:mem:library:MODE=MySQL;DB_CLOSE_DELAY=-1
 driver-class-name: org.h2.Driver
 username: sa
 password: Test@123 
jpa:
show-sql: true   
hibernate:
  ddl-auto: update
database-platform: org.hibernate.dialect.H2Dialect  
logging:
 file: /logs/demo/library_management.log 
 level:
  org.springframework.web: ERROR
  com.canidateid: INFO
  org.hibernate: ERRO
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

Spring boot main class:

package com.library.demo.main;

import org.nlab.smtp.pool.SmtpConnectionPool;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableAutoConfiguration
@EntityScan(basePackages = {"com.library.demo.model"})
@EnableJpaRepositories(basePackages = {"com.library.demo.repository"})
@ComponentScan(basePackages = { "com.library.demo.*" })
public class LibraryManagementApplication {

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

EmailService class:

 @Autowired
  private SmtpConnectionPool smtpConnectionPool;

  public void sendEmail() {
    try (ClosableSmtpConnection transport = smtpConnectionPool.borrowObject()) {
      MimeMessage mimeMessage = new MimeMessage(transport.getSession());

      ...

      transport.sendMessage(mimeMessage);
    }
  }

SMTPConfiguration.java: The class same like reference URL only I have added my smtp configuration.

While Spring boot application startup I'm facing below error, Please help me to resolve the problem

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-09-03 15:22:48 - Application run failed
org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [SmtpConnectionPool [maxTotal=8, blockWhenExhausted=true, maxWaitMillis=-1, lifo=true, fairness=false, testOnCreate=false, testOnBorrow=false, testOnReturn=false, testWhileIdle=false, timeBetweenEvictionRunsMillis=-1, numTestsPerEvictionRun=3, minEvictableIdleTimeMillis=1800000, softMinEvictableIdleTimeMillis=-1, evictionPolicy=org.apache.commons.pool2.impl.DefaultEvictionPolicy@79a71171, closeLock=java.lang.Object@1322c982, closed=false, evictionLock=java.lang.Object@636613, evictor=null, evictionIterator=null, factoryClassLoader=java.lang.ref.WeakReference@23c01dcd, oname=org.apache.commons.pool2:type=GenericObjectPool,name=pool, creationStackTrace=java.lang.Exception
    at org.apache.commons.pool2.impl.BaseGenericObjectPool.<init>(BaseGenericObjectPool.java:143)
    at org.apache.commons.pool2.impl.GenericObjectPool.<init>(GenericObjectPool.java:105)
    at org.apache.commons.pool2.impl.GenericObjectPool.<init>(GenericObjectPool.java:88)
    at org.nlab.smtp.pool.SmtpConnectionPool.<init>(SmtpConnectionPool.java:17)
    at com.library.demo.main.SMTPConfiguration.smtpConnectionPool(SMTPConfiguration.java:32)
    at com.library.demo.main.SMTPConfiguration$$EnhancerBySpringCGLIB$$67c6ab52.CGLIB$smtpConnectionPool$3(<generated>)
    at com.library.demo.main.SMTPConfiguration$$EnhancerBySpringCGLIB$$67c6ab52$$FastClassBySpringCGLIB$$4e630d15.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:365)
    at com.library.demo.main.SMTPConfiguration$$EnhancerBySpringCGLIB$$67c6ab52.smtpConnectionPool(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:582)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1247)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1096)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:583)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:583)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265)
    at com.library.demo.main.LibraryManagementApplication.main(LibraryManagementApplication.java:19)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
, borrowedCount=0, returnedCount=0, createdCount=0, destroyedCount=0, destroyedByEvictorCount=0, destroyedByBorrowValidationCount=0, activeTimes=StatsStore [values=[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], size=100, index=0], idleTimes=StatsStore [values=[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], size=100, index=0], waitTimes=StatsStore [values=[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], size=100, index=0], maxBorrowWaitTimeMillis=0, swallowedExceptionListener=null, factoryType=null, maxIdle=8, minIdle=0, factory=org.nlab.smtp.transport.factory.SmtpConnectionFactory@62adac5d, allObjects={}, createCount=0, idleObjects=[], abandonedConfig=null]] with key 'smtppool'; nested exception is javax.management.InstanceAlreadyExistsException: MXBean already registered with name org.apache.commons.pool2:type=GenericObjectPool,name=pool
    at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:625)
    at org.springframework.jmx.export.MBeanExporter.lambda$registerBeans$2(MBeanExporter.java:551)
    at java.util.HashMap.forEach(Unknown Source)
    at org.springframework.jmx.export.MBeanExporter.registerBeans(MBeanExporter.java:551)
    at org.springframework.jmx.export.MBeanExporter.afterSingletonsInstantiated(MBeanExporter.java:434)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265)
    at com.library.demo.main.LibraryManagementApplication.main(LibraryManagementApplication.java:19)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: javax.management.InstanceAlreadyExistsException: MXBean already registered with name org.apache.commons.pool2:type=GenericObjectPool,name=pool
    at com.sun.jmx.mbeanserver.MXBeanLookup.addReference(Unknown Source)
    at com.sun.jmx.mbeanserver.MXBeanSupport.register(Unknown Source)
    at com.sun.jmx.mbeanserver.MBeanSupport.preRegister2(Unknown Source)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerDynamicMBean(Unknown Source)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerObject(Unknown Source)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(Unknown Source)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(Unknown Source)
    at org.springframework.jmx.support.MBeanRegistrationSupport.doRegister(MBeanRegistrationSupport.java:137)
    at org.springframework.jmx.export.MBeanExporter.registerBeanInstance(MBeanExporter.java:671)
    at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:615)
    ... 19 common frames omitted
2020-09-03 15:22:48 - Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1223f2d0: startup date [Thu Sep 03 15:22:43 IST 2020]; root of context hierarchy
2020-09-03 15:22:48 - Unregistering JMX-exposed beans on shutdown

If I exclude jmx configuration in my main class then application started but smtp configuration not taken by the application.

@EnableAutoConfiguration(exclude={JmxAutoConfiguration.class})

Please Help

1

There are 1 best solutions below

0
medvedick On

Got same issue. When you create bean SmtpConnectionPool you should pass factory and config to constructor. In config you should disable Jmx.

Here is example:

  public static GenericObjectPoolConfig<ClosableSmtpConnection> standardConfig() {
    GenericObjectPoolConfig<ClosableSmtpConnection> config = new GenericObjectPoolConfig<>();
    config.setTestOnBorrow(true);
    config.setMinIdle(0);
    config.setMaxIdle(8);
    config.setMaxTotal(1);
    config.setJmxEnabled(false); // <-- Here is you disable jmx

    config.setMinEvictableIdleTime(Duration.ofMinutes(5));
    config.setTimeBetweenEvictionRuns(Duration.ofMillis(10000));

    config.setMaxWait(Duration.ofMillis(10000));
    return config;
  }
  
  @Bean
  public SmtpConnectionPool smtpConnectionPool(SmtpConnectionFactory factory) {
    GenericObjectPoolConfig<ClosableSmtpConnection> config = standardConfig();
    return new SmtpConnectionPool(factory, config); // <-- Here is you create bean of connection pool
  }

By the way i could not see any configuration properties for spring mail in your question. You should add it to your application.yml

Something like this:

spring:
  mail:
    host: ${MAIL_HOST:smtp.server.exmaple}
    port: ${MAIL_PORT:587}
    username: ${MAIL_USERNAME:[email protected]}
    password: ${MAIL_PASSWORD:somepassword}
    properties:
      mail.transport.protocol: ${MAIL_TRANSPORT_PROTOCOL:smtp}
      mail.smtp.auth: ${MAIL_AUTH:true}
      mail.smtp.starttls.enable: ${MAIL_STARTTLS:true}
      mail.smtp.ssl.trust: ${MAIL_SSL_TRUST:smtp.server.exmaple}