5

I am parsing strings representing German-style numbers (i.e., decimal comma and optional full stop for grouping thousands), e.g., "2.804,13"; this is just done using a DecimalFormat based on my desired Locale:

package loc_test;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;

public class ParseNumbers {
    static final DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.GERMANY);

    public static void main(String[] args) throws Exception {
        for (String s : new String[] { "2.815,53", "2815,53" }) {
            System.out.println(String.format("%s \t-> %s", s, df.parse(s)));
        }
    }
}

This gives the desired output, e.g., when compiled and run from the command line:

2.815,53        -> 2815.53
2815,53         -> 2815.53

However, when I bundle it as a jpackage image (./gradlew jpackageImage using Gradle and the Badass Runtime Plugin) and run the resulting binary, the output seems to indicate that a wrong locale is used:

2.815,53    -> 2.815
2815,53     -> 281553

I have tried out a few things (in vain), and the problem does not seem to be in the code itself:

  1. Added Locale.setDefault(Locale.GERMANY) in the main. Locale.getDefault() will show the German locale, but it has no effect.
  2. Explicitly set -Duser.country=DE and -Duser.language=de via Java command line parameters. System.getProperty(...) shows the expected values, but again, no effect. (Also, from the command line, it also works with these being set to the system defaults.)
  3. Added -Djava.locale.providers=COMPAT,CLDR,SPI to the jpackage Java command line parameters (from this thread). Again, I can verify from the main that the property is set correctly, but it makes no difference. (And seems to have been an issue with earlier Java versions anyway.)

I have seen the problem first using Java 16 with Gradle 7.2; and it has persisted after an update to Java 17 and Gradle 7.3. The problem has occurred both on an English Linux system and a German Windows machine.

Using --info with the jpackageImage command, Gradle shows me which JDK it is using:

Starting process 'command '.../.gradle/jdks/jdk-17+35/bin/jpackage''. Working directory: .../LocTest/app Command: .../.gradle/jdks/jdk-17+35/bin/jpackage --type app-image --input .../LocTest/app/build/install/app/lib --main-jar app.jar --main-class LocTest.App --dest .../LocTest/app/build/jpackage --name app --runtime-image .../LocTest/app/build/jre --java-options -Duser.country=DE --java-options -Duser.language=de --java-options -Djava.locale.providers=COMPAT,CLDR,SPI

When I use that .gradle/jdks/jdk-17+35/bin/java to run the class, i.e., run:

~/.gradle/jdks/jdk-17+35/bin/java -cp app/build/classes/java/main loc_test.App`

With this, the problem does not occur; the numbers are correct.

However, the problem does occur when I use the (supposedly same) JRE bundled with the jpackage image, i.e., I get the wrong numbers using:

./app/build/jpackage/app/lib/runtime/bin/java -cp app/build/classes/java/main/ loc_test.App

Why would the java, when packaged, ignore the locale? Am I missing something here, or might this be a bug in jpackage or the plugin, or in the Java release itself?

The whole Gradle example project (very small) can be found at Github.

Hans
  • 2,419
  • 2
  • 30
  • 37
  • Have you verified programmatically the version of Java at runtime? – Basil Bourque Nov 17 '21 at 18:10
  • 1
    CLDR is used first by default in modern Java. You are overruling that. Your change may be justified for the sake of troubleshooting, but I want to make sure you’re aware of [*JEP 252: Use CLDR Locale Data by Default*](https://openjdk.java.net/jeps/252). – Basil Bourque Nov 17 '21 at 18:11
  • @BasilBourque, thanks for the pointer - CLDR appears to not be a part of the problem: When I add the correct module as pointed out by the accepted answer, I get the right output with and without setting the locale providers. (So I'll be leaving that out.) – Hans Nov 17 '21 at 18:33

1 Answers1

8

Check what modules are included in your runtime image.

For example when I run java --list-modules on JDK 17 I notice this module:

jdk.localedata

I don't know if it is required for this, but I bet that module isn't being included by jpackage unless it is specifically requested.

Run ./app/build/jpackage/app/lib/runtime/bin/java --list-modules and compare with ~/.gradle/jdks/jdk-17+35/bin/java --list-modules to confirm. Then consider making an image with jlink that includes jdk.localedata if it was missing to test this hypothesis.

swpalmer
  • 3,890
  • 2
  • 23
  • 31
  • Thank you, this was indeed the issue. The modules listing for the jpackage java consisted of the one entry `java.base@17.0.1`. After adding `modules = ['jdk.localedata']` to my `runtime` configuration in `build.gradle`, that shows up as well, and the numbers are parsed correctly. – Hans Nov 17 '21 at 18:27