Oracle has released "GraalVM", which allows, among other things, to build a native application out of any java/scala code. The drop in resource consumption (specifically the RAM) is extremely useful for a micro-service architecture.
An example of the performance improvement was done here.
And some efforts were done to use it with Akka: here, which worked fine, if you do a bit of extra-work.
Regarding Play!, an investigation was done ~1 year ago, but no significant update since then, at least, no easy-to-find documentation to make Play! compiles as a native image.
Play 2.7.2 (also tested Play 2.8.0-M1, no difference)
Scala
Centos 7.4
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (build 1.8.0_212-20190420092731.buildslave.jdk8u-src-tar--b03)
OpenJDK GraalVM CE 19.0.0 (build 25.212-b03-jvmci-19-b01, mixed mode)
Native packager in the plugins.sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.22")
I also used the different dependencies described here.
sbt graalvm-native-image:packageBin
Should produce a valid native image, working the same way as "sbt run"
sbt graalvm-native-image:packageBin
produces the following output:
[info] Build on Server(pid: 11141, port: 36524)
[info] [toto:11141] classlist: 21,260.28 ms
[info] [toto:11141] (cap): 1,767.01 ms
[info] [toto:11141] setup: 2,388.54 ms
[error] SLF4J: Class path contains multiple SLF4J bindings.
[error] SLF4J: Found binding in [jar:file:/root/.ivy2/cache/org.slf4j/slf4j-jdk14/jars/slf4j-jdk14-1.7.26.jar!/org/slf4j/impl/StaticLoggerBinder.class]
[error] SLF4J: Found binding in [jar:file:/root/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
[error] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
[error] SLF4J: Actual binding is of type [org.slf4j.impl.JDK14LoggerFactory]
[error] warning: unknown locality of class Lplay/api/ApplicationLoader$JavaApplicationLoaderAdapter$1;, assuming class is not local. To remove the warning report an issue to the library or language author. The issue is caused by Lplay/api/ApplicationLoader$JavaApplicationLoaderAdapter$1; which is not following the naming convention.
[info] [toto:11141] (typeflow): 13,441.84 ms
[info] [toto:11141] (objects): 17,188.40 ms
[info] [toto:11141] (features): 1,067.56 ms
[info] [toto:11141] analysis: 32,508.22 ms
[info] [toto:11141] (clinit): 1,399.26 ms
[info] [toto:11141] universe: 2,136.28 ms
[info] [toto:11141] (parse): 1,952.24 ms
[info] [toto:11141] (inline): 4,481.05 ms
[info] [toto:11141] (compile): 20,128.95 ms
[info] [toto:11141] compile: 27,921.33 ms
[info] [toto:11141] image: 2,155.43 ms
[info] [toto:11141] write: 496.31 ms
[info] [toto:11141] [total]: 89,001.17 ms
[success] Total time: 127 s, completed Jun 18, 2019 3:54:19 PM
Even if the final output says "[success]", if you try to launch the generated application:
[root@localhost toto]# target/graalvm-native-image/toto
Oops, cannot start the server.
@7c8imkl6p: Cannot load play.application.loader[play.application.loader [play.api.inject.guice.GuiceApplicationLoader] was not loaded.]
at play.utils.Reflect$.loadClass$1(Reflect.scala:132)
at play.utils.Reflect$.configuredClass(Reflect.scala:145)
at play.api.ApplicationLoader$.apply(ApplicationLoader.scala:166)
at play.core.server.ProdServerStart$.start(ProdServerStart.scala:58)
at play.core.server.ProdServerStart$.main(ProdServerStart.scala:31)
at play.core.server.ProdServerStart.main(ProdServerStart.scala)
Caused by: java.lang.ClassNotFoundException: play.api.inject.guice.GuiceApplicationLoader
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:51)
at java.lang.ClassLoader.loadClass(Target_java_lang_ClassLoader.java:131)
at play.utils.Reflect$.loadClass$1(Reflect.scala:126)
... 5 more
Project: https://github.com/gilles-degols/play-graal/
This is a very basic Play project generated automatically, from which I removed every service, controller and filter. The idea is first to compile it as simple as possible, then gradually try more advanced features of Play! with it.
Hi @gilles-degols.
This is not in our short-term plan but is definitely an interesting investigation.
actually the problem here is that you use Guice @gilles-degols if you use play without guys it will work if you include everything necessary inside the image. with guice you basically need to add all classes to the image that will be included dynamically.
@schmitch you are right! By using a project here not relying on Guice, and meddling a bit with it I was able to compile & run the native-image.
I guess we can close this ticket, but it would be interesting for the future to update the initial post you wrote here, to give hope to the next people trying to use Play! as native-image. The error you had last time still appears for me, but I can run the compiled image just fine afterwards (and no problem for scala 2.12.8).
Thank you very much again for your initial blog post & this comment!
Most helpful comment
@schmitch you are right! By using a project here not relying on Guice, and meddling a bit with it I was able to compile & run the native-image.
I guess we can close this ticket, but it would be interesting for the future to update the initial post you wrote here, to give hope to the next people trying to use Play! as native-image. The error you had last time still appears for me, but I can run the compiled image just fine afterwards (and no problem for scala 2.12.8).
Thank you very much again for your initial blog post & this comment!