5

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)
George Karanikas
  • 1,244
  • 1
  • 12
  • 38
  • Did you try assisted configuration ? This will do an analysis of your application and generate configuration files for reflection, resources, proxy and JNI. More details : https://github.com/oracle/graal/blob/master/substratevm/CONFIGURE.md – Taha BASRI Dec 26 '19 at 10:02

2 Answers2

0

Not sure it might be enough, but you could try to initialize com.sun.mail.util.MailLogger at build-time passing the additional --initialize-at-build-time=com.sun.mail.util.MailLogger as argument

This will preload the class before runtime, which may solve or not the compilation issue

On a side-note, I guess you can chain your runtime initialization arguments in a single comma-separated list, i.e.

--initialize-at-runtime=io.micronaut.views.thymeleaf.ThymeleafFactory,io.micronaut.views.thymeleaf.ThumeleafViewsRenderer, ...
pagoda_5b
  • 7,333
  • 1
  • 27
  • 40
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.

George Karanikas
  • 1,244
  • 1
  • 12
  • 38