LoginSignup
12
5

More than 5 years have passed since last update.

Javaの組み込みWebサーバ3つ、Tomcat、Jetty、Undertowのうち、GraalVMで動作したのは?

Posted at

GraalVMをご存知ですか?Javaをネイティブコンパイルし、ケースによってはメモリ消費を1/16に押さえてくれるツールです。
ただし、GraalVMがコンパイルできるアプリケーションには制約があり、すべてのJavaアプリケーションがネイティブコンパイルできるわけではありません。

今回はネイティブなWebアプリケーションを作成するために、Webサーバを組み込んだJavaアプリケーションがGraalVMでコンパイル、実行できるかを検証してみました。

施行

gradle shadowを使ってFat Jarを作成、GraalVMのnative-imageコマンドにて-jarオプションでFat Jarを指定してコンパイルします。

環境

$ native-image --version
GraalVM Version 1.0.0-rc13
$ java -version
openjdk version "1.8.0_202"
OpenJDK Runtime Environment (build 1.8.0_202-20190206132754.buildslave.jdk8u-src-tar--b08)
OpenJDK GraalVM CE 1.0.0-rc13 (build 25.202-b08-jvmci-0.55, mixed mode)

Tomcat(tomcat-embed-core v9.0.17)

だめでした。

ソース

ソース抜粋
public static void main(String[] args) throws Exception {
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8080);
    tomcat.getConnector();
    File base = new File("src/main/static/");
    Context context = tomcat.addContext("/app", base.getAbsolutePath());
    Tomcat.addServlet(context, "default", new DefaultServlet()).addMapping("/");

    Tomcat.addServlet(context, "hello", new HttpServlet() {
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            Writer w = resp.getWriter();
            w.write("Hello, World!");
            w.flush();
        }
    }).addMapping("/hello");
    tomcat.start();
    tomcat.getServer().await();
}

コンパイルに失敗。

$ native-image  -jar build/libs/app-all.jar
Build on Server(pid: 2099, port: 50704)
[app-all:2099]    classlist:   1,259.46 ms
[app-all:2099]        (cap):   1,512.61 ms
[app-all:2099]        setup:   7,054.04 ms
[app-all:2099]     analysis:  10,298.20 ms
Error: unsupported features in 2 methods
Detailed message:
Error: Class initialization failed: com.sun.naming.internal.ResourceManager$AppletParameter
Original exception that caused the problem: java.lang.ExceptionInInitializerError
...(省略)

残念。

Jetty(jetty-server v9.4.15.v20190215)

行けました!

ソース抜粋
public static void main(String[] args) throws Exception {
    Server server = new Server(8080);

    server.setHandler(new AbstractHandler() {

        @Override
        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
            response.setContentType("text/plain");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out = response.getWriter();
            out.println("Hello World!");
            baseRequest.setHandled(true);
        }
    });

    server.start();
    server.join();
}

コンパイル & 実行

$ native-image -jar build/libs/app-all.jar
Build on Server(pid: 2523, port: 51183)*
[app-all:2523]    classlist:   3,325.81 ms
[app-all:2523]        (cap):   1,309.95 ms
[app-all:2523]        setup:   8,400.08 ms
2019-04-04 22:16:54.214:INFO::ForkJoinPool-2-worker-3: Logging initialized @22713ms to org.eclipse.jetty.util.log.StdErrLog
[app-all:2523]   (typeflow):  11,805.43 ms
[app-all:2523]    (objects):   9,466.98 ms
[app-all:2523]   (features):     354.09 ms
[app-all:2523]     analysis:  37,080.95 ms
[app-all:2523]     universe:     650.05 ms
[app-all:2523]      (parse):   3,276.85 ms
[app-all:2523]     (inline):   6,226.93 ms
[app-all:2523]    (compile):  31,668.53 ms
[app-all:2523]      compile:  57,307.38 ms
[app-all:2523]        image:   2,385.43 ms
[app-all:2523]        write:     859.86 ms
[app-all:2523]      [total]: 145,307.12 ms
$ ./app-all
2019-04-04 22:19:55.501:INFO:oejs.Server:main: jetty-9.4.z-SNAPSHOT; built: 2019-02-15T16:53:49.381Z; git: eb70b240169fcf1abbd86af36482d1c49826fa0b; jvm 1.8.0_202
2019-04-04 22:19:55.502:INFO:oejs.AbstractConnector:main: Started ServerConnector@6b64b5db{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2019-04-04 22:19:55.502:INFO:oejs.Server:main: Started @2ms

無事起動!ブラウザでアクセスもできました!
すばらしい。

Undertow(undertow-core v2.0.19.Final)

だめでした。。。

ソース抜粋
public static void helloWorldHandler(HttpServerExchange exchange) {
    exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, "text/plain");
    exchange.getResponseSender().send("Hello World!");
}

public static void main(String[] args) {
    Undertow server = Undertow.builder().addHttpListener(8080, "0.0.0.0", App::helloWorldHandler).build();
    server.start();
}

コンパイル。

$ native-image -jar build/libs/app-all.jar
Build on Server(pid: 2523, port: 51183)
[app-all:2523]    classlist:   2,361.03 ms
[app-all:2523]        (cap):   1,720.81 ms
[app-all:2523]        setup:   7,458.13 ms
[app-all:2523]     analysis:   6,485.28 ms
Error: com.oracle.graal.pointsto.constraints.UnresolvedElementException: Discovered unresolved type during parsing: org.osgi.framework.FrameworkUtil. To diagnose the issue you can use the --allow-incomplete-classpath option. The missing type is then reported at run time when it is accessed the first time.

その後、--allow-incomplete-classpathオプションを付けたりなんだりしたのですが、どうしてもコンパイルできませんでした。残念。

というわけで正解はJettyでした。

一つでもコンパイルできたものがあったということで、成果ありです。1/16のメモリリソースでのWebアプリ運用の光が見えてきました!
あとは、JPAが動けば、簡単なWebアプリケーションは作れそうですね。QuarkusでJPAが動いているようなので、コンパイルできることを期待しています。

12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5