From 0bd61e232008e3e029e21714646e0bb4b71892f2 Mon Sep 17 00:00:00 2001 From: glaude Date: Sun, 18 Oct 2026 18:15:32 +0200 Subject: [PATCH] Add a real JPMS module descriptor: com.github.zrdj.java.identifiers The published jar has neither a module-info.class nor an Automatic-Module-Name entry, so a named module that requires it gets the name derived from the jar file, "java.identifiers" - a name in the java.* namespace the JDK reserves for platform modules. tibbots/bots wants to use this library from such a named module. Ship module-info.java with the module name matching the root package, exporting both packages and requiring the four f4b6a3 modules transitively, since Ulid, Tsid, Ksuid, UuidNamespace and UuidLocalDomain appear in the public signatures of Identifiers and Codecs. The version moves to 0.3.0; the decision record explains why. Co-Authored-By: Claude Opus 5.5 --- ...le-descriptor-instead-of-automatic-name.md | 90 +++++++++++++++++++ README.md | 2 +- pom.xml | 2 +- src/main/java/module-info.java | 11 +++ 4 files changed, 103 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..509ec32 --- /dev/null +++ b/.docs/project/decisions/2026-10-18-real-module-descriptor-instead-of-automatic-name.md @@ -0,0 +1,90 @@ +--- +type: decision +title: A real module descriptor, not an automatic module name +updated: 2026-10-18 +status: accepted +origin: [] +--- + +## Context + +`tibbots/bots` is cut into named JPMS modules and wants to use this library from one of them. A +named module can only `requires` another named module. An automatic module (one derived from a +plain jar without a descriptor) works there too, but its name is derived from the jar filename, not +chosen by the library. + +Checked directly against the published artifact +(`https://jitpack.io/com/github/zrdj/java-identifiers/0.2.1/java-identifiers-0.2.1.jar`): no +`module-info.class` and no `Automatic-Module-Name` manifest entry. Absent both, the JPMS filename +algorithm turns `java-identifiers-0.2.1.jar` into the automatic module name `java.identifiers` — a +name in the `java.*` namespace the JDK reserves for platform modules. + +The same situation was resolved in `zrdj/java-properties` (pull request #18) for the same consumer. +There it was measured that the reserved name compiles and runs today, but only because the consumer +packages into a single jar and starts from the classpath, so the runtime module system never +resolves the name. + +## Decision + +**Ship a real `module-info.java`, module name `com.github.zrdj.java.identifiers`, matching the root +package.** No `Automatic-Module-Name` fallback: that manifest entry would need the same +non-`java.*` value anyway, and a real descriptor additionally states the module's dependencies and +its public surface explicitly. + +Exported packages are both that exist: + +- `com.github.zrdj.java.identifiers` — `Identifiers`, `Codecs`, `Codec` +- `com.github.zrdj.java.identifiers.codecs` — the `UUIDCodec`, `UlidCodec`, `TsidCodec` and + `KsuidCodec` enums + +Required modules, all four as `requires transitive`: + +- `com.github.f4b6a3.uuid` (uuid-creator 6.1.1) +- `com.github.f4b6a3.ulid` (ulid-creator 5.2.4) +- `com.github.f4b6a3.tsid` (tsid-creator 5.2.6) +- `com.github.f4b6a3.ksuid` (ksuid-creator 4.1.1) + +## Rationale + +- **The trigger is a latent failure, not a current one.** A consumer can use this library from a + named module today; what it cannot do is choose the name it uses. The derived name lands in the + namespace the JDK reserves for itself, and the first module-path start, `jlink` image, or JDK + that tightens the check turns a green build red. No test in either repo would catch that + beforehand, because the descriptor is the only place the name appears. +- **A descriptor is also documentation.** `requires java.identifiers;` in a consumer reads as a + platform module. `requires com.github.zrdj.java.identifiers;` reads as what it is. +- **Both packages are exported.** The `codecs` package holds the implementations behind `Codecs.*`, + and the code reaches them only through that facade, but its enums are `public` and reachable on + the classpath today. Hiding the package would break any consumer that uses `UUIDCodec.Base32` + directly, and this change is meant to remove a latent problem, not to narrow the API. +- **`requires transitive` for all four f4b6a3 modules.** Each of them appears in this library's + public signatures: `Identifiers.Ulid()`, `Identifiers.Tsid()` and `Identifiers.Ksuid()` return + `Ulid`, `Tsid` and `Ksuid`; `Codecs.UlidBase32()`, `Codecs.TsidBase32()` and + `Codecs.KsuidBase62()` return `Codec`, `Codec` and `Codec`; and + `Identifiers.UUIDv2(...)`, `UUIDv3(...)` and `UUIDv5(...)` take `UuidLocalDomain` and + `UuidNamespace` from uuid-creator. A consumer calling any of these must read the module that + declares the type, so the dependency is implied rather than left to every consumer to repeat. +- **The f4b6a3 module names are stable.** None of the four jars ships a `module-info.class`, but + each declares an `Automatic-Module-Name` in its manifest (`com.github.f4b6a3.uuid`, + `com.github.f4b6a3.ulid`, `com.github.f4b6a3.tsid`, `com.github.f4b6a3.ksuid`), checked in the + jars of the versions pinned in `pom.xml`. That is a name chosen by the author, not derived from + the filename. + +## Consequences + +- `pom.xml`'s `` moves from `0.2.0` to `0.3.0`, catching up with the already-tagged + `0.2.1` and going one further because this changes what JPMS consumers write, not a patch. The + README's dependency snippet moves to `0.3.0` as well. +- `maven.compiler.source`/`target` stay at `11`, and no other build configuration changes: + `maven-compiler-plugin` 3.16.0 detects `module-info.java` on its own, and the JUnit 4 tests keep + running on the classpath under the default surefire. +- javac warns `requires transitive directive for an automatic module` for each of the four + `requires transitive` lines. The warning states a fact about the f4b6a3 jars, not a defect here; + it goes away on its own if they ever ship real descriptors under the same names. +- Classpath consumers (an unnamed module, or a plain `` without a `module-info.java` of + its own) are unaffected: a modular jar keeps working unchanged when read from the classpath. +- The f4b6a3 libraries must keep publishing the same module names for this descriptor to resolve. + A dependency upgrade that changes or drops one of them breaks module-path consumers and has to be + checked against the jar manifest before it is merged. +- Consumers already on `0.2.1` keep working. This release removes a latent problem; nothing is + urgent about adopting it. diff --git a/README.md b/README.md index c7e0f87..0629b08 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Release artifact com.github.zrdj java-identifiers - 0.2.1 + 0.3.0 ``` diff --git a/pom.xml b/pom.xml index 9bb17b4..e5e086d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.zrdj java-identifiers - 0.2.0 + 0.3.0 11 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..3850f94 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,11 @@ +// Named explicitly because the name derived from the jar file ("java.identifiers") would land in +// the java.* namespace the JDK reserves for platform modules. +module com.github.zrdj.java.identifiers { + requires transitive com.github.f4b6a3.uuid; + requires transitive com.github.f4b6a3.ulid; + requires transitive com.github.f4b6a3.tsid; + requires transitive com.github.f4b6a3.ksuid; + + exports com.github.zrdj.java.identifiers; + exports com.github.zrdj.java.identifiers.codecs; +}