Affected version: Spring Boot 3.0.0.

Repository with minimal example: https://github.com/Tienisto/spring-boot-mail-aot-demo

The following code does not work in AOT mode. (It works in JVM)

public void sendMail() throws MessagingException {
  final MimeMessage message = mailSender.createMimeMessage();

  // final MimeMessageHelper helper = new MimeMessageHelper(message, false); <-- this works
  final MimeMessageHelper helper = new MimeMessageHelper(message, true);

  helper.setFrom(sender);
  helper.setTo(recipient);
  helper.setSubject("My Subject");
  helper.setText("Hello World");

  mailSender.send(message);
}

Stacktrace:

2022-12-18T23:18:12.488+01:00 ERROR 20108 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.mail.MailSendException: Failed messages: jakarta.mail.MessagingException: IOException while sending message;
  nested exception is:
        jakarta.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed;
        boundary="----=_Part_0_1083492086.1671401891772"; message exceptions (1) are:
Failed message 1: jakarta.mail.MessagingException: IOException while sending message;
  nested exception is:
        jakarta.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed;
        boundary="----=_Part_0_1083492086.1671401891772"] with root cause

org.springframework.mail.MailSendException: Failed messages: jakarta.mail.MessagingException: IOException while sending message;
  nested exception is:
        jakarta.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed;
        boundary="----=_Part_0_1083492086.1671401891772"
        at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:491) ~[demo.exe:6.0.2]
        at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:361) ~[demo.exe:6.0.2]
        at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:356) ~[demo.exe:6.0.2]
        at com.example.demo.MailService.sendMail(MailService.java:37) ~[demo.exe:na]
        at com.example.demo.MyController.sendMail(MyController.java:17) ~[demo.exe:na]
        at java.base@17.0.5/java.lang.reflect.Method.invoke(Method.java:568) ~[demo.exe:na]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207) ~[demo.exe:6.0.2]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1003) ~[demo.exe:6.0.2]
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:895) ~[demo.exe:6.0.2]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:705) ~[demo.exe:6.0]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:880) ~[demo.exe:6.0.2]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814) ~[demo.exe:6.0]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[demo.exe:10.1.1]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[demo.exe:6.0.2]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[demo.exe:6.0.2]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[demo.exe:6.0.2]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[demo.exe:6.0.2]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[demo.exe:6.0.2]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[demo.exe:6.0.2]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[na:na]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[na:na]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[demo.exe:10.1.1]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[na:na]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[demo.exe:10.1.1]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[na:na]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[na:na]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400) ~[na:na]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[demo.exe:10.1.1]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) ~[na:na]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739) ~[na:na]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[demo.exe:10.1.1]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[na:na]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[na:na]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[na:na]
        at java.base@17.0.5/java.lang.Thread.run(Thread.java:833) ~[demo.exe:na]

Comment From: sbrannen

Do you mean AOT mode on the JVM?

Or do you mean it fails when running in AOT mode within a GraalVM native image?

If the latter, it might be due to missing resources located in the META-INF folder in the jakarta.mail artifact, in which case you could try registering those resources: META-INF/jakarta.* and META-INF/mailcap via Spring's RuntimeHints.resources() support.

Comment From: Tienisto

I mean GraalVM sorry

Comment From: sdeleuze

I thought initially it was due to spring-projects/spring-boot#33059 which changes the coordinates of the dependency we use, but I get the same issue if I use `com.sun.mail:jakarta.mail:2.0.1' so that should be something else. I am investigating.

Comment From: sdeleuze

It was another occurrence of hidden reflection. I was able to find it running the native application with build/native/nativeCompile/demo -Djakarta.activation.debug=true.

META-INF/mailcap contains entries like multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed; x-java-fallback-entry=true, and despite the unusual case, com.sun.mail.handlers.multipart_mixed is a class instantiated reflectively!

So it worked after adding this META-INF/native-image/reflect-config.json file:

[
  {
    "name": "com.sun.mail.handlers.multipart_mixed",
    "allDeclaredConstructors": true
  },
  {
    "name": "com.sun.mail.handlers.text_plain",
    "allDeclaredConstructors": true
  },
  {
    "name": "com.sun.mail.handlers.text_xml",
    "allDeclaredConstructors": true
  },
  {
    "name": "com.sun.mail.handlers.message_rfc822",
    "allDeclaredConstructors": true
  }
]

I will create a related PR in https://github.com/oracle/graalvm-reachability-metadata to add those missing reflection entries + tests.

Comment From: sdeleuze

Will be fixed once https://github.com/oracle/graalvm-reachability-metadata/pull/160/files is merged.