We are trying to migrate parts of our monolith to microservices and we decided to go with micronaut. We are extracting now an email service which given some inputs will render email and talk with an SMTP server to send them.
For that, we use Java Mail which seems to have some problems when we try to create a GraalVM docker image. Has anyone managed to get this right or is it simply impossible to make it work?
Some additional information:
Dockerfile:
FROM oracle/graalvm-ce:19.2.0 as graalvm
COPY . /home/app/email-service
WORKDIR /home/app/email-service
RUN gu install native-image
RUN native-image --no-server -cp build/libs/email-service-*.jar
FROM frolvlad/alpine-glibc
EXPOSE 8080
COPY --from=graalvm /home/app/email-service .
ENTRYPOINT ["./email-service"]
native-image.properties:
Some other "lazy" initializations are in place for JDBC driver, redis, kafka, and thymeleaf.
Args = --initialize-at-run-time=io.micronaut.views.thymeleaf.ThymeleafFactory \
--initialize-at-run-time=io.micronaut.views.thymeleaf.ThymeleafViewsRenderer \
--initialize-at-run-time=io.micronaut.views.velocity.VelocityViewsRenderer \
--initialize-at-run-time=io.micronaut.configuration.lettuce.session.$RedisHttpSessionConfigurationDefinition \
--initialize-at-run-time=io.micronaut.configuration.kafka.embedded.KafkaEmbedded \
--initialize-at-run-time=oracle.jdbc.driver.OracleDriver \
--initialize-at-run-time=java.sql.DriverManager \
--initialize-at-run-time=org.hibernate.jpa.HibernatePersistenceProvider \
--initialize-at-run-time=com.sun.mail.util.MailLogger \
-H:IncludeResources=logback.xml|application.yml \
-H:Name=email-service \
-H:Class=com.acme.MySuperDuperApplication
Micronaut Version: 1.2.0
Java Mail Version: 1.6.2 (com.sun.mail:javax.mail:1.6.2
)
Native Image Compilation Error:
Warning: Aborting stand-alone image build. com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of com.sun.mail.util.MailLogger are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace:
at parsing javax.mail.internet.MailDateFormat.access$000(MailDateFormat.java:149)
Call path from entry point to javax.mail.internet.MailDateFormat.access$000():
at javax.mail.internet.MailDateFormat.access$000(MailDateFormat.java:149)
at javax.mail.internet.MailDateFormat$AbstractDateParser.parse(MailDateFormat.java:426)
at javax.mail.internet.MailDateFormat.parse(MailDateFormat.java:251)
at java.text.DateFormat.parse(DateFormat.java:364)
at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:49)
at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:33)
at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.parse(BuiltInsForMultipleTypes.java:204)
at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.get(BuiltInsForMultipleTypes.java:167)
at freemarker.ext.beans.HashAdapter.get(HashAdapter.java:73)
at freemarker.ext.beans.HashAdapter$1$1$1.getValue(HashAdapter.java:124)
at sun.security.ssl.SSLSocketImpl$NotifyHandshakeThread.run(SSLSocketImpl.java:2687)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:460)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)
After trying a few different things I'm posting what finally worked for us so that others might be helped.
Firstly we upgraded to the latest version of micronaut (2.3.4
at the moment) and also we used the latest version of com.sun.mail:jakarta.mail
(2.0.0
at the moment).
Secondly our nativeImage args were the following (omitting some for other dependencies):
--report-unsupported-elements-at-runtime
-H:+ReportExceptionStackTraces
-H:-DeleteLocalSymbols
-H:+PreserveFramePointer
-H:IncludeResources=META-INF/mailcap
-H:IncludeResources=META-INF/mailcap.default
-H:IncludeResources=META-INF/javamail.default.address.map
-H:IncludeResources=META-INF/javamail.charset.map
-H:IncludeResources=META-INF/javamail.default.providers
-H:IncludeResources=META-INF/services/javax.mail.Provider
Then we had to mark a few classes for introspection using @TypeHint
:
@TypeHit({
SMTPTransport.class,
MimeMultipart.class,
MailcapCommandMap.class,
text_html.class,
multipart_mixed.class,
handler_base.class,
image_gif.class,
image_jpeg.class,
message_rfc822.class,
text_xml.class,
text_plain.class
})
Lastly, we had an SMTP Sender bean on which we had to configure mailcap (so perhaps the resource inclusion doesn't work that well):
@PostConstruct
public void initialize() {
MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
CommandMap.setDefaultCommandMap(mc);
}
With all those in place, we were able to create a native image that could send emails without any issues.