ネイティブ・イメージでのリソースへのアクセス
デフォルトでは、native-image
ツールでは、生成時にクラスパスにあるリソースは最終イメージに統合されません。Class.getResource()
やClass.getResourceAsStream()
などのコール(または対応するClassLoader
メソッド)で特定のリソース(null
ではなく)を返すには、実行時にアクセス可能なリソースを指定する必要があります。これは、次の内容で構成ファイルを使用して実現できます:
{
"resources": {
"includes": [
{"pattern": "<Java regexp that matches resource(s) to be included in the image>"},
{"pattern": "<another regexp>"},
...
],
"excludes": [
{"pattern": "<Java regexp that matches resource(s) to be excluded from the image>"},
{"pattern": "<another regexp>"},
...
]
}
}
構成ファイルのパスは、-H:ResourceConfigurationFiles=/path/to/resource-config.json
を使用してnative-image
に渡す必要があります。または、native-image
に対して個々のリソース・パスを直接指定することもできます:
native-image -H:IncludeResources="<Java regexp that matches resources to be included in the image>" -H:ExcludeResources="<Java regexp that matches resources to be excluded from the image>" ...
-H:IncludeResources
および-H:ExcludeResources
オプションを複数回渡して、それぞれリソースを包含または除外するための複数の正規表現を定義できます。
ネイティブ実行可能ファイルに含まれるリソースを確認するには、オプション-H:Log=registerResource:<log level>
を使用します。<log level>
は、1
から5
の範囲(詳細が最も低いレベルから最も高いレベル)である必要があります。
使用例
次のプロジェクト構造を考えます:
my-app-root
└── src
├── main
│ └── com.my.app
│ ├── Resource0.txt
│ └── Resource1.txt
└── resources
├── Resource2.txt
└── Resource3.txt
この場合、次のようになります:
- すべてのリソースは、構成ファイルで
{"pattern":".*/Resource.*txt$"}
として指定された".*/Resource.*txt$"
を使用するか、コマンドラインで-H:IncludeResources=".*/Resource.*txt$"
を使用してロードできます。 Resource0.txt
は、.*/Resource0.txt$
を使用してロードできます。Resource0.txt
およびResource1.txt
は、.*/Resource0.txt$
および.*/Resource1.txt$
(またはかわりに単一の.*/(Resource0|Resource1).txt$
)を使用してロードできます。- また、
Resource2.txt
ファイルを除くすべてを含める場合は、-H:IncludeResources=".*/Resource.*txt$"
の後に-H:ExcludeResources=".*/Resource2.txt$"
を指定することによりそのファイルを除外できます。
次のデモでは、ネイティブ実行可能ファイルにリソースを含める方法について説明します。アプリケーションfortune
は、従来のfortune
UNIXプログラムをシミュレートします(詳細は、fortuneを参照)。
-
次のJavaコードをFortune.javaファイルに保存します:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Random; import java.util.Scanner; public class Fortune { private static final String SEPARATOR = "%"; private static final Random RANDOM = new Random(); private ArrayList<String> fortunes = new ArrayList<>(); public Fortune(String path) { // Scan the file into the array of fortunes Scanner s = new Scanner(new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(path)))); s.useDelimiter(SEPARATOR); while (s.hasNext()) { fortunes.add(s.next()); } } private void printRandomFortune() throws InterruptedException { int r = RANDOM.nextInt(fortunes.size()); //Pick a random number String f = fortunes.get(r); //Use the random number to pick a random fortune for (char c: f.toCharArray()) { // Print out the fortune System.out.print(c); Thread.sleep(100); } } public static void main(String[] args) throws InterruptedException { Fortune fortune = new Fortune("/fortunes.u8"); fortune.printRandomFortune(); } }
-
fortunes.u8リソース・ファイルを開き、Fortune.javaと同じディレクトリに保存します。
-
コンパイルします:
$JAVA_HOME/bin/javac Fortune.java
-
リソース・パスを指定してネイティブの実行可能ファイルをビルドします:
$JAVA_HOME/bin/native-image Fortune -H:IncludeResources=".*u8$"
-
実行可能ファイルのイメージを実行します:
./fortune
Javaリソースおよびその他の動的機能の構成支援に関するガイドも参照してください。
ロケール
イメージに含めるロケールと、デフォルトにするロケールを指定することもできます。たとえば、デフォルトのロケールをスイス・ドイツ語に切り替え、さらにフランス語と英語も含めるには、次のホスト型オプションを使用できます。
native-image -Duser.country=CH -Duser.language=de -H:IncludeLocales=fr,en
ロケールは言語タグを使用して指定します。-H:+IncludeAllLocales
を使用してすべてのロケールを含めることもできますが、そうすると結果のバイナリのサイズが大きくなることに注意してください。
ネイティブ・イメージのリソース・バンドル
Javaローカライゼーション・サポート(java.util.ResourceBundle
)を使用すると、JavaコードでL10Nリソースをロードし、時間のロケールやフォーマットなどの実際のランタイム設定に適したユーザー・メッセージを表示できます。
ネイティブ・イメージでは、生成されたバイナリでの使用に適したバンドルをロードおよび格納できるように、アプリケーションで必要なリソース・バンドルの事前認識が必要になります。バンドルは、リソース構成ファイル(前述の説明を参照)のbundles
セクションで指定できます:
{
"bundles": [
{"name":"your.pkg.Bundle"},
{"name":"another.pkg.Resource"},
{"name":"etc.Bundle"}
],
"resources": <see above>
}
または、次のようにバンドルをnative-image
のオプションとして直接指定することもできます:
native-image -H:IncludeResourceBundles=your.pgk.Bundle,another.pkg.Resource,etc.Bundle ...
デフォルトでは、要求されたすべてのロケールについて、要求されたバンドルが含められます。これを最適化するために、ロケール固有の部分文字列でIncludeResourceBundles
を使用できます。たとえば、-H:+IncludeResourceBundles=com.company.bundles.MyBundle_fr-FR
では、フランス語のバンドルのみが含められます。
Javaモジュールのリソース
リソースが<Java regexp that matches resources to be included in the image>
で指定されているか、リソース・バンドルがバンドル名で指定されている場合、これらのリソースまたはバンドルが取得する必要があるモジュールを正確に指定できます。これを行うには、resource-regexまたはバンドル名の前にmodule-nameを指定し、セパレータとして:
を指定します。たとえば:
{
"resources": {
"includes": [
{
"pattern": "library-module:^resource-file.txt$"
}
]
},
"bundles": [
{"name":"main-module:your.pkg.Bundle"}
]
}
これにより、native-imageには、Javaモジュールlibrary-module
からのresource-file.txt
のみが含まれます。そのため、他のモジュールまたはクラスパスにパターン^resource-file.txt$
に一致するリソースが含まれている場合でも、モジュールlibrary-module
内のリソースのみがimage-inclusionに登録されます。同じバンドル名your.pkg.Bundle
でアクセス可能な他のバンドルがある場合も同様で、main-module
のバンドルのみが含まれます。また、ネイティブ・イメージでは、モジュールがイメージ実行時にアクセス可能であることが保証されます。つまり、次のコード・パターン:
InputStream resource = ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourcePath);
は、前述のように登録されたリソースに対して常に期待どおりに機能します(モジュールに静的分析で到達可能と思われるコードが含まれていない場合でも)。
ローカライゼーションのJVMモード
リソース・バンドル・ルックアップは、JVMの多くのインフラストラクチャを利用する複雑で動的なメカニズムです。その結果、HelloWorld
などの小さいアプリケーションに対して実行可能ファイルのサイズが大きくなります。このため、デフォルトで最適化モードが設定され、すべてのバンドルが事前に認識されているという事実を利用してこのルックアップが簡略化されます。元のJVMルックアップを使用する場合は、-H:-LocalizationOptimizedMode
オプションを使用します。