Micronaut 和 Java 邮件 API

Micronaut and Java Mail API

我们正在尝试将单体应用程序的一部分迁移到微服务,因此我们决定使用 micronaut。我们现在正在提取一个电子邮件服务,它提供一些输入将呈现电子邮件并与 SMTP 服务器交谈以发送它们。

为此,我们使用 Java 邮件,当我们尝试创建 GraalVM docker 图像时,它似乎有一些问题。有没有人设法做到这一点,或者根本不可能让它发挥作用?

一些附加信息:

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"]

原生-image.properties:

"lazy" 驱动程序、redis、kafka 和 thymeleaf 的一些其他 "lazy" 初始化已经到位。

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 版本: 1.2.0
Java 邮件版本: 1.6.2 (com.sun.mail:javax.mail:1.6.2)

本机图像编译错误:

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[=14=]0(MailDateFormat.java:149)
Call path from entry point to javax.mail.internet.MailDateFormat.access[=14=]0(): 
    at javax.mail.internet.MailDateFormat.access[=14=]0(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.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)

不确定这是否足够,但您可以尝试在构建时初始化 com.sun.mail.util.MailLogger --initialize-at-build-time=com.sun.mail.util.MailLogger 作为参数

这将在运行前预加载class,这可能解决或不解决编译问题

附带说明一下,我想您可以将运行时初始化参数链接到一个逗号分隔的列表中,即

--initialize-at-runtime=io.micronaut.views.thymeleaf.ThymeleafFactory,io.micronaut.views.thymeleaf.ThumeleafViewsRenderer, ...

在尝试了一些不同的东西之后,我发布了最终对我们有用的东西,这样其他人可能会得到帮助。

首先我们升级到最新版本的micronaut(目前2.3.4),同时我们使用了最新版本的com.sun.mail:jakarta.mail(目前2.0.0)。 其次,我们的 nativeImage args 如下(省略了一些其他依赖项):

--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

然后我们不得不标记一些 类 用于自省使用 @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
})

最后,我们有一个 SMTP Sender bean,我们必须在其上配置 mailcap(因此资源包含可能效果不佳):

@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);
}

有了所有这些,我们就能够创建一个可以毫无问题地发送电子邮件的原生图像。