Skip to main content

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.

info

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.

Read resource content
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.

Resource config
{
"resources": {
"includes": [
{
"pattern": "input.txt"
}
]
}
}

This resource config file is passed to native-image.

Use resource configuration files
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.

Use command line options
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.

warning

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.

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.

Set default locale
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.

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.

Include 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.

warning

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.

Resource bundles
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.

Include resource bundles
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.

Resource bundles config
{
"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