Javaアプリケーション配布方法 ~jlink ランタイム作成~

jlinkでランタイム用のイメージを作成する。

この記事では、Java9以降のモジュールシステム(JPMS) を使ったアプリケーションを
JIMAGE(カスタムランタイム)という形で配布する方法について解説します。

JIMAGE形式では、配布先のPCにあらかじめ、Javaをインストールしておかなくても、
配布したファイルだけで、実行が可能になります。

Javaアプリケーションの配布方法

従来までは、配布先にJava(JRE)をあらかじめインストールしておいてもらい
作成したアプリケーション(jarファイルなど)を配布していました。

しかし、依存するJavaバージョンが異なるアプリケーションを複数併用したい場合、
すぐにJavaバージョンを上げる事ができません。

そこで、オラクルはJavaのアプリケーション配布方法として、次の方法を提唱しています。
アプリケーション開発者は、JIMAGE(カスタムランタイム)を生成して、
アプリケーション実行に必要なランタイムを内包した状態で配布する事。

Java9以降では、Javaコード自体がいくつかのモジュールにまとめらています。
そのおかげで、Javaはアプリケーションに必要最低限のランタイムコードだけをビルドする事ができるようになりました。
従来よりも、ランタイム配布サイズが小さくなります。

そして、Java11以降では、
オラクルが提供するOpenJDKから、JREのダウンロード提供はされなくなりました。

JIMAGEとは

いわゆるカスタムランタイムの事です。
イメージといっても、単一のファイルではない。
いくつかのディレクトリと、バイナリファイル郡が含まれます。

作成方法は、jlinkコマンドを使って、
リンクするモジュール(jmod, またはjarファイル)を指定するするだけで完成します。

依存するモジュールと、起点(ルート)になるモジュールを指定するだけです。

JIMAGEのファイル構成

先に成果物として出力される、
JIMAGE (カスタムランタイム)のファイル構成をお見せします。

$ tree -L 2 .
.
├── bin
│   ├── java
│   └── keytool
├── conf
│   ├── net.properties
│   └── security
├── include
│   ├── classfile_constants.h
│   ├── darwin
│   ├── jni.h
│   ├── jvmti.h
│   └── jvmticmlr.h
├── legal
│   └── java.base
├── lib
│   ├── classlist
│   ├── jli
│   ├── jrt-fs.jar
│   ├── jspawnhelper
│   ├── jvm.cfg
│   ├── libjava.dylib
│   ├── libjimage.dylib
│   ├── libjsig.dylib
│   ├── libnet.dylib
│   ├── libnio.dylib
│   ├── libosxsecurity.dylib
│   ├── libverify.dylib
│   ├── libzip.dylib
│   ├── modules
│   ├── security
│   ├── server
│   └── tzdb.dat
└── release

JREと似たような構成になっています。

binディレクトリには、javaコマンドがある。
このjavaコマンドには、jlinkで指定したモジュール群が含まれています。

アプリケーションの実行に必要な、モジュールはすべてこのディレクトリ構成に含まれているので、
これらをZip化して配布するだけで、配布先で実行することができます。

jlink コマンドの使い方

jlinkコマンドには、多くのオプションがありますが、
基本的には、モジュールの場所を指定して、起点になるルートモジュールを指定するだけです。

$ jlink -h

Usage: jlink <options> --module-path <modulepath> --add-modules <module>[,<module>...]
Possible options include:
      --add-modules <mod>[,<mod>...]    Root modules to resolve
      --bind-services                   Link in service provider modules and
                                        their dependences
  -c, --compress=<0|1|2>                Enable compression of resources:
                                          Level 0: No compression
                                          Level 1: Constant string sharing
                                          Level 2: ZIP
      --disable-plugin <pluginname>     Disable the plugin mentioned
      --endian <little|big>             Byte order of generated jimage
                                        (default:native)
  -h, --help, -?                        Print this help message
      --ignore-signing-information      Suppress a fatal error when signed
                                        modular JARs are linked in the image.
                                        The signature related files of the
                                        signed modular JARs are not copied to
                                        the runtime image.
      --launcher <name>=<module>[/<mainclass>]
                                        Add a launcher command of the given
                                        name for the module and the main class
                                        if specified  
      --limit-modules <mod>[,<mod>...]  Limit the universe of observable
                                        modules
      --list-plugins                    List available plugins
  -p, --module-path <path>              Module path
      --no-header-files                 Exclude include header files
      --no-man-pages                    Exclude man pages
      --output <path>                   Location of output path
      --post-process-path <imagefile>   Post process an existing image
      --resources-last-sorter <name>    The last plugin allowed to sort
                                        resources
      --save-opts <filename>            Save jlink options in the given file
  -G, --strip-debug                     Strip debug information
      --suggest-providers [<name>,...]  Suggest providers that implement the
                                        given service types from the module path
  -v, --verbose                         Enable verbose tracing
      --version                         Version information
      @<filename>                       Read options from file

