Skip to content

Make profile activation conditions locale-independent - #12735

Open
SEPURI-SAI-KRISHNA wants to merge 1 commit into
apache:masterfrom
SEPURI-SAI-KRISHNA:fix-condition-locale
Open

Make profile activation conditions locale-independent#12735
SEPURI-SAI-KRISHNA wants to merge 1 commit into
apache:masterfrom
SEPURI-SAI-KRISHNA:fix-condition-locale

Conversation

@SEPURI-SAI-KRISHNA

Copy link
Copy Markdown

What

Uses Locale.ROOT in the three places where profile activation condition evaluation was
implicitly relying on the JVM default locale:

  • ConditionFunctions.upper(..)toUpperCase()toUpperCase(Locale.ROOT)
  • ConditionFunctions.lower(..)toLowerCase()toLowerCase(Locale.ROOT)
  • ConditionParser.toString(..)String.format("%.0f", ..)String.format(Locale.ROOT, "%.0f", ..)

Why

Profile activation should not depend on the locale of the machine running the build, but today
it does. Turkish and Azeri map i to the dotted İ and I to the dotless ı, so this profile:

<profile>
  <activation>
    <condition>upper(${os.name}) == 'WINDOWS'</condition>
  </activation>
</profile>

silently fails to activate on a JVM started with -Duser.language=tr, because upper('windows')
returns WİNDOWS. Reproduction:

$ jshell -R-Duser.language=tr
jshell> "windows".toUpperCase()
$1 ==> "WİNDOWS"
jshell> "LINUX".toLowerCase()
$2 ==> "lınux"

ConditionParser.toString(Object) has the same class of problem: it formats whole doubles with
String.format("%.0f", ..) and no Locale, so under a locale whose default numbering system is
not latin (e.g. hi-IN-u-nu-deva) it renders 42 as ४२.

This brings the three call sites in line with the rest of the codebase, which already passes an
explicit Locale in ~41 places — including ExecutableFinder, in this same package.

Why the existing tests missed it

Every string the current tests convert (hello, WORLD, success, HELLO WORLD) happens to
contain no i or I, so ConditionParserTest passes unchanged under -Duser.language=tr.

Tests

Two tests added to ConditionParserTest, both of which fail without the production change:

  • testCaseConversionFunctionsAreLocaleIndependent — exercises upper/lower under en and tr
  • testNumberFormattingIsLocaleIndependent — exercises number rendering under hi-IN-u-nu-deva

They follow the existing save/restore pattern from
VersionTest.testCaseInsensitiveOrderingOfQualifiersIsLocaleIndependent in the same module.

Reverting only the production change and re-running gives:

[ERROR] ConditionParserTest.testCaseConversionFunctionsAreLocaleIndependent
        upper() in tr ==> expected: <WINDOWS> but was: <WİNDOWS>
[ERROR] ConditionParserTest.testNumberFormattingIsLocaleIndependent
        expected: <42> but was: <४२>

With the change, the full maven-impl module suite passes: 552 tests, 0 failures, checkstyle /
spotless / rat clean.

If this is wanted on maven-4.0.x as well, happy for it to be backported.


PR checklist

The template ships with this checklist. Tick these:

  • Your pull request should address just one issue, without pulling in other changes.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied.
  • Run mvn verify to make sure basic checks pass.
  • I hereby declare this contribution to be licenced under the Apache License Version 2.0, January 2004

Leave UNTICKED unless you actually run it (see note below):

  • You have run the Core IT successfully.

The ICLA line can stay unticked — the production change is ~20 lines, under the template's
"~20 lines of code" threshold.

upper() and lower() called String.toUpperCase()/toLowerCase() without a
Locale, and ConditionParser.toString() formatted whole doubles with
String.format() without a Locale. All three therefore depended on the
default locale of the machine running the build, so the same POM could
activate different profiles on different machines.

Turkish maps i to the dotted capital İ, so upper('windows') returns
WİNDOWS and a condition such as upper(${os.name}) == 'WINDOWS' silently
fails to activate. Under a locale whose default numbering system is not
latin, such as hi-IN-u-nu-deva, toString(42.0) returns ४२ rather than
42.

Use Locale.ROOT in all three places, matching ExecutableFinder in the
same package and the other explicit-Locale call sites in the codebase.

The existing tests missed this because none of the strings they convert
contain an i or I. Add tests pinning the behaviour under tr and
hi-IN-u-nu-deva; both fail without the production change.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-scoped, correct fix for a real locale-sensitivity bug in profile activation condition evaluation. All three locale-sensitive call sites are properly fixed (upper() and lower() in ConditionFunctions.java, toString() in ConditionParser.java).

The tests are thorough and follow the established save/restore pattern from VersionTest.testCaseInsensitiveOrderingOfQualifiersIsLocaleIndependent. Good choice of test inputs — strings containing i/I (affected by Turkish locale case mapping) and a locale with Devanagari numbering for the number formatting test.

Minor nit: new Locale("tr") is deprecated since Java 19 in favor of Locale.of("tr"), but the existing codebase uses the same deprecated constructor in VersionTest.java, so this is consistent.

Excellent PR description with reproduction steps and explanation of why existing tests missed the bug.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of gnodet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants