Hi I am having problems sending emails asychronously. I have set up everything as indicated in this tutorial.

I have annotated my email service with @Service, set it up as a bean, annotated the method with @Async.

I have then @Autowired that service into a controller and used the method. Although it does not carry it out asynchronously and takes two seconds to get past emailSerivce.sendMail();

Any Idea what I'm doing wrong?

My Email Service is setup like this.

 @Service
    public class EmailService {

    //get log4j handler
    private static final Logger logger = Logger.getLogger(EmailService.class);

    // Sends an email with the following parameters
    @Async
    public Future<Page> sendEmail(CustomPropertiesForm cpf, String [] recipients, MimeBodyPart attachment, String subject, String content){

        // Initialise local variables
        Boolean success = false;
        Boolean isAuthenticationRequired = false;
        String smtpAuthUsername = null;
        String smtpAuthPassword = null;

        try{
             Thread.sleep(5000);
            logger.info("Sending Email");

            // Build properties
            Properties props = new Properties();

            if(cpf.getSmtpAuthenticationRequired().equals("YES")){
                isAuthenticationRequired = true;
                props.put("mail.smtp.auth", "true");
                smtpAuthUsername = cpf.getSmtpAuthUsername();
                smtpAuthPassword = cpf.getSmtpAuthPassword();
            } else {
                props.put("mail.smtp.auth", "false");
            }

            if (cpf.getSmtpSSLRequired().equalsIgnoreCase("YES")) {
                //- See more at: http://www.jvmhost.com/articles/how-to-send-mail-with-javamail-and-tomcat#sthash.3IN3wreU.dpuf
                props.put("mail.smtp.ssl.enable", "true");     
                props.put("mail.transport.protocol", cpf.getSmtpProtocol());
                props.put("mail.smtps.port", cpf.getSmtpHostPort());
                props.put("mail.smtps.ssl.trust", cpf.getSmtpHostName()); 
            } else {
                props.put("mail.smtp.ssl.enable", "false");                
            }


            props.put("mail.smtp.host", cpf.getSmtpHostName());
            props.put("mail.smtp.port", cpf.getSmtpHostPort());
            props.put("mail.transport.protocol", cpf.getSmtpProtocol());

            // Create mail Session
            Session mailSession = Session.getDefaultInstance(props);
            mailSession.setDebug(false);    
            Transport transport = mailSession.getTransport();

            // Create Message
            MimeMessage message = new MimeMessage(mailSession);

            // Add recipients
            for(String string : recipients){
                message.addRecipient(Message.RecipientType.TO,new InternetAddress(string));
            }

            // Set From Address
            message.setFrom(new InternetAddress(cpf.getDefaultApplicationEmailAddress()));

            // Set subject
            message.setSubject(subject);            
            // Set content
            MimeBodyPart mbpText = new MimeBodyPart();
            mbpText.setText(content);

            MimeMultipart mp = new MimeMultipart();
            mp.addBodyPart(mbpText);        

            message.setContent(mp);            

            // Add attachment
            if(attachment != null){    
                mp.addBodyPart(attachment);
            }

            // Connect to mail server and send email
            if(isAuthenticationRequired){
                transport.connect(cpf.getSmtpHostName(), Integer.parseInt(cpf.getSmtpHostPort()), smtpAuthUsername, smtpAuthPassword);
                transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
                transport.close();                   
            } else {
                transport.connect();
                transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
                transport.close();  
            }

            success = true;
            logger.info("Email Sent");

        } catch (Exception e){
            logger.error(e.getMessage() + " " + e.getCause() + " " + e.fillInStackTrace());    
            System.out.println(e.getMessage() + " " + e.getCause() + " " + e.fillInStackTrace());
        }
    }    
    Page result = new Page();
    result.setName("name");
    result.setWebsite("website.com");
    return new AsyncResult<Page>(result);   
}

In my dispatcher servlet I set it up as a bean,

<bean id="emailService"
      class="ie.premiumpower.services.EmailService">
</bean>   

I then make use of this email service from my controller,

@Controller
public class NotSecureController extends AbstractController {

@Autowired
private EmailService emailService;

@RequestMapping("notsecure/taskSummary/shareTaskSummary.json")
public String getQuestions(
        @RequestParam("taskInstanceId") int taskInstanceId,
        @RequestParam("emailAdresses[]") String emailAddresses[]) {


try {
        long currTime = System.currentTimeMillis();
        System.out.println("Email being sent at time: " + currTime);

        Future<Page> sendEmail = emailService.sendEmail(cpf, emailAddresses, null, subject, content);
        while (!(sendEmail.isDone())) {
            Thread.sleep(10);
        }
        long aftTime = System.currentTimeMillis();
        aftTime = aftTime - currTime;
        System.out.println("Email finished being sent in time: " + aftTime);
        System.out.println(": " + sendEmail.get());
    } catch (Exception e) {
        System.out.println("Exception: " + e.getMessage());
        e.printStackTrace();
    }

Even when I change it to return a Future it still takes 7 seconds (with the sleep).

My web.xml

<?xml version="1.0" encoding="UTF-8"?>
 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee           http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping> 

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml
        /WEB-INF/spring-security.xml  
    </param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>           
<resource-ref>
    <description>ArcFlashMap DB Connection</description>
    <res-ref-name>jdbc/AFM_DB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>    
<servlet>
    <servlet-name>LoadResourcesServlet</servlet-name>
    <servlet-class>ie.premiumpower.services.reports.common.LoadResourcesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>
<servlet>
    <servlet-name>rest</servlet-name>            
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.json</url-pattern>
</servlet-mapping>
<session-config>
    <session-timeout>
        -1
    </session-timeout>
</session-config>

My applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:task="http://www.springframework.org/schema/task"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:util="http://www.springframework.org/schema/util"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
   http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
   http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd 
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">


<context:annotation-config />    
<mvc:annotation-driven /> 
<mvc:interceptors>
    <bean id="webContentInterceptor" 
          class="org.springframework.web.servlet.mvc.WebContentInterceptor">
        <property name="cacheSeconds" value="0"/>
        <property name="useExpiresHeader" value="true"/>
        <property name="useCacheControlHeader" value="true"/>
        <property name="useCacheControlNoStore" value="true"/>
    </bean>
</mvc:interceptors>

<task:annotation-driven executor="executor" />
<task:executor id="executor" pool-size="7"/>

And my dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">



<context:component-scan base-package="ie.premiumpower.controllers" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <ref bean="jsonHttpMessageConverter"/>
        </list>
    </property>
</bean>

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="mediaTypes">
        <map>
            <entry key="json" value="application/json"/>
        </map>
    </property>
    <property name="defaultViews">
        <list>
            <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
                <property name="prefixJson" value="false"/>
            </bean>
        </list>
    </property>
</bean>

<bean id="messageSource"
      class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:messages" />
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

<bean id="localeChangeInterceptor"
      class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
</bean>

<bean id="localeResolver"
      class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    <property name="defaultLocale" value="en"/>
</bean>

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
        <ref bean="localeChangeInterceptor" />
    </property>
</bean>

<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="10000000"/>
</bean>    
<bean id="emailService"
      class="ie.premiumpower.services.EmailService">
</bean>   

Exception when trying to run program with component-scan pointent to ie.premiumpower.

        Sep 23, 2015 11:20:02 AM org.apache.catalina.core.StandardContext loadOnStartup
    SEVERE: Servlet /SafeSiteLive threw load() exception
    java.lang.IllegalStateException: Annotation-specified bean name 'emailService' for bean class [ie.premiumpower.services.scheduledTaskServices.EmailService] conflicts with existing, non-compatible bean definition of same name and class [ie.premiumpower.services.EmailService]
        at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:274)
        at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:215)
        at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:84)
        at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:73)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1335)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1325)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:135)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:93)
        at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
        at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
        at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:442)
        at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:458)
        at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:339)
        at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:306)
        at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)
        at javax.servlet.GenericServlet.init(GenericServlet.java:158)
        at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284)
        at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197)
        at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1087)
        at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5229)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5516)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
        at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:672)
        at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1862)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
        at java.util.concurrent.FutureTask.run(FutureTask.java:262)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)

Solution 1:

Try this :

Ok, can you try one thing, put the async thread to sleep for 5 seconds by calling Thread.sleep(5000). This way, you will know, that this is not only for creating async task 2 seconds are required. If that doesn't help, then remove the Thread.sleep, and do the following below.

If you would like to get the result back, use a Future tag, as mentioned below :

    @Async
    public Future<Page> sendEmail(params) throws InterruptedException{
   return new AsyncResult<Page>(your_result);
}

In your controller :

    Future<Page> sendEmail = sendEmail(params);

And check this way, if they are done :

 while (!(sendEmail.isDone() ) {
            Thread.sleep(10);
        }

Lemme know.

Solution 2:

In my case, I invoke the async method within the same service, it doesn't have a chance to add proxy class within the same class by spring.

Required config for Spring Async

  1. @EnableAsync with @Configuration
  2. @Async on a public method
  3. Should not call within same class