ここでは特に重要なオプションだけ説明しておきます。
jlinkには依存するモジュール(jmod, またはモジュール化したjarファイル)の場所を指定します。

--module-path

依存するモジュールを含めて、jmodファイルの場所を指定します。
(ディレクトリPATHでも、ファイルPATHでもOK)

コロンで複数指定可能。

jmodファイルじゃなくても、モジュール化したjarファイルを使ってもOK。

--add-modules

起点となる、ルートのモジュール名を指定する。(最低1つは必要)
ファイルPATHじゃなくて、モジュール名。

複数指定することも可能。(カンマで区切る)

jlinkは、ルートモジュールを起点に依存するモジュールを探索する。
ルートモジュール、依存モジュールが全て、
--module-pathで指定した場所に存在する必要がある。
($JAVA_HOME/jmods は暗黙的に含まれている)

--launcher

--launcher スクリプト名称=モジュール名/メインクラス名

binディレクトリに、起動用のスクリプト生成してくれる。
Linux/Macの場合、shellスクリプト。
Windowsなら、batファイルが生成される。

ランタイムイメージを作ってみよう

1. モジュール化したjarファイルを用意

module-info.java を含めてモジュール化したjarファイルを作成しましょう。

2. jmodファイルを作成

jlinkする際は、自分で作成したアプリケーションのJMODファイルか、
ネイティブコードに依存しないのであれば、モジュール化したjarファイルだけで大丈夫です。

今回はせっかくなので、jmodファイルにしておきます。

jmodファイルを作成するには、jmodコマンドを使います

mkdir -p out/jmods
jmod create \
    --class-path build/libs/sample-1.0-SNAPSHOT.jar \
    out/jmods/net.tyablog.sample.jmod

class-pathに指定してるのが、モジュール化(module-info.javaを含む)したjarファイルです。
出力ファイルは、モジュール名称と同じにしておくのが間違いありません。
拡張子は.jmodを使います。

3. jlink でイメージを作成

mkdir -p out/jimage
jlink \
    --module-path out/jmods \
    --add-modules net.tyablog.sample \
    --compress=2 \
    --no-header-files \
    --no-man-pages \
    --output out/jimage

4. 完成!

binディレクトリのjavaコマンドにはすでにモジュールが組み込まれた状態になっています。
実行モジュール名/メインクラス名を指定するだけで、実行可能な状態。

$ ./out/jimage/bin/java -m net.tyablog.sample/net.tyablog.sample.Sample
hello

起動スクリプトの用意 (launcherオプション)

--launcherオプションを指定すると、起動用のスクリプトを生成してくれます。
スクリプトの内容は上記のモジュール名/メインクラス名を指定しただけのものです。

jlink \
    --module-path out/jmods \
    --add-modules net.tyablog.sample \
    --launcher sample=net.tyablog.sample/net.tyablog.sample.Sample \
    --compress=2 \
    --no-header-files \
    --no-man-pages \
    --output out/jimage

さっきと違うのはこの部分。
--launcher sample=net.tyablog.sample/net.tyablog.sample.Sample

sampleとなってるところが、スクリプト名になるので、お好きな名称で大丈夫。 

$ ./out/jimage/bin/sample
hello

まとめ

  • Java9以降のアプリケーションは、JIMAGE(カスタムランタイム)を生成して同梱した状態で配布ができる。
    オラクルが提唱しているが、javaのセキュリティアップデートなどは
    アプリケーション配布者が責任をもつ必要がある。
    (更新物の配布など)

  • JIMAGEは、アプリケーションに必要なモジュールだけが入ったバイナリファイル群。
    アプリケーションのクラスファイルや、依存モジュールが全て含まれている。
    そのままだと、ディレクトリ構造になっているので、
    Zipでアーカイブして配布するのが良い。

  • jlinkコマンドは、ルートモジュール(--add-modules)を起点に、依存モジュールを探索する。

  • --launcherオプションで、起動ようのスクリプトを生成することができる。
    中身は、javaコマンドにJVMオプション、実行モジュール名/メインクラス名を指定している。

1件のコメント

コメントを残す