I've met an error trying to send an email from ForkJoinPool common pool (CompletableFuture.runAsync(), ForkJoinPool.commonPool().execute(), ...).

The error I can reproduce is Provider for jakarta.activation.spi.MailcapRegistryProvider cannot be found but I also met a Not provider of jakarta.mail.util.StreamProvider was found.

  • It emerges only from packaged jar (including container env built with bootBuildImage). Running an app directly from IDE cause no error.
  • Changing JDK had no effect (tested with Temurin 17 JDK, Liberica 17 JDK and JRE).
  • Sending email from Kotlin coroutine thread pool cause no error.
  • Sending email from newly created pool cause no error (tested with Executors.newFixedThreadPool(8)).

I've made reference project freshly created by Spring initializr: https://github.com/turboezh/mail-test. Spring Boot version is 3.0.6.

Comment From: scottfrederick

Thanks for the report and the sample. I've been able to reproduce the Not provider of jakarta.mail.util.StreamProvider was found error.

This is the full stack trace when the error occurs:

java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
    at jakarta.mail.util.FactoryFinder.find(FactoryFinder.java:64)
    at jakarta.mail.util.StreamProvider.provider(StreamProvider.java:177)
    at jakarta.mail.Session.<init>(Session.java:254)
    at jakarta.mail.Session.getInstance(Session.java:323)
    at org.springframework.mail.javamail.JavaMailSenderImpl.getSession(JavaMailSenderImpl.java:163)
    at org.springframework.mail.javamail.JavaMailSenderImpl.createMimeMessage(JavaMailSenderImpl.java:341)
    at abc.email.test.EmailSender.sendSimple(EmailSender.java:33)
    at abc.email.test.EmailSender.lambda$test$0(EmailSender.java:59)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)

Spring Boot auto-configures a org.springframework.mail.javamail.JavaMailSenderImpl, but Boot is not in the code path when messages are sent as JavaMailSenderImpl is part of Spring Framework. Since jakarta.mail APIs are involved, the root cause may be out of the Spring Framework team's control also, but we'll need to transfer the issue so the Framework team can take a look.

I thought this might be a case of thread safety when the CompletableFuture invocation and the After async invocation run at the same time, but that does not appear to be the case. If the Before async and After async invocations are removed from the sample and a Thread.sleep(10000) is added after the CompletableFuture.runAsync returns, the error still happens.

Comment From: ztomic

It could be problem with class loader of ForkJoinPool, something like in https://github.com/spring-projects/spring-boot/issues/19427

Comment From: turboezh

It could be problem with class loader of ForkJoinPool

Yes, it seems it is the case.

Adding

Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());

before mail sending leads to Not provider of jakarta.mail.util.StreamProvider was found.

P.S.: Now I see tons of Classloader issue with CompletableFuture problems. It seems this makes the default CompletableFuture pool a bit useless. :smiling_face_with_tear:

Comment From: turboezh

Another news: I don't know why, but after changing dependency to com.sun.mail:jakarta.mail:2.0.1 it all works fine without errors. org.eclipse.angus:jakarta.mail:2.0.1 and 1.0.0 fails.

Comment From: evaldasu

Another news: I don't know why, but after changing dependency to com.sun.mail:jakarta.mail:2.0.1 it all works fine without errors. org.eclipse.angus:jakarta.mail:2.0.1 and 1.0.0 fails.

It is because com.sun.mail:jakarta.mail:2.0.1 doesn't have this.streamProvider = StreamProvider.provider(); in it's private Session constructor as opposed to org.eclipse.angus:jakarta.mail.

And the error Not provider of jakarta.mail.util.StreamProvider was found comes from StreamProvider.provider()

Comment From: dipsk2

Is there a fix or a workaround that someone found for this issue? We are facing the same.

Comment From: turboezh

@dipsk2, this one works for me:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-mail") {
        exclude("org.eclipse.angus", "jakarta.mail")
        implementation("com.sun.mail:jakarta.mail:2.0.1")
    }
}

I think a latest version will work too.

Comment From: bclozel

Closing this issue as this is indeed independent of Spring Framework as explained in this comment.

Comment From: jmehrens

JakartaMail PR 701 should fix this issue:

https://github.com/jakartaee/mail-api/pull/701