I have a little problem with @Async
methods that return void
(or Unit
, I'm writing in Kotlin) in my Spring app.
I don't know why, but when @Async
method returns void
it just doesn't execute, or at least it doesn't do what it is supposed to. Need to say, that in my async methods, I want to send an email using the JavaMailSender
, so no big deal. Here is the method:
@Async
override fun sendEmail(locale: Locale, subject: String, message: String, to: String) {
val msg = sender.createMimeMessage()
println("1")
val helper = MimeMessageHelper(msg, true, "utf-8")
helper.setFrom("email@example.com")
helper.setTo(to)
println("2")
helper.setSubject(getSubject(locale, null))
println("3")
helper.setText(processTemplate(locale, null, null), true)
println("4")
sender.send(msg)
println("5")
}
But an email never comes, there is no exception logged (I am running a testNG test).
When I change the signature of the function to make it return Future<String>
and add some dummy return line to the end of the function and then I call service.sendEmail(...).get()
, the method's body magically executes and the email arrives.
In my @Configuration
class, there is @EnableAsync
. I also implemented AsyncConfigurer
and provided own executor and exception handler, because I thought it could have been something with my executor bean definition, but nothing helps.
This is driving me crazy, because I just want to silently execute something in the background and it doesn't work. By silently I mean that I don't want to be bothered by exceptions thrown inside.
Do you have any ideas?
Update:
So as @pleft advised, I put some prints inside my method.
Now, when I run mvn clean test
, I can see that 1,2,3 are printed, but not each time. Sometimes just 1 and 2 got printed. I also put print in my AsyncUncaughtExceptionHandler
, but that one is not called.
It looks like the background thread is being killed too soon or something.
Update 2:
My ServiceConfig:
@Configuration
@EnableScheduling
@EnableAsync
@ComponentScan
class ServiceConfig : ApplicationContextAware, EnvironmentAware, AsyncConfigurer {
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
return AsyncUncaughtExceptionHandler { ex, method, params ->
println("Exception thrown")
}
}
override fun getAsyncExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 2
executor.maxPoolSize = 2
executor.setQueueCapacity(500)
executor.threadNamePrefix = "asyncThread"
executor.initialize()
return executor
}
}
/// other beans definitions..
Maybe it is important, maybe not, but I'm using Thymeleaf in that processTemplate
method.