Resources
Resources are non-code files in Java apps. Typical resources include configuration files in various formats and data files.
Resource Files
By default, the native image build doesn't integrate any of the resources. So method calls to get resources will always return null
. This includes the following methods:
Class.getResource()
Class.getResourceAsStream()
ClassLoader.getResource()
ClassLoader.getResourceAsStream()
ClassLoader.getResources()
All resources that should be accessible at image run time need to be explicitly specified. This can done by provided resource configuration files with the -H:ResourceConfigurationFiles
option or the -H:IncludeResources
option.
Configuration
The configuration file is a JSON file that specifies included and excluded files. Paths of resources are matched with regular expression patterns.
Refer to the JSON schema of resource config.
The code below reads a text file and displays its content. When running in JVM mode, the code can successfully load the input.txt
file in the current directory.
package io.vividcode.graalvm.resources;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
public class ReadResourceContent {
public static void main(String[] args) throws IOException {
InputStream stream =
Objects.requireNonNull(ReadResourceContent.class.getResourceAsStream("/input.txt"));
String content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(content);
}
}
When running in native mode, the code throws NullPointerException
, because the InputStream
object is null
. To solve this issue, a resource config file is used to specify the resource to include.
{
"resources": {
"includes": [
{
"pattern": "input.txt"
}
]
}
}
This resource config file is passed to native-image
.
native-image io.vividcode.graalvm.resources.ReadResourceContent \
-H:ResourceConfigurationFiles=./resource-config.txt read-resource-content
If only a few resources need to be included or excluded, the -H:IncludeResources
and -H:ExcludeResources
options can be used instead of a config file. These two options can be used multiple times.
native-image io.vividcode.graalvm.resources.ReadResourceContent \
-H:IncludeResources=input.txt read-resource-content
If there are a large number of resources to be specified, we can use a pattern like ^.*\.(csv|yaml|yml|txt)$
to include many resources.
Debug Resource Registration
If some resources are not included or excluded as expected, using the -H:Log=registerResource:3
option to show related logs.
native-image io.vividcode.graalvm.resources.ReadResourceContent \
-H:ResourceConfigurationFiles=./resource-config.txt \
-H:Log=registerResource:3 \
read-resource-content
Below is the logs from native-image
. It shows that the input.txt
resource is registered.
[thread:1] scope: main
[thread:1] scope: main.registerResource
ServiceLoaderFeature: registerResource: META-INF/services/java.nio.file.spi.FileSystemProvider
[thread:1] scope: main.registerResource
ServiceLoaderFeature: registerResource: META-INF/services/jdk.internal.logger.DefaultLoggerFinder
[thread:1] scope: main.registerResource
ServiceLoaderFeature: registerResource: META-INF/services/sun.util.resources.LocaleData$CommonResourceBundleProvider
[thread:1] scope: main.registerResource
ServiceLoaderFeature: registerResource: META-INF/services/sun.util.resources.LocaleData$SupplementaryResourceBundleProvider
[thread:1] scope: main.registerResource
Resources have been added by ServiceLoaderFeature. Automatic registration can be disabled with -H:-UseServiceLoaderFeature
[thread:1] scope: main.registerResource
ResourcesFeature: registerResource: java.base:java/lang/uniName.dat
[thread:1] scope: main.registerResource
ResourcesFeature: registerResource: input.txt
Locales
Default Locale
To reduce the size of native images, only the default locale is included by default. The default locale is determined by the JVM which runs the native image generation. The default locale can be changed using the user.country
and user.language
system properties, or the -H:DefaultLocale
option.
The -H:DefaultLocale
option is already deprecated, user.country
and user.language
system properties should be used instead.
The code below displays the default locale.
package io.vividcode.graalvm.resources;
import java.util.Locale;
public class DefaultLocale {
public static void main(String[] args) {
System.out.println(Locale.getDefault());
}
}
The code below shows how to set the default locale when running native-image
.
native-image io.vividcode.graalvm.resources.DefaultLocale \
-Duser.country=CN -Duser.language=zh default-locale
Include Locales
Extra locales can also be included using the -H:IncludeLocales
option.
The code below tries to display the country name of China using four different locales.
package io.vividcode.graalvm.resources;
import java.util.List;
import java.util.Locale;
public class Locales {
public static void main(String[] args) {
List.of(Locale.ITALY, Locale.FRANCE, Locale.US, Locale.CHINA)
.forEach(
locale ->
System.out.printf(
"Locale -> %s, Country name -> %s%n",
locale, Locale.CHINA.getDisplayCountry(locale)));
}
}
When executing the native image, it shows the following exception about missing resource bundles.
Exception in thread "main" java.util.MissingResourceException: Can't find bundle for base name sun.util.resources.LocaleNames, locale it_IT
To fix this issue, we can use the -H:IncludeLocales
to add extra locales.
native-image io.vividcode.graalvm.resources.Locales -H:IncludeLocales=it,fr,en,zh locales
Another choice is using the -H:+IncludeAllLocales
option to include all locales.
The -H:+IncludeAllLocales
option can increase the size of the native image. With this option, the size increases by about 10MB
.
Resource Bundles
Resource bundles can be specified using the resource configuration file or the -H:IncludeResourceBundles
option.
In the code below, the value of message
key from the resource bundle AppResource
is displayed. The locale can be changed using program arguments.
package io.vividcode.graalvm.resources;
import java.util.Locale;
import java.util.ResourceBundle;
public class ResourceBundles {
public static void main(String[] args) {
Locale locale = args.length > 1 ? new Locale(args[0], args[1]) : Locale.getDefault();
System.out.println(ResourceBundle.getBundle("AppResource", locale).getString("message"));
}
}
The code below builds the native image with the resource bundle AppResource
. The -H:IncludeLocales
option is required.
native-image io.vividcode.graalvm.resources.ResourceBundles -H:IncludeResourceBundles=AppResource \
-H:IncludeLocales=en,zh-CN resource-bundles
When executing the native image with the argument zh CN
, it shows the message of zh-CN
locale.
./resource-bundles zh CN
The locales specified in the -H:IncludeLocales
option must match the locales provided by resource bundles. For example, the resource bundle has the AppResource_zh_CN.properties
file with the zh-CN
locale. The -H:IncludeLocales
option must have the value zh-CN
. Using value zh
doesn't work.
Resource bundles can also be configured using the resource configuration file.
{
"bundles": [
{
"name":"AppResource",
"locales": [
"en",
"zh-CN"
]
}
]
}
The command below shows how to use the resource config file.
native-image io.vividcode.graalvm.resources.ResourceBundles \
-H:ResourceConfigurationFiles=./resource-bundle-config.txt resource-bundles