Async not working
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
- @EnableAsync with @Configuration
- @Async on a public method
- Should not call within same class