From ecb4adc486df090ca4310a7ee67553f5556e864f Mon Sep 17 00:00:00 2001 From: glaude Date: Sun, 18 Oct 2026 18:19:54 +0200 Subject: [PATCH 1/2] Add a real JPMS module descriptor: com.github.zrdj.java.predicates Without a module-info.class or an Automatic-Module-Name entry, consumers on the module path get the automatic name derived from the jar file name, "java.predicates", which lies in the java.* namespace the JDK reserves for platform modules. It works today only because classpath starts never resolve the name. Add module-info.java naming the module after its root package and exporting all five packages, since every one of them is reachable on the classpath today. The library has no non-test dependencies, so there is no requires clause. The compiler plugin picks up the descriptor without further build configuration; bump the version to 0.6.0 in pom.xml and the README, and record the decision. Co-Authored-By: Claude Opus 5.5 --- ...le-descriptor-instead-of-automatic-name.md | 65 +++++++++++++++++++ README.md | 2 +- pom.xml | 2 +- src/main/java/module-info.java | 9 +++ 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 .docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md create mode 100644 src/main/java/module-info.java diff --git a/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md b/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md new file mode 100644 index 0000000..f55a1b4 --- /dev/null +++ b/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md @@ -0,0 +1,65 @@ +--- +type: decision +title: A real module descriptor, not an automatic module name +updated: 2026-10-18 +status: accepted +origin: [] +--- + +## Context + +`tibbots/bots` wants to use the zrdj libraries from its named JPMS modules, so all of them get a +real module descriptor in one sweep; `java-properties` went first +(`zrdj/java-properties`, pull request #18), and this repo follows the same model. A named module can +only `requires` another named module. A plain jar without a descriptor still works there as an +automatic module, but its name is derived from the jar file name instead of being chosen. + +Until now this library shipped no `module-info.class` and no `Automatic-Module-Name` manifest entry. +Absent both, the JPMS file name algorithm turns `java-predicates-0.5.2.jar` into the automatic module +name `java.predicates` — a name in the `java.*` namespace the JDK reserves for platform modules. Such +a name compiles and runs today as long as the consumer starts from the classpath, where the runtime +module system never resolves it; it is a latent failure, not a current one. + +## Decision + +**Ship a real `module-info.java`, module name `com.github.zrdj.java.predicates`, matching the root +package.** No `Automatic-Module-Name` fallback: that entry would need the same non-`java.*` value +anyway, and a real descriptor additionally states the module's dependencies and its public surface +explicitly. + +The module has no `requires` clause beyond the implicit `java.base`: the only dependencies in +`pom.xml` are JUnit 4 and AssertJ, both in test scope, and no main source file imports anything +outside `java.*` and the library's own packages. + +All five packages are exported: + +- `com.github.zrdj.java.predicates` — `Predicates`, the fluent entry point +- `com.github.zrdj.java.predicates.collections` — `ArrayPredicates`, `CollectionPredicates` +- `com.github.zrdj.java.predicates.helper` — `Action`, `Throws` +- `com.github.zrdj.java.predicates.objects` — `ObjectPredicates` +- `com.github.zrdj.java.predicates.primitives` — `IntegerPredicates`, `StringPredicates` + +## Rationale + +- **The derived name sits in a reserved namespace.** The first module-path start, `jlink` image or + JDK that tightens the check would turn a consumer's green build red, and no test in either repo + would catch it beforehand, because the descriptor is the only place the name appears. +- **A descriptor is also documentation.** `requires java.predicates;` in a consumer reads as a + platform module; `requires com.github.zrdj.java.predicates;` reads as what it is. +- **Every package is exported, none held back.** Today every package is reachable on the classpath, + and its classes are public. The predicate packages are returned from the `Predicates` entry point, + and even `helper`, which looks internal, holds public types (`Action`, `Throws`) that some consumer + may already use directly. Hiding a package would break such a consumer as soon as it moves to the + module path, for no gain this decision needs. + +## Consequences + +- `pom.xml`'s `` moves from `0.5.2` to `0.6.0`, a minor rather than a patch step because + this changes how JPMS consumers name the library. The README's dependency snippet follows to + `0.6.0`. +- `maven.compiler.source`/`target` stay at `11`. JPMS exists since Java 9, and `maven-compiler-plugin` + 3.16.0 detects `module-info.java` and compiles it without ``; the tests, which share the + root package, compile and run unchanged. No build configuration change beyond the version bump. +- Classpath consumers are unaffected: a modular jar read from the classpath behaves as before. +- Consumers on `0.5.2` keep working. This release removes a latent problem; nothing about adopting + it is urgent. diff --git a/README.md b/README.md index 12c6fd2..98c4415 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Release artifact com.github.zrdj java-predicates - 0.5.2 + 0.6.0 ``` diff --git a/pom.xml b/pom.xml index 1ad6e32..7b9f20c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.zrdj java-predicates - 0.5.2 + 0.6.0 jar java-predicates diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..a41f48e --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,9 @@ +// The name javac would derive from the jar file name, "java.predicates", lies in the java.* +// namespace the JDK reserves for platform modules, so the module names itself after its root package. +module com.github.zrdj.java.predicates { + exports com.github.zrdj.java.predicates; + exports com.github.zrdj.java.predicates.collections; + exports com.github.zrdj.java.predicates.helper; + exports com.github.zrdj.java.predicates.objects; + exports com.github.zrdj.java.predicates.primitives; +} From e6bd5d584c83745ca4d4151cc35b4415537ad689 Mon Sep 17 00:00:00 2001 From: glaude Date: Sun, 18 Oct 2026 18:23:22 +0200 Subject: [PATCH 2/2] Pin surefire 3.6.0 and open the test-only package on the module path With module-info.java present, surefire 3.x (what CI's Maven resolves) runs the tests on the module path and patches the test classes into the module. The test-only package com.github.zrdj.java.predicates.testcases, which holds the inherited @Test methods, is not exported, so JUnit 4 in the unnamed module failed with InaccessibleObjectException for five test classes. The dev container's Maven defaulted to surefire 2.12.4, which runs on the classpath, so the local build stayed green. Pin maven-surefire-plugin to 3.6.0 so local and CI run the same way, and open the testcases package to ALL-UNNAMED via argLine. The ADR records the pin and its reason. Co-Authored-By: Claude Opus 5.5 --- ...real-module-descriptor-instead-of-automatic-name.md | 7 +++++-- pom.xml | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md b/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md index f55a1b4..b78ab2e 100644 --- a/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md +++ b/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md @@ -58,8 +58,11 @@ All five packages are exported: this changes how JPMS consumers name the library. The README's dependency snippet follows to `0.6.0`. - `maven.compiler.source`/`target` stay at `11`. JPMS exists since Java 9, and `maven-compiler-plugin` - 3.16.0 detects `module-info.java` and compiles it without ``; the tests, which share the - root package, compile and run unchanged. No build configuration change beyond the version bump. + 3.16.0 detects `module-info.java` and compiles it without ``. +- `maven-surefire-plugin` is pinned to 3.6.0, the version CI's Maven resolves, because with a + descriptor surefire 3.x runs the tests on the module path while the dev container's default 2.12.4 + ran them on the classpath and so hid the failure; on the module path the test-only package + `testcases` is opened to JUnit 4 via `--add-opens` in surefire's `argLine`. - Classpath consumers are unaffected: a modular jar read from the classpath behaves as before. - Consumers on `0.5.2` keep working. This release removes a latent problem; nothing about adopting it is urgent. diff --git a/pom.xml b/pom.xml index 7b9f20c..c30460c 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,16 @@ ${maven.compiler.target} + + org.apache.maven.plugins + maven-surefire-plugin + 3.6.0 + + + --add-opens com.github.zrdj.java.predicates/com.github.zrdj.java.predicates.testcases=ALL-UNNAMED + + org.apache.maven.plugins