ascheman opened a new issue, #997:
URL: https://github.com/apache/maven-enforcer/issues/997
Follow-up to #995 (the `module-info` module rules: `requireExplicitModules`,
`requireMinimalExports`, `banUnjustifiedOpens`).
## Problem
The Java module system requires every package to belong to exactly one
module. When two modules on the module path
contain the same package, the boot layer refuses to resolve:
```
java.lang.module.ResolutionException: Module a.consumer contains package
com.acme.p,
module a.provider exports package com.acme.p to a.consumer
```
Such a split package is easy to introduce by accident — most often when a
build-time code generator, an unpacked or
shaded dependency, or a copied resource puts classes of a *foreign* package
into the current project's output. The
failure surfaces late (at packaging, `jlink`, or first boot), and the
message describes the symptom rather than the
build step that caused it.
Two further shapes make it worse:
- **Silent variant.** If the project does not `requires` the split module,
the resolver never pulls it in — so it
never complains. The build is green and the application boots, but a type
or an endpoint that was supposed to come
from that module is silently absent (a 404, a late
`LayerInstantiationException`, a `NoClassDefFoundError`).
- **On the classpath.** A split package is legal there and the JVM never
complains at all — but it is precisely what
will break a later migration to the module path.
There is currently no Enforcer rule that catches any of these at build time.
## Proposed rule: `banSplitPackages`
Primary target is the **module path**, but the rule is useful on the
**classpath** too (as a modularization aid), so
it covers both. Bind it after compilation (e.g. `process-classes`, or
`verify`); it fails or warns when a package
produced by the current project also belongs to one of its dependencies.
Detection:
- **Project packages** — enumerate the packages actually present under the
compiled output directory (the
directories that hold `.class` files). This is intentionally based on the
real output, not the compiled
`module-info` descriptor: the offending classes are frequently added
*after* `compile` (generation, copy), and the
descriptor's `ModulePackages` attribute is only finalized at packaging
time.
- **Dependency packages** — resolve the project's dependency artifacts with
`java.lang.module.ModuleFinder`, whose
`ModuleReference.descriptor().packages()` yields the full package set and
transparently handles automatic modules;
fall back to scanning jar entries for plain (non-modular) artifacts.
- **Intersect against the full declared-dependency set**, not only the
modules the project `requires`. This is what
lets the rule catch the **silent variant** that the resolver misses: the
split provider is a real Maven dependency,
so it is in the set even when the module graph would never pull it in.
- **Report** every package owned by more than one place, naming both owners.
The rule handles the Maven 4 module source hierarchy (POM model 4.1.0),
where one project compiles several modules,
each to `${project.build.outputDirectory}/<module-name>/`.
## Severity model
The severity depends on how real the problem is, and is configurable:
| Overlap kind | Default | Rationale |
| --- | --- | --- |
| Project **module** vs. a dependency **module** (incl. the silent,
not-`requires`d case) | **error** | An actual `ResolutionException` (or a
silent missing type) — a real defect. |
| Any overlap involving a **non-modular / classpath** artifact | **warn** |
Legal on the classpath today; matters only for a future move to modules — flag
it, don't fail. |
The classpath check is **on by default at `warn`** (at least a warning is
emitted); set `classpathSeverity=ignore`
to opt out entirely, or `error` to enforce module-readiness strictly. The
module-path/severity defaults can still be
lowered globally through the plugin's per-rule `<level>`.
## Configuration
| Parameter | Default | Meaning |
| --- | --- | --- |
| `classpathSeverity` | `warn` | Severity for overlaps involving non-modular
artifacts: `warn`, `error`, or `ignore` (opt-out). Module-vs-module overlaps
are always errors. |
| `allowedSplitPackages` | – | Packages permitted to overlap (escape hatch).
|
| `ignoredModules` / `ignoredArtifacts` | – | Dependencies to exclude from
the check. |
| `message` | – | Custom failure message. |
## Relationship to existing rules
- Complements MojoHaus `banDuplicateClasses` (the classpath
duplicate-*class* check); this rule is the
*package*-level counterpart and is module-aware. The real classpath bug is
a duplicate class (handled there); a
split package *without* a duplicate class is the module-migration hazard
this rule surfaces.
- Sibling of the `module-info` rules proposed in #995
(`requireExplicitModules`, `requireMinimalExports`,
`banUnjustifiedOpens`) and can reuse their `module-info` reader.
## Notes and non-goals
- A guard is a **safety net, not a fix**: the durable cure is placing
generated or copied classes in the module that
owns their package.
- Only **declared dependencies** are inspected. A provider pulled in purely
at run time (not a Maven dependency)
cannot be seen at build time and is out of scope; an
`additionalModulePath` / `scanArtifacts` parameter could let
advanced users feed such paths in.
- On a large legacy classpath the `warn` default may be chatty;
`allowedSplitPackages` / `ignoredArtifacts` /
`classpathSeverity=ignore` are the escape hatches.
## Motivation
This surfaced from a real multi-module modular application: a code generator
wrote classes into a consumer module's
output, producing a split package that failed `jlink` and boot — and, in one
configuration, **silently** returned
404 because the affected module was never resolved. Write-up:
<https://vidocq.dev/posts/multi-module-jpms-apps-on-vidocq-one-opens-one-split-package-and-a-fix/>.
I'm happy to contribute the implementation.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]