Skip to content

JPMS (Java modules) incompatibility: optional dependencies break compilation for modular consumers #770

@LarsEckart

Description

@LarsEckart

when investigating #768 my genie came to a conclusion and I figured to rather create a new top lvl issue for this, just for the java modules topic.

Also create a reproducer example at https://github.com/approvals/ApprovalTests.java.StarterProject/tree/reproduce-jpms-issue-770

Summary

When a consumer project uses Java Platform Module System (JPMS) via module-info.java, compilation fails because the module system eagerly resolves all types referenced by all classes in the approvaltests jar — including types from optional dependencies that are not on the module path.

This is broader than #768 (which reports the gson-specific symptom). Every optional dependency that is referenced in a class signature causes problems for JPMS consumers.

Reproduction

Add a module-info.java to a consumer project that depends on approvaltests (without explicitly adding gson):

module myproject {
    exports org.samples;
}

Compile → fails with:

package com.google.gson does not exist
  cannot find symbol: class GsonBuilder
  location: class org.approvaltests.JsonApprovals

package java.sql is not visible
  (package java.sql is declared in module java.sql, which is not in the module graph)

reference to verify is ambiguous
  both method verify(java.lang.Object) and method verify(ResultSet) match

Root Cause

In 2018 (commit 2e3bbfd), all dependencies were marked <optional>true</optional>. This works fine for classpath-based consumers (types are lazily resolved), but JPMS forces full upfront resolution of every type in the module graph.

Affected optional dependencies include at least:

  • gsonJsonApprovals, JsonUtils expose GsonBuilder in public API
  • java.sqlApprovals.verify(ResultSet) causes ambiguous overloads when java.sql module is not in the graph
  • Likely also: xstream, jackson, velocity, commons-net, quartz, servlet-api, xom

Real-World Impact

This affects chicory (a JPMS project) and likely any other modular Java project.
Chicory already has workarounds in their build: --add-modules java.sql, --add-modules java.desktop, and a Java 11 profile that disables the module path entirely.

Options to Discuss

  1. Remove <optional> from core deps (gson, java.sql-related) — simplest but adds transitive deps for all consumers
  2. Separate modules (e.g. approvaltests-gson, approvaltests-jdbc) — clean but breaking change
  3. Hide optional types from public API — remove GsonBuilder/ResultSet etc. from method signatures, use reflection internally
  4. Add module-info.java to approvaltests with requires static for optional deps — proper JPMS support but requires moving off Java 8 source level
  5. Combination — e.g. move optional-dep classes to sub-modules + add module-info

Needs discussion with maintainers before picking an approach.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